From 7dc0b7fc52541578a573cb2d73a18b5c0f8d9b9c Mon Sep 17 00:00:00 2001 From: Johnathan Corgan Date: Mon, 2 Feb 2026 03:39:13 +0000 Subject: [PATCH] Session 53: Route cache warming and session protocol refinements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Session protocol (fips-session-protocol.md): - Clarified DNS entry point applies to IP-based apps; native FIPS uses npub - Updated transport failover examples (UDP → WiFi, WiFi → LTE) - Added roaming description to session independence section - Expanded §5 with coords-on-demand mechanism for route cache recovery - Added Noise IK vs XK privacy tradeoff note to §6 Routing (fips-routing.md): - DataPacket now has optional coordinates (COORDS_PRESENT flag) - Updated handle_data_packet to cache coords from packets - Sender state machine: WARM/COLD based on CoordsRequired errors - Updated packet type summary table Architecture (fips-architecture.md): - Fixed cross-references to session protocol sections --- docs/design/fips-architecture.md | 3 +- docs/design/fips-routing.md | 71 ++++++++++++---- docs/design/fips-session-protocol.md | 121 ++++++++++++++++----------- 3 files changed, 126 insertions(+), 69 deletions(-) diff --git a/docs/design/fips-architecture.md b/docs/design/fips-architecture.md index 25016e5..08d6887 100644 --- a/docs/design/fips-architecture.md +++ b/docs/design/fips-architecture.md @@ -931,7 +931,8 @@ this node wants to reach. This is the primary cache for endpoint nodes. > **Terminology note**: These parameters configure *routing sessions*—hop-by-hop > cached state at intermediate routers. For *crypto session* (end-to-end > encryption) parameters, see the Crypto Session section below. See -> [fips-session-protocol.md](fips-session-protocol.md) §5 for the distinction. +> [fips-session-protocol.md](fips-session-protocol.md) §3 for crypto sessions +> and §5 for route cache warming. | Parameter | Type | Default | Description | |-----------|------|---------|-------------| diff --git a/docs/design/fips-routing.md b/docs/design/fips-routing.md index 2f99f3e..f49a4a0 100644 --- a/docs/design/fips-routing.md +++ b/docs/design/fips-routing.md @@ -298,8 +298,8 @@ No global routing tables. Each node makes purely local decisions. > **Terminology note**: This section describes *routing sessions*—hop-by-hop > cached state at intermediate routers. FIPS also has *crypto sessions*—end-to-end > authenticated encryption between source and destination. See -> [fips-session-protocol.md](fips-session-protocol.md) §5 for the distinction and §6 -> for crypto session details. +> [fips-session-protocol.md](fips-session-protocol.md) §3 for crypto session details +> and §5 for route cache warming. ### Routing Session Purpose @@ -349,13 +349,18 @@ struct SessionAck { handshake_payload: Option>, // Noise IK message 2 } -/// Minimal data packet +/// Data packet with optional coordinates struct DataPacket { - flags: u8, + flags: u8, // Bit 0: COORDS_PRESENT hop_limit: u8, payload_length: u16, src_addr: Ipv6Addr, // 16 bytes dest_addr: Ipv6Addr, // 16 bytes + + // Optional: present only if COORDS_PRESENT flag is set + src_coords: Option>, + dest_coords: Option>, + payload: Vec, } @@ -375,9 +380,10 @@ struct CoordsRequired { | payload_length | 2 bytes | | src_addr | 16 bytes | | dest_addr | 16 bytes | -| **Total header** | **36 bytes** | +| **Minimal header** | **36 bytes** | -Comparable to IPv6 (40 bytes). No coordinates in data packets. +Comparable to IPv6 (40 bytes). When COORDS_PRESENT flag is set, coordinates +add variable overhead based on tree depth (typically 200-400 bytes). ### Routing Session Setup Flow @@ -419,6 +425,23 @@ impl Router { } fn handle_data_packet(&mut self, packet: DataPacket, from: PeerId) { + // If packet carries coordinates, cache them + if packet.flags & COORDS_PRESENT != 0 { + if let (Some(src_coords), Some(dest_coords)) = + (&packet.src_coords, &packet.dest_coords) + { + self.coord_cache.insert(packet.dest_addr, CacheEntry { + coords: dest_coords.clone(), + expires: now() + CACHE_TTL, + }); + self.coord_cache.insert(packet.src_addr, CacheEntry { + coords: src_coords.clone(), + expires: now() + CACHE_TTL, + }); + } + } + + // Route using cache (now populated if coords were present) match self.coord_cache.get(&packet.dest_addr) { Some(entry) => { entry.last_used = now(); @@ -467,13 +490,16 @@ by: When a router's cache entry is evicted mid-session: ```text -1. Data packet arrives, cache miss +1. Data packet arrives (minimal header), cache miss 2. Router sends CoordsRequired to packet source -3. Source receives error, re-sends SessionSetup (or packet with coords) -4. Path warms again, data flow resumes +3. Source marks route as cold +4. Source resends with COORDS_PRESENT flag set +5. Router caches coordinates from packet, forwards +6. After N successful packets, source clears flag ``` -From application perspective: brief latency spike, transparent recovery. +The crypto session remains active throughout—only routing state is refreshed. +From application perspective: one packet delayed, transparent recovery. ### Sender Behavior @@ -481,21 +507,29 @@ From application perspective: brief latency spike, transparent recovery. impl Sender { fn send(&mut self, dest: Ipv6Addr, data: &[u8]) { if !self.session_established(dest) { - // Need to establish session first + // Need to establish crypto session first let dest_coords = self.discover_or_cached(dest)?; self.send_session_setup(dest, &dest_coords); self.await_session_ack(dest)?; } - self.send_data_packet(dest, data); + // Check route state + let include_coords = self.route_state(dest) == RouteCold; + self.send_data_packet(dest, data, include_coords); } fn handle_coords_required(&mut self, err: CoordsRequired) { - // Path went cold, re-establish - self.mark_session_cold(err.dest_addr); - self.send_session_setup(err.dest_addr, &self.cached_coords(err.dest_addr)); + // Route cache expired at intermediate router + // Crypto session still valid - just need to re-warm route + self.mark_route_cold(err.dest_addr); + // Next send() will include coordinates } } + +enum RouteState { + RouteWarm, // Send minimal headers + RouteCold, // Include coordinates until warm +} ``` --- @@ -509,12 +543,13 @@ impl Sender { | LookupResponse | Return coordinates | ~400 bytes | Reply to discovery | | SessionSetup | Warm router caches + crypto init | ~400-700 bytes | Before data transfer | | SessionAck | Confirm session + crypto response | ~300-500 bytes | Session confirmation | -| DataPacket | Application data | 36 bytes + payload | Bulk of traffic | -| CoordsRequired | Request retransmit | ~50 bytes | Cache miss recovery | +| DataPacket | Application data | 36 bytes + payload (minimal) | Bulk of traffic | +| DataPacket | With coordinates | ~300-500 bytes + payload | After CoordsRequired | +| CoordsRequired | Request coords in next packet | ~50 bytes | Cache miss recovery | > **Note**: SessionSetup/SessionAck sizes vary based on coordinate depth and > whether they carry crypto handshake payloads (combined establishment per -> [fips-session-protocol.md](fips-session-protocol.md) §5.5). +> [fips-session-protocol.md](fips-session-protocol.md) §3.4 and §5.1). --- diff --git a/docs/design/fips-session-protocol.md b/docs/design/fips-session-protocol.md index d7c681e..a33c436 100644 --- a/docs/design/fips-session-protocol.md +++ b/docs/design/fips-session-protocol.md @@ -9,6 +9,10 @@ including peer discovery, authentication, tree announcements, and data routing. ## 1. Application-Initiated Traffic Flow +> **Note**: This section applies to traditional IP-based applications using the +> TUN interface. Applications using the native FIPS datagram service address +> destinations directly by npub, and routing proceeds from there without DNS. + Traffic flow begins at the application layer with a DNS query, which triggers a cascade of events through the FIPS stack. @@ -17,10 +21,10 @@ a cascade of events through the FIPS stack. An application wants to send IPv6 traffic to another FIPS node, identified by an npub. The flow: -1. **DNS Query**: Application queries a local FIPS DNS server for the npub - (format TBD - perhaps `npub1xxx...xxx.fips` or similar) +1. **DNS Query**: Application queries the local FIPS DNS service for the npub + mapping to an IPv6 address -2. **FIPS DNS Server** performs two functions: +2. **FIPS DNS service** performs two functions: - **Address derivation**: Converts the npub to an identity and derives the corresponding `fd::/8` IPv6 address - **Cache priming**: Stores the identity mapping (IPv6 address ↔ npub ↔ node_id) @@ -34,6 +38,8 @@ an npub. The flow: 5. **TUN Processing**: When the packet arrives at the TUN, FIPS already has the cached mapping from the DNS lookup, enabling immediate routing decisions +6. **Note**: This identity cache is only necessary when using the FIPS IPv6 shim + ### 1.2 Design Rationale Using DNS as the trigger point ensures the routing cache is populated *before* @@ -194,12 +200,13 @@ management) and transmitted after the session is established. FIPS sessions exist above the routing layer. A session between two npubs survives: -- Transport failover (UDP → Tor → back to UDP) +- Transport failover (UDP → WiFi → back to UDP) - Route changes (different intermediate hops) -- Transport address changes on either end +- Transport address changes on either end (WiFi → LTE → WiFi) The session is bound to **npub identities**, not transport addresses or routing -paths. +paths. This allows FIPS endpoints to roam over their transports as needed while +maintaining an established session. ### 3.4 Session Establishment Flow @@ -207,7 +214,7 @@ FIPS uses Noise IK for session establishment. The initiator knows the destination's npub; the responder learns the initiator's identity from the handshake. This is the same asymmetry as link-layer connections. -The handshake is carried inside SessionSetup/SessionAck messages (see §5.5), +The handshake is carried inside SessionSetup/SessionAck messages (see §5.1), which also establish routing session state at intermediate nodes. ### 3.5 Simultaneous Session Initiation (Crossing Hellos) @@ -255,61 +262,67 @@ Route cache entries: --- -## 5. Session Terminology +## 5. Route Cache Warming -FIPS uses two distinct session concepts at different layers: +### 5.1 Initial Warming via Handshake -| Term | Layer | Purpose | Endpoints | -|---------------------|-------------|------------------------------|------------------------| -| **Crypto Session** | End-to-end | Authentication + encryption | Source ↔ Destination | -| **Routing Session** | Hop-by-hop | Cache coordinates at routers | Along the path | +The crypto session handshake (SessionSetup/SessionAck) warms route caches at +intermediate routers as it transits. Each message carries the sender's +coordinates; routers extract and cache `(src_addr, dest_addr) → next_hop` for +both directions. After the handshake completes, data packets use minimal +36-byte headers and routers forward based on cached routes. -### 5.1 Crypto Session +### 5.2 Cache Miss Recovery -- Established between two npub identities -- Provides confidentiality and authenticity via Noise IK -- Survives route changes and transport failover -- Keyed by: `(local_npub, remote_npub)` +When an intermediate router's cache entry expires or is evicted, it cannot +forward data packets (which carry only addresses, not coordinates). The router +returns a CoordsRequired error to the sender. -### 5.2 Routing Session +The crypto session remains valid—only the routing state is lost. Recovery uses +coords-on-demand: data packets include an optional coordinates field. When the +sender receives CoordsRequired: -- Warms coordinate caches at intermediate routers -- Enables minimal 36-byte data packet headers -- Must be re-established when router caches expire -- Keyed by: `(src_addr, dest_addr)` at each router +1. Sender marks the route as "cold" +2. Subsequent data packets include coordinates (flag bit set) +3. Routers along the path cache coordinates as packets transit +4. Once route is warm again, sender clears the flag and resumes minimal headers -### 5.3 Combined Establishment +This avoids a full SessionSetup round-trip for what is purely a routing cache +refresh. -Both sessions are established together: the routing session setup carries the -crypto handshake, minimizing round-trips. +### 5.3 DataPacket Coordinate Flag + +The DataPacket `flags` field includes a `COORDS_PRESENT` bit: + +| Bit | Meaning | +|-----|------------------------------------------------------| +| 0 | COORDS_PRESENT - coordinates follow the fixed header | + +When set, the packet includes `src_coords` and `dest_coords` after the standard +header fields. Routers process these coordinates the same way as SessionSetup: +cache both directions and forward using greedy routing. + +### 5.4 Sender State Machine ```text -1. Route discovery (if needed) - └─► LookupRequest/Response → obtain destination coordinates - -2. SessionSetup + Crypto Init - └─► Source sends SessionSetup containing: - - src/dest coordinates (for router caching) - - Noise IK handshake initiation (for destination) - └─► Routers cache coordinates as packet transits - └─► Destination receives crypto init, begins handshake - -3. SessionAck + Crypto Response - └─► Destination sends SessionAck containing: - - Its coordinates (for reverse path caching) - - Noise IK handshake response - └─► Routers cache reverse path - └─► Source completes crypto handshake - -4. Data flow - └─► Encrypted payloads with minimal 36-byte headers - └─► Both crypto session and routing session now active + ┌──────────────┐ + │ WARM │ ◄── Normal: send minimal headers + └──────┬───────┘ + │ CoordsRequired received + ▼ + ┌──────────────┐ + │ COLD │ ◄── Send packets with coords + └──────┬───────┘ + │ N packets sent successfully + ▼ + ┌──────────────┐ + │ WARM │ + └──────────────┘ ``` -SessionSetup and SessionAck messages carry both routing information (coordinates -for router caching) and crypto payload (handshake messages, opaque to routers). -Routers process the routing portion and forward; only endpoints process the -crypto portion. +The sender transitions back to WARM after sending a configurable number of +packets with coordinates (e.g., 3) without receiving CoordsRequired. This +provides confidence that caches along the path are populated. --- @@ -325,6 +338,14 @@ FIPS uses two independent Noise Protocol handshakes at different layers: Both use `Noise_IK_secp256k1_ChaChaPoly_SHA256` with the same cryptographic primitives, but with separate keys and sessions. +> **Privacy note**: Noise IK does not provide initiator anonymity if the +> responder's static key is compromised—an attacker who obtains the responder's +> nsec can decrypt the initiator's identity from captured handshake messages. +> Noise XK would protect initiator identity in this scenario, but requires an +> additional round-trip (3 handshake messages vs 2), increasing session setup +> from 3 packets to 4. Further deployment experience is needed to evaluate +> whether the privacy benefit justifies the latency cost. + ### 6.1 Why Two Layers? **Link encryption** protects against passive observers on each hop but allows