Files
fips/src/node/handlers/dispatch.rs
T
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

123 lines
4.1 KiB
Rust

//! Link message dispatch and peer removal.
use crate::node::Node;
use crate::NodeAddr;
use tracing::{debug, info};
impl Node {
/// Dispatch a decrypted link message to the appropriate handler.
///
/// Link messages are protocol messages exchanged between authenticated peers.
pub(in crate::node) async fn dispatch_link_message(&mut self, from: &NodeAddr, plaintext: &[u8]) {
if plaintext.is_empty() {
return;
}
let msg_type = plaintext[0];
let payload = &plaintext[1..];
// TODO: Implement remaining link message handlers
match msg_type {
0x10 => {
// TreeAnnounce
self.handle_tree_announce(from, payload).await;
}
0x20 => {
// FilterAnnounce
self.handle_filter_announce(from, payload).await;
}
0x30 => {
// LookupRequest
debug!("Received LookupRequest (not yet implemented)");
}
0x31 => {
// LookupResponse
debug!("Received LookupResponse (not yet implemented)");
}
0x40 => {
// SessionDatagram
self.handle_session_datagram(from, payload).await;
}
0x50 => {
// Disconnect
self.handle_disconnect(from, payload);
}
_ => {
debug!(msg_type = msg_type, "Unknown link message type");
}
}
}
/// Handle a Disconnect notification from a peer.
///
/// The peer is signaling an orderly departure. We immediately remove
/// them from all state rather than waiting for timeout detection.
fn handle_disconnect(&mut self, from: &NodeAddr, payload: &[u8]) {
let disconnect = match crate::protocol::Disconnect::decode(payload) {
Ok(msg) => msg,
Err(e) => {
debug!(from = %from, error = %e, "Malformed disconnect message");
return;
}
};
info!(
node_addr = %from,
reason = %disconnect.reason,
"Peer sent disconnect notification"
);
self.remove_active_peer(from);
}
/// Remove an active peer and clean up all associated state.
///
/// Frees session index, removes link and address mappings. Used for
/// both graceful disconnect and timeout-based eviction.
///
/// Also handles tree state cleanup: if the removed peer was our parent,
/// selects an alternative or becomes root, and marks remaining peers
/// for pending tree announce (delivered on next tick).
pub(in crate::node) fn remove_active_peer(&mut self, node_addr: &NodeAddr) {
let peer = match self.peers.remove(node_addr) {
Some(p) => p,
None => {
debug!(node_addr = %node_addr, "Peer already removed");
return;
}
};
let link_id = peer.link_id();
// Free session index
if let (Some(tid), Some(idx)) = (peer.transport_id(), peer.our_index()) {
self.peers_by_index.remove(&(tid, idx.as_u32()));
let _ = self.index_allocator.free(idx);
}
// Remove link and address mapping
self.remove_link(&link_id);
// Tree state cleanup
let tree_changed = self.handle_peer_removal_tree_cleanup(node_addr);
if tree_changed {
// Mark all remaining peers for pending tree announce.
// These will be sent on the next tick via check_tree_state().
for peer in self.peers.values_mut() {
peer.mark_tree_announce_pending();
}
}
// Bloom filter cleanup: our outgoing filter changed (lost a peer's filter)
let remaining_peers: Vec<NodeAddr> = self.peers.keys().copied().collect();
self.bloom_state.mark_all_updates_needed(remaining_peers);
info!(
node_addr = %node_addr,
link_id = %link_id,
tree_changed = tree_changed,
"Peer removed and state cleaned up"
);
}
}