27 Commits

Author SHA1 Message Date
ddidderr ccf30507ee chore(deps): update getrandom to 0.4.2
lanparty-client-core pulls getrandom from the workspace dependency set for
client identity generation. Bump the direct workspace dependency from 0.3.4 to
0.4.2 and refresh Cargo.lock so builds use the newer release for that direct
dependency.

Cargo still keeps older getrandom versions in the lockfile through transitive
dependencies, so this change does not try to force the whole graph onto one
major line. The added WASI and WIT packages are getrandom 0.4's normal target
support dependencies.

Test Plan:
- cargo clippy, cargo clippy --benches, and cargo clippy --tests before and
  after cargo +nightly fmt
- cargo fmt --check
- 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: none
2026-05-27 17:17:57 +02:00
ddidderr b17b6f0683 test(relay): cover real client and gateway sessions
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
2026-05-21 23:07:20 +02:00
ddidderr 21a69626e0 feat(obs): report broadcast frame counters
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
2026-05-21 21:54:35 +02:00
ddidderr bdb571799a feat(net): accept relay hostnames
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
2026-05-21 21:40:00 +02:00
ddidderr 2afdae44c6 feat(client): print runtime diagnostics snapshots
The client now builds ClientDiagnostics snapshots from the connected session,
known TAP state, route-pinning status, and tunnel counters. Windows prints one
snapshot after TAP and relay-route setup, then repeats snapshots while bridging
so frame/datagram counters and drops are visible during manual phase-1 tests.

Non-Windows builds print the same relay and QUIC diagnostics with TAP fields
marked unknown before waiting for Ctrl-C. TAP IP remains unknown until a later
Windows adapter IP inspection slice wires that source of truth.

Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win
- 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
2026-05-21 20:16:32 +02:00
ddidderr 802fe3d082 feat(client): expose tunnel traffic counters
Client relay I/O now shares atomic tunnel counters across cloned
ClientRelayIo handles and the owning ClientSession. The counters cover
successful Ethernet frame rx/tx, relay datagram rx/tx, and dropped or
malformed frames so client diagnostics have the traffic totals called
out in PLAN.md.

The counters live in client-core because that crate owns relay datagram
classification and Ethernet payload validation. The Windows TAP runner can
later sample this snapshot without duplicating protocol decisions.

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

Refs: PLAN.md
2026-05-21 20:11:11 +02:00
ddidderr aa9105541f feat(gateway): log bridged Ethernet frames
The gateway bridge now emits structured frame log lines for successful LAN to
relay and relay to LAN forwarding. Logs include the physical interface,
direction, peer id when one is known, MACs, ethertype or length field, frame
length, action, and drop reason.

This uses the shared `lanparty-obs` frame vocabulary instead of adding a second
ad hoc diagnostics model to the gateway. The log line stays local to the
gateway because the relay still owns its own room/target-specific formatting.

Test Plan:
- cargo fmt --check
- 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
2026-05-21 20:06:41 +02:00
ddidderr f88c8a94f8 feat(client): report relay route before TAP activation
Use the new route snapshot helper in the Windows client startup path before the
TAP adapter is opened and marked connected. The client now reports the current
relay destination route: selected source address, next hop, interface index,
interface LUID, route prefix, and metric.

This is still diagnostic only. Route pinning remains unwired, and route lookup
failure is a warning so manual TAP frame-pump testing is not blocked by a route
inspection failure. Once mutation is implemented, this snapshot gives the code
the pre-TAP interface data it needs to preserve the real internet path.

Verification note: I attempted to check `lanparty-client-win` for
`x86_64-pc-windows-msvc`, but this host still lacks the Windows C headers
needed by `ring`; the build stops at `assert.h` before the binary crate can be
typechecked for Windows.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check -p lanparty-client-route --target x86_64-pc-windows-msvc
- cargo clippy -p lanparty-client-route --target x86_64-pc-windows-msvc -- -D warnings
- git diff --check

Refs: PLAN.md
2026-05-21 19:08:07 +02:00
ddidderr 5001f3c35c feat(client): add Windows route snapshot helper
Add `lanparty-client-route` as the Win32 boundary for route-table work. The
first API is intentionally read-only: `best_route_to` wraps `GetBestRoute2`
and returns the selected source address, next hop, route prefix, interface
index/LUID, and route metric for a relay destination IP.

This keeps the route-protection work separate from the QUIC client binary, so
we can typecheck the Windows IP Helper calls on this Linux host without pulling
in the `ring` build path that currently blocks full Windows binary checks.
Actual route pinning and metric mutation remain later slices.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check -p lanparty-client-route --target x86_64-pc-windows-msvc
- cargo clippy -p lanparty-client-route --target x86_64-pc-windows-msvc -- -D warnings
- git diff --check

Refs: PLAN.md
2026-05-21 19:06:55 +02:00
ddidderr c315add886 feat(client): open TAP adapter on Windows
The client can now reach the relay with a stable virtual MAC, and the TAP crate
can discover and open installed TAP-Windows6 adapters. Wire those pieces
together at startup so the Windows binary opens the first TAP adapter and marks
its media status connected after the relay handshake succeeds.

The binary reports the TAP device path plus the driver MAC and MTU. If those do
not match the tunnel identity or relay-selected MTU, it warns explicitly instead
of pretending configuration is complete.

Frame pumping and route protection remain separate follow-up slices. The full
Windows client binary still cannot be target-checked on this Linux host because
its QUIC/TLS stack needs Windows C headers for ring, but the TAP crate itself is
Windows-target checked and clippy-clean.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- Windows-target cargo clippy for lanparty-client-tap with -D warnings
- git diff --check

Refs: PLAN.md Windows TAP client
2026-05-21 18:50:09 +02:00
ddidderr a09852dada feat(client): add TAP-Windows adapter crate
The Windows client still needs a real Ethernet TAP boundary before it can pump
frames between the adapter and the relay session. Keep that OS-specific surface
separate from the QUIC client state by adding a `lanparty-client-tap` crate.

The new crate discovers TAP-Windows6 adapters through the Windows network
adapter registry class, validates `tap0901` component ids, constructs the
`\\.\Global\{NetCfgInstanceId}.tap` device path, opens the TAP device handle,
and exposes blocking Ethernet frame read/write helpers. It also wraps the
TAP-Windows IOCTLs for media status, driver MAC, and driver MTU.

This does not wire the TAP crate into `lanparty-client-win` yet and does not
attempt route protection. The value of this slice is the target-checkable OS
boundary that the next client pump can depend on.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- Windows-target cargo check for lanparty-client-tap with clang-cl/lld-link
- Windows-target cargo clippy for lanparty-client-tap with -D warnings
- git diff --check

Refs: PLAN.md Windows TAP client
2026-05-21 18:48:14 +02:00
ddidderr a3d24a1173 feat(client): persist virtual MAC identity
Remote clients need a stable locally administered MAC address so the relay,
gateway, DHCP lease, and LAN peers keep seeing the same tunnel identity across
runs. Requiring users to pass `--virtual-mac` made that responsibility manual.

Add a platform-neutral client identity store that loads a JSON identity file or
generates a new valid virtual MAC with OS randomness and persists it. The file
stores the MAC in the same string form shown by the CLI. The Windows client now
uses `lanparty-client-identity.json` by default while keeping `--virtual-mac` as
a manual test override.

TAP binding still remains future work; this slice only owns the client identity
that will be assigned to the TAP adapter.

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

Refs: PLAN.md MAC identity
2026-05-21 18:35:20 +02:00
ddidderr 93f0a17f79 feat(client): add relay CLI for Windows binary
lanparty-client-win now has a real command-line surface for the relay-facing
client session. It accepts the relay address, expected TLS server name, pinned
DER relay certificate, room code, virtual TAP MAC, and advertised datagram
budget, then connects through lanparty-client-core as role = client.

The binary reports the assigned peer id, room id, and effective TAP MTU from the
welcome response, then waits for Ctrl-C. TAP adapter binding and Windows route
pinning remain future slices, but the executable now exercises the real relay
control-plane path instead of the starter placeholder.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings

Refs: PLAN.md Windows client relay connection
2026-05-21 18:21:00 +02:00
ddidderr 914bd48346 feat(client): add relay session core
lanparty-client-core now owns the platform-neutral remote client relay session.
It validates relay trust input, room, virtual MAC identity, and datagram budget,
then connects to the relay, sends a client hello, and stores the assigned
welcome metadata.

The session exposes Ethernet datagram send and receive helpers that stamp
outgoing frames with the relay-assigned room and peer ids, ignore frames for
other rooms or from itself, and reject malformed Ethernet payloads before
handing them to the future TAP bridge.

The loopback Quinn test verifies the full client-side control and datagram path
without requiring Windows TAP access: pinned certificate trust, role = client
hello, announced virtual MAC, welcome parsing, and Ethernet datagrams in both
directions.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings

Refs: PLAN.md Windows client relay connection and QUIC datagrams
2026-05-21 18:19:11 +02:00
ddidderr 128903c312 feat(gateway): add relay Ethernet datagram helpers
GatewayConnection can now send and receive Ethernet frames over the admitted
relay QUIC connection. Outgoing frames are wrapped in the shared overlay format
with the gateway's assigned room id and peer id; incoming datagrams are ignored
unless they are Ethernet frames for the assigned room from another peer.

The receive helper also parses the payload as an Ethernet frame before exposing
it, which keeps the future AF_PACKET bridge from injecting malformed runt
payloads if the relay path ever misbehaves.

The loopback connector test now verifies the full post-handshake datagram path:
the gateway sends a frame to the test relay, the relay validates the overlay
metadata, and the gateway receives a relay-sent Ethernet frame back.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings

Refs: PLAN.md gateway relay datagram send/receive
2026-05-21 18:12:07 +02:00
ddidderr 1b00deb419 feat(gateway): open AF_PACKET sockets
The gateway now has a small Linux PacketSocket wrapper for raw Ethernet frame
I/O. It resolves the configured interface with if_nametoindex, opens an
AF_PACKET/SOCK_RAW socket for ETH_P_ALL, binds it to the interface, and exposes
thin send_frame and recv_frame helpers around the owned file descriptor.

The gateway binary opens this socket after completing the relay control
handshake. The frame bridge loop is still intentionally left for a later slice,
but the process now proves the two required resources are available: relay
admission and raw L2 access on the LAN interface.

Tests cover interface-name validation and missing-interface lookup without
requiring root or CAP_NET_RAW.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings

Refs: PLAN.md Linux AF_PACKET gateway socket
2026-05-21 18:09:03 +02:00
ddidderr 763a55bfba feat(gateway): connect to relay control plane
The gateway binary now has a real relay-facing configuration and QUIC control
handshake. It accepts a relay socket address, expected TLS server name, pinned
DER relay certificate, room code, LAN interface name, and advertised datagram
budget, then connects as role = gateway and waits for a welcome response.

The ALPN token moved into lanparty-ctrl so relay and gateway share the same
protocol identifier instead of carrying duplicate private constants. The gateway
still stops after the control-plane connection; AF_PACKET capture and injection
remain a later slice.

The connector test spins up a local Quinn server with a self-signed certificate,
trusts that certificate explicitly, verifies the outgoing gateway hello, and
checks the received welcome metadata.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings

Refs: PLAN.md Linux gateway outbound relay connection
2026-05-21 18:06:22 +02:00
ddidderr 756523927a feat(relay): forward live Ethernet datagrams
The relay now keeps active peer sessions alongside room admission state. After
a successful hello/welcome handshake, the connection enters a datagram loop
and stays registered until the QUIC connection closes.

Incoming datagrams are only considered for forwarding when their overlay room
id, peer id, and Ethernet frame type match the peer assigned by the relay.
The relay then reuses the existing room forwarding decision logic, clones the
matching live target sessions, and sends a relay-stamped Ethernet datagram to
each connected target that can carry the frame.

This keeps spoofable wire metadata out of the trust boundary: clients can put
whatever they want in an overlay header, but the relay forwards using the
room and peer identity established during the control handshake.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings

Refs: PLAN.md QUIC DATAGRAM Ethernet forwarding path
2026-05-21 17:55:58 +02:00
ddidderr 77894c4706 feat(relay): bind QUIC endpoint
Make the relay binary bind a real Quinn endpoint instead of only printing its
configuration. This is the next runtime step toward the public relay while still
keeping connection handling out of this commit.

The relay now builds a self-signed development TLS configuration, advertises the
lanparty ALPN, enables QUIC datagram buffers, binds the configured UDP address,
prints the actual local address, and waits for Ctrl-C before closing the
endpoint. The generated certificate is explicitly a development placeholder;
production certificate and client trust handling remain future work.

The rustls dependency is pinned to the ring provider to match Quinn's selected
crypto backend and avoid process-level provider ambiguity at runtime.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- timeout 2s cargo run -p lanparty-relay -- --listen 127.0.0.1:0 || test $? -eq 124

Refs: PLAN.md public relay QUIC data path
Refs: https://docs.rs/quinn/0.11.9
2026-05-21 17:38:56 +02:00
ddidderr be9596c188 feat(relay): add runtime CLI config
Replace the placeholder relay binary with a typed command-line configuration
entry point. This gives the future QUIC server loop the listen endpoint and room
limit configuration it needs without mixing command parsing into networking or
room-state code.

The relay now accepts --listen as either a socket address or an explicit UDP
shorthand such as 443/udp, defaults to 0.0.0.0:443/udp, and validates that the
per-room client limit is positive. The binary currently reports the parsed
configuration and clearly states that the QUIC server loop is not wired yet, so
this commit does not pretend to provide a working relay.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo run -p lanparty-relay -- --listen 443/udp

Refs: PLAN.md public relay --listen requirement
2026-05-21 17:32:47 +02:00
ddidderr b3d1a9c046 feat(relay): add Ethernet forwarding decisions
Add socket-free relay forwarding logic for Ethernet datagrams. The future QUIC
relay loop can now ask the room registry which peer IDs should receive a frame
instead of embedding switching policy in network IO code.

Forwarding validates that the ingress peer belongs to the room, drops malformed
Ethernet frames, rejects client frames whose source MAC does not match the MAC
announced during admission, never reflects frames back to ingress, routes known
client unicasts directly, and floods broadcast/multicast or unknown unicast
frames to the other room peers. The decision reports shared observability action
and drop-reason values so the networking layer can log consistently.

This still does not send bytes over QUIC; it only defines the room-local switch
decision that the datagram loop will use.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings

Refs: PLAN.md Switching model
2026-05-21 17:28:17 +02:00
ddidderr 879cb689a4 feat(ctrl): add control stream frame codec
Add the framing layer that turns typed control messages into bytes for reliable
QUIC streams. This keeps the codec next to the control schema while leaving the
actual QUIC read/write loops for a later relay/client/gateway slice.

The codec uses a four-byte big-endian length prefix followed by JSON. JSON is a
phase-1 choice for inspectability during manual tunnel bring-up; the explicit
length prefix keeps stream parsing deterministic and the 64 KiB cap prevents a
peer from announcing unbounded control payloads. Decoding validates the message
after deserialization so forged stream bytes cannot bypass constructor checks.

The next networking slice can use complete_control_frame_len to split a stream
buffer and decode_control_frame once a complete frame is available.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings

Refs: PLAN.md reliable QUIC control stream requirements
2026-05-21 17:25:26 +02:00
ddidderr d1e6530829 feat(relay): add room admission state
Add a tested relay room layer before introducing QUIC socket handling. The relay
now has a focused place to enforce room membership rules instead of mixing those
rules into the future networking loop.

RoomRegistry accepts validated endpoint hellos, assigns room and peer IDs,
returns server welcome data, limits clients per room, permits only one gateway,
rejects duplicate client MACs, and keeps the room TAP MTU stable once the first
peer joins. A later peer must support the existing room MTU rather than silently
shrinking it after an earlier client may already have configured its TAP adapter.

The networking pieces still need to call this layer from the reliable control
stream and use the resulting peer metadata for datagram forwarding.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings

Refs: PLAN.md relay responsibilities and MAC identity
2026-05-21 17:21:46 +02:00
ddidderr 7aeaa0aeb9 feat(obs): add shared diagnostics models
Add the observability vocabulary needed for phase-1 frame logging and client
status reporting. Runtime crates can now emit structured events without each
binary inventing separate field names for the same tunnel state.

The new models cover frame direction, action, drop reason, parsed Ethernet frame
logs, malformed frame logs, tunnel counters, relay/QUIC/TAP client diagnostics,
and user-facing diagnostic messages. TunnelStats now lives in lanparty-obs and
is re-exported by lanparty-ctrl so stats remain one shared type whether they are
logged locally or carried over the control stream.

This still does not add logging sinks or tracing integration; those should be
wired in when the relay, gateway, and client loops exist.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings

Refs: PLAN.md Logging / diagnostics
2026-05-21 17:17:44 +02:00
ddidderr a9c143e447 feat(ctrl): define tunnel control messages
Add the reliable control-plane schema that will run over QUIC streams. This
covers the phase-1 handshake shape without mixing in relay sockets, TAP access,
or gateway packet IO.

The schema includes endpoint hello messages with role, room, MAC, and datagram
budget, plus server welcome, rejection, peer lifecycle, stats, and disconnect
messages. Constructors and validation enforce room-code syntax, client MAC
identity rules, reserved peer IDs, and effective TAP MTU limits. Decoded control
messages can be validated explicitly so serde input cannot silently bypass the
same invariants.

The actual stream codec remains future work; this commit only fixes the typed
contract the codec will carry.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings

Refs: PLAN.md reliable QUIC control stream requirements
2026-05-21 17:12:56 +02:00
ddidderr f06760d1ac feat(proto): add tunnel frame primitives
Build the shared protocol contract that the client, gateway, and relay will use
for Ethernet datagrams. The MVP needs these pieces agreed on before socket or
TAP work can be reasoned about safely.

This adds strict MAC parsing and client identity validation, Ethernet header
inspection, fixed overlay datagram encoding and decoding, and MTU helpers for
the no-fragmentation QUIC datagram design. The protocol crate stays
transport-agnostic so platform and network code can depend on it without
pulling in OS-specific behavior.

Remaining work is to put these primitives behind the control-plane handshake,
relay forwarding loop, Windows TAP client, and Linux AF_PACKET gateway.

Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings

Refs: PLAN.md Phase 1: prove the illusion
2026-05-21 17:09:01 +02:00
ddidderr 3c395db3df chore: first project structure 2026-05-21 16:55:51 +02:00