Commit Graph
219 Commits
Author SHA1 Message Date
Johnathan Corgan 801a5ab222 Integrate discovery protocol into data plane for multi-hop routing
Wire the discovery protocol into the data plane path so that
find_next_hop() consults route_cache as a fallback when coord_cache
has no entry. When session initiation fails due to missing routes,
trigger discovery (initiate_lookup) instead of immediately sending
ICMPv6 Destination Unreachable. On discovery completion, retry
session initiation for any pending TUN packets.

Changes:
- find_next_hop() falls back to route_cache when coord_cache misses
- handle_tun_outbound() triggers discovery on session failure
- handle_lookup_response() retries session after discovery completes
- handle_coords_required() triggers discovery for missing coordinates
- Add pending_lookups deduplication with 10-second timeout
- Periodic cleanup of stale lookups in tick handler
- New test: route_cache fallback verification in find_next_hop
2026-02-14 16:59:40 +00:00
Johnathan Corgan 2a37e2716f DNS responder for .fips domain, two-node UDP example
Add DNS responder that resolves <npub>.fips queries to FipsAddress IPv6
addresses. Resolution is pure computation (npub → NodeAddr → IPv6) with
identity cache priming as a side effect, enabling subsequent TUN packet
routing to non-peer destinations.

- New dns.rs module: resolve_fips_query(), handle_dns_packet() using
  simple-dns crate, run_dns_responder() async UDP server loop
- DnsConfig in config.rs: enabled, bind_addr (127.0.0.1), port (5354)
- Fourth select! arm in RX event loop for DNS identity channel
- DNS task spawn/abort in node lifecycle
- 10 new tests (420 total)

Add examples/two-node-udp/ with standalone walkthrough for testing two
FIPS nodes in Linux network namespaces over a veth pair. Includes
network diagram, config files, DNS routing setup, and troubleshooting.
2026-02-13 11:35:24 +00:00
Johnathan Corgan 7776c0f4ca Data plane integration: TUN outbound → session encryption → mesh delivery
Wire the TUN reader to the session layer for end-to-end IPv6 packet
delivery through the mesh. TUN reader forwards FIPS-destined packets
(fd::/8) to Node via tokio::sync::mpsc channel. Identity cache maps
FipsAddress prefix bytes to NodeAddr + PublicKey for reverse address
resolution, populated at peer promotion and session creation. Outbound
handler validates IPv6, looks up destination identity, routes through
established session or initiates new one with pending packet queue
(16/dest, 256 dests max). Queue flushed on session establishment.
ICMPv6 Destination Unreachable for unknown destinations. 6 new tests
including 3-node forwarded TUN data and pending queue flush. 410 tests
pass, clean build.
2026-02-13 04:19:30 +00:00
Johnathan Corgan 51597b6a5a End-to-end session establishment with 100-node bidirectional data test
Implement Noise IK session handshake between arbitrary endpoints, carried
inside SessionDatagram envelopes through the mesh. Sessions use a three-state
machine (Initiating → Established for initiator, Responding → Established
for responder on first DataPacket). Includes session initiation API,
encrypted data transfer, simultaneous initiation tie-break, error signal
handlers (CoordsRequired, PathBroken), and local delivery wiring in the
forwarding handler.

New files: node/session.rs (state types), handlers/session.rs (~500 lines,
all session message handlers + send path), tests/session.rs (11 tests).

100-node integration test establishes sessions across random topology,
sends bidirectional encrypted datagrams through injected TUN channels,
and verifies 200/200 deliveries with 100% session establishment. Reports
routing statistics including avg 4.1 link hops per datagram.

404 tests pass (up from 393).
2026-02-13 03:13:21 +00:00
Johnathan Corgan 85ca8a9ae4 100-node discovery integration test with per-node batched lookups
Add test_discovery_100_nodes: 100-node random topology (250 edges),
990 lookups (each node resolves every 10th peer), per-source-node
batching with quiescence detection. Validates 100% lookup resolution
via route_cache verification. 393 tests pass.
2026-02-12 18:15:34 +00:00
Johnathan Corgan 9a7fa921ab Discovery protocol: LookupRequest/LookupResponse handlers
Implement the coordinate discovery protocol with flood-based lookup
and reverse-path response routing.

Wire format: LookupRequest (0x30) encode/decode with TTL, visited
bloom filter, origin coords. LookupResponse (0x31) encode/decode
with target coords and Schnorr proof signature.

Handler logic: request dedup by request_id, visited filter loop
prevention, TTL enforcement, lazy purge of expired entries (10s).
Response routing: originator caches route in route_cache, transit
nodes reverse-path forward via recent_requests.

Node state: RecentRequest struct, route_cache (RouteCache), and
recent_requests map for dedup + reverse-path forwarding.

13 handler tests (9 unit + 4 integration) plus 4 protocol tests.
392 tests pass, clean build.
2026-02-12 14:46:33 +00:00
Johnathan Corgan 009101ee4a SessionDatagram forwarding handler with coordinate cache warming
Add handle_session_datagram handler replacing the 0x40 dispatch stub.
Transit nodes now decode the datagram envelope, enforce hop limits,
warm coordinate caches from SessionSetup/SessionAck/DataPacket payloads,
route via find_next_hop, and generate CoordsRequired/PathBroken error
signals on routing failure.

14 new tests covering decode errors, hop limit enforcement, local
delivery, cache warming for all session message types, single-hop and
multi-hop forwarding through live node chains, error signal generation,
and cache warming enabling subsequent routing. 375 tests pass.
2026-02-12 13:39:34 +00:00
Johnathan Corgan 62ce267677 Session message encode/decode, disconnect and routing integration tests
Add encode/decode for all session-layer message types (SessionSetup,
SessionAck, DataPacket, CoordsRequired, PathBroken) and SessionDatagram
link-layer envelope. Wire format follows design doc §8.0-8.6 with
address-only coordinate serialization (16 bytes/entry).

Struct additions: handshake_payload on SessionSetup/SessionAck, flags
on SessionAck, optional src_coords/dest_coords on DataPacket for route
cache warming (COORDS_PRESENT flag).

New disconnect integration tests verify cascading cleanup in multi-node
networks: chain peer removal, star hub departure, chain partition with
bloom filter update verification, all reason codes.

New routing tests validate bloom-filter-only transit behavior and peer
removal effects: confirm transit nodes without cached coords return None
(loop prevention), routing stops after partition, source-only coords
insufficient for multi-hop delivery.

361 tests pass (up from 335), zero warnings.
2026-02-12 13:01:29 +00:00
Johnathan Corgan d41009b778 SessionDatagram redesign: add src_addr, reclassify error signals
Add src_addr to SessionDatagram envelope (34-byte header: msg_type +
src_addr + dest_addr + hop_limit) so transit routers can route error
signals back to the packet's originator.

Reclassify CoordsRequired/PathBroken as link-layer error signals
(plaintext inside SessionDatagram) rather than e2e encrypted session
messages. Transit routers generate these when forwarding fails and
route them to src_addr; if source is also unreachable, drop silently.

Remove redundant src_addr/dest_addr/hop_limit from SessionSetup,
SessionAck, and DataPacket (now in envelope). DataPacket header
reduced from 36 to 4 bytes. Remove PathBroken.original_src.

Fix routing loop vulnerability: gate bloom filter path on having
cached dest_coords to prevent blind forwarding between peers.
Simplify select_best_candidate() to require coordinates.

Fix gossip protocol type codes (0x11->0x20, 0x12->0x30, 0x13->0x31)
for consistency across all design docs.

All 5 design docs updated and cross-checked for consistency.
335 tests pass, zero warnings.
2026-02-12 11:32:45 +00:00
Johnathan Corgan 2f8e97c0ab Implement greedy routing with bloom filter priority
Add the full next-hop routing algorithm to Node::find_next_hop():
- Local delivery, direct peer, bloom filter candidates, greedy tree
  routing fallback, with (link_cost, tree_distance, node_addr) ordering
- select_best_candidate() scores by peer→dest distance (not us→peer)
  with self-distance check to prevent routing loops
- TreeState::find_next_hop() for greedy tree routing with progress
  guarantee
- ActivePeer::link_cost() placeholder (constant 1.0) for future link
  quality metrics

Add routing tests including 100-node all-pairs reachability simulation
(9900/9900 delivered, 0 loops, avg 4.0 hops, max 8).

Update fips-routing.md to reflect bloom filter routing as the primary
forwarding mechanism, with greedy tree routing as fallback during
convergence windows.
2026-02-11 16:55:14 +00:00
Johnathan Corgan 066865ddd7 Split protocol.rs into protocol/ directory, standardize imports, clean lib.rs
- Replace use super::*; in node/lifecycle.rs with explicit imports,
  remove 10 resulting unused imports from node/mod.rs
- Split protocol.rs (1838 lines) into protocol/ directory:
  mod.rs (re-exports), error.rs, link.rs, tree.rs, filter.rs,
  discovery.rs, session.rs — each with co-located tests
- Remove unused flat re-exports from lib.rs for internal utility
  modules (wire, index, rate_limit, icmp, noise, tun)
- 316 tests pass, zero warnings
2026-02-11 05:02:02 +00:00
Johnathan Corgan cc29c51cac Refactor node/handlers.rs and node/tests.rs into subdirectories
Split handlers.rs (986 lines) into handlers/ with 5 subfiles organized
by responsibility: rx_loop, encrypted, handshake, dispatch, timeout.

Split tests.rs (2350 lines) into tests/ with 4 subfiles: unit tests,
handshake integration, spanning tree convergence, and bloom filter tests.
Shared test helpers extracted to tests/mod.rs.

Visibility adjusted from pub(super) to pub(in crate::node) for handler
methods now two levels deep. Unused imports cleaned up in node/mod.rs.

All 316 tests pass, zero warnings.
2026-02-11 03:49:45 +00:00
Johnathan Corgan 5d7af5b478 Implement FilterAnnounce send/receive, remove TTL/K-hop scoping
Add bloom filter reachability announcement protocol:
- FilterAnnounce encode/decode (wire format 0x20, 1035 bytes)
- node/bloom.rs: send/receive with debounce, split-horizon loop prevention
- Handler wiring: dispatch, tick, peer promotion/removal, cross-connection
- Five integration tests: 10-node, star, chain, ring, 100-node convergence

Remove TTL/K-hop mechanism from code and design docs after discovering
that per-entry TTL scoping is fundamentally incompatible with flat bloom
filter merge + regeneration architecture. Each node re-originates filters
with fresh TTL, making propagation unbounded regardless of TTL value.
Split-horizon remains the primary loop prevention mechanism.

Document spanning tree known limitations (v1) in spanning-tree-dynamics.md.

316 tests pass, clean build, zero warnings.
2026-02-11 03:16:25 +00:00
Johnathan Corgan 7bc5b21c3a Fix cross-connection session alignment for simultaneous auto-connect
When both nodes simultaneously initiate connections, each promotes its
inbound handshake first, leaving both sides with mismatched Noise sessions
and session indices. Resolve this in handle_msg2 using the existing
tie-breaker (smaller node_addr's outbound wins):

- Winner (smaller node): swaps ActivePeer to outbound session + indices
  via new replace_session() method
- Loser (larger node): keeps inbound session and original their_index
  (already references winner's outbound our_index from msg1)

Key insight: the loser must NOT update their_index from msg2, because the
msg2 sender_idx carries the winner's inbound index which becomes stale
after the winner swaps.

Defer outbound connection cleanup from promote_connection() to handle_msg2()
so outbound session state is available for the swap.

Verified with live two-node test over UDP in network namespaces: both nodes
connect, exchange TreeAnnounce, and converge to spanning tree within ~2s.
2026-02-11 01:41:21 +00:00
Johnathan Corgan 74eaeab3d4 Add spanning tree integration tests, fix parent ancestry propagation bug
Multi-node integration test infrastructure with 100-node random topology
convergence test (5 topology variants). Two-phase drain loop processes
Noise IK handshakes and TreeAnnounce cascades over live UDP until
quiescence.

Fix parent ancestry propagation in handle_tree_announce: when a node's
current parent updates its tree state (e.g., learning a new root from
upstream), coordinates must be recomputed even though the parent itself
didn't change. Without this, chains longer than 2 hops fail to converge.
2026-02-11 00:09:11 +00:00
Johnathan Corgan 1c2cda480d Implement spanning tree announcement send/receive protocol
Add TreeAnnounce v1 wire format with versioned encoding (version byte
0x01), slim ancestry entries (32 bytes each, no per-entry signatures),
and transitive trust model where only the direct peer's declaration
signature is verified.

Key changes:
- Enrich TreeCoordinate with CoordEntry metadata (sequence, timestamp)
- TreeAnnounce encode/decode with roundtrip tests
- Per-peer rate limiting (500ms minimum interval) on ActivePeer
- Parent selection: depth-based algorithm with broken-path detection
- New node/tree.rs module: send/receive, periodic refresh, cleanup
- Wire up dispatch, initial announce on promotion, tick integration
- Update gossip protocol design doc with trust model (§2.7)

304 tests pass, clean build.
2026-02-10 23:35:09 +00:00
Johnathan Corgan 9d36977ec8 Fix pending cross-connection cleanup, add auto-retry with exponential backoff
Fix promote_connection() to detect and clean up pending outbound
handshakes to the same peer, not just already-promoted peers. Previously,
when an inbound handshake completed while an outbound was still pending,
the outbound would linger until the 30s timeout.

Add auto-retry for failed outbound connections to auto-connect peers:
- New RetryState struct and node/retry.rs module
- Exponential backoff (default 5s base, max 5 attempts)
- Config: node.max_retries, node.base_retry_interval_secs
- check_timeouts() schedules retries, rx loop processes them
- promote_connection() clears retry state on success
- Remove unused PeerConnection retry fields (state now at Node level)

Move cleanup_stale_connection() logging to callers for context-appropriate
messages.

289 tests pass, clean build.
2026-02-10 22:25:31 +00:00
Johnathan Corgan 4445c46066 Fix secp256k1 parity in Noise IK, add disconnect protocol, cross-connection handling, timeout cleanup
Noise IK parity fix:
- Pre-message hash normalizes responder static key to even parity (0x02)
  so initiator and responder hash chains match regardless of actual parity
- ECDH uses shared_secret_point() + SHA-256(x-only) instead of
  SharedSecret::new() which includes a parity-dependent version byte
- Fixes handshake failure for ~50% of keys when initiator has only npub

Graceful disconnect protocol (link message 0x50):
- DisconnectReason enum with 8 reason codes
- Disconnect struct with encode/decode
- send_encrypted_link_message() reusable helper
- handle_disconnect() with immediate peer removal
- send_disconnect_to_all_peers() called during Node::stop()

Cross-connection fix in handle_msg1():
- addr_to_link check now distinguishes inbound duplicates (reject) from
  outbound links (cross-connection, allow and resolve via tie-breaker)
- remove_link() only clears addr_to_link if entry maps to same link_id
- Link cleanup and addr_to_link restoration in cross-connection branches

Handshake timeout cleanup:
- RX loop uses tokio::select! with 1-second interval tick
- check_timeouts() scans for stale (>30s) and failed connections
- cleanup_stale_connection() removes all associated state

Tests: 279 passing (4 new: cross-connection, stale cleanup, failed
cleanup, odd-parity handshake)
2026-02-10 21:25:26 +00:00
Johnathan Corgan e6f63678ba Refactor node.rs into node/ module directory
Split 2,597-line node.rs into four files under src/node/:
- mod.rs: types, struct definition, constructors, accessors, Debug impl
- lifecycle.rs: start(), stop(), peer connection initiation
- handlers.rs: RX loop, packet dispatch, message handlers, promote_connection
- tests.rs: full test suite (35 tests including integration tests)

No functional changes. All 267 tests pass, no new clippy warnings.
2026-02-10 19:05:05 +00:00