use super::ProtocolError;
use crate::NodeAddr;
use crate::tree::TreeCoordinate;
use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum SessionMessageType {
DataPacket = 0x10,
SenderReport = 0x11,
ReceiverReport = 0x12,
PathMtuNotification = 0x13,
CoordsWarmup = 0x14,
EndpointData = 0x15,
CoordsRequired = 0x20,
PathBroken = 0x21,
MtuExceeded = 0x22,
}
impl SessionMessageType {
pub fn from_byte(b: u8) -> Option<Self> {
match b {
0x10 => Some(SessionMessageType::DataPacket),
0x11 => Some(SessionMessageType::SenderReport),
0x12 => Some(SessionMessageType::ReceiverReport),
0x13 => Some(SessionMessageType::PathMtuNotification),
0x14 => Some(SessionMessageType::CoordsWarmup),
0x15 => Some(SessionMessageType::EndpointData),
0x20 => Some(SessionMessageType::CoordsRequired),
0x21 => Some(SessionMessageType::PathBroken),
0x22 => Some(SessionMessageType::MtuExceeded),
_ => None,
}
}
pub fn to_byte(self) -> u8 {
self as u8
}
}
impl fmt::Display for SessionMessageType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
SessionMessageType::DataPacket => "DataPacket",
SessionMessageType::SenderReport => "SenderReport",
SessionMessageType::ReceiverReport => "ReceiverReport",
SessionMessageType::PathMtuNotification => "PathMtuNotification",
SessionMessageType::CoordsWarmup => "CoordsWarmup",
SessionMessageType::EndpointData => "EndpointData",
SessionMessageType::CoordsRequired => "CoordsRequired",
SessionMessageType::PathBroken => "PathBroken",
SessionMessageType::MtuExceeded => "MtuExceeded",
};
write!(f, "{}", name)
}
}
pub(crate) fn coords_wire_size(coords: &TreeCoordinate) -> usize {
2 + coords.entries().len() * 16
}
pub(crate) fn encode_coords(coords: &TreeCoordinate, buf: &mut Vec<u8>) {
let addrs: Vec<&NodeAddr> = coords.node_addrs().collect();
let count = addrs.len() as u16;
buf.extend_from_slice(&count.to_le_bytes());
for addr in addrs {
buf.extend_from_slice(addr.as_bytes());
}
}
pub(crate) fn decode_coords(data: &[u8]) -> Result<(TreeCoordinate, usize), ProtocolError> {
if data.len() < 2 {
return Err(ProtocolError::MessageTooShort {
expected: 2,
got: data.len(),
});
}
let count = u16::from_le_bytes([data[0], data[1]]) as usize;
let needed = 2 + count * 16;
if data.len() < needed {
return Err(ProtocolError::MessageTooShort {
expected: needed,
got: data.len(),
});
}
if count == 0 {
return Err(ProtocolError::Malformed(
"coordinate with zero entries".into(),
));
}
let mut addrs = Vec::with_capacity(count);
for i in 0..count {
let offset = 2 + i * 16;
let mut bytes = [0u8; 16];
bytes.copy_from_slice(&data[offset..offset + 16]);
addrs.push(NodeAddr::from_bytes(bytes));
}
let coord =
TreeCoordinate::from_addrs(addrs).map_err(|e| ProtocolError::Malformed(e.to_string()))?;
Ok((coord, needed))
}
pub(crate) fn decode_optional_coords(
data: &[u8],
) -> Result<(Option<TreeCoordinate>, usize), ProtocolError> {
if data.len() < 2 {
return Err(ProtocolError::MessageTooShort {
expected: 2,
got: data.len(),
});
}
let count = u16::from_le_bytes([data[0], data[1]]) as usize;
let needed = 2 + count * 16;
if data.len() < needed {
return Err(ProtocolError::MessageTooShort {
expected: needed,
got: data.len(),
});
}
if count == 0 {
return Ok((None, 2));
}
let mut addrs = Vec::with_capacity(count);
for i in 0..count {
let offset = 2 + i * 16;
let mut bytes = [0u8; 16];
bytes.copy_from_slice(&data[offset..offset + 16]);
addrs.push(NodeAddr::from_bytes(bytes));
}
let coord =
TreeCoordinate::from_addrs(addrs).map_err(|e| ProtocolError::Malformed(e.to_string()))?;
Ok((Some(coord), needed))
}
fn encode_empty_coords(buf: &mut Vec<u8>) {
buf.extend_from_slice(&0u16.to_le_bytes());
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct SessionFlags {
pub request_ack: bool,
pub bidirectional: bool,
}
impl SessionFlags {
pub fn new() -> Self {
Self::default()
}
pub fn with_ack(mut self) -> Self {
self.request_ack = true;
self
}
pub fn bidirectional(mut self) -> Self {
self.bidirectional = true;
self
}
pub fn to_byte(&self) -> u8 {
let mut flags = 0u8;
if self.request_ack {
flags |= 0x01;
}
if self.bidirectional {
flags |= 0x02;
}
flags
}
pub fn from_byte(byte: u8) -> Self {
Self {
request_ack: byte & 0x01 != 0,
bidirectional: byte & 0x02 != 0,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct FspFlags {
pub coords_present: bool,
pub key_epoch: bool,
pub unencrypted: bool,
}
impl FspFlags {
pub fn new() -> Self {
Self::default()
}
pub fn to_byte(&self) -> u8 {
let mut flags = 0u8;
if self.coords_present {
flags |= 0x01;
}
if self.key_epoch {
flags |= 0x02;
}
if self.unencrypted {
flags |= 0x04;
}
flags
}
pub fn from_byte(byte: u8) -> Self {
Self {
coords_present: byte & 0x01 != 0,
key_epoch: byte & 0x02 != 0,
unencrypted: byte & 0x04 != 0,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct FspInnerFlags {
pub spin_bit: bool,
}
impl FspInnerFlags {
pub fn new() -> Self {
Self::default()
}
pub fn to_byte(&self) -> u8 {
if self.spin_bit { 0x01 } else { 0x00 }
}
pub fn from_byte(byte: u8) -> Self {
Self {
spin_bit: byte & 0x01 != 0,
}
}
}
#[derive(Clone, Debug)]
pub struct SessionSetup {
pub src_coords: TreeCoordinate,
pub dest_coords: TreeCoordinate,
pub flags: SessionFlags,
pub handshake_payload: Vec<u8>,
}
impl SessionSetup {
pub fn new(src_coords: TreeCoordinate, dest_coords: TreeCoordinate) -> Self {
Self {
src_coords,
dest_coords,
flags: SessionFlags::new(),
handshake_payload: Vec::new(),
}
}
pub fn with_flags(mut self, flags: SessionFlags) -> Self {
self.flags = flags;
self
}
pub fn with_handshake(mut self, payload: Vec<u8>) -> Self {
self.handshake_payload = payload;
self
}
pub fn encode(&self) -> Vec<u8> {
let mut body = Vec::new();
body.push(self.flags.to_byte());
encode_coords(&self.src_coords, &mut body);
encode_coords(&self.dest_coords, &mut body);
let hs_len = self.handshake_payload.len() as u16;
body.extend_from_slice(&hs_len.to_le_bytes());
body.extend_from_slice(&self.handshake_payload);
let payload_len = body.len() as u16;
let mut buf = Vec::with_capacity(4 + body.len());
buf.push(0x01); buf.push(0x00); buf.extend_from_slice(&payload_len.to_le_bytes());
buf.extend_from_slice(&body);
buf
}
pub fn decode(payload: &[u8]) -> Result<Self, ProtocolError> {
if payload.is_empty() {
return Err(ProtocolError::MessageTooShort {
expected: 1,
got: 0,
});
}
let flags = SessionFlags::from_byte(payload[0]);
let mut offset = 1;
let (src_coords, consumed) = decode_coords(&payload[offset..])?;
offset += consumed;
let (dest_coords, consumed) = decode_coords(&payload[offset..])?;
offset += consumed;
if payload.len() < offset + 2 {
return Err(ProtocolError::MessageTooShort {
expected: offset + 2,
got: payload.len(),
});
}
let hs_len = u16::from_le_bytes([payload[offset], payload[offset + 1]]) as usize;
offset += 2;
if payload.len() < offset + hs_len {
return Err(ProtocolError::MessageTooShort {
expected: offset + hs_len,
got: payload.len(),
});
}
let handshake_payload = payload[offset..offset + hs_len].to_vec();
Ok(Self {
src_coords,
dest_coords,
flags,
handshake_payload,
})
}
}
#[derive(Clone, Debug)]
pub struct SessionAck {
pub src_coords: TreeCoordinate,
pub dest_coords: TreeCoordinate,
pub flags: u8,
pub handshake_payload: Vec<u8>,
}
impl SessionAck {
pub fn new(src_coords: TreeCoordinate, dest_coords: TreeCoordinate) -> Self {
Self {
src_coords,
dest_coords,
flags: 0,
handshake_payload: Vec::new(),
}
}
pub fn with_handshake(mut self, payload: Vec<u8>) -> Self {
self.handshake_payload = payload;
self
}
pub fn encode(&self) -> Vec<u8> {
let mut body = Vec::new();
body.push(self.flags);
encode_coords(&self.src_coords, &mut body);
encode_coords(&self.dest_coords, &mut body);
let hs_len = self.handshake_payload.len() as u16;
body.extend_from_slice(&hs_len.to_le_bytes());
body.extend_from_slice(&self.handshake_payload);
let payload_len = body.len() as u16;
let mut buf = Vec::with_capacity(4 + body.len());
buf.push(0x02); buf.push(0x00); buf.extend_from_slice(&payload_len.to_le_bytes());
buf.extend_from_slice(&body);
buf
}
pub fn decode(payload: &[u8]) -> Result<Self, ProtocolError> {
if payload.is_empty() {
return Err(ProtocolError::MessageTooShort {
expected: 1,
got: 0,
});
}
let flags = payload[0];
let mut offset = 1;
let (src_coords, consumed) = decode_coords(&payload[offset..])?;
offset += consumed;
let (dest_coords, consumed) = decode_coords(&payload[offset..])?;
offset += consumed;
if payload.len() < offset + 2 {
return Err(ProtocolError::MessageTooShort {
expected: offset + 2,
got: payload.len(),
});
}
let hs_len = u16::from_le_bytes([payload[offset], payload[offset + 1]]) as usize;
offset += 2;
if payload.len() < offset + hs_len {
return Err(ProtocolError::MessageTooShort {
expected: offset + hs_len,
got: payload.len(),
});
}
let handshake_payload = payload[offset..offset + hs_len].to_vec();
Ok(Self {
src_coords,
dest_coords,
flags,
handshake_payload,
})
}
}
#[derive(Clone, Debug)]
pub struct SessionMsg3 {
pub flags: u8,
pub handshake_payload: Vec<u8>,
}
impl SessionMsg3 {
pub fn new(handshake_payload: Vec<u8>) -> Self {
Self {
flags: 0,
handshake_payload,
}
}
pub fn encode(&self) -> Vec<u8> {
let mut body = Vec::new();
body.push(self.flags);
let hs_len = self.handshake_payload.len() as u16;
body.extend_from_slice(&hs_len.to_le_bytes());
body.extend_from_slice(&self.handshake_payload);
let payload_len = body.len() as u16;
let mut buf = Vec::with_capacity(4 + body.len());
buf.push(0x03); buf.push(0x00); buf.extend_from_slice(&payload_len.to_le_bytes());
buf.extend_from_slice(&body);
buf
}
pub fn decode(payload: &[u8]) -> Result<Self, ProtocolError> {
if payload.is_empty() {
return Err(ProtocolError::MessageTooShort {
expected: 1,
got: 0,
});
}
let flags = payload[0];
let mut offset = 1;
if payload.len() < offset + 2 {
return Err(ProtocolError::MessageTooShort {
expected: offset + 2,
got: payload.len(),
});
}
let hs_len = u16::from_le_bytes([payload[offset], payload[offset + 1]]) as usize;
offset += 2;
if payload.len() < offset + hs_len {
return Err(ProtocolError::MessageTooShort {
expected: offset + hs_len,
got: payload.len(),
});
}
let handshake_payload = payload[offset..offset + hs_len].to_vec();
Ok(Self {
flags,
handshake_payload,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SessionSenderReport {
pub interval_start_counter: u64,
pub interval_end_counter: u64,
pub interval_start_timestamp: u32,
pub interval_end_timestamp: u32,
pub interval_bytes_sent: u32,
pub cumulative_packets_sent: u64,
pub cumulative_bytes_sent: u64,
}
pub const SESSION_SENDER_REPORT_SIZE: usize = 46;
impl SessionSenderReport {
pub fn encode(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(SESSION_SENDER_REPORT_SIZE);
buf.extend_from_slice(&[0u8; 2]); buf.extend_from_slice(&self.interval_start_counter.to_le_bytes());
buf.extend_from_slice(&self.interval_end_counter.to_le_bytes());
buf.extend_from_slice(&self.interval_start_timestamp.to_le_bytes());
buf.extend_from_slice(&self.interval_end_timestamp.to_le_bytes());
buf.extend_from_slice(&self.interval_bytes_sent.to_le_bytes());
buf.extend_from_slice(&self.cumulative_packets_sent.to_le_bytes());
buf.extend_from_slice(&self.cumulative_bytes_sent.to_le_bytes());
buf
}
pub fn decode(body: &[u8]) -> Result<Self, ProtocolError> {
if body.len() < SESSION_SENDER_REPORT_SIZE {
return Err(ProtocolError::MessageTooShort {
expected: SESSION_SENDER_REPORT_SIZE,
got: body.len(),
});
}
let p = &body[2..];
Ok(Self {
interval_start_counter: u64::from_le_bytes(p[0..8].try_into().unwrap()),
interval_end_counter: u64::from_le_bytes(p[8..16].try_into().unwrap()),
interval_start_timestamp: u32::from_le_bytes(p[16..20].try_into().unwrap()),
interval_end_timestamp: u32::from_le_bytes(p[20..24].try_into().unwrap()),
interval_bytes_sent: u32::from_le_bytes(p[24..28].try_into().unwrap()),
cumulative_packets_sent: u64::from_le_bytes(p[28..36].try_into().unwrap()),
cumulative_bytes_sent: u64::from_le_bytes(p[36..44].try_into().unwrap()),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SessionReceiverReport {
pub highest_counter: u64,
pub cumulative_packets_recv: u64,
pub cumulative_bytes_recv: u64,
pub timestamp_echo: u32,
pub dwell_time: u16,
pub max_burst_loss: u16,
pub mean_burst_loss: u16,
pub jitter: u32,
pub ecn_ce_count: u32,
pub owd_trend: i32,
pub burst_loss_count: u32,
pub cumulative_reorder_count: u32,
pub interval_packets_recv: u32,
pub interval_bytes_recv: u32,
}
pub const SESSION_RECEIVER_REPORT_SIZE: usize = 66;
impl SessionReceiverReport {
pub fn encode(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(SESSION_RECEIVER_REPORT_SIZE);
buf.extend_from_slice(&[0u8; 2]); buf.extend_from_slice(&self.highest_counter.to_le_bytes());
buf.extend_from_slice(&self.cumulative_packets_recv.to_le_bytes());
buf.extend_from_slice(&self.cumulative_bytes_recv.to_le_bytes());
buf.extend_from_slice(&self.timestamp_echo.to_le_bytes());
buf.extend_from_slice(&self.dwell_time.to_le_bytes());
buf.extend_from_slice(&self.max_burst_loss.to_le_bytes());
buf.extend_from_slice(&self.mean_burst_loss.to_le_bytes());
buf.extend_from_slice(&[0u8; 2]); buf.extend_from_slice(&self.jitter.to_le_bytes());
buf.extend_from_slice(&self.ecn_ce_count.to_le_bytes());
buf.extend_from_slice(&self.owd_trend.to_le_bytes());
buf.extend_from_slice(&self.burst_loss_count.to_le_bytes());
buf.extend_from_slice(&self.cumulative_reorder_count.to_le_bytes());
buf.extend_from_slice(&self.interval_packets_recv.to_le_bytes());
buf.extend_from_slice(&self.interval_bytes_recv.to_le_bytes());
buf
}
pub fn decode(body: &[u8]) -> Result<Self, ProtocolError> {
if body.len() < SESSION_RECEIVER_REPORT_SIZE {
return Err(ProtocolError::MessageTooShort {
expected: SESSION_RECEIVER_REPORT_SIZE,
got: body.len(),
});
}
let p = &body[2..];
Ok(Self {
highest_counter: u64::from_le_bytes(p[0..8].try_into().unwrap()),
cumulative_packets_recv: u64::from_le_bytes(p[8..16].try_into().unwrap()),
cumulative_bytes_recv: u64::from_le_bytes(p[16..24].try_into().unwrap()),
timestamp_echo: u32::from_le_bytes(p[24..28].try_into().unwrap()),
dwell_time: u16::from_le_bytes(p[28..30].try_into().unwrap()),
max_burst_loss: u16::from_le_bytes(p[30..32].try_into().unwrap()),
mean_burst_loss: u16::from_le_bytes(p[32..34].try_into().unwrap()),
jitter: u32::from_le_bytes(p[36..40].try_into().unwrap()),
ecn_ce_count: u32::from_le_bytes(p[40..44].try_into().unwrap()),
owd_trend: i32::from_le_bytes(p[44..48].try_into().unwrap()),
burst_loss_count: u32::from_le_bytes(p[48..52].try_into().unwrap()),
cumulative_reorder_count: u32::from_le_bytes(p[52..56].try_into().unwrap()),
interval_packets_recv: u32::from_le_bytes(p[56..60].try_into().unwrap()),
interval_bytes_recv: u32::from_le_bytes(p[60..64].try_into().unwrap()),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PathMtuNotification {
pub path_mtu: u16,
}
pub const PATH_MTU_NOTIFICATION_SIZE: usize = 2;
impl PathMtuNotification {
pub fn new(path_mtu: u16) -> Self {
Self { path_mtu }
}
pub fn encode(&self) -> Vec<u8> {
self.path_mtu.to_le_bytes().to_vec()
}
pub fn decode(body: &[u8]) -> Result<Self, ProtocolError> {
if body.len() < PATH_MTU_NOTIFICATION_SIZE {
return Err(ProtocolError::MessageTooShort {
expected: PATH_MTU_NOTIFICATION_SIZE,
got: body.len(),
});
}
Ok(Self {
path_mtu: u16::from_le_bytes([body[0], body[1]]),
})
}
}
#[derive(Clone, Debug)]
pub struct CoordsRequired {
pub dest_addr: NodeAddr,
pub reporter: NodeAddr,
}
pub const COORDS_REQUIRED_SIZE: usize = 34;
impl CoordsRequired {
pub fn new(dest_addr: NodeAddr, reporter: NodeAddr) -> Self {
Self {
dest_addr,
reporter,
}
}
pub fn encode(&self) -> Vec<u8> {
let body_len = 1 + 1 + 16 + 16; let mut buf = Vec::with_capacity(4 + body_len);
buf.push(0x00); buf.push(0x04); let payload_len = body_len as u16;
buf.extend_from_slice(&payload_len.to_le_bytes());
buf.push(SessionMessageType::CoordsRequired.to_byte());
buf.push(0x00); buf.extend_from_slice(self.dest_addr.as_bytes());
buf.extend_from_slice(self.reporter.as_bytes());
buf
}
pub fn decode(payload: &[u8]) -> Result<Self, ProtocolError> {
if payload.len() < 33 {
return Err(ProtocolError::MessageTooShort {
expected: 33,
got: payload.len(),
});
}
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),
})
}
}
#[derive(Clone, Debug)]
pub struct PathBroken {
pub dest_addr: NodeAddr,
pub reporter: NodeAddr,
pub last_known_coords: Option<TreeCoordinate>,
}
impl PathBroken {
pub fn new(dest_addr: NodeAddr, reporter: NodeAddr) -> Self {
Self {
dest_addr,
reporter,
last_known_coords: None,
}
}
pub fn with_last_coords(mut self, coords: TreeCoordinate) -> Self {
self.last_known_coords = Some(coords);
self
}
pub fn encode(&self) -> Vec<u8> {
let mut body = Vec::new();
body.push(SessionMessageType::PathBroken.to_byte());
body.push(0x00); 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);
}
let payload_len = body.len() as u16;
let mut buf = Vec::with_capacity(4 + body.len());
buf.push(0x00); buf.push(0x04); buf.extend_from_slice(&payload_len.to_le_bytes());
buf.extend_from_slice(&body);
buf
}
pub fn decode(payload: &[u8]) -> Result<Self, ProtocolError> {
if payload.len() < 35 {
return Err(ProtocolError::MessageTooShort {
expected: 35,
got: payload.len(),
});
}
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,
})
}
}
#[derive(Clone, Debug)]
pub struct MtuExceeded {
pub dest_addr: NodeAddr,
pub reporter: NodeAddr,
pub mtu: u16,
}
pub const MTU_EXCEEDED_SIZE: usize = 36;
impl MtuExceeded {
pub fn new(dest_addr: NodeAddr, reporter: NodeAddr, mtu: u16) -> Self {
Self {
dest_addr,
reporter,
mtu,
}
}
pub fn encode(&self) -> Vec<u8> {
let body_len = MTU_EXCEEDED_SIZE; let mut buf = Vec::with_capacity(4 + body_len);
buf.push(0x00); buf.push(0x04); let payload_len = body_len as u16;
buf.extend_from_slice(&payload_len.to_le_bytes());
buf.push(SessionMessageType::MtuExceeded.to_byte());
buf.push(0x00); 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
}
pub fn decode(payload: &[u8]) -> Result<Self, ProtocolError> {
if payload.len() < 35 {
return Err(ProtocolError::MessageTooShort {
expected: 35,
got: payload.len(),
});
}
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::*;
fn make_node_addr(val: u8) -> NodeAddr {
let mut bytes = [0u8; 16];
bytes[0] = val;
NodeAddr::from_bytes(bytes)
}
fn make_coords(ids: &[u8]) -> TreeCoordinate {
TreeCoordinate::from_addrs(ids.iter().map(|&v| make_node_addr(v)).collect()).unwrap()
}
#[test]
fn test_session_message_type_roundtrip() {
let types = [
SessionMessageType::DataPacket,
SessionMessageType::SenderReport,
SessionMessageType::ReceiverReport,
SessionMessageType::PathMtuNotification,
SessionMessageType::CoordsWarmup,
SessionMessageType::EndpointData,
SessionMessageType::CoordsRequired,
SessionMessageType::PathBroken,
SessionMessageType::MtuExceeded,
];
for ty in types {
let byte = ty.to_byte();
let restored = SessionMessageType::from_byte(byte);
assert_eq!(restored, Some(ty));
}
}
#[test]
fn test_session_message_type_invalid() {
assert!(SessionMessageType::from_byte(0xFF).is_none());
assert!(SessionMessageType::from_byte(0x99).is_none());
}
#[test]
fn test_session_flags() {
let flags = SessionFlags::new().with_ack().bidirectional();
assert!(flags.request_ack);
assert!(flags.bidirectional);
let byte = flags.to_byte();
let restored = SessionFlags::from_byte(byte);
assert_eq!(flags, restored);
}
#[test]
fn test_session_flags_default() {
let flags = SessionFlags::new();
assert!(!flags.request_ack);
assert!(!flags.bidirectional);
assert_eq!(flags.to_byte(), 0);
}
#[test]
fn test_session_setup() {
let setup = SessionSetup::new(make_coords(&[1, 0]), make_coords(&[2, 0]))
.with_flags(SessionFlags::new().with_ack());
assert!(setup.flags.request_ack);
assert!(!setup.flags.bidirectional);
}
#[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));
}
#[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());
}
#[test]
fn test_session_setup_encode_decode() {
let handshake = vec![0xAA; 82]; let setup = SessionSetup::new(make_coords(&[1, 2, 0]), make_coords(&[3, 4, 0]))
.with_flags(SessionFlags::new().with_ack().bidirectional())
.with_handshake(handshake.clone());
let encoded = setup.encode();
assert_eq!(encoded[0], 0x01);
assert_eq!(encoded[1], 0x00); let payload_len = u16::from_le_bytes([encoded[2], encoded[3]]);
assert_eq!(payload_len as usize, encoded.len() - 4);
let decoded = SessionSetup::decode(&encoded[4..]).unwrap();
assert_eq!(decoded.flags, setup.flags);
assert_eq!(decoded.src_coords, setup.src_coords);
assert_eq!(decoded.dest_coords, setup.dest_coords);
assert_eq!(decoded.handshake_payload, handshake);
}
#[test]
fn test_session_setup_no_handshake() {
let setup = SessionSetup::new(make_coords(&[5, 0]), make_coords(&[6, 0]));
let encoded = setup.encode();
let decoded = SessionSetup::decode(&encoded[4..]).unwrap();
assert!(decoded.handshake_payload.is_empty());
assert_eq!(decoded.src_coords, setup.src_coords);
assert_eq!(decoded.dest_coords, setup.dest_coords);
}
#[test]
fn test_session_ack_encode_decode() {
let handshake = vec![0xBB; 33]; let ack = SessionAck::new(make_coords(&[7, 8, 0]), make_coords(&[3, 4, 0]))
.with_handshake(handshake.clone());
let encoded = ack.encode();
assert_eq!(encoded[0], 0x02);
assert_eq!(encoded[1], 0x00);
let decoded = SessionAck::decode(&encoded[4..]).unwrap();
assert_eq!(decoded.src_coords, ack.src_coords);
assert_eq!(decoded.dest_coords, ack.dest_coords);
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();
assert_eq!(encoded.len(), 4 + COORDS_REQUIRED_SIZE);
assert_eq!(encoded[0], 0x00);
assert_eq!(encoded[1], 0x04); assert_eq!(encoded[4], 0x20);
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();
assert_eq!(encoded[0], 0x00);
assert_eq!(encoded[1], 0x04); assert_eq!(encoded[4], 0x21);
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());
}
#[test]
fn test_session_ack_decode_too_short() {
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() {
let addrs: Vec<u8> = (0..11).collect();
let src = make_coords(&addrs);
let dest = make_coords(&[20, 21, 22, 23, 24]);
let setup = SessionSetup::new(src.clone(), dest.clone()).with_handshake(vec![0x55; 82]);
let encoded = setup.encode();
let decoded = SessionSetup::decode(&encoded[4..]).unwrap();
assert_eq!(decoded.src_coords, src);
assert_eq!(decoded.dest_coords, dest);
}
#[test]
fn test_fsp_flags_default() {
let flags = FspFlags::new();
assert!(!flags.coords_present);
assert!(!flags.key_epoch);
assert!(!flags.unencrypted);
assert_eq!(flags.to_byte(), 0x00);
}
#[test]
fn test_fsp_flags_roundtrip() {
for byte in 0u8..=0x07 {
let flags = FspFlags::from_byte(byte);
assert_eq!(flags.to_byte(), byte);
}
}
#[test]
fn test_fsp_flags_individual_bits() {
let cp = FspFlags::from_byte(0x01);
assert!(cp.coords_present);
assert!(!cp.key_epoch);
assert!(!cp.unencrypted);
let k = FspFlags::from_byte(0x02);
assert!(!k.coords_present);
assert!(k.key_epoch);
assert!(!k.unencrypted);
let u = FspFlags::from_byte(0x04);
assert!(!u.coords_present);
assert!(!u.key_epoch);
assert!(u.unencrypted);
}
#[test]
fn test_fsp_flags_ignores_reserved_bits() {
let flags = FspFlags::from_byte(0xFF);
assert!(flags.coords_present);
assert!(flags.key_epoch);
assert!(flags.unencrypted);
assert_eq!(flags.to_byte(), 0x07); }
#[test]
fn test_fsp_inner_flags_default() {
let flags = FspInnerFlags::new();
assert!(!flags.spin_bit);
assert_eq!(flags.to_byte(), 0x00);
}
#[test]
fn test_fsp_inner_flags_roundtrip() {
let flags = FspInnerFlags::from_byte(0x01);
assert!(flags.spin_bit);
assert_eq!(flags.to_byte(), 0x01);
let flags = FspInnerFlags::from_byte(0x00);
assert!(!flags.spin_bit);
assert_eq!(flags.to_byte(), 0x00);
}
#[test]
fn test_fsp_inner_flags_ignores_reserved() {
let flags = FspInnerFlags::from_byte(0xFE);
assert!(!flags.spin_bit);
assert_eq!(flags.to_byte(), 0x00);
let flags = FspInnerFlags::from_byte(0xFF);
assert!(flags.spin_bit);
assert_eq!(flags.to_byte(), 0x01);
}
#[test]
fn test_session_message_type_new_values() {
assert_eq!(SessionMessageType::SenderReport.to_byte(), 0x11);
assert_eq!(SessionMessageType::ReceiverReport.to_byte(), 0x12);
assert_eq!(SessionMessageType::PathMtuNotification.to_byte(), 0x13);
assert_eq!(SessionMessageType::CoordsWarmup.to_byte(), 0x14);
assert_eq!(SessionMessageType::EndpointData.to_byte(), 0x15);
}
#[test]
fn test_session_message_type_display() {
assert_eq!(
format!("{}", SessionMessageType::SenderReport),
"SenderReport"
);
assert_eq!(
format!("{}", SessionMessageType::ReceiverReport),
"ReceiverReport"
);
assert_eq!(
format!("{}", SessionMessageType::PathMtuNotification),
"PathMtuNotification"
);
assert_eq!(
format!("{}", SessionMessageType::CoordsWarmup),
"CoordsWarmup"
);
assert_eq!(
format!("{}", SessionMessageType::EndpointData),
"EndpointData"
);
}
fn sample_session_sender_report() -> SessionSenderReport {
SessionSenderReport {
interval_start_counter: 100,
interval_end_counter: 200,
interval_start_timestamp: 5000,
interval_end_timestamp: 6000,
interval_bytes_sent: 50_000,
cumulative_packets_sent: 10_000,
cumulative_bytes_sent: 5_000_000,
}
}
#[test]
fn test_session_sender_report_encode_size() {
let sr = sample_session_sender_report();
let encoded = sr.encode();
assert_eq!(encoded.len(), SESSION_SENDER_REPORT_SIZE);
}
#[test]
fn test_session_sender_report_roundtrip() {
let sr = sample_session_sender_report();
let encoded = sr.encode();
let decoded = SessionSenderReport::decode(&encoded).unwrap();
assert_eq!(sr, decoded);
}
#[test]
fn test_session_sender_report_too_short() {
assert!(SessionSenderReport::decode(&[0u8; 10]).is_err());
}
fn sample_session_receiver_report() -> SessionReceiverReport {
SessionReceiverReport {
highest_counter: 195,
cumulative_packets_recv: 9_500,
cumulative_bytes_recv: 4_750_000,
timestamp_echo: 5900,
dwell_time: 5,
max_burst_loss: 3,
mean_burst_loss: 384,
jitter: 1200,
ecn_ce_count: 0,
owd_trend: -50,
burst_loss_count: 2,
cumulative_reorder_count: 10,
interval_packets_recv: 95,
interval_bytes_recv: 47_500,
}
}
#[test]
fn test_session_receiver_report_encode_size() {
let rr = sample_session_receiver_report();
let encoded = rr.encode();
assert_eq!(encoded.len(), SESSION_RECEIVER_REPORT_SIZE);
}
#[test]
fn test_session_receiver_report_roundtrip() {
let rr = sample_session_receiver_report();
let encoded = rr.encode();
let decoded = SessionReceiverReport::decode(&encoded).unwrap();
assert_eq!(rr, decoded);
}
#[test]
fn test_session_receiver_report_too_short() {
assert!(SessionReceiverReport::decode(&[0u8; 10]).is_err());
}
#[test]
fn test_session_receiver_report_negative_owd_trend() {
let rr = SessionReceiverReport {
owd_trend: -12345,
..sample_session_receiver_report()
};
let encoded = rr.encode();
let decoded = SessionReceiverReport::decode(&encoded).unwrap();
assert_eq!(decoded.owd_trend, -12345);
}
#[test]
fn test_path_mtu_notification_encode_size() {
let n = PathMtuNotification::new(1400);
let encoded = n.encode();
assert_eq!(encoded.len(), PATH_MTU_NOTIFICATION_SIZE);
}
#[test]
fn test_path_mtu_notification_roundtrip() {
let n = PathMtuNotification::new(1400);
let encoded = n.encode();
let decoded = PathMtuNotification::decode(&encoded).unwrap();
assert_eq!(decoded.path_mtu, 1400);
}
#[test]
fn test_path_mtu_notification_too_short() {
assert!(PathMtuNotification::decode(&[]).is_err());
assert!(PathMtuNotification::decode(&[0x00]).is_err());
}
#[test]
fn test_path_mtu_notification_boundary_values() {
for mtu in [0u16, 1280, 1500, u16::MAX] {
let n = PathMtuNotification::new(mtu);
let encoded = n.encode();
let decoded = PathMtuNotification::decode(&encoded).unwrap();
assert_eq!(decoded.path_mtu, mtu);
}
}
#[test]
fn test_mtu_exceeded_encode_size() {
let err = MtuExceeded::new(make_node_addr(0xAA), make_node_addr(0xBB), 1400);
let encoded = err.encode();
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();
assert_eq!(encoded[0], 0x00);
assert_eq!(encoded[1], 0x04); assert_eq!(encoded[4], 0x22);
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()); }
#[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);
assert_eq!(
SessionMessageType::from_byte(0x22),
Some(SessionMessageType::MtuExceeded)
);
}
#[test]
fn test_mtu_exceeded_display() {
assert_eq!(
format!("{}", SessionMessageType::MtuExceeded),
"MtuExceeded"
);
}
#[test]
fn test_session_msg3_encode_decode() {
let handshake = vec![0xCC; 73]; let msg3 = SessionMsg3::new(handshake.clone());
let encoded = msg3.encode();
assert_eq!(encoded[0], 0x03);
assert_eq!(encoded[1], 0x00); let payload_len = u16::from_le_bytes([encoded[2], encoded[3]]);
assert_eq!(payload_len as usize, encoded.len() - 4);
let decoded = SessionMsg3::decode(&encoded[4..]).unwrap();
assert_eq!(decoded.flags, 0);
assert_eq!(decoded.handshake_payload, handshake);
}
#[test]
fn test_session_msg3_decode_too_short() {
assert!(SessionMsg3::decode(&[]).is_err());
assert!(SessionMsg3::decode(&[0x00]).is_err()); }
#[test]
fn test_session_msg3_empty_handshake() {
let msg3 = SessionMsg3::new(vec![]);
let encoded = msg3.encode();
let decoded = SessionMsg3::decode(&encoded[4..]).unwrap();
assert!(decoded.handshake_payload.is_empty());
}
}