Session 53: Route cache warming and session protocol refinements

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
This commit is contained in:
Johnathan Corgan
2026-02-02 03:39:13 +00:00
parent a8115f7622
commit 7dc0b7fc52
3 changed files with 126 additions and 69 deletions
+2 -1
View File
@@ -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 > **Terminology note**: These parameters configure *routing sessions*—hop-by-hop
> cached state at intermediate routers. For *crypto session* (end-to-end > cached state at intermediate routers. For *crypto session* (end-to-end
> encryption) parameters, see the Crypto Session section below. See > 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 | | Parameter | Type | Default | Description |
|-----------|------|---------|-------------| |-----------|------|---------|-------------|
+53 -18
View File
@@ -298,8 +298,8 @@ No global routing tables. Each node makes purely local decisions.
> **Terminology note**: This section describes *routing sessions*—hop-by-hop > **Terminology note**: This section describes *routing sessions*—hop-by-hop
> cached state at intermediate routers. FIPS also has *crypto sessions*—end-to-end > cached state at intermediate routers. FIPS also has *crypto sessions*—end-to-end
> authenticated encryption between source and destination. See > authenticated encryption between source and destination. See
> [fips-session-protocol.md](fips-session-protocol.md) §5 for the distinction and §6 > [fips-session-protocol.md](fips-session-protocol.md) §3 for crypto session details
> for crypto session details. > and §5 for route cache warming.
### Routing Session Purpose ### Routing Session Purpose
@@ -349,13 +349,18 @@ struct SessionAck {
handshake_payload: Option<Vec<u8>>, // Noise IK message 2 handshake_payload: Option<Vec<u8>>, // Noise IK message 2
} }
/// Minimal data packet /// Data packet with optional coordinates
struct DataPacket { struct DataPacket {
flags: u8, flags: u8, // Bit 0: COORDS_PRESENT
hop_limit: u8, hop_limit: u8,
payload_length: u16, payload_length: u16,
src_addr: Ipv6Addr, // 16 bytes src_addr: Ipv6Addr, // 16 bytes
dest_addr: Ipv6Addr, // 16 bytes dest_addr: Ipv6Addr, // 16 bytes
// Optional: present only if COORDS_PRESENT flag is set
src_coords: Option<Vec<NodeId>>,
dest_coords: Option<Vec<NodeId>>,
payload: Vec<u8>, payload: Vec<u8>,
} }
@@ -375,9 +380,10 @@ struct CoordsRequired {
| payload_length | 2 bytes | | payload_length | 2 bytes |
| src_addr | 16 bytes | | src_addr | 16 bytes |
| dest_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 ### Routing Session Setup Flow
@@ -419,6 +425,23 @@ impl Router {
} }
fn handle_data_packet(&mut self, packet: DataPacket, from: PeerId) { 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) { match self.coord_cache.get(&packet.dest_addr) {
Some(entry) => { Some(entry) => {
entry.last_used = now(); entry.last_used = now();
@@ -467,13 +490,16 @@ by:
When a router's cache entry is evicted mid-session: When a router's cache entry is evicted mid-session:
```text ```text
1. Data packet arrives, cache miss 1. Data packet arrives (minimal header), cache miss
2. Router sends CoordsRequired to packet source 2. Router sends CoordsRequired to packet source
3. Source receives error, re-sends SessionSetup (or packet with coords) 3. Source marks route as cold
4. Path warms again, data flow resumes 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 ### Sender Behavior
@@ -481,21 +507,29 @@ From application perspective: brief latency spike, transparent recovery.
impl Sender { impl Sender {
fn send(&mut self, dest: Ipv6Addr, data: &[u8]) { fn send(&mut self, dest: Ipv6Addr, data: &[u8]) {
if !self.session_established(dest) { if !self.session_established(dest) {
// Need to establish session first // Need to establish crypto session first
let dest_coords = self.discover_or_cached(dest)?; let dest_coords = self.discover_or_cached(dest)?;
self.send_session_setup(dest, &dest_coords); self.send_session_setup(dest, &dest_coords);
self.await_session_ack(dest)?; 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) { fn handle_coords_required(&mut self, err: CoordsRequired) {
// Path went cold, re-establish // Route cache expired at intermediate router
self.mark_session_cold(err.dest_addr); // Crypto session still valid - just need to re-warm route
self.send_session_setup(err.dest_addr, &self.cached_coords(err.dest_addr)); 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 | | LookupResponse | Return coordinates | ~400 bytes | Reply to discovery |
| SessionSetup | Warm router caches + crypto init | ~400-700 bytes | Before data transfer | | SessionSetup | Warm router caches + crypto init | ~400-700 bytes | Before data transfer |
| SessionAck | Confirm session + crypto response | ~300-500 bytes | Session confirmation | | SessionAck | Confirm session + crypto response | ~300-500 bytes | Session confirmation |
| DataPacket | Application data | 36 bytes + payload | Bulk of traffic | | DataPacket | Application data | 36 bytes + payload (minimal) | Bulk of traffic |
| CoordsRequired | Request retransmit | ~50 bytes | Cache miss recovery | | 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 > **Note**: SessionSetup/SessionAck sizes vary based on coordinate depth and
> whether they carry crypto handshake payloads (combined establishment per > 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).
--- ---
+71 -50
View File
@@ -9,6 +9,10 @@ including peer discovery, authentication, tree announcements, and data routing.
## 1. Application-Initiated Traffic Flow ## 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 Traffic flow begins at the application layer with a DNS query, which triggers
a cascade of events through the FIPS stack. 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 application wants to send IPv6 traffic to another FIPS node, identified by
an npub. The flow: an npub. The flow:
1. **DNS Query**: Application queries a local FIPS DNS server for the npub 1. **DNS Query**: Application queries the local FIPS DNS service for the npub
(format TBD - perhaps `npub1xxx...xxx.fips` or similar) 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 - **Address derivation**: Converts the npub to an identity and derives the
corresponding `fd::/8` IPv6 address corresponding `fd::/8` IPv6 address
- **Cache priming**: Stores the identity mapping (IPv6 address ↔ npub ↔ node_id) - **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 5. **TUN Processing**: When the packet arrives at the TUN, FIPS already has the
cached mapping from the DNS lookup, enabling immediate routing decisions 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 ### 1.2 Design Rationale
Using DNS as the trigger point ensures the routing cache is populated *before* 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 FIPS sessions exist above the routing layer. A session between two npubs
survives: survives:
- Transport failover (UDP → Tor → back to UDP) - Transport failover (UDP → WiFi → back to UDP)
- Route changes (different intermediate hops) - 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 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 ### 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 destination's npub; the responder learns the initiator's identity from the
handshake. This is the same asymmetry as link-layer connections. 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. which also establish routing session state at intermediate nodes.
### 3.5 Simultaneous Session Initiation (Crossing Hellos) ### 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 | The crypto session handshake (SessionSetup/SessionAck) warms route caches at
|---------------------|-------------|------------------------------|------------------------| intermediate routers as it transits. Each message carries the sender's
| **Crypto Session** | End-to-end | Authentication + encryption | Source ↔ Destination | coordinates; routers extract and cache `(src_addr, dest_addr) → next_hop` for
| **Routing Session** | Hop-by-hop | Cache coordinates at routers | Along the path | 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 When an intermediate router's cache entry expires or is evicted, it cannot
- Provides confidentiality and authenticity via Noise IK forward data packets (which carry only addresses, not coordinates). The router
- Survives route changes and transport failover returns a CoordsRequired error to the sender.
- Keyed by: `(local_npub, remote_npub)`
### 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 1. Sender marks the route as "cold"
- Enables minimal 36-byte data packet headers 2. Subsequent data packets include coordinates (flag bit set)
- Must be re-established when router caches expire 3. Routers along the path cache coordinates as packets transit
- Keyed by: `(src_addr, dest_addr)` at each router 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 ### 5.3 DataPacket Coordinate Flag
crypto handshake, minimizing round-trips.
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 ```text
1. Route discovery (if needed) ┌──────────────┐
└─► LookupRequest/Response → obtain destination coordinates │ WARM │ ◄── Normal: send minimal headers
└──────┬───────┘
2. SessionSetup + Crypto Init │ CoordsRequired received
└─► Source sends SessionSetup containing:
- src/dest coordinates (for router caching) ┌──────────────┐
- Noise IK handshake initiation (for destination) │ COLD │ ◄── Send packets with coords
└─► Routers cache coordinates as packet transits └──────┬───────┘
└─► Destination receives crypto init, begins handshake │ N packets sent successfully
3. SessionAck + Crypto Response ┌──────────────┐
└─► Destination sends SessionAck containing: │ WARM │
- 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
``` ```
SessionSetup and SessionAck messages carry both routing information (coordinates The sender transitions back to WARM after sending a configurable number of
for router caching) and crypto payload (handshake messages, opaque to routers). packets with coordinates (e.g., 3) without receiving CoordsRequired. This
Routers process the routing portion and forward; only endpoints process the provides confidence that caches along the path are populated.
crypto portion.
--- ---
@@ -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 Both use `Noise_IK_secp256k1_ChaChaPoly_SHA256` with the same cryptographic
primitives, but with separate keys and sessions. 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? ### 6.1 Why Two Layers?
**Link encryption** protects against passive observers on each hop but allows **Link encryption** protects against passive observers on each hop but allows