The MVP pass criteria include pinging a LAN host from the Windows TAP address.
The relay integration suite already proved generic Ethernet forwarding and ARP
traffic, but did not explicitly exercise ICMPv4 echo-shaped frames on the real
client/relay/gateway session path.
Add a session test that sends an IPv4 ICMP echo-request-shaped frame from the
client peer toward a LAN-host MAC and an echo-reply-shaped frame from the
gateway side back to the client MAC. The packets include normal IPv4 and ICMP
checksums, so the local proof maps more directly to the manual ping test while
still staying independent of real Windows TAP and LAN hardware.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay \
bridges_icmpv4_ping_frames_between_client_and_gateway_sessions -- --nocapture
- cargo test -p lanparty-relay
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: MVP ping pass condition
The MVP pass criteria include ARP from the Windows TAP side to a LAN host. The
real-session relay integration tests proved generic Ethernet forwarding, but
that did not explicitly exercise ARP-shaped traffic.
Add a real client/relay/gateway session test that sends an ARP request-shaped
broadcast frame from the client to the gateway and an ARP reply-shaped unicast
frame from the gateway back to the client. The test keeps the proof at the L2
boundary while staying independent of a real Windows TAP adapter or LAN host.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay \
bridges_arp_frames_between_client_and_gateway_sessions -- --nocapture
- cargo test -p lanparty-relay
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: MVP ARP pass condition
The manual lifecycle check depends on the Windows client diagnostics moving
from connected, to disconnected, and back to connected when the LAN gateway is
restarted. Existing formatter tests covered a gateway leave, but not the
replacement join updating the tracked gateway peer id afterward.
Add a client lifecycle unit test that formats the first gateway join, its leave,
and a replacement gateway join. The assertions prove the client-side relay
status clears the old peer id and then reports the new gateway as connected.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win \
tracks_gateway_replacement_after_disconnect -- --nocapture
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: MVP lifecycle sanity check
TESTING.md asks for a lifecycle sanity check where the Windows client stays
connected while the LAN gateway restarts. The relay integration tests covered a
single gateway/client lifetime, but they did not prove that a replacement
gateway receives the existing client catch-up state and can bridge frames after
the first gateway leaves.
Add a real-session relay test that connects a gateway and client, shuts down the
first gateway, verifies the client receives the gateway leave event, connects a
second gateway, verifies both sides receive the expected lifecycle state, and
then sends Ethernet frames in both directions through the replacement gateway.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay reconnects_gateway_while_client_stays_joined \
-- --nocapture
- cargo test -p lanparty-relay
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: MVP lifecycle sanity check
The manual MVP proof asks the tester to compare the Windows client output with
TESTING.md. That block had the right concepts, but the order did not match the
current startup path after TAP route protection moved earlier in the flow.
Update the expected client output to include the connect, TAP-open, interface,
bridge, and lifecycle-event lines in the order the binary emits them. Also call
out the expected MTU, metric, and default-route protection lines so they are not
mistaken for surprising output during the Windows handoff run.
Test Plan:
- cargo test -p lanparty-client-win
- git diff --check
Refs: MVP Windows client handoff
The manual MVP run depends on copying the generated development certificate
from the relay host to both the gateway and Windows client. The relay already
wrote the file, but the startup log only said that the relay was listening.
Print the configured development certificate path after a successful bind/write
and align TESTING.md's expected relay output with that line. This gives the
tester an explicit file path to copy before starting the gateway and client.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay
- timeout 2s cargo run -q -p lanparty-relay -- --listen 127.0.0.1:0
--dev-cert-der-out <tmp-cert>
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
Refs: MVP relay certificate handoff
The MVP test guide already asks the tester to verify that the MSVC tools are
available, but the full Windows client build depends on native C tooling through
its dependency stack. Make the expected shell explicit so a missing `cl`, `lib`,
or `link` failure points directly to Visual Studio Build Tools setup instead of
looking like a Rust or project error.
Test Plan:
- git diff --check
Refs: MVP Windows client build handoff
The MVP depends on the Windows TAP adapter receiving DHCP from the real LAN,
while still preventing a remote client from pretending to be a DHCP server.
The lower-level safety classifiers already encode that direction split, but the
client relay boundary did not test both sides together.
Extend the client-core QUIC session test with a LAN-side DHCPv4 server reply
that must be accepted toward TAP, and a remote-client DHCPv4 server reply that
must be dropped before relay send. This keeps the critical DHCP path covered at
the same layer that records client stats and feeds the Windows TAP frame pump.
Test Plan:
- cargo fmt
- cargo test -p lanparty-client-core
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
Refs: MVP DHCP tunnel acceptance
The Windows client now fails fast outside Windows before tunnel setup because
its real MVP runtime depends on TAP-Windows6 and Windows route protection. The
old non-Windows runtime branch still built a control-event loop and printed
that TAP pumping was not wired yet, which described an obsolete smoke-test path
rather than current behavior.
Replace that branch with an unreachable guard and compile the related runtime
formatting/status helpers only for Windows or unit tests. Non-Windows builds
still type-check the client, but the runtime behavior is now only the explicit
unsupported-platform error.
Test Plan:
- cargo fmt
- cargo test -p lanparty-client-win
- cargo clippy --workspace --all-targets -- -D warnings
- cargo test --workspace
Refs: MVP Windows client runtime gate
The Windows client now reports runtime errors in its graceful disconnect
message so relay logs can show why a client stopped. That message comes from
an anyhow error chain, so keep it useful but bounded before sending it over the
control stream and using it as the QUIC close reason.
Truncate client shutdown messages to 1024 bytes, preserving UTF-8 boundaries
and adding an ellipsis when truncation happens. Normal shutdown text and small
error chains are unchanged.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
Refs: MVP Windows client diagnostics hardening
The Windows client sends a best-effort disconnect control message after the
runtime loop exits. That message previously always said `client shutting down`,
even when the loop ended because TAP I/O, relay event handling, or relay-route
verification failed.
Preserve the returned error, but include its chain in the disconnect message so
the relay log records why the client left during manual MVP testing. Clean exits
still report the original shutdown text.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP Windows client diagnostics
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
The manual MVP proof depends on a real Windows build environment and an
installed TAP-Windows6 adapter. Make the test guide catch those prerequisites
before the operator starts the relay/gateway/client flow.
Add `where.exe link` alongside the existing MSVC tool checks, and add a short
post-build smoke check for `--help` and `--list-tap-adapters`. This keeps the
handoff focused while making missing linker or TAP setup failures show up before
any room is joined.
Test Plan:
- cargo fmt --check
- git diff --check
- git diff --cached --check
Refs: MVP Windows E2E handoff
The MVP client only has a real TAP and route-protection runtime on Windows. A
non-Windows build was able to connect to the relay as a no-TAP client and then
wait for control events, which could consume a room slot while giving the false
impression that the Windows client path had been exercised.
Reject normal runtime startup on non-Windows platforms before reading tunnel
inputs or joining the relay. Keep non-Windows builds useful for type checking,
and document that they are not runnable tunnel clients.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win
- cargo check -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo run -p lanparty-client-win -- --relay 127.0.0.1:1 \
--relay-ca-cert /tmp/does-not-matter.der --room ROOM1
- git diff --check
- git diff --cached --check
Refs: MVP Windows TAP client boundary
After a peer is registered, later accepted-handshake failures clean up room and
session state. If the welcome was already sent, the joining peer could otherwise
keep a live QUIC connection briefly while the relay has removed it from room
membership.
Close the connection after accepted-handshake cleanup so the peer learns the
handshake failed instead of continuing with stale local state.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP lifecycle ordering
Catch-up lifecycle events to the newly joined peer are part of the accepted
handshake. They seed the peer's view of room membership before the rest of the
room can rely on that peer being ready.
The gateway-join ordering already sends the current client list to a new
gateway before notifying clients that a gateway is available. Make that delivery
mandatory: if the relay cannot send the catch-up event stream, clean up the
accepted peer and fail the handshake instead of publishing an incompletely
seeded gateway. The same required catch-up behavior applies to newly joined
clients after their welcome.
Existing-peer join/leave notifications remain best effort; a stale existing
peer should not prevent the relay from accepting healthy joins.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP lifecycle ordering
Update the lifecycle sanity check to match the client output shape. Gateway
state changes are reflected in the periodic `client diagnostics:` line, and the
user-facing missing-gateway diagnostic is printed as a warning.
This keeps the manual Windows E2E guide from asking the tester to find exact
standalone lines that the current client does not emit.
Test Plan:
- git diff --check
- git diff --cached --check
Refs: MVP E2E handoff
Make the manual MVP proof easier to run without adding product fluff. Add a
short fast path, explicit log-capture commands, the expected release binary
paths, an extra Windows route-table check, and the relay-route pin disposition
to the report-back checklist.
The test guide remains centered on the real acceptance criteria: DHCP over the
TAP tunnel, TAP-sourced ping to a LAN host, switch MAC learning on the gateway
port, LAN-game discovery or join, and a gateway restart sanity check.
Test Plan:
- git diff --check
- git diff --cached --check
Refs: MVP E2E handoff
The relay used one accepted-peer handshake order for clients and gateways:
register the peer, notify existing peers, send the welcome, then send catch-up
events to the new peer. That is correct for a client joining an existing
gateway because the gateway must learn the client's MAC before the client can
send frames.
The same ordering is risky for a gateway joining a room that already has
clients. Existing clients could learn that the gateway is available before the
gateway has received the existing client MAC table. Reverse that part of the
sequence for gateways: send the gateway welcome, send existing peer catch-up
events to the gateway, then notify clients that the gateway joined.
Keep client joins on the old ordering, and keep handshake-failure cleanup quiet
for gateway welcome failures because clients have not been notified yet.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay
- cargo clippy -p lanparty-relay --all-targets -- -D warnings
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP gateway lifecycle ordering
The Windows client reads TAP frames and then applies the same local safety path
used for malformed, jumbo, over-MTU, and forbidden remote-client traffic. The
read buffer was only one standard Ethernet frame long, which made an oversized
TAP frame more likely to fail at the device-read boundary before the client
could count and log the drop.
Keep the accepted Ethernet-frame limit unchanged, but make the TAP read buffer
large enough to receive oversized frames and let the existing safety checks
reject them in the normal path. Document the distinction in the TAP crate
summary.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-tap -p lanparty-client-win
- cargo check -p lanparty-client-tap --target x86_64-pc-windows-msvc
- cargo clippy -p lanparty-client-tap -p lanparty-client-win \
--all-targets -- -D warnings
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP TAP drop diagnostics
The Windows client relies on scoped guards to restore TAP MTU, interface metric,
default-route policy, and the relay host route when the client exits. Those
cleanups are still best-effort because they run from Drop, but silently ignoring
errors makes the manual MVP test harder to recover from if Windows refuses one
of the restore operations.
Log cleanup failures with the interface family, index, LUID, or relay route
identity involved. Successful cleanup remains quiet, and the guard ownership
model is unchanged.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-route
- cargo check -p lanparty-client-route --target x86_64-pc-windows-msvc
- cargo clippy -p lanparty-client-route --all-targets -- -D warnings
- cargo clippy -p lanparty-client-route --target x86_64-pc-windows-msvc \
-- -D warnings
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP Windows cleanup diagnostics
The Windows client already prepared TAP before relay resolution, but it still
opened the QUIC control connection before installing the relay host route. TAP
was not active yet, so this was not the self-routing failure mode, but it left
the relay flow unpinned during the control handshake.
Resolve the relay, pin and verify the relay host route, then open the relay
connection. Keep the after-TAP verification as the guard against DHCP or TAP
route policy taking over the relay path. Update README.md and TESTING.md so the
MVP startup description and expected log order match that safer flow.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check -p lanparty-client-route --target x86_64-pc-windows-msvc
- git diff --check
- git diff --cached --check
Windows-target check attempted:
- cargo check -p lanparty-client-win --target x86_64-pc-windows-msvc
With LLVM tools configured, that still stops inside ring on this Linux host
because the Windows C headers are unavailable, starting with assert.h.
Refs: MVP relay-route protection
The Windows client pinned the relay host route before opening TAP, but only
verified that Windows was actually using that host route after TAP activation.
If an existing route or pinning failure prevented the host route from becoming
the active relay path, the client could discover that only after touching TAP
route policy.
Verify the pinned host route immediately after installing/reusing it, before
opening the TAP adapter. Keep the existing after-activation verification as the
runtime guard against TAP route takeover, and document the new expected startup
log line in the MVP guide.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check -p lanparty-client-route --target x86_64-pc-windows-msvc
- git diff --check
- git diff --cached --check
Windows-target check attempted:
- cargo check -p lanparty-client-win --target x86_64-pc-windows-msvc
With LLVM tools configured, that still stops inside ring on this Linux host
because the Windows C headers are unavailable, starting with assert.h.
Refs: MVP relay-route protection
The Windows client protects the relay connection by pinning a host route before
activating TAP, then checking that the best route still matches that pinned
host route after TAP route policy changes. That predicate is part of the route
boundary, not the Windows binary's frame-pump logic.
Move the exact match check onto RouteSnapshot and cover the important mismatch
cases: default-route fallback, wrong next hop, wrong interface index/LUID, and
IPv6 on-link host routes. The Windows client keeps the same behavior but calls
the route-crate helper.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-route matches_pinned_host_route_identity
- cargo test -p lanparty-client-route matches_ipv6_on_link_pinned_host_route
- cargo test -p lanparty-client-route
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy -p lanparty-client-route --all-targets -- -D warnings
- cargo clippy -p lanparty-client-win --all-targets -- -D warnings
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check -p lanparty-client-route --target x86_64-pc-windows-msvc
- git diff --check
- git diff --cached --check
Refs: MVP relay-route protection
Windows MVP debugging needs more than aggregate drop counters when LAN traffic
reaches the client but is kept out of the TAP adapter. A DHCP or discovery
failure is much easier to diagnose when the client log says which relayed frame
was filtered and why.
Expose a client receive outcome that preserves the existing accepted-frame API
while allowing the Windows frame pump to log filtered RelayToTap frames with
the source peer and drop reason. Document the new log signal in the README and
manual MVP test guide.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-core connects_to_relay_control_stream_as_client
- cargo test -p lanparty-client-win formats_client_frame_log_lines
- cargo test -p lanparty-client-core
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy -p lanparty-client-core --all-targets -- -D warnings
- cargo clippy -p lanparty-client-win --all-targets -- -D warnings
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Windows-target check attempted:
- cargo check -p lanparty-client-win --target x86_64-pc-windows-msvc
The Windows-target check is still blocked on this Linux host before compiling
lanparty-client-win because ring cannot find the MSVC lib.exe tool.
Refs: MVP client diagnostics
The Windows TAP receive path depends on accepting relay frames that are
addressed to the client virtual MAC as well as broadcast and multicast LAN
traffic. That is what carries DHCP, ARP, and LAN discovery back into the TAP
adapter.
Add a focused client-core test for the destination classifier so unrelated
unicast stays dropped while directed, broadcast, and multicast LAN frames
continue to be accepted.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-core accepts_lan_destinations_for_client_tap
- cargo test -p lanparty-client-core
- cargo clippy -p lanparty-client-core --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP TAP receive validation
The MVP success path depends on the Windows TAP adapter getting an IPv4 DHCP
lease from the physical LAN. The relay already allowed remote DHCP client
requests and filtered remote DHCP server replies, but only the DHCPv6 client
request path had explicit coverage.
Add a focused relay room test for a DHCPv4 client request from the remote client
to the LAN gateway. This keeps the most important DHCP path documented in the
same forwarding tests as the safety filters.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay allows_remote_dhcpv4_client_requests
- cargo test -p lanparty-relay
- cargo clippy -p lanparty-relay --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP DHCP validation
The relay welcome now carries the gateway peer id, but the first Windows client
startup line still only showed whether a gateway was connected. Surface the peer
id there when it is known so manual MVP testers can record it even before or
without relying on a later lifecycle catch-up line.
Keep the older yes/no text when a gateway is connected but the peer id is not
available, which preserves useful output for older welcome payloads.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win gateway_status
- cargo clippy -p lanparty-client-win --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP manual validation
Refresh the manual MVP test guide around the current relay, gateway, and
Windows client behavior. The guide now calls out the values to record during a
run, the exact process order, expected gateway lifecycle signals, and a short
gateway restart sanity check.
Also update the README component notes so the control plane and Windows client
docs mention the gateway peer id carried in welcomes.
Test Plan:
- git diff --check
- git diff --cached --check
Refs: MVP manual validation
The client initialized gateway connectivity from ServerWelcome, but welcome only
exposed a boolean. If a gateway disconnected before the client saw the catch-up
PeerJoined event, the later unknown PeerLeft could not be tied to the gateway
and the status could stay connected.
Carry an optional gateway peer id in ServerWelcome. The relay fills it from the
joining gateway or the existing room gateway, and the Windows client stores it
so a matching unknown PeerLeft clears gateway connectivity. The boolean remains
for wire compatibility with older welcomes that do not carry the id.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-ctrl server_welcome
- cargo test -p lanparty-relay accepts_gateway_and_client_into_room
- cargo test -p lanparty-relay reports_missing_gateway_to_client_joining_first
- cargo test -p lanparty-client-win relay_lifecycle
- cargo test -p lanparty-client-win \
clears_gateway_status_when_welcome_gateway_leaves_before_join_event
- cargo test -p lanparty-relay bridges_real_client_and_gateway_sessions
- cargo test -p lanparty-client-core connects_to_relay_control_stream_as_client
- 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 lifecycle cleanup
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
The MVP test guide relies on --iface being both accepted and discoverable from
lanparty-gateway --help. The parser test already covered acceptance, but it did
not protect the rendered help text.
Render the Clap long help in a unit test and assert that the visible alias is
advertised. This keeps the handoff guide and binary self-documentation aligned.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-gateway iface_alias
- cargo run -p lanparty-gateway -- --help
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP gateway test handoff
The MVP test guide tells operators that --iface is accepted as a shorter alias
for --interface, but Clap hid the alias from lanparty-gateway --help. Make the
alias visible so the binary's self-documentation matches the handoff guide.
This does not change parsing behavior; --iface was already accepted and covered
by the existing gateway parser test.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-gateway accepts_iface_alias_for_gateway_interface
- cargo run -p lanparty-gateway -- --help
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP gateway test handoff
The client previously reported "Broadcast traffic flowing" as soon as either
broadcast TX or RX was nonzero. During the MVP DHCP/ARP proof, one-way
broadcast is useful but weaker evidence than bidirectional broadcast.
Keep the existing healthy message for two-way broadcast, but report
outbound-only broadcast as a warning that the client is still waiting for a LAN
broadcast reply, and report inbound-only broadcast separately. This makes the
Windows client status lines more precise when DHCP is stuck.
Test Plan:
- cargo test -p lanparty-obs
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP Windows DHCP diagnostics
The relay and gateway already emit structured frame logs, but the Windows
client only exposed aggregate counters. During the MVP end-to-end test that
left a blind spot between TAP reads/writes and the relay datagram path.
Add client-side frame log lines for accepted TAP-to-relay sends,
relay-to-TAP writes, and local TAP-frame drops before relay send. The logs use
the shared FrameLog vocabulary with TapToRelay and RelayToTap directions so the
client, relay, and gateway logs can be correlated during DHCP, ARP, ping, and
LAN-game discovery checks.
Test Plan:
- cargo test -p lanparty-client-win formats_client_frame_log_lines
- cargo test -p lanparty-client-win
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP Windows client diagnostics
The MVP test guide asked for a Windows release build but did not make the MSVC
native-toolchain dependency explicit. The client dependency stack includes
native code, so a fresh Windows test machine needs the Rust MSVC host and C++
Build Tools available before cargo can build lanparty-client-win.
Add a short prerequisite section with quick checks for rustc host, cl.exe, and
lib.exe. This gives the manual Windows tester a faster explanation if the build
environment is missing Visual Studio Build Tools.
Test Plan:
- cargo test -p lanparty-client-win --help >/dev/null
- git diff --check
- git diff --cached --check
Refs: MVP Windows test handoff
The gateway already sent periodic CAM refresh frames so the physical switch
keeps remote client MACs learned on the gateway port. Those periodic refreshes
were silent, which made the manual MVP switch-learning check harder to diagnose
after the initial peer-joined refresh had passed.
Keep the peer id and MAC attached to periodic refresh work and emit the same
structured CAM refresh log line with reason=periodic. The join-triggered
refresh still logs reason=peer_joined, so the test guide can distinguish the
immediate proof from the recurring keepalive signal.
Test Plan:
- cargo test -p lanparty-gateway cam_refresh
- cargo test -p lanparty-gateway
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP switch MAC-learning diagnostics
Use the canonical --interface flag in the README and MVP test guide so the
handoff command matches lanparty-gateway --help. Keep --iface documented as the
shorter accepted alias because the CLI and tests still support it.
Test Plan:
- ./target/release/lanparty-gateway --help
- cargo test -p lanparty-gateway accepts_iface_alias_for_gateway_interface
- git diff --check
- git diff --cached --check
Refs: MVP test handoff polish
Peers advertise a datagram budget during hello, and the relay folds that into
the room MTU/no-fragmentation model. Honest clients already avoid sending
larger encoded frames, but the relay was still trusting ingress traffic to obey
that contract before forwarding it.
Drop datagrams that exceed the accepted peer's negotiated max before decode or
forwarding, and log them as datagram_budget. This keeps malformed datagram
disconnect accounting reserved for invalid overlay/ethernet bytes instead of
policy budget drops.
Test Plan:
- cargo test -p lanparty-relay ingress_budget
- cargo test -p lanparty-relay
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP relay datagram budget audit
Add a relay regression test for the join ordering used by gateway MAC-state
seeding. The test sends a second client's hello but intentionally delays reading
that client's welcome until after the existing peer receives PeerJoined.
This guards the ordering from the relay admission path: existing peers are
notified before the joining peer can proceed from its welcome and begin sending
Ethernet datagrams. That matters for first DHCP/ARP frames after a Windows
client joins a room with an existing LAN gateway.
Test Plan:
- cargo test -p lanparty-relay notifies_existing_peer_before_join_welcome
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: PLAN.md MVP relay lifecycle and gateway MAC learning
The gateway authorizes remote-to-LAN frames from the relay lifecycle table. A
new client can start sending Ethernet datagrams as soon as it receives its
welcome, so the relay should notify already-present peers before returning that
welcome to the joining peer.
Register the accepted peer, send the PeerJoined event to existing peers, and
then send the welcome to the joining peer. If the welcome write fails after the
pre-notification, remove the accepted peer and send PeerLeft so existing peers
do not retain stale client MAC state.
This still is not a cross-connection delivery proof, but it puts the relay-side
ordering in the right direction for first DHCP/ARP frames after a client joins.
Test Plan:
- cargo test -p lanparty-relay
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: PLAN.md MVP relay lifecycle and gateway MAC learning
Add the concrete pass conditions for the manual MVP proof and a small fill-in
block for relay, room, gateway interface, and LAN host values. This makes the
operator flow easier to follow before starting the relay, gateway, and Windows
client.
The guide also calls out the gateway lifecycle and CAM-refresh log lines that
matter for switch MAC learning, plus the non-link-local TAP IPv4 address to use
for ping tests.
Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: PLAN.md MVP manual end-to-end proof
Gateway filtering and CAM refresh depend on the remote-client MAC table that
is seeded by relay lifecycle events. If a lifecycle stream and packet work are
both ready in the same loop turn, handle the lifecycle event first so the local
MAC table is as fresh as possible before deciding whether to inject or forward
frames.
This does not create a cross-stream ordering guarantee. It is a local scheduling
preference that reduces first-packet drops after client joins without weakening
source-MAC authorization or LAN-destination filtering.
Test Plan:
- cargo test -p lanparty-gateway
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: PLAN.md MVP gateway lifecycle and L2 bridge behavior
Windows can report more than one unicast address on the TAP adapter. If an
APIPA address remains present next to the real LAN DHCP address, choosing the
first IPv4 address can make diagnostics warn about 169.254.x.x even though the
adapter already has a usable LAN address.
Prefer a non-link-local IPv4 address for TAP diagnostics, then fall back to any
IPv4 address, then to the first non-IPv4 address. This keeps the existing APIPA
warning when APIPA is the only IPv4 signal, but reports the real DHCP address
when Windows exposes both addresses.
README.md and TESTING.md now document that diagnostics prefer the real LAN IPv4
when several TAP addresses are visible.
Test Plan:
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
Refs: PLAN.md client diagnostics; TESTING.md TAP IP verification
The gateway runs in promiscuous mode so it can capture frames for remote client
MACs, but that also means it sees unrelated unicast traffic between physical
LAN machines. Before this change those frames were sent to the public relay and
only then discarded as unknown destinations.
Use the remote-client table seeded by relay lifecycle events to decide whether
a LAN frame should leave the gateway. Broadcast and multicast traffic still
flows to the relay, and unicast to a connected remote client still flows to the
relay. Unicast to any other destination is counted and logged locally as
UnknownDestination.
This keeps busy LAN traffic out of the relay data path and makes the gateway
behavior match the MVP switching model: LAN frames go to matching remote
clients, while broadcast and multicast fan out.
README.md documents the local filter, and TESTING.md explains why
LanToRemote UnknownDestination can be normal on a busy LAN.
Test Plan:
- cargo test -p lanparty-gateway
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
Refs: PLAN.md switching model; TESTING.md MVP log signals
The gateway already emits periodic CAM refresh frames so the physical switch
keeps remote client MACs on the gateway port. A newly joined client previously
waited until either its own tunneled traffic reached the LAN or the first
60-second refresh tick fired.
Emit one padded CAM refresh frame immediately when a valid client PeerJoined
control event is observed. This makes the switch MAC-table check in the MVP
procedure visible sooner and keeps the periodic refresh as the aging guard.
The refresh uses the same maintenance frame shape as the periodic path and is
logged with the client peer id and MAC.
README.md and TESTING.md now document the immediate refresh behavior and the
log signal to look for during manual LAN testing.
Test Plan:
- cargo test -p lanparty-gateway
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
Refs: PLAN.md CAM refresh; TESTING.md MVP switch MAC-table check
IPv4 link-local on the TAP adapter means Windows self-assigned APIPA because
LAN DHCP did not complete through the tunnel. The previous user diagnostic
only said "TAP IP detected", which made a failed DHCP path look neutral
during MVP testing.
Return a full UserDiagnostic from TAP IP classification so IPv4 link-local
can be a warning while normal IPv4 still reports the received DHCP address.
Keep IPv6 link-local neutral because it is expected on many Windows
interfaces and is not evidence of LAN DHCP success or failure.
TESTING.md now tells the operator to troubleshoot 169.254.x.x like
`Waiting for TAP IP`.
Test Plan:
- cargo test -p lanparty-obs
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
Refs: PLAN.md diagnostics; TESTING.md MVP guide
The previous startup ordering loaded the virtual MAC before touching TAP, but it
also resolved the relay endpoint before clearing stale TAP media state. That
left a post-crash TAP adapter able to influence DNS or route selection before
the client had pinned the relay path.
Split Windows client startup config into a local phase and a resolved runtime
phase. The local phase reads the certificate, room, TAP adapter selection, and
client identity without performing DNS. Windows startup now writes the TAP
NetworkAddress value and marks TAP media disconnected before resolving the relay
endpoint or opening the QUIC connection.
A regression test uses an intentionally unresolved relay hostname to prove that
building the startup config does not resolve DNS. The client still resolves the
relay before activation and still validates the driver-reported TAP MAC before
bridging.
Test Plan:
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
- cargo check -p lanparty-client-win --target x86_64-pc-windows-gnu
- blocked by missing x86_64-w64-mingw32-gcc for ring on this host
Refs: PLAN.md Windows routing / metric handling
The Windows startup path opened the TAP adapter to clear stale media state
before loading the persisted tunnel identity and writing the NetworkAddress
registry value. That made the first TAP touch happen with whatever MAC Windows
already had loaded from a previous run.
Build the client runtime config first so the virtual MAC and resolved relay
address are known before TAP activation. The pre-connect TAP preparation now
writes the selected adapter's NetworkAddress value before opening the adapter to
mark media disconnected. The relay route is still pinned only after the relay
handshake, while TAP remains media-disconnected.
This reduces the chance of a first-run or post-crash TAP MAC mismatch during
the manual Windows MVP test. The client still validates the driver-reported MAC
before bridging and still fails closed if Windows has not reloaded the registry
value.
Test Plan:
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
- cargo check -p lanparty-client-win --target x86_64-pc-windows-gnu
- blocked by missing x86_64-w64-mingw32-gcc for ring on this host
Refs: PLAN.md Windows routing / metric handling; TESTING.md MVP guide
The relay already enforces client source MAC identity before forwarding, but
this gateway bridge could still write any safety-clean relayed frame to
AF_PACKET. That left the final physical-LAN boundary depending entirely on the
relay forwarding path.
Keep a lifecycle-seeded remote client table in the gateway bridge and reject
relay frames whose datagram peer id is unknown or whose Ethernet source MAC does
not match the announced client MAC. CAM refresh now uses the same announced
table instead of learning source MACs from relay traffic.
This is conservative: if data arrives before the lifecycle event, the gateway
drops that frame with UnauthorizedSourceMac. Later packets proceed after the
control event is processed.
Test Plan:
- cargo test -p lanparty-gateway connects_to_relay_control_stream_as_gateway
- cargo test -p lanparty-gateway
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
Refs: PLAN.md safety filters; TESTING.md MVP acceptance guide
The relay should only send a remote client unicast traffic for that client's
virtual MAC, while broadcast and multicast traffic fan out to every relevant
peer. The Windows client is still the last boundary before TAP, so it should not
write a relayed unicast frame for some other MAC into the adapter if the relay
or a future code path misroutes it.
Add a receive-side destination guard in client-core. Relayed frames now reach
TAP only when their destination is the client's virtual MAC or a
broadcast/multicast address; other unicast frames are counted and skipped.
Extend the client relay-session test with a wrong-client unicast before the
valid frame, and document the client-side skip in the README.
Test Plan:
- cargo test -p lanparty-client-core
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
Refs: PLAN.md LAN frames to matching remote clients
The MVP overlay reserves its flags field for later features such as
fragmentation or payload encryption, but version 1 does not define any flag
semantics. Accepting nonzero flags would let unknown behavior silently traverse
the relay and reach the tunnel endpoints.
Make zero the only valid v1 flag value. Overlay encoding and decoding now reject
reserved nonzero flags, production send paths use the explicit
OVERLAY_FLAGS_NONE constant, and the relay emits forwarded datagrams with the
same zero-flag policy instead of preserving peer-supplied bits.
Document the reserved-flag rule in the protocol crate overview.
Test Plan:
- cargo test -p lanparty-proto overlay
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
Refs: PLAN.md no-fragmentation MVP overlay format
LAN-to-remote switch-control filtering is enforced by the gateway and relay,
but the Windows client is the final boundary before frames enter the TAP
adapter. A malformed or buggy relay path should not be able to make the client
write LAN control traffic, invalid-source frames, or jumbo frames into TAP.
Reuse the shared gateway/LAN safety classifier on received relay Ethernet
frames. Filtered frames are counted and skipped, and recv_ethernet only returns
frames that are safe to hand to the platform TAP writer.
Extend the client relay-session test so the mock relay sends a filtered frame
before the valid one, then document the receive-side TAP boundary in the
README.
Test Plan:
- cargo test -p lanparty-client-core
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
Refs: PLAN.md LAN-to-remote control-plane filtering
The relay already filters unsafe remote-client traffic, but the gateway is the
last process before the physical LAN. Treating a relayed Ethernet frame as safe
just because it came from the relay leaves the LAN boundary dependent on one
upstream check.
Add a gateway-local remote-to-LAN safety decision before AF_PACKET writes. The
gateway now skips and logs relayed frames with invalid source MACs, L2 control
traffic, remote VLAN tags, DHCP-server replies, IPv6 Router Advertisements,
IPv6 fragments, or jumbo payloads. The public receive helper also loops past
filtered frames so callers only receive frames that can be injected.
Document the final gateway boundary check in the README and extend the gateway
relay integration test so an unsafe relayed frame is filtered before the valid
frame is delivered.
Test Plan:
- cargo test -p lanparty-gateway
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
Refs: PLAN.md remote-to-LAN safety filters
Safety filtering now applies at several tunnel boundaries. The relay remains
the trust boundary, while the client and gateway also drop unsafe frames before
spending relay bandwidth. Duplicating EtherType and IPv4/IPv6 parsers across
crates would make those rules drift as the MVP grows.
Move the Ethernet safety classifiers into lanparty-proto, expose typed safety
drop reasons, and map them back into the existing DropReason vocabulary. The
relay now uses the shared client and gateway classifiers, the gateway keeps its
local LAN-send drops through the shared classifier, and the client drops the
same remote-to-LAN safety cases before QUIC DATAGRAM encoding.
Document the client-side local drops and list the additional suspicious drop
reasons in the manual MVP test guide.
Test Plan:
- cargo test -p lanparty-proto safety
- 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
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check -p lanparty-client-tap --target x86_64-pc-windows-gnu --tests
- cargo check -p lanparty-client-route --target x86_64-pc-windows-gnu --tests
- cargo check -p lanparty-client-tap --target x86_64-pc-windows-msvc --tests
- cargo check -p lanparty-client-route --target x86_64-pc-windows-msvc --tests
- git diff --check
Refs: PLAN.md safety filters and client source-MAC isolation
The relay already filters invalid source MACs, jumbo frames, and L2 control
plane traffic. The gateway bridge was still encoding those LAN frames and
sending them to the relay first, so gateway logs could say forwarded even when
the relay would later drop the frame.
Classify that same local LAN-send subset before QUIC DATAGRAM encoding. The
gateway now records and reports these frames as local drops, keeps the relay as
the trust boundary, and avoids spending relay bandwidth on frames that can never
reach remote clients.
Document that gateway-side local drops cover invalid source MACs, L2 control
plane traffic, jumbo frames, and datagram-budget failures.
Test Plan:
- cargo test -p lanparty-gateway
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md LAN-to-remote control-plane filtering
The relay already rejects client frames whose source MAC does not match the
announced virtual MAC. The Windows bridge can still see those frames from TAP,
though, and sending them to the relay wastes datagram budget and makes the
client-side counters less useful during manual tests.
Carry the configured virtual MAC into ClientRelayIo and drop invalid or
unauthorized TAP source MACs before QUIC DATAGRAM encoding. The relay keeps the
same checks as the trust boundary, but client diagnostics now account for these
drops locally.
Document the local source-MAC check and list InvalidSourceMac as a suspicious
manual-test drop reason.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-core connects_to_relay_control_stream_as_client
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md source-MAC authorization and safety filters
The Windows TAP reader used the validating read helper, so a malformed or runt
TAP frame would surface as a read error and stop the whole client bridge. The
client relay send path already has the right semantics for bad local frames: it
counts malformed, jumbo, and over-budget frames, reports the drop reason, and
keeps the bridge running.
Read raw TAP frames in the Windows pump and pass the bytes through
send_ethernet_with_outcome. Device read errors still stop the bridge, but frame
validation failures now follow the documented local-drop path instead of tearing
down the session.
Test Plan:
All cargo commands used these environment variables:
RUSTUP_HOME=/tmp/softlan-vpn-rustup
CARGO_HOME=/tmp/softlan-vpn-cargo
- cargo test -p lanparty-client-core connects_to_relay_control_stream_as_client
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check -p lanparty-client-tap --tests
--target x86_64-pc-windows-gnu
- cargo check -p lanparty-client-tap --tests
--target x86_64-pc-windows-msvc
- cargo check -p lanparty-client-route --tests
--target x86_64-pc-windows-gnu
- cargo check -p lanparty-client-route --tests
--target x86_64-pc-windows-msvc
- git diff --check
Known limitation: full lanparty-client-win Windows cross-check remains blocked
on this host by ring requiring x86_64-w64-mingw32-gcc for the GNU target.
Refs: PLAN.md better malformed-frame handling
A gateway with the selected Ethernet cable unplugged can still open an
AF_PACKET socket and join the relay room. That makes clients see a connected LAN
gateway even though DHCP and LAN discovery cannot make the physical round trip.
Check the selected Linux interface's sysfs carrier file before creating the raw
socket. If sysfs reports carrier 0, fail before the gateway joins the relay.
Missing or unrecognized carrier files remain allowed so this does not reject
interfaces where the kernel cannot expose link state in that form. README and
TESTING now document the preflight and the operator fix.
Test Plan:
All cargo commands used these environment variables:
RUSTUP_HOME=/tmp/softlan-vpn-rustup
CARGO_HOME=/tmp/softlan-vpn-cargo
- cargo test -p lanparty-gateway \
packet::tests::detects_disconnected_interfaces_from_sysfs_carrier
- cargo test -p lanparty-gateway
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md wired Ethernet gateway requirement
If the Windows client is killed hard, the TAP adapter can be left in a connected
media state. A retry should not resolve or connect to the relay while stale TAP
state might still influence Windows routing.
Select and open the intended TAP adapter before relay endpoint resolution, force
its media state to disconnected, then proceed with the existing relay connect,
route pin, TAP route protection, and bridge startup flow. This also makes
missing or ambiguous TAP adapters fail before the client joins the relay room.
The README and MVP test guide now show the new startup line and the early TAP
preflight troubleshooting checks.
Test Plan:
All cargo commands used these environment variables:
RUSTUP_HOME=/tmp/softlan-vpn-rustup
CARGO_HOME=/tmp/softlan-vpn-cargo
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check -p lanparty-client-tap --tests
--target x86_64-pc-windows-gnu
- cargo check -p lanparty-client-tap --tests
--target x86_64-pc-windows-msvc
- cargo check -p lanparty-client-route --tests
--target x86_64-pc-windows-gnu
- cargo check -p lanparty-client-route --tests
--target x86_64-pc-windows-msvc
- git diff --check
Known limitation: full lanparty-client-win Windows cross-check is still blocked
on this Linux host by the external ring toolchain setup. The default GNU target
lacks x86_64-w64-mingw32-gcc, the default MSVC target lacks lib.exe, and the
LLVM MSVC attempt gets as far as ring C compilation but lacks Windows CRT
headers such as assert.h.
Refs: PLAN.md route-protection startup requirement
A crashed or forcibly killed Windows client can leave the scoped relay host
route behind. The next run should still be allowed to start when Windows says
that the exact route row already exists, because that route already protects the
relay path from TAP default-route takeover.
Handle ERROR_OBJECT_ALREADY_EXISTS from CreateIpForwardEntry2 as a successful
borrowed pin. Routes created by the current client are still deleted on Drop;
pre-existing routes are left alone so we do not remove administrator-managed or
stale routes that this process did not create. The client startup log now marks
whether the route was created or already existed, and the README and MVP test
guide explain the behavior.
Test Plan:
All cargo commands used these environment variables:
RUSTUP_HOME=/tmp/softlan-vpn-rustup
CARGO_HOME=/tmp/softlan-vpn-cargo
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check -p lanparty-client-route --tests
--target x86_64-pc-windows-gnu
- cargo check -p lanparty-client-route --tests
--target x86_64-pc-windows-msvc
- cargo clippy -p lanparty-client-route --tests
--target x86_64-pc-windows-gnu -- -D warnings
- cargo clippy -p lanparty-client-route --tests
--target x86_64-pc-windows-msvc -- -D warnings
- git diff --check
Known limitation: full lanparty-client-win Windows cross-check is still blocked
on this Linux host by the external ring toolchain setup. The GNU target lacks
x86_64-w64-mingw32-gcc, and the MSVC target lacks lib.exe/MSVC environment.
Refs: PLAN.md route-protection requirement
The MVP bridge treats each remote player as a normal host on the LAN, not as a
trunk port. Allowing client-origin VLAN-tagged frames would let a remote client
send traffic outside the simple untagged Ethernet model, and could also hide
IPv4/IPv6 control traffic behind an outer VLAN EtherType that the existing
safety filters do not parse.
Filter 802.1Q, 802.1ad, and common QinQ-tagged frames from remote clients before
they can reach the physical LAN. LAN-origin tagged frames are still allowed back
toward clients so the gateway remains a transparent receiver for whatever the
local wired network emits. Add a dedicated drop reason so relay logs make the
policy clear.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay -p lanparty-obs
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: MVP relay L2 safety filters
The Windows client already treats TAP route, metric, and MTU changes as scoped
runtime state that is restored when the process exits. TAP media status was the
odd one out: startup marked the adapter connected, but there was no matching
cleanup path to mark it disconnected again.
Add a small TAP media guard that owns an Arc to the opened adapter and marks
media disconnected on drop. The frame pump now receives an Arc<TapAdapter>, so
the guard can run on normal Ctrl-C, startup errors after TAP open, route-check
failures, or frame-pump failures without fighting ownership of the TAP reader
thread. Cleanup errors are logged because Drop cannot return them.
Update the README and MVP test guide so the documented cleanup behavior matches
the client runtime.
The full Windows client cross-check is still blocked on this host before project
code by ring needing x86_64-w64-mingw32-gcc. The TAP and route helper crates
still check for the Windows GNU target.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win -p lanparty-client-tap -p lanparty-client-route
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check --target x86_64-pc-windows-gnu -p lanparty-client-route -p lanparty-client-tap
- git diff --check
Refs: MVP Windows TAP cleanup
The relay now looks through ordinary IPv6 extension headers to catch remote
DHCPv6 server replies and Router Advertisements. IPv6 fragments are still an
evasion risk because later fragments may not contain the upper-layer ports or
ICMPv6 type that the relay safety policy checks.
For the MVP, make that boundary conservative: remote-client IPv6 fragments are
filtered before they can reach the physical LAN. LAN-origin fragments are still
allowed to flow back to remote clients, so this does not block ordinary LAN
traffic returning through the gateway. Add a dedicated diagnostics drop reason
so logs explain the policy clearly.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay -p lanparty-obs
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: MVP relay L2 safety filters
The MVP safety policy says remote clients must not inject DHCP server replies
onto the LAN. The relay enforced that for IPv4 DHCP, but DHCPv6 uses UDP
547 -> 546 over IPv6 and was not covered by the existing check.
Split the DHCP server-reply predicate into IPv4 and IPv6 paths. The IPv6 path
reuses the extension-header walker so server replies hidden behind ordinary IPv6
extension headers are still treated as unsafe. Keep client DHCPv6 requests
allowed, and continue allowing LAN-origin DHCPv6 replies to flow back to remote
clients.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: MVP relay L2 safety filters
Remote clients must not be able to inject IPv6 Router Advertisements onto the
LAN. The relay already filtered direct ICMPv6 RA packets, but the check only
looked at the IPv6 base header's immediate next-header value. A client could
hide the ICMPv6 RA behind ordinary IPv6 extension headers and bypass that MVP
safety policy.
Walk the IPv6 extension-header chain for hop-by-hop, routing, destination
options, fragment, and AH headers before checking the ICMPv6 message type. This
keeps non-RA ICMPv6 traffic eligible for normal relay forwarding while closing
the obvious RA evasion path. Document the stronger relay safety boundary in the
README.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: MVP relay L2 safety filters
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
Add a --list-tap-adapters mode to the Windows client. The command prints the
TAP-Windows6 adapter instance ids and exits without requiring relay, room, or
certificate arguments.
This makes the manual MVP test smoother on machines with multiple TAP adapters:
the operator can ask the binary for the exact ids, then rerun with
--tap-instance-id instead of relying on a separate PowerShell query or waiting
for the ambiguous-adapter startup error.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md Windows client TAP adapter responsibility
Add a Windows client --tap-instance-id option for selecting a specific
TAP-Windows6 adapter by NetCfgInstanceId / InterfaceGuid. The client still opens
the only installed TAP adapter automatically, but now refuses to choose an
arbitrary adapter when multiple TAP-Windows6 adapters are present.
This keeps the MVP test run from silently configuring and opening the wrong TAP
adapter on machines that already have VPN or test TAP devices installed. The
error lists available adapter instance ids so the operator can rerun with the
intended value.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md Windows client TAP adapter responsibility
Add a relay-server test that connects the production client-core session and the
production gateway session to the same in-process relay. The test verifies room
join catch-up, negotiated peer identities, Ethernet datagrams in both
directions, stats reporting, and room/session cleanup after graceful shutdown.
The existing relay tests covered forwarding with raw Quinn peers. This adds a
higher-level proof that the three MVP components agree on the same control and
datagram contracts before the final Windows TAP and physical LAN test.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay bridges_real_client_and_gateway_sessions -- --nocapture
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md Phase 1 relay/client/gateway proof
Add a dedicated manual test guide for the MVP proof. The guide keeps the
operator flow concrete: relay startup, certificate handoff, Linux gateway
startup, elevated Windows client startup, expected log signals, acceptance
checks, troubleshooting, cleanup, and the information to report back.
This is intentionally scoped to the manual MVP. It documents that installer,
GUI, production certificates, authentication, and end-to-end payload encryption
are outside this proof.
Test Plan:
- git diff --check
- reviewed TESTING.md for command flow and expected log signals
Refs: PLAN.md Phase 1 success criteria
Expose the active QUIC connection RTT in shared client diagnostics and in the
Windows client status line. This gives operators a live relay-path latency
signal without pretending to measure end-to-end gateway or LAN latency.
The new diagnostics field defaults to unknown when older JSON snapshots omit it,
so consumers can read pre-change snapshots without special migration code. The
user-facing diagnostics now print Relay RTT only when the client has an active
QUIC measurement.
Test Plan:
- cargo fmt --check
- git diff --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
Refs: PLAN.md logging and diagnostics section
The plan explicitly keeps the physical LAN gateway wired-only for the MVP.
Managed Wi-Fi adapters are not reliable for arbitrary source-MAC injection, but
the gateway previously accepted any interface that could be opened as an
Ethernet-like packet socket.
Reject Linux interfaces that sysfs marks as wireless before opening the raw
packet socket. The check looks for the common `wireless` and `phy80211` markers
under `/sys/class/net/<iface>`, and keeps path separators out of interface names
so the sysfs lookup stays scoped to a single netdev name.
Document the wired-only enforcement in the gateway README section.
Test Plan:
- cargo fmt --check
- git diff --check
- cargo test -p lanparty-gateway
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
Refs: PLAN.md
The Linux AF_PACKET read helper discarded malformed or runt LAN captures before
the bridge loop saw them. That made those frames invisible to gateway drop
counters and frame logs, which is not great for the phase-one heavy diagnostics
called for in the plan.
Return raw inbound capture bytes from the read helper and let the bridge loop
make the drop decision. Malformed LAN frames are now counted as malformed drops,
logged with the normal gateway frame log shape, and skipped without stopping the
bridge. Valid LAN frames still flow through the existing send path and budget
checks.
Document the accounting behavior in the gateway README section.
Test Plan:
- cargo fmt --check
- git diff --check
- cargo test -p lanparty-gateway
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
Refs: PLAN.md
The Windows TAP pump used the same send helper as direct callers, so malformed,
jumbo, or over-budget TAP frames were reported as errors that stopped the
bridge. That is too brittle for the no-fragmentation MVP: local frames that
cannot fit the tunnel should be counted and dropped, while real QUIC send
failures should still surface as fatal.
Add a client send outcome that reports whether a frame was sent or locally
dropped. The existing send_ethernet API still returns an error for direct
callers when the outcome is a local drop. The live TAP pump uses the outcome API
so it can log the drop reason and keep forwarding later frames.
Document the new client behavior in the README.
Test Plan:
- cargo fmt --check
- git diff --check
- cargo test -p lanparty-client-core
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
Attempted:
- cargo check -p lanparty-client-win --target x86_64-pc-windows-msvc
(blocked by missing MSVC lib.exe on this Linux host)
Refs: PLAN.md
The gateway AF_PACKET read path used the standard 1514 byte Ethernet frame
length as its receive buffer. VLAN-tagged or jumbo LAN frames could therefore
be truncated before the bridge reached the encoded-datagram budget check, so
logs and drop accounting saw a corrupted shorter frame.
Use an overlay payload-sized capture buffer instead. This lets the Linux
gateway observe the whole frame that the kernel reports, then leave the
existing Ethernet parsing and negotiated QUIC datagram budget checks to decide
whether the frame can cross the tunnel. The bridge still never fragments
Ethernet frames.
Document the behavior in the gateway README section and add a compile-time
guard so the capture buffer stays above the standard Ethernet frame size.
Test Plan:
- cargo fmt --check
- git diff --check
- cargo test -p lanparty-gateway
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
Refs: PLAN.md
The friendly client diagnostics reported relay/gateway/IP/broadcast signals,
but they still hid two important readiness failures in the dense status line:
the relay route was not pinned, or no TAP adapter was available. Those are the
kind of states a CLI user or future GUI needs surfaced immediately.
Add user diagnostics for an unpinned relay route, a missing TAP adapter, and a
TAP adapter that is present but still has no IP address. The existing healthy
path stays concise: once the TAP has an address, the diagnostics continue to
report that address instead of also saying that the adapter is ready.
README now mentions that the short diagnostics include route and TAP readiness
warnings.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-obs -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: PLAN.md Logging / diagnostics
The gateway may see full-sized frames from the physical LAN even when the room
uses a smaller tunnel MTU. With no overlay fragmentation, those frames cannot be
encoded into the negotiated QUIC datagram budget. The live bridge previously
propagated that budget error and stopped, which made one oversized LAN frame a
fatal condition.
Teach the gateway send path to return a send outcome. Normal direct callers
still get an error for over-budget sends, but the Linux bridge loop now records,
logs, and drops those frames with a DatagramBudget drop reason before continuing
with later traffic. Malformed local Ethernet still remains an error because that
indicates a broken local boundary rather than ordinary LAN traffic.
The gateway stats test now covers the extra drop, and the frame-log test covers
the new drop reason. README now documents that over-budget LAN frames are
counted, dropped, and logged instead of fragmented or killing the bridge.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-obs -p lanparty-gateway
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: PLAN.md No fragmentation for MVP
Peers announce a QUIC datagram budget in their hello, and the relay clamps that
value against the transport's negotiated max before room admission. The relay
used that clamped value for MTU selection, but stored the raw transport budget
in the live peer session. A peer that intentionally advertised a smaller budget
could therefore receive egress datagrams larger than it promised to accept.
Store the post-clamp hello budget in AcceptedPeer and PeerSession instead. That
keeps the existing relay egress skip path tied to the same negotiated size used
for room MTU decisions.
The handshake regression now advertises a budget below the QUIC transport
budget and asserts that the accepted peer records the advertised value. The
README decomposition also calls out the per-peer egress-budget invariant.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: PLAN.md No fragmentation for MVP
The PLAN calls out client diagnostics that a user can read directly, not only
machine-shaped counters. The Windows client already built ClientDiagnostics
snapshots, but it printed a dense status line and left the UserDiagnostic model
unused.
Derive user-facing diagnostics from ClientDiagnostics in lanparty-obs so a
future GUI and the current CLI can share the same status vocabulary. The
messages report the states the runtime actually observes: relay reachability,
LAN gateway presence, TAP IP presence, and observed broadcast traffic. TAP IPs
are only described as DHCP when they are non-link-local IPv4 addresses, because
link-local IPv4 and IPv6 addresses do not prove DHCP success.
The client now prints those user-facing lines after the existing detailed
counter line. Gateway latency is intentionally not reported here; the current
protocol does not measure gateway RTT.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-obs -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: PLAN.md Logging / diagnostics
PLAN.md requires rejecting broadcast, multicast, and otherwise invalid source
MACs. Client ingress already had a forged-source check against the registered
client identity, but gateway ingress could still forward Ethernet frames whose
source MAC was not valid unicast.
Add an explicit `InvalidSourceMac` drop reason and filter invalid Ethernet
source MACs before client source authorization and last-seen refresh. This keeps
invalid source addresses out of both remote-client and gateway forwarding paths
while preserving `UnauthorizedSourceMac` for valid unicast sources that simply
belong to another client identity.
Document the invalid-source filter in the relay README decomposition.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-obs -p lanparty-relay
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
PLAN.md keeps MVP forwarding to one Ethernet frame per QUIC datagram with no
fragmentation. The relay already skipped targets whose negotiated datagram
budget could not carry an encoded forwarded frame, but that skip was silent.
Retain the no-fragmentation behavior and log the skipped egress target with the
room, ingress peer, target peer, encoded length, and target datagram budget.
Store the peer id in relay sessions so the diagnostic can identify the skipped
target directly.
Document the egress budget-skip diagnostic in the relay README section.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
PLAN.md calls for heavy diagnostics and better malformed-frame handling. The
relay already disconnected peers after repeated malformed datagrams, but overlay
or header-level malformed datagrams had no operator-facing count until the peer
was closed.
Log each malformed peer datagram with room, peer id, role, current count,
threshold, and whether the threshold is disconnecting the peer. Keep the
existing threshold behavior unchanged; this only makes the counter visible.
Document the malformed-datagram count in the relay README section.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
PLAN.md describes relay frame logs with separate forwarded, dropped, filtered,
and rate-limited actions. The relay had the shared `Filtered` action in its log
vocabulary, but safety-policy rejects were still reported as generic drops.
Classify forged client source MACs and L2 safety-filter matches as filtered
forwarding decisions. Malformed frames and unknown destinations remain drops,
while rate limits continue to use the rate-limited action.
Document the distinction in the relay README section.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
PLAN.md keeps MVP traffic to one Ethernet frame per QUIC datagram with no
fragmentation. The relay already negotiates a datagram budget, but the client
and gateway send paths still relied on Quinn to reject oversized encoded
Ethernet datagrams.
Add a shared protocol validation helper for encoded datagram length versus the
negotiated QUIC budget. Thread the negotiated budget into client and gateway
send boundaries, reject oversized datagrams before send, and count those valid
but unsent frames as local drops.
Document the budget check in the workspace decomposition and gateway behavior.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-proto -p lanparty-client-core -p lanparty-gateway
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
PLAN.md calls for a user-facing warning when TAP routing could steal the
relay path. The client already disables TAP default routes under a scoped guard,
but the startup output only reported the previous raw flag value.
Format the route-protection message as a warning whenever default routes were
enabled before the scoped override. Keep the already-disabled case quiet and
explicit, and cover both messages with tests that run on non-Windows builds.
Document the startup warning alongside the existing route-protection behavior.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win -p lanparty-client-route
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
PLAN.md calls out "Broadcast traffic flowing" as a user-facing diagnostic.
The tunnel stats only reported total Ethernet frame counts, so the client
could not distinguish whether broadcast traffic was actually crossing the
tunnel.
Add defaulted broadcast tx/rx counters to TunnelStats while preserving the
existing constructor and old JSON compatibility. Client and gateway accounting
now increments those counters from validated Ethernet frames, and the client
diagnostics line reports the broadcast flow next to total frame counts.
Relay peer stats logs include the new counters so operators can see broadcast
activity from forwarded stats snapshots too.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-obs -p lanparty-client-core -p lanparty-gateway \
-p lanparty-client-win -p lanparty-relay
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
PLAN.md calls for user-facing diagnostics that can say whether the remote
client is connected to the LAN gateway. Startup already reported the initial
welcome bit, but periodic diagnostics only carried relay reachability and route
pinning.
Add gateway_connected to RelayDiagnostics and seed the client status from the
welcome. The client control-event logger now updates that status when gateway
join and leave events arrive, so later diagnostics reflect relay lifecycle
changes while the tunnel is running.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-obs -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
PLAN.md calls out room modes such as relay, direct-p2p, and relay fallback so
future transport choices can fit the protocol. Add an explicit ConnectionMode
field to ServerWelcome and default it to relay for existing decoded welcomes.
The relay still operates only in relay mode today. Client and gateway startup
logs now print the selected mode, which makes the current relay path visible
without changing routing or forwarding behavior.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-ctrl server_welcome -- --nocapture
- cargo test -p lanparty-client-win -p lanparty-gateway
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
PLAN.md describes the first client flow as entering a relay domain and room
code, but the client and gateway CLIs only accepted socket-address literals.
Add a small shared RelayEndpoint parser so bare hosts default to UDP/443 while
IP literals and explicit host:port values stay supported.
The runtime configs still store resolved SocketAddr values. That keeps the
Windows route-pinning path on a concrete relay IP before TAP activation while
avoiding duplicated endpoint grammar between client and gateway. The relay
listen config reuses the same default port constant so UDP/443 has one source.
README examples now use lanparty-relay.local and document the shared endpoint
syntax.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-net
- cargo test -p lanparty-client-win \
accepts_relay_domain_with_default_port -- --nocapture
- cargo test -p lanparty-gateway \
accepts_iface_alias_for_gateway_interface -- --nocapture
- cargo test -p lanparty-net -p lanparty-client-win -p lanparty-gateway
- cargo clippy -p lanparty-net -p lanparty-client-win -p lanparty-gateway \
--all-targets -- -D warnings
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
PLAN.md tells LAN hosts to start the gateway with --iface, while the binary
only accepted --interface. Add --iface as a Clap alias so the documented
Phase 1 command works without changing the canonical config field name.
The README gateway example now uses the shorthand from the plan, and a focused
parse test covers the alias mapping to the same interface field.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-gateway \
accepts_iface_alias_for_gateway_interface -- --nocapture
- cargo test -p lanparty-gateway
- cargo clippy -p lanparty-gateway --all-targets -- -D warnings
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
The Windows client sampled TAP diagnostics once when opening the adapter. That
can miss the useful DHCP result because the TAP interface may not receive a LAN
address until after frame bridging starts.
Keep the stable TAP diagnostics fields from startup, but retain the interface
identity and re-read the TAP unicast IP whenever the periodic diagnostics tick
prints and reports a snapshot. Later status lines can now show the DHCP address
that arrives after the tunnel is already moving traffic.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
The plan explicitly calls out that the MVP relay sees plaintext Ethernet frames
because it terminates QUIC for every peer connection. Document that boundary in
the README so operators and future security work do not infer end-to-end payload
privacy from QUIC alone.
Also state the intended future direction: room-key payload encryption should
keep the relay routing header visible while encrypting Ethernet payload bytes
between clients and the LAN gateway.
Test Plan:
- git diff --check
Refs: PLAN.md
The Windows client already generates and announces a stable locally administered
MAC, but it only rejected the TAP adapter when the driver reported a different
address. Persist the tunnel MAC to tap-windows6's NetworkAddress registry value
before opening the adapter so the driver can load the intended current address.
The TAP crate now keeps the driver registry key name from discovery, formats the
NetworkAddress value as the 12-digit hex string expected by NDIS, and rejects
invalid multicast, broadcast, or globally administered MACs before writing.
Runtime validation stays in place. tap-windows6 reads NetworkAddress during
adapter initialization, so an adapter that Windows already initialized with an
old value may still need disable/enable or reinstall on the real Windows test
machine before the GET_MAC ioctl reports the new identity.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-tap
- cargo test -p lanparty-client-win
- cargo clippy -p lanparty-client-tap --all-targets -- -D warnings
- cargo check -p lanparty-client-tap --target x86_64-pc-windows-gnu
- cargo check -p lanparty-client-tap --target x86_64-pc-windows-msvc
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
The plan calls for relay switch state to remember when peers were last seen.
Wrap room peers in a small entry that keeps PeerInfo alongside a monotonic
Instant, and expose the timestamp through room snapshots for relay diagnostics.
Joining initializes last_seen. Valid Ethernet frames with the peer's authorized
source refresh the timestamp before safety filters or rate limits run, so
filtered but well-formed traffic still proves the peer is active. Malformed
frames and forged client source MACs do not refresh it.
Room leave now removes the associated timestamp with the peer entry. That keeps
membership, MAC indexes, rate-limit buckets, and diagnostics state aligned.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay last_seen -- --nocapture
- cargo test -p lanparty-relay
- cargo clippy -p lanparty-relay --all-targets -- -D warnings
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
Peers can now end their relay session with a post-handshake Disconnect
control message, but the relay previously only returned from the peer loop.
Explicitly close that peer connection before leaving the room so graceful
disconnects are finite from both sides.
The relay still runs the normal leave path after the close. That keeps room
cleanup, session removal, and PeerLeft notification centralized, while the
PeerLeft reason now comes from the peer's Disconnect message.
The integration test covers two connected clients. One sends a TimedOut
Disconnect, the relay closes that client connection, and the remaining peer
receives PeerLeft with the TimedOut reason.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-relay \
forwards_graceful_disconnect_reason_to_remaining_peers \
-- --nocapture
- cargo test -p lanparty-relay
- cargo clippy -p lanparty-relay --all-targets -- -D warnings
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
The relay already accepts post-handshake Disconnect control messages, but the
client and gateway shutdown paths only sent a QUIC application close. That made
normal shutdown indistinguishable from transport closure until the relay
inferred a generic Normal leave.
Client and gateway shutdown now send a best-effort Disconnect message with the
human-readable shutdown reason before closing QUIC. The client-core drain uses
Quinn's runtime timer instead of taking a Tokio runtime dependency. The gateway
uses its existing Tokio runtime and applies the same short drain window on both
explicit shutdown and Ctrl-C in the Linux bridge loop.
The endpoint integration tests now assert that the server receives Disconnect
after the stats stream, which also protects against closing too quickly and
aborting the control stream.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-core \
connects_to_relay_control_stream_as_client -- --nocapture
- cargo test -p lanparty-gateway \
connects_to_relay_control_stream_as_gateway -- --nocapture
- cargo test -p lanparty-client-core
- cargo test -p lanparty-gateway
- cargo clippy -p lanparty-client-core --all-targets -- -D warnings
- cargo clippy -p lanparty-gateway --all-targets -- -D warnings
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
The relay now sends room lifecycle events to gateways, but the gateway was only
learning remote MACs after seeing relay traffic. That delayed CAM refresh for a
silent remote client and left PeerLeft unable to retire stale refresh entries.
Add a gateway control-event receive path and consume it inside the Linux bridge
loop. Client PeerJoined events seed the CAM refresh table by peer id and MAC,
and PeerLeft removes that peer. Relay traffic can still refresh or correct the
same table from observed source MACs.
The bridge loop selects on accepting a control stream, then reads the selected
stream inside the branch. That avoids dropping an already accepted control
stream if another select branch wins while the stream body is still pending.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-gateway \
connects_to_relay_control_stream_as_gateway -- --nocapture
- cargo test -p lanparty-gateway updates_cam_refresh_from_lifecycle_events \
-- --nocapture
- cargo test -p lanparty-gateway
- cargo clippy -p lanparty-gateway --all-targets -- -D warnings
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
Gateway traffic now contributes to the shared TunnelStats model. The gateway
counts Ethernet frames sent to the relay, Ethernet frames received from the
relay, incoming relay datagrams, dropped datagrams, and malformed frames.
Expose a gateway stats snapshot and send it to the relay over the same reliable
post-handshake Stats control stream used by clients. The Linux bridge loop sends
periodic stats snapshots as diagnostics; send failures are logged instead of
bringing down the bridge because stats are observational.
The gateway relay-session test now has the server decode a Stats event before
shutdown, so the stats control path is covered without a close race.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-gateway \
connects_to_relay_control_stream_as_gateway -- --nocapture
- cargo test -p lanparty-gateway
- cargo clippy -p lanparty-gateway --all-targets -- -D warnings
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md
Client counters were only local diagnostics even though the control protocol and
relay now understand Stats messages. Add a client-core sender that opens a
peer-to-relay unidirectional stream with the current TunnelStats snapshot.
The Windows client reports stats whenever it prints its diagnostics snapshot.
Failures are logged instead of stopping the frame pump because stats reporting
is diagnostic and should not be the reason a live tunnel goes down.
The client-core integration test now has the server decode the stats stream
before shutdown so the send path is covered without a timing race.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-core \
connects_to_relay_control_stream_as_client -- --nocapture
- cargo test -p lanparty-client-core
- cargo test -p lanparty-client-win
- cargo clippy -p lanparty-client-core --all-targets -- -D warnings
- cargo clippy -p lanparty-client-win --all-targets -- -D warnings
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md