mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-09 16:24:45 +00:00
## FLP Wire Format Revision Replace the 1-byte discriminator with a structured wire format: - 4-byte common prefix (ver+phase, flags, payload_len) and 16-byte established frame header with AEAD AAD binding - 5-byte encrypted inner header (4-byte session-relative timestamp + 1-byte message type) on all link messages - Phase-based packet dispatch replacing discriminator-based dispatch - SessionDatagram reassigned from type 0x40 to 0x00; add SenderReport (0x01) and ReceiverReport (0x02) message types for MMP - SessionDatagram: rename hop_limit to ttl, add path_mtu field (u16 LE) with min(datagram.path_mtu, transport.mtu()) at forwarding - Updated handshake packets (msg1: 87->90 bytes, msg2: 42->45 bytes) - FIPS_OVERHEAD updated from 135 to 144 bytes ## MMP Link-Layer Measurement Protocol Add the Metrics Measurement Protocol for link quality measurement between FIPS peers. Measures RTT, loss, jitter, throughput, OWD trend, and ETX from periodic sender/receiver reports exchanged over established links. Module layout: - mmp/algorithms.rs: JitterEstimator, SrttEstimator, DualEwma, OwdTrend, SpinBit, ETX computation - mmp/report.rs: SenderReport (48B) and ReceiverReport (68B) wire format - mmp/sender.rs: per-peer TX counters and interval tracking - mmp/receiver.rs: per-peer RX counters, jitter, loss, gap tracking - mmp/metrics.rs: derived metrics from report processing (SRTT, goodput_bps) - mmp/mod.rs: MmpMode (Full/Lightweight/Minimal), MmpConfig, MmpPeerState - node/handlers/mmp.rs: report dispatch, timer-driven generation, operator logging (periodic + teardown) Integration: per-frame TX/RX hooks in encrypted message handling, report dispatch from link message router, timer-driven generation from tick handler, and periodic operator logging with throughput formatting. Three operating modes: Full (sender + receiver reports, spin bit, CE echo), Lightweight (receiver reports only), Minimal (spin bit + CE echo only). ## Design Documentation Updated FLP sections across all design documents to match the implemented wire format, including revised overhead calculations and numeric values. 568 tests pass, clippy clean.
136 lines
4.5 KiB
Rust
136 lines
4.5 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..];
|
|
|
|
match msg_type {
|
|
0x00 => {
|
|
// SessionDatagram
|
|
self.handle_session_datagram(from, payload).await;
|
|
}
|
|
0x01 => {
|
|
// SenderReport
|
|
self.handle_sender_report(from, payload);
|
|
}
|
|
0x02 => {
|
|
// ReceiverReport
|
|
self.handle_receiver_report(from, payload);
|
|
}
|
|
0x10 => {
|
|
// TreeAnnounce
|
|
self.handle_tree_announce(from, payload).await;
|
|
}
|
|
0x20 => {
|
|
// FilterAnnounce
|
|
self.handle_filter_announce(from, payload).await;
|
|
}
|
|
0x30 => {
|
|
// LookupRequest
|
|
self.handle_lookup_request(from, payload).await;
|
|
}
|
|
0x31 => {
|
|
// LookupResponse
|
|
self.handle_lookup_response(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;
|
|
}
|
|
};
|
|
|
|
// MMP teardown log (before we drop the peer)
|
|
if let Some(mmp) = peer.mmp() {
|
|
Self::log_mmp_teardown(node_addr, mmp);
|
|
}
|
|
|
|
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: clear state for removed peer, mark remaining
|
|
self.bloom_state.remove_peer_state(node_addr);
|
|
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"
|
|
);
|
|
}
|
|
}
|