mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-10 08:37:02 +00:00
Remove spin bit, slim down MMP reports with extensibility header
Delete SpinBitState, FLAG_SP (FMP bit 2), and FspInnerFlags.spin_bit. Spin bit superseded by MMP receiver report timestamp echo for RTT. Reclaims FMP flags bit 2 and FSP inner flags bit 0. SenderReport: 48 -> 20 bytes. Three fields: interval_packets_sent, interval_bytes_sent, cumulative_packets_sent. ReceiverReport: 68 -> 54 bytes. Ten decision-driving and diagnostic fields retained; removed max_burst_loss, mean_burst_loss, interval_packets_recv, interval_bytes_recv. Both report types use extensibility header: repurposed 3 reserved bytes as [format_version:1][total_length:2 LE]. Decoders skip unknown trailing bytes for forward compatibility. Session-layer reports updated to match.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
//! Encrypted frame handling (hot path).
|
||||
|
||||
use crate::node::Node;
|
||||
use crate::node::wire::{EncryptedHeader, FLAG_CE, FLAG_KEY_EPOCH, FLAG_SP, strip_inner_header};
|
||||
use crate::noise::NoiseError;
|
||||
use crate::node::Node;
|
||||
use crate::node::wire::{EncryptedHeader, strip_inner_header, FLAG_CE, FLAG_KEY_EPOCH};
|
||||
use crate::transport::ReceivedPacket;
|
||||
use std::time::Instant;
|
||||
use tracing::{debug, info, trace, warn};
|
||||
@@ -53,8 +53,8 @@ impl Node {
|
||||
// Check and perform cutover in a scoped borrow.
|
||||
{
|
||||
let peer = self.peers.get(&node_addr).unwrap();
|
||||
let k_bit_flipped =
|
||||
received_k_bit != peer.current_k_bit() && peer.pending_new_session().is_some();
|
||||
let k_bit_flipped = received_k_bit != peer.current_k_bit()
|
||||
&& peer.pending_new_session().is_some();
|
||||
|
||||
if k_bit_flipped {
|
||||
let display_name = self.peer_display_name(&node_addr);
|
||||
@@ -70,10 +70,9 @@ impl Node {
|
||||
debug_assert!(
|
||||
peer.transport_id().is_some()
|
||||
&& peer.our_index().is_some()
|
||||
&& self.peers_by_index.contains_key(&(
|
||||
peer.transport_id().unwrap(),
|
||||
peer.our_index().unwrap().as_u32()
|
||||
)),
|
||||
&& self.peers_by_index.contains_key(
|
||||
&(peer.transport_id().unwrap(), peer.our_index().unwrap().as_u32())
|
||||
),
|
||||
"peers_by_index should contain pre-registered new index after K-bit flip"
|
||||
);
|
||||
}
|
||||
@@ -149,7 +148,6 @@ impl Node {
|
||||
// MMP per-frame processing and statistics
|
||||
let now = Instant::now();
|
||||
let ce_flag = header.flags & FLAG_CE != 0;
|
||||
let sp_flag = header.flags & FLAG_SP != 0;
|
||||
|
||||
if let Some(peer) = self.peers.get_mut(&node_addr) {
|
||||
if let Some(mmp) = peer.mmp_mut() {
|
||||
@@ -160,17 +158,14 @@ impl Node {
|
||||
ce_flag,
|
||||
now,
|
||||
);
|
||||
let _spin_rtt = mmp.spin_bit.rx_observe(sp_flag, header.counter, now);
|
||||
}
|
||||
peer.set_current_addr(packet.transport_id, packet.remote_addr.clone());
|
||||
peer.link_stats_mut()
|
||||
.record_recv(packet.data.len(), packet.timestamp_ms);
|
||||
peer.link_stats_mut().record_recv(packet.data.len(), packet.timestamp_ms);
|
||||
peer.touch(packet.timestamp_ms);
|
||||
}
|
||||
|
||||
// Dispatch to link message handler
|
||||
self.dispatch_link_message(&node_addr, link_message, ce_flag)
|
||||
.await;
|
||||
self.dispatch_link_message(&node_addr, link_message, ce_flag).await;
|
||||
}
|
||||
|
||||
/// Log a decryption failure with replay suppression.
|
||||
|
||||
@@ -59,6 +59,7 @@ impl Node {
|
||||
trace!(
|
||||
from = %self.peer_display_name(from),
|
||||
cum_pkts = sr.cumulative_packets_sent,
|
||||
interval_pkts = sr.interval_packets_sent,
|
||||
interval_bytes = sr.interval_bytes_sent,
|
||||
"Received SenderReport"
|
||||
);
|
||||
@@ -290,7 +291,6 @@ impl Node {
|
||||
etx = format_args!("{:.2}", m.etx),
|
||||
goodput = %format_throughput(m.goodput_bps()),
|
||||
tx_pkts = mmp.sender.cumulative_packets_sent(),
|
||||
tx_bytes = mmp.sender.cumulative_bytes_sent(),
|
||||
rx_pkts = mmp.receiver.cumulative_packets_recv(),
|
||||
rx_bytes = mmp.receiver.cumulative_bytes_recv(),
|
||||
"MMP link teardown"
|
||||
@@ -489,7 +489,6 @@ impl Node {
|
||||
send_mtu = mmp.path_mtu.current_mtu(),
|
||||
observed_mtu = mmp.path_mtu.last_observed_mtu(),
|
||||
tx_pkts = mmp.sender.cumulative_packets_sent(),
|
||||
tx_bytes = mmp.sender.cumulative_bytes_sent(),
|
||||
rx_pkts = mmp.receiver.cumulative_packets_recv(),
|
||||
rx_bytes = mmp.receiver.cumulative_bytes_recv(),
|
||||
"MMP session teardown"
|
||||
|
||||
@@ -251,13 +251,7 @@ impl Node {
|
||||
mmp.receiver.record_recv(
|
||||
header.counter, timestamp, plaintext.len(), ce_flag, now,
|
||||
);
|
||||
// Spin bit: advance state machine for correct TX reflection.
|
||||
// RTT samples not fed into SRTT — timestamp-echo provides
|
||||
// accurate RTT; spin bit includes variable inter-frame delays.
|
||||
let inner_flags = FspInnerFlags::from_byte(inner_flags_byte);
|
||||
let _spin_rtt = mmp.spin_bit.rx_observe(
|
||||
inner_flags.spin_bit, header.counter, now,
|
||||
);
|
||||
let _inner_flags = FspInnerFlags::from_byte(inner_flags_byte);
|
||||
}
|
||||
|
||||
// Feed path_mtu from datagram envelope to MMP path MTU tracking.
|
||||
@@ -915,6 +909,7 @@ impl Node {
|
||||
trace!(
|
||||
src = %self.peer_display_name(src_addr),
|
||||
cum_pkts = sr.cumulative_packets_sent,
|
||||
interval_pkts = sr.interval_packets_sent,
|
||||
interval_bytes = sr.interval_bytes_sent,
|
||||
"Received SessionSenderReport"
|
||||
);
|
||||
@@ -1273,7 +1268,6 @@ impl Node {
|
||||
})?;
|
||||
let wants_coords = entry.coords_warmup_remaining() > 0;
|
||||
let timestamp = entry.session_timestamp(now_ms);
|
||||
let spin_bit = entry.mmp().is_some_and(|m| m.spin_bit.tx_bit());
|
||||
if !entry.is_established() {
|
||||
return Err(NodeError::SendFailed {
|
||||
node_addr: *dest_addr,
|
||||
@@ -1289,7 +1283,7 @@ impl Node {
|
||||
|
||||
// Build inner plaintext (doesn't depend on counter)
|
||||
let msg_type = SessionMessageType::DataPacket.to_byte(); // 0x10
|
||||
let inner_flags = FspInnerFlags { spin_bit }.to_byte();
|
||||
let inner_flags = FspInnerFlags::new().to_byte();
|
||||
let inner_plaintext = fsp_prepend_inner_header(timestamp, msg_type, inner_flags, &port_payload);
|
||||
|
||||
// Determine whether coords fit within transport MTU.
|
||||
@@ -1422,10 +1416,8 @@ impl Node {
|
||||
reason: "no session".into(),
|
||||
})?;
|
||||
let timestamp = entry.session_timestamp(now_ms);
|
||||
let spin_bit = entry.mmp().is_some_and(|m| m.spin_bit.tx_bit());
|
||||
|
||||
// Build inner flags with spin bit
|
||||
let inner_flags = FspInnerFlags { spin_bit }.to_byte();
|
||||
let inner_flags = FspInnerFlags::new().to_byte();
|
||||
|
||||
// Get mutable access for encryption
|
||||
let entry = self.sessions.get_mut(dest_addr).ok_or_else(|| NodeError::SendFailed {
|
||||
@@ -1506,7 +1498,6 @@ impl Node {
|
||||
reason: "no session".into(),
|
||||
})?;
|
||||
let timestamp = entry.session_timestamp(now_ms);
|
||||
let spin_bit = entry.mmp().is_some_and(|m| m.spin_bit.tx_bit());
|
||||
|
||||
// Get mutable access for encryption
|
||||
let entry = self.sessions.get_mut(dest_addr).ok_or_else(|| NodeError::SendFailed {
|
||||
@@ -1527,7 +1518,7 @@ impl Node {
|
||||
|
||||
// FSP inner header only, no body payload
|
||||
let msg_type = SessionMessageType::CoordsWarmup.to_byte();
|
||||
let inner_flags = FspInnerFlags { spin_bit }.to_byte();
|
||||
let inner_flags = FspInnerFlags::new().to_byte();
|
||||
let inner_plaintext = fsp_prepend_inner_header(timestamp, msg_type, inner_flags, &[]);
|
||||
|
||||
// Build FSP header with CP flag
|
||||
|
||||
+2
-6
@@ -40,7 +40,7 @@ use crate::tree::TreeState;
|
||||
use crate::upper::hosts::HostMap;
|
||||
use crate::upper::icmp_rate_limit::IcmpRateLimiter;
|
||||
use crate::upper::tun::{TunError, TunOutboundRx, TunState, TunTx};
|
||||
use self::wire::{build_encrypted, build_established_header, prepend_inner_header, FLAG_CE, FLAG_KEY_EPOCH, FLAG_SP};
|
||||
use self::wire::{build_encrypted, build_established_header, prepend_inner_header, FLAG_CE, FLAG_KEY_EPOCH};
|
||||
use crate::{Config, ConfigError, Identity, IdentityError, NodeAddr, PeerIdentity};
|
||||
use rand::Rng;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
@@ -1637,11 +1637,7 @@ impl Node {
|
||||
// Prepend 4-byte session-relative timestamp (inner header)
|
||||
let timestamp_ms = peer.session_elapsed_ms();
|
||||
|
||||
// MMP: read spin bit value before entering session borrow
|
||||
let sp_flag = peer.mmp()
|
||||
.map(|mmp| mmp.spin_bit.tx_bit())
|
||||
.unwrap_or(false);
|
||||
let mut flags = if sp_flag { FLAG_SP } else { 0 };
|
||||
let mut flags = 0u8;
|
||||
if ce_flag {
|
||||
flags |= FLAG_CE;
|
||||
}
|
||||
|
||||
@@ -89,11 +89,6 @@ pub const FSP_FLAG_K: u8 = 0x02;
|
||||
/// Unencrypted — payload is plaintext (error signals).
|
||||
pub const FSP_FLAG_U: u8 = 0x04;
|
||||
|
||||
// Inner flag bit constants (byte 5 of decrypted inner header).
|
||||
|
||||
/// Spin bit for end-to-end RTT measurement (inside AEAD).
|
||||
#[allow(dead_code)]
|
||||
pub const FSP_INNER_FLAG_SP: u8 = 0x01;
|
||||
|
||||
// ============================================================================
|
||||
// Common Prefix
|
||||
|
||||
+5
-9
@@ -71,9 +71,6 @@ pub const FLAG_KEY_EPOCH: u8 = 0x01;
|
||||
#[allow(dead_code)]
|
||||
/// Congestion Experienced echo flag.
|
||||
pub const FLAG_CE: u8 = 0x02;
|
||||
#[allow(dead_code)]
|
||||
/// Spin bit for RTT measurement.
|
||||
pub const FLAG_SP: u8 = 0x04;
|
||||
|
||||
// ============================================================================
|
||||
// Common Prefix
|
||||
@@ -516,11 +513,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_common_prefix_parse() {
|
||||
let data = [0x10, 0x04, 0x20, 0x00]; // ver=1, phase=0, flags=SP, payload_len=32
|
||||
let data = [0x10, 0x02, 0x20, 0x00]; // ver=1, phase=0, flags=CE, payload_len=32
|
||||
let prefix = CommonPrefix::parse(&data).unwrap();
|
||||
assert_eq!(prefix.version, 1);
|
||||
assert_eq!(prefix.phase, 0);
|
||||
assert_eq!(prefix.flags, FLAG_SP);
|
||||
assert_eq!(prefix.flags, FLAG_CE);
|
||||
assert_eq!(prefix.payload_len, 32);
|
||||
}
|
||||
|
||||
@@ -698,10 +695,10 @@ mod tests {
|
||||
let header = build_established_header(
|
||||
SessionIndex::new(1),
|
||||
0,
|
||||
FLAG_KEY_EPOCH | FLAG_SP,
|
||||
FLAG_KEY_EPOCH | FLAG_CE,
|
||||
100,
|
||||
);
|
||||
assert_eq!(header[1], 0x05); // bits 0 and 2 set
|
||||
assert_eq!(header[1], 0x03); // bits 0 and 1 set
|
||||
|
||||
let parsed = EncryptedHeader::parse(&[
|
||||
header[0], header[1], header[2], header[3],
|
||||
@@ -712,8 +709,7 @@ mod tests {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
]).unwrap();
|
||||
assert_eq!(parsed.flags & FLAG_KEY_EPOCH, FLAG_KEY_EPOCH);
|
||||
assert_eq!(parsed.flags & FLAG_CE, 0);
|
||||
assert_eq!(parsed.flags & FLAG_SP, FLAG_SP);
|
||||
assert_eq!(parsed.flags & FLAG_CE, FLAG_CE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user