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
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 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
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
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
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