mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-11 17:17:54 +00:00
Merge refactor-sans-io: routing sans-IO on the next line
This commit is contained in:
+3
-4
@@ -39,10 +39,9 @@ pub use negotiation::{
|
||||
FMP_FEAT_WANTS_SR, NEGOTIATION_HEADER_SIZE, NegotiationPayload, NodeProfile, TlvEntry,
|
||||
};
|
||||
pub use session::{
|
||||
COORDS_REQUIRED_SIZE, CoordsRequired, FspFlags, FspInnerFlags, MTU_EXCEEDED_SIZE, MtuExceeded,
|
||||
PATH_MTU_NOTIFICATION_SIZE, PathBroken, PathMtuNotification, SESSION_RECEIVER_REPORT_SIZE,
|
||||
SESSION_SENDER_REPORT_SIZE, SessionAck, SessionFlags, SessionMessageType, SessionMsg3,
|
||||
SessionReceiverReport, SessionSenderReport, SessionSetup,
|
||||
FspFlags, FspInnerFlags, PATH_MTU_NOTIFICATION_SIZE, PathMtuNotification,
|
||||
SESSION_RECEIVER_REPORT_SIZE, SESSION_SENDER_REPORT_SIZE, SessionAck, SessionFlags,
|
||||
SessionMessageType, SessionMsg3, SessionReceiverReport, SessionSenderReport, SessionSetup,
|
||||
};
|
||||
pub(crate) use session::{coords_wire_size, decode_optional_coords, encode_coords};
|
||||
pub use tree::TreeAnnounce;
|
||||
|
||||
+1
-390
@@ -182,7 +182,7 @@ pub(crate) fn decode_optional_coords(
|
||||
}
|
||||
|
||||
/// Encode a count of zero (for empty/absent coordinate fields).
|
||||
fn encode_empty_coords(buf: &mut Vec<u8>) {
|
||||
pub(crate) fn encode_empty_coords(buf: &mut Vec<u8>) {
|
||||
buf.extend_from_slice(&0u16.to_le_bytes());
|
||||
}
|
||||
|
||||
@@ -858,269 +858,6 @@ impl PathMtuNotification {
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Error Messages
|
||||
// ============================================================================
|
||||
|
||||
/// Link-layer error signal indicating router cache miss.
|
||||
///
|
||||
/// Generated by a transit router when it cannot forward a SessionDatagram
|
||||
/// due to missing cached coordinates for the destination. Carried inside
|
||||
/// a new SessionDatagram addressed back to the original source
|
||||
/// (src_addr=reporter, dest_addr=original_source). Plaintext — not
|
||||
/// end-to-end encrypted, since the transit router has no session with
|
||||
/// the source.
|
||||
///
|
||||
/// ## Wire Format
|
||||
///
|
||||
/// | Offset | Field | Size | Description |
|
||||
/// |--------|----------|---------|------------------------------------|
|
||||
/// | 0 | msg_type | 1 byte | 0x20 |
|
||||
/// | 1 | flags | 1 byte | Reserved |
|
||||
/// | 2 | dest_addr| 16 bytes| The node_addr we couldn't route to |
|
||||
/// | 18 | reporter | 16 bytes| NodeAddr of reporting router |
|
||||
///
|
||||
/// Payload: 34 bytes
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CoordsRequired {
|
||||
/// Destination that couldn't be routed.
|
||||
pub dest_addr: NodeAddr,
|
||||
/// Router reporting the miss.
|
||||
pub reporter: NodeAddr,
|
||||
}
|
||||
|
||||
/// Wire size of CoordsRequired payload: msg_type(1) + flags(1) + dest_addr(16) + reporter(16).
|
||||
pub const COORDS_REQUIRED_SIZE: usize = 34;
|
||||
|
||||
impl CoordsRequired {
|
||||
/// Create a new CoordsRequired error.
|
||||
pub fn new(dest_addr: NodeAddr, reporter: NodeAddr) -> Self {
|
||||
Self {
|
||||
dest_addr,
|
||||
reporter,
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode as wire format (4-byte FSP prefix + msg_type + body).
|
||||
///
|
||||
/// Error signals use phase=0x0 with U flag set.
|
||||
pub fn encode(&self) -> Vec<u8> {
|
||||
// Body: msg_type + flags(reserved) + dest_addr + reporter
|
||||
let body_len = 1 + 1 + 16 + 16; // 34 bytes
|
||||
let mut buf = Vec::with_capacity(4 + body_len);
|
||||
// FSP prefix: version 0, phase 0x0, U flag set
|
||||
buf.push(0x00); // version 0, phase 0x0
|
||||
buf.push(0x04); // U flag
|
||||
let payload_len = body_len as u16;
|
||||
buf.extend_from_slice(&payload_len.to_le_bytes());
|
||||
// msg_type byte (after prefix, before body)
|
||||
buf.push(SessionMessageType::CoordsRequired.to_byte());
|
||||
buf.push(0x00); // reserved flags
|
||||
buf.extend_from_slice(self.dest_addr.as_bytes());
|
||||
buf.extend_from_slice(self.reporter.as_bytes());
|
||||
buf
|
||||
}
|
||||
|
||||
/// Decode from wire format (after FSP prefix and msg_type byte consumed).
|
||||
pub fn decode(payload: &[u8]) -> Result<Self, ProtocolError> {
|
||||
// flags(1) + dest_addr(16) + reporter(16) = 33
|
||||
if payload.len() < 33 {
|
||||
return Err(ProtocolError::MessageTooShort {
|
||||
expected: 33,
|
||||
got: payload.len(),
|
||||
});
|
||||
}
|
||||
// payload[0] is flags (reserved, ignored)
|
||||
let mut dest_bytes = [0u8; 16];
|
||||
dest_bytes.copy_from_slice(&payload[1..17]);
|
||||
let mut reporter_bytes = [0u8; 16];
|
||||
reporter_bytes.copy_from_slice(&payload[17..33]);
|
||||
|
||||
Ok(Self {
|
||||
dest_addr: NodeAddr::from_bytes(dest_bytes),
|
||||
reporter: NodeAddr::from_bytes(reporter_bytes),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Error indicating routing failure (local minimum or unreachable).
|
||||
///
|
||||
/// Carried inside a SessionDatagram addressed back to the original source.
|
||||
/// The reporting router creates a new SessionDatagram with src_addr=reporter
|
||||
/// and dest_addr=original_source, so the `original_src` field from the old
|
||||
/// design is no longer needed — it's the SessionDatagram's dest_addr.
|
||||
///
|
||||
/// ## Wire Format
|
||||
///
|
||||
/// | Offset | Field | Size | Description |
|
||||
/// |--------|-------------------|----------|-------------------------------|
|
||||
/// | 0 | msg_type | 1 byte | 0x21 |
|
||||
/// | 1 | flags | 1 byte | Reserved |
|
||||
/// | 2 | dest_addr | 16 bytes | The unreachable node_addr |
|
||||
/// | 18 | reporter | 16 bytes | NodeAddr of reporting router |
|
||||
/// | 34 | last_coords_count | 2 bytes | u16 LE |
|
||||
/// | 36 | last_known_coords | 16 × n | Stale coords that failed |
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PathBroken {
|
||||
/// Destination that couldn't be reached.
|
||||
pub dest_addr: NodeAddr,
|
||||
/// Node that detected the failure.
|
||||
pub reporter: NodeAddr,
|
||||
/// Optional: last known coordinates of destination.
|
||||
pub last_known_coords: Option<TreeCoordinate>,
|
||||
}
|
||||
|
||||
impl PathBroken {
|
||||
/// Create a new PathBroken error.
|
||||
pub fn new(dest_addr: NodeAddr, reporter: NodeAddr) -> Self {
|
||||
Self {
|
||||
dest_addr,
|
||||
reporter,
|
||||
last_known_coords: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add last known coordinates.
|
||||
pub fn with_last_coords(mut self, coords: TreeCoordinate) -> Self {
|
||||
self.last_known_coords = Some(coords);
|
||||
self
|
||||
}
|
||||
|
||||
/// Encode as wire format (4-byte FSP prefix + msg_type + body).
|
||||
///
|
||||
/// Error signals use phase=0x0 with U flag set.
|
||||
pub fn encode(&self) -> Vec<u8> {
|
||||
// Build body first to compute length
|
||||
let mut body = Vec::new();
|
||||
body.push(SessionMessageType::PathBroken.to_byte());
|
||||
body.push(0x00); // reserved flags
|
||||
body.extend_from_slice(self.dest_addr.as_bytes());
|
||||
body.extend_from_slice(self.reporter.as_bytes());
|
||||
if let Some(ref coords) = self.last_known_coords {
|
||||
encode_coords(coords, &mut body);
|
||||
} else {
|
||||
encode_empty_coords(&mut body);
|
||||
}
|
||||
|
||||
// Prepend FSP prefix: version 0, phase 0x0, U flag set
|
||||
let payload_len = body.len() as u16;
|
||||
let mut buf = Vec::with_capacity(4 + body.len());
|
||||
buf.push(0x00); // version 0, phase 0x0
|
||||
buf.push(0x04); // U flag
|
||||
buf.extend_from_slice(&payload_len.to_le_bytes());
|
||||
buf.extend_from_slice(&body);
|
||||
buf
|
||||
}
|
||||
|
||||
/// Decode from wire format (after FSP prefix and msg_type byte consumed).
|
||||
pub fn decode(payload: &[u8]) -> Result<Self, ProtocolError> {
|
||||
// flags(1) + dest_addr(16) + reporter(16) + coords_count(2) = 35 minimum
|
||||
if payload.len() < 35 {
|
||||
return Err(ProtocolError::MessageTooShort {
|
||||
expected: 35,
|
||||
got: payload.len(),
|
||||
});
|
||||
}
|
||||
// payload[0] is flags (reserved, ignored)
|
||||
let mut dest_bytes = [0u8; 16];
|
||||
dest_bytes.copy_from_slice(&payload[1..17]);
|
||||
let mut reporter_bytes = [0u8; 16];
|
||||
reporter_bytes.copy_from_slice(&payload[17..33]);
|
||||
|
||||
let (last_known_coords, _consumed) = decode_optional_coords(&payload[33..])?;
|
||||
|
||||
Ok(Self {
|
||||
dest_addr: NodeAddr::from_bytes(dest_bytes),
|
||||
reporter: NodeAddr::from_bytes(reporter_bytes),
|
||||
last_known_coords,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Error indicating a forwarded packet exceeded the next-hop transport MTU.
|
||||
///
|
||||
/// Generated by a transit router when `send_encrypted_link_message()`
|
||||
/// fails with `TransportError::MtuExceeded`. The reporter includes the
|
||||
/// bottleneck MTU so the source can immediately reduce its sending MTU.
|
||||
///
|
||||
/// ## Wire Format
|
||||
///
|
||||
/// | Offset | Field | Size | Description |
|
||||
/// |--------|-----------|----------|------------------------------------|
|
||||
/// | 0 | msg_type | 1 byte | 0x22 |
|
||||
/// | 1 | flags | 1 byte | Reserved |
|
||||
/// | 2 | dest_addr | 16 bytes | The destination we were forwarding |
|
||||
/// | 18 | reporter | 16 bytes | NodeAddr of reporting router |
|
||||
/// | 34 | mtu | 2 bytes | Bottleneck MTU (u16 LE) |
|
||||
///
|
||||
/// Payload: 36 bytes
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MtuExceeded {
|
||||
/// Destination that the oversized packet was heading to.
|
||||
pub dest_addr: NodeAddr,
|
||||
/// Router that detected the MTU violation.
|
||||
pub reporter: NodeAddr,
|
||||
/// Transport MTU at the bottleneck hop.
|
||||
pub mtu: u16,
|
||||
}
|
||||
|
||||
/// Wire size of MtuExceeded payload: msg_type(1) + flags(1) + dest_addr(16) + reporter(16) + mtu(2).
|
||||
pub const MTU_EXCEEDED_SIZE: usize = 36;
|
||||
|
||||
impl MtuExceeded {
|
||||
/// Create a new MtuExceeded error.
|
||||
pub fn new(dest_addr: NodeAddr, reporter: NodeAddr, mtu: u16) -> Self {
|
||||
Self {
|
||||
dest_addr,
|
||||
reporter,
|
||||
mtu,
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode as wire format (4-byte FSP prefix + msg_type + body).
|
||||
///
|
||||
/// Error signals use phase=0x0 with U flag set.
|
||||
pub fn encode(&self) -> Vec<u8> {
|
||||
let body_len = MTU_EXCEEDED_SIZE; // 36 bytes
|
||||
let mut buf = Vec::with_capacity(4 + body_len);
|
||||
// FSP prefix: version 0, phase 0x0, U flag set
|
||||
buf.push(0x00); // version 0, phase 0x0
|
||||
buf.push(0x04); // U flag
|
||||
let payload_len = body_len as u16;
|
||||
buf.extend_from_slice(&payload_len.to_le_bytes());
|
||||
// msg_type byte
|
||||
buf.push(SessionMessageType::MtuExceeded.to_byte());
|
||||
buf.push(0x00); // reserved flags
|
||||
buf.extend_from_slice(self.dest_addr.as_bytes());
|
||||
buf.extend_from_slice(self.reporter.as_bytes());
|
||||
buf.extend_from_slice(&self.mtu.to_le_bytes());
|
||||
buf
|
||||
}
|
||||
|
||||
/// Decode from wire format (after FSP prefix and msg_type byte consumed).
|
||||
pub fn decode(payload: &[u8]) -> Result<Self, ProtocolError> {
|
||||
// flags(1) + dest_addr(16) + reporter(16) + mtu(2) = 35
|
||||
if payload.len() < 35 {
|
||||
return Err(ProtocolError::MessageTooShort {
|
||||
expected: 35,
|
||||
got: payload.len(),
|
||||
});
|
||||
}
|
||||
// payload[0] is flags (reserved, ignored)
|
||||
let mut dest_bytes = [0u8; 16];
|
||||
dest_bytes.copy_from_slice(&payload[1..17]);
|
||||
let mut reporter_bytes = [0u8; 16];
|
||||
reporter_bytes.copy_from_slice(&payload[17..33]);
|
||||
let mtu = u16::from_le_bytes([payload[33], payload[34]]);
|
||||
|
||||
Ok(Self {
|
||||
dest_addr: NodeAddr::from_bytes(dest_bytes),
|
||||
reporter: NodeAddr::from_bytes(reporter_bytes),
|
||||
mtu,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -1198,28 +935,6 @@ mod tests {
|
||||
assert!(!setup.flags.bidirectional);
|
||||
}
|
||||
|
||||
// ===== CoordsRequired Tests =====
|
||||
|
||||
#[test]
|
||||
fn test_coords_required() {
|
||||
let err = CoordsRequired::new(make_node_addr(1), make_node_addr(2));
|
||||
|
||||
assert_eq!(err.dest_addr, make_node_addr(1));
|
||||
assert_eq!(err.reporter, make_node_addr(2));
|
||||
}
|
||||
|
||||
// ===== PathBroken Tests =====
|
||||
|
||||
#[test]
|
||||
fn test_path_broken() {
|
||||
let err = PathBroken::new(make_node_addr(2), make_node_addr(3))
|
||||
.with_last_coords(make_coords(&[2, 0]));
|
||||
|
||||
assert_eq!(err.dest_addr, make_node_addr(2));
|
||||
assert_eq!(err.reporter, make_node_addr(3));
|
||||
assert!(err.last_known_coords.is_some());
|
||||
}
|
||||
|
||||
// ===== Encode/Decode Roundtrip Tests =====
|
||||
|
||||
#[test]
|
||||
@@ -1275,55 +990,6 @@ mod tests {
|
||||
assert_eq!(decoded.handshake_payload, handshake);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_coords_required_encode_decode() {
|
||||
let err = CoordsRequired::new(make_node_addr(0xAA), make_node_addr(0xBB));
|
||||
|
||||
let encoded = err.encode();
|
||||
// 4 prefix + 1 msg_type + 1 flags + 16 dest + 16 reporter = 38
|
||||
assert_eq!(encoded.len(), 4 + COORDS_REQUIRED_SIZE);
|
||||
// Check FSP prefix: phase 0x0, U flag
|
||||
assert_eq!(encoded[0], 0x00);
|
||||
assert_eq!(encoded[1], 0x04); // U flag
|
||||
// msg_type after prefix
|
||||
assert_eq!(encoded[4], 0x20);
|
||||
|
||||
// decode after prefix + msg_type consumed
|
||||
let decoded = CoordsRequired::decode(&encoded[5..]).unwrap();
|
||||
assert_eq!(decoded.dest_addr, err.dest_addr);
|
||||
assert_eq!(decoded.reporter, err.reporter);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_broken_encode_decode_no_coords() {
|
||||
let err = PathBroken::new(make_node_addr(0xCC), make_node_addr(0xDD));
|
||||
|
||||
let encoded = err.encode();
|
||||
// Check FSP prefix
|
||||
assert_eq!(encoded[0], 0x00);
|
||||
assert_eq!(encoded[1], 0x04); // U flag
|
||||
assert_eq!(encoded[4], 0x21); // msg_type
|
||||
|
||||
let decoded = PathBroken::decode(&encoded[5..]).unwrap();
|
||||
assert_eq!(decoded.dest_addr, err.dest_addr);
|
||||
assert_eq!(decoded.reporter, err.reporter);
|
||||
assert!(decoded.last_known_coords.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_broken_encode_decode_with_coords() {
|
||||
let coords = make_coords(&[0xCC, 0xDD, 0xEE]);
|
||||
let err = PathBroken::new(make_node_addr(0x11), make_node_addr(0x22))
|
||||
.with_last_coords(coords.clone());
|
||||
|
||||
let encoded = err.encode();
|
||||
let decoded = PathBroken::decode(&encoded[5..]).unwrap();
|
||||
|
||||
assert_eq!(decoded.dest_addr, err.dest_addr);
|
||||
assert_eq!(decoded.reporter, err.reporter);
|
||||
assert_eq!(decoded.last_known_coords.unwrap(), coords);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_session_setup_decode_too_short() {
|
||||
assert!(SessionSetup::decode(&[]).is_err());
|
||||
@@ -1334,18 +1000,6 @@ mod tests {
|
||||
assert!(SessionAck::decode(&[]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_coords_required_decode_too_short() {
|
||||
assert!(CoordsRequired::decode(&[]).is_err());
|
||||
assert!(CoordsRequired::decode(&[0x00; 10]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_broken_decode_too_short() {
|
||||
assert!(PathBroken::decode(&[]).is_err());
|
||||
assert!(PathBroken::decode(&[0x00; 20]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_session_setup_deep_coords() {
|
||||
// Depth-10 coordinate (11 entries: self + 10 ancestors)
|
||||
@@ -1562,49 +1216,6 @@ mod tests {
|
||||
|
||||
// ===== MtuExceeded Tests =====
|
||||
|
||||
#[test]
|
||||
fn test_mtu_exceeded_encode_size() {
|
||||
let err = MtuExceeded::new(make_node_addr(0xAA), make_node_addr(0xBB), 1400);
|
||||
let encoded = err.encode();
|
||||
// 4 prefix + 36 body = 40
|
||||
assert_eq!(encoded.len(), 4 + MTU_EXCEEDED_SIZE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mtu_exceeded_encode_decode() {
|
||||
let err = MtuExceeded::new(make_node_addr(0xAA), make_node_addr(0xBB), 1400);
|
||||
|
||||
let encoded = err.encode();
|
||||
// Check FSP prefix: phase 0x0, U flag
|
||||
assert_eq!(encoded[0], 0x00);
|
||||
assert_eq!(encoded[1], 0x04); // U flag
|
||||
// msg_type after prefix
|
||||
assert_eq!(encoded[4], 0x22);
|
||||
|
||||
// decode after prefix + msg_type consumed
|
||||
let decoded = MtuExceeded::decode(&encoded[5..]).unwrap();
|
||||
assert_eq!(decoded.dest_addr, err.dest_addr);
|
||||
assert_eq!(decoded.reporter, err.reporter);
|
||||
assert_eq!(decoded.mtu, 1400);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mtu_exceeded_decode_too_short() {
|
||||
assert!(MtuExceeded::decode(&[]).is_err());
|
||||
assert!(MtuExceeded::decode(&[0x00; 20]).is_err());
|
||||
assert!(MtuExceeded::decode(&[0x00; 34]).is_err()); // exactly 1 byte short
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mtu_exceeded_boundary_mtu_values() {
|
||||
for mtu in [0u16, 1280, 1500, u16::MAX] {
|
||||
let err = MtuExceeded::new(make_node_addr(1), make_node_addr(2), mtu);
|
||||
let encoded = err.encode();
|
||||
let decoded = MtuExceeded::decode(&encoded[5..]).unwrap();
|
||||
assert_eq!(decoded.mtu, mtu);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mtu_exceeded_message_type_value() {
|
||||
assert_eq!(SessionMessageType::MtuExceeded.to_byte(), 0x22);
|
||||
|
||||
Reference in New Issue
Block a user