use bytes::{Buf, BufMut};
use std::net::{IpAddr, SocketAddr};
use std::time::Duration;
use crate::coding::{self, Codec};
use crate::transport::{TransportAddr, TransportCapabilities, TransportType};
use crate::varint::VarInt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct CapabilityFlags(u16);
impl CapabilityFlags {
const SUPPORTS_FULL_QUIC: u16 = 1 << 0;
const HALF_DUPLEX: u16 = 1 << 1;
const BROADCAST: u16 = 1 << 2;
const METERED: u16 = 1 << 3;
const POWER_CONSTRAINED: u16 = 1 << 4;
const LINK_LAYER_ACKS: u16 = 1 << 5;
const MTU_TIER_SHIFT: u16 = 6;
const MTU_TIER_MASK: u16 = 0b11 << 6;
const BANDWIDTH_TIER_SHIFT: u16 = 8;
const BANDWIDTH_TIER_MASK: u16 = 0b11 << 8;
const LATENCY_TIER_SHIFT: u16 = 10;
const LATENCY_TIER_MASK: u16 = 0b11 << 10;
pub const fn empty() -> Self {
Self(0)
}
pub const fn from_raw(raw: u16) -> Self {
Self(raw)
}
pub const fn to_raw(self) -> u16 {
self.0
}
pub fn from_capabilities(caps: &TransportCapabilities) -> Self {
let mut flags = 0u16;
if caps.supports_full_quic() {
flags |= Self::SUPPORTS_FULL_QUIC;
}
if caps.half_duplex {
flags |= Self::HALF_DUPLEX;
}
if caps.broadcast {
flags |= Self::BROADCAST;
}
if caps.metered {
flags |= Self::METERED;
}
if caps.power_constrained {
flags |= Self::POWER_CONSTRAINED;
}
if caps.link_layer_acks {
flags |= Self::LINK_LAYER_ACKS;
}
let mtu_tier = match caps.mtu {
0..=499 => 0,
500..=1199 => 1,
1200..=4095 => 2,
_ => 3,
};
flags |= (mtu_tier as u16) << Self::MTU_TIER_SHIFT;
let bandwidth_tier = match caps.bandwidth_class() {
crate::transport::BandwidthClass::VeryLow => 0,
crate::transport::BandwidthClass::Low => 1,
crate::transport::BandwidthClass::Medium => 2,
crate::transport::BandwidthClass::High => 3,
};
flags |= (bandwidth_tier as u16) << Self::BANDWIDTH_TIER_SHIFT;
let latency_tier = if caps.typical_rtt >= Duration::from_secs(2) {
0
} else if caps.typical_rtt >= Duration::from_millis(500) {
1
} else if caps.typical_rtt >= Duration::from_millis(100) {
2
} else {
3
};
flags |= (latency_tier as u16) << Self::LATENCY_TIER_SHIFT;
Self(flags)
}
pub const fn supports_full_quic(self) -> bool {
(self.0 & Self::SUPPORTS_FULL_QUIC) != 0
}
pub const fn half_duplex(self) -> bool {
(self.0 & Self::HALF_DUPLEX) != 0
}
pub const fn broadcast(self) -> bool {
(self.0 & Self::BROADCAST) != 0
}
pub const fn metered(self) -> bool {
(self.0 & Self::METERED) != 0
}
pub const fn power_constrained(self) -> bool {
(self.0 & Self::POWER_CONSTRAINED) != 0
}
pub const fn link_layer_acks(self) -> bool {
(self.0 & Self::LINK_LAYER_ACKS) != 0
}
pub const fn mtu_tier(self) -> u8 {
((self.0 & Self::MTU_TIER_MASK) >> Self::MTU_TIER_SHIFT) as u8
}
pub const fn bandwidth_tier(self) -> u8 {
((self.0 & Self::BANDWIDTH_TIER_MASK) >> Self::BANDWIDTH_TIER_SHIFT) as u8
}
pub const fn latency_tier(self) -> u8 {
((self.0 & Self::LATENCY_TIER_MASK) >> Self::LATENCY_TIER_SHIFT) as u8
}
pub fn mtu_range(self) -> (usize, usize) {
match self.mtu_tier() {
0 => (0, 499),
1 => (500, 1199),
2 => (1200, 4095),
_ => (4096, 65535),
}
}
pub fn latency_range(self) -> (Duration, Duration) {
match self.latency_tier() {
0 => (Duration::from_secs(2), Duration::from_secs(60)),
1 => (Duration::from_millis(500), Duration::from_secs(2)),
2 => (Duration::from_millis(100), Duration::from_millis(500)),
_ => (Duration::ZERO, Duration::from_millis(100)),
}
}
pub const fn with_supports_full_quic(mut self, value: bool) -> Self {
if value {
self.0 |= Self::SUPPORTS_FULL_QUIC;
} else {
self.0 &= !Self::SUPPORTS_FULL_QUIC;
}
self
}
pub const fn with_half_duplex(mut self, value: bool) -> Self {
if value {
self.0 |= Self::HALF_DUPLEX;
} else {
self.0 &= !Self::HALF_DUPLEX;
}
self
}
pub const fn with_broadcast(mut self, value: bool) -> Self {
if value {
self.0 |= Self::BROADCAST;
} else {
self.0 &= !Self::BROADCAST;
}
self
}
pub const fn with_metered(mut self, value: bool) -> Self {
if value {
self.0 |= Self::METERED;
} else {
self.0 &= !Self::METERED;
}
self
}
pub const fn with_power_constrained(mut self, value: bool) -> Self {
if value {
self.0 |= Self::POWER_CONSTRAINED;
} else {
self.0 &= !Self::POWER_CONSTRAINED;
}
self
}
pub const fn with_link_layer_acks(mut self, value: bool) -> Self {
if value {
self.0 |= Self::LINK_LAYER_ACKS;
} else {
self.0 &= !Self::LINK_LAYER_ACKS;
}
self
}
pub const fn with_mtu_tier(mut self, tier: u8) -> Self {
let tier = if tier > 3 { 3 } else { tier };
self.0 = (self.0 & !Self::MTU_TIER_MASK) | ((tier as u16) << Self::MTU_TIER_SHIFT);
self
}
pub const fn with_bandwidth_tier(mut self, tier: u8) -> Self {
let tier = if tier > 3 { 3 } else { tier };
self.0 =
(self.0 & !Self::BANDWIDTH_TIER_MASK) | ((tier as u16) << Self::BANDWIDTH_TIER_SHIFT);
self
}
pub const fn with_latency_tier(mut self, tier: u8) -> Self {
let tier = if tier > 3 { 3 } else { tier };
self.0 = (self.0 & !Self::LATENCY_TIER_MASK) | ((tier as u16) << Self::LATENCY_TIER_SHIFT);
self
}
pub const fn broadband() -> Self {
Self::empty()
.with_supports_full_quic(true)
.with_broadcast(true)
.with_mtu_tier(2) .with_bandwidth_tier(3) .with_latency_tier(3) }
pub const fn ble() -> Self {
Self::empty()
.with_broadcast(true)
.with_power_constrained(true)
.with_link_layer_acks(true)
.with_mtu_tier(0) .with_bandwidth_tier(2) .with_latency_tier(2) }
pub const fn lora_long_range() -> Self {
Self::empty()
.with_half_duplex(true)
.with_broadcast(true)
.with_power_constrained(true)
.with_mtu_tier(0) .with_bandwidth_tier(0) .with_latency_tier(0) }
}
pub const FRAME_TYPE_ADD_ADDRESS: u64 = 0x3d7e90;
pub const FRAME_TYPE_PUNCH_ME_NOW: u64 = 0x3d7e91;
pub const FRAME_TYPE_REMOVE_ADDRESS: u64 = 0x3d7e92;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AddAddress {
pub sequence: u64,
pub priority: u64,
pub transport_type: TransportType,
pub address: TransportAddr,
pub capabilities: Option<CapabilityFlags>,
}
impl AddAddress {
pub fn udp(sequence: u64, priority: u64, socket_addr: SocketAddr) -> Self {
Self {
sequence,
priority,
transport_type: TransportType::Udp,
address: TransportAddr::Udp(socket_addr),
capabilities: None,
}
}
pub fn new(sequence: u64, priority: u64, address: TransportAddr) -> Self {
Self {
sequence,
priority,
transport_type: address.transport_type(),
address,
capabilities: None,
}
}
pub fn with_capabilities(
sequence: u64,
priority: u64,
address: TransportAddr,
capabilities: CapabilityFlags,
) -> Self {
Self {
sequence,
priority,
transport_type: address.transport_type(),
address,
capabilities: Some(capabilities),
}
}
pub fn from_capabilities(
sequence: u64,
priority: u64,
address: TransportAddr,
capabilities: &TransportCapabilities,
) -> Self {
Self {
sequence,
priority,
transport_type: address.transport_type(),
address,
capabilities: Some(CapabilityFlags::from_capabilities(capabilities)),
}
}
pub fn socket_addr(&self) -> Option<SocketAddr> {
self.address.as_socket_addr()
}
pub fn has_capabilities(&self) -> bool {
self.capabilities.is_some()
}
pub fn capability_flags(&self) -> Option<CapabilityFlags> {
self.capabilities
}
pub fn supports_full_quic(&self) -> Option<bool> {
self.capabilities.map(|c| c.supports_full_quic())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PunchMeNow {
pub round: u64,
pub paired_with_sequence_number: u64,
pub address: SocketAddr,
pub target_peer_id: Option<[u8; 32]>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RemoveAddress {
pub sequence: u64,
}
const TRANSPORT_TYPE_UDP: u64 = 0;
const TRANSPORT_TYPE_BLE: u64 = 1;
const TRANSPORT_TYPE_LORA: u64 = 2;
const TRANSPORT_TYPE_SERIAL: u64 = 3;
const TRANSPORT_TYPE_AX25: u64 = 4;
const TRANSPORT_TYPE_I2P: u64 = 5;
const TRANSPORT_TYPE_YGGDRASIL: u64 = 6;
impl Codec for AddAddress {
fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
if buf.remaining() < 1 {
return Err(coding::UnexpectedEnd);
}
let sequence = VarInt::decode(buf)?.into_inner();
let priority = VarInt::decode(buf)?.into_inner();
let transport_type_raw = if buf.remaining() > 0 {
VarInt::decode(buf)?.into_inner()
} else {
TRANSPORT_TYPE_UDP
};
let transport_type = match transport_type_raw {
TRANSPORT_TYPE_UDP => TransportType::Udp,
TRANSPORT_TYPE_BLE => TransportType::Ble,
TRANSPORT_TYPE_LORA => TransportType::LoRa,
TRANSPORT_TYPE_SERIAL => TransportType::Serial,
TRANSPORT_TYPE_AX25 => TransportType::Ax25,
TRANSPORT_TYPE_I2P => TransportType::I2p,
TRANSPORT_TYPE_YGGDRASIL => TransportType::Yggdrasil,
_ => TransportType::Udp, };
let address = match transport_type {
TransportType::Udp => {
if buf.remaining() < 1 {
return Err(coding::UnexpectedEnd);
}
let addr_type = buf.get_u8();
let ip = match addr_type {
4 => {
if buf.remaining() < 4 {
return Err(coding::UnexpectedEnd);
}
let mut addr = [0u8; 4];
buf.copy_to_slice(&mut addr);
IpAddr::from(addr)
}
6 => {
if buf.remaining() < 16 {
return Err(coding::UnexpectedEnd);
}
let mut addr = [0u8; 16];
buf.copy_to_slice(&mut addr);
IpAddr::from(addr)
}
_ => return Err(coding::UnexpectedEnd),
};
if buf.remaining() < 2 {
return Err(coding::UnexpectedEnd);
}
let port = buf.get_u16();
TransportAddr::Udp(SocketAddr::new(ip, port))
}
TransportType::Ble => {
if buf.remaining() < 6 {
return Err(coding::UnexpectedEnd);
}
let mut device_id = [0u8; 6];
buf.copy_to_slice(&mut device_id);
let service_uuid = if buf.remaining() > 0 {
let has_uuid = buf.get_u8();
if has_uuid == 1 && buf.remaining() >= 16 {
let mut uuid = [0u8; 16];
buf.copy_to_slice(&mut uuid);
Some(uuid)
} else {
None
}
} else {
None
};
TransportAddr::Ble {
device_id,
service_uuid,
}
}
TransportType::LoRa => {
if buf.remaining() < 8 {
return Err(coding::UnexpectedEnd);
}
let mut device_addr = [0u8; 4];
buf.copy_to_slice(&mut device_addr);
let spreading_factor = buf.get_u8();
let bandwidth_khz = buf.get_u16();
let coding_rate = buf.get_u8();
TransportAddr::LoRa {
device_addr,
params: crate::transport::LoRaParams {
spreading_factor,
bandwidth_khz,
coding_rate,
},
}
}
TransportType::Serial => {
let name_len = VarInt::decode(buf)?.into_inner() as usize;
if buf.remaining() < name_len {
return Err(coding::UnexpectedEnd);
}
let mut name_bytes = vec![0u8; name_len];
buf.copy_to_slice(&mut name_bytes);
let port_name =
String::from_utf8(name_bytes).unwrap_or_else(|_| String::from("/dev/null"));
TransportAddr::Serial { port: port_name }
}
TransportType::Ax25 | TransportType::I2p | TransportType::Yggdrasil => {
TransportAddr::Udp(SocketAddr::new(IpAddr::from([0, 0, 0, 0]), 0))
}
};
let capabilities = if buf.remaining() > 0 {
let has_caps = buf.get_u8();
if has_caps == 1 && buf.remaining() >= 2 {
Some(CapabilityFlags::from_raw(buf.get_u16()))
} else {
None
}
} else {
None
};
Ok(Self {
sequence,
priority,
transport_type,
address,
capabilities,
})
}
fn encode<B: BufMut>(&self, buf: &mut B) {
VarInt::from_u64(self.sequence)
.unwrap_or(VarInt::from_u32(0))
.encode(buf);
VarInt::from_u64(self.priority)
.unwrap_or(VarInt::from_u32(0))
.encode(buf);
let transport_type_raw = match self.transport_type {
TransportType::Udp => TRANSPORT_TYPE_UDP,
TransportType::Ble => TRANSPORT_TYPE_BLE,
TransportType::LoRa => TRANSPORT_TYPE_LORA,
TransportType::Serial => TRANSPORT_TYPE_SERIAL,
TransportType::Ax25 => TRANSPORT_TYPE_AX25,
TransportType::I2p => TRANSPORT_TYPE_I2P,
TransportType::Yggdrasil => TRANSPORT_TYPE_YGGDRASIL,
};
VarInt::from_u64(transport_type_raw)
.unwrap_or(VarInt::from_u32(0))
.encode(buf);
match &self.address {
TransportAddr::Udp(socket_addr) => {
match socket_addr.ip() {
IpAddr::V4(ipv4) => {
buf.put_u8(4); buf.put_slice(&ipv4.octets());
}
IpAddr::V6(ipv6) => {
buf.put_u8(6); buf.put_slice(&ipv6.octets());
}
}
buf.put_u16(socket_addr.port());
}
TransportAddr::Ble {
device_id,
service_uuid,
} => {
buf.put_slice(device_id);
match service_uuid {
Some(uuid) => {
buf.put_u8(1); buf.put_slice(uuid);
}
None => {
buf.put_u8(0); }
}
}
TransportAddr::LoRa {
device_addr,
params,
} => {
buf.put_slice(device_addr);
buf.put_u8(params.spreading_factor);
buf.put_u16(params.bandwidth_khz);
buf.put_u8(params.coding_rate);
}
TransportAddr::Serial { port } => {
let name_bytes = port.as_bytes();
VarInt::from_u64(name_bytes.len() as u64)
.unwrap_or(VarInt::from_u32(0))
.encode(buf);
buf.put_slice(name_bytes);
}
TransportAddr::Ax25 { callsign, ssid } => {
let callsign_bytes = callsign.as_bytes();
VarInt::from_u64(callsign_bytes.len() as u64)
.unwrap_or(VarInt::from_u32(0))
.encode(buf);
buf.put_slice(callsign_bytes);
buf.put_u8(*ssid);
}
TransportAddr::I2p { destination } => {
buf.put_slice(destination.as_ref());
}
TransportAddr::Yggdrasil { address } => {
buf.put_slice(address);
}
TransportAddr::Broadcast { transport_type: _ } => {
buf.put_u8(4);
buf.put_slice(&[0, 0, 0, 0]);
buf.put_u16(0);
}
}
match &self.capabilities {
Some(caps) => {
buf.put_u8(1); buf.put_u16(caps.to_raw());
}
None => {
buf.put_u8(0); }
}
}
}
impl Codec for PunchMeNow {
fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
if buf.remaining() < 1 {
return Err(coding::UnexpectedEnd);
}
let round = VarInt::decode(buf)?.into_inner();
let paired_with_sequence_number = VarInt::decode(buf)?.into_inner();
let addr_type = buf.get_u8();
let ip = match addr_type {
4 => {
if buf.remaining() < 4 {
return Err(coding::UnexpectedEnd);
}
let mut addr = [0u8; 4];
buf.copy_to_slice(&mut addr);
IpAddr::from(addr)
}
6 => {
if buf.remaining() < 16 {
return Err(coding::UnexpectedEnd);
}
let mut addr = [0u8; 16];
buf.copy_to_slice(&mut addr);
IpAddr::from(addr)
}
_ => return Err(coding::UnexpectedEnd),
};
if buf.remaining() < 2 {
return Err(coding::UnexpectedEnd);
}
let port = buf.get_u16();
let target_peer_id = if buf.remaining() > 0 {
let has_peer_id = buf.get_u8();
if has_peer_id == 1 {
if buf.remaining() < 32 {
return Err(coding::UnexpectedEnd);
}
let mut peer_id = [0u8; 32];
buf.copy_to_slice(&mut peer_id);
Some(peer_id)
} else {
None
}
} else {
None
};
Ok(Self {
round,
paired_with_sequence_number,
address: SocketAddr::new(ip, port),
target_peer_id,
})
}
fn encode<B: BufMut>(&self, buf: &mut B) {
VarInt::from_u64(self.round)
.unwrap_or(VarInt::from_u32(0))
.encode(buf);
VarInt::from_u64(self.paired_with_sequence_number)
.unwrap_or(VarInt::from_u32(0))
.encode(buf);
match self.address.ip() {
IpAddr::V4(ipv4) => {
buf.put_u8(4); buf.put_slice(&ipv4.octets());
}
IpAddr::V6(ipv6) => {
buf.put_u8(6); buf.put_slice(&ipv6.octets());
}
}
buf.put_u16(self.address.port());
match &self.target_peer_id {
Some(peer_id) => {
buf.put_u8(1); buf.put_slice(peer_id);
}
None => {
buf.put_u8(0); }
}
}
}
impl Codec for RemoveAddress {
fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
if buf.remaining() < 1 {
return Err(coding::UnexpectedEnd);
}
let sequence = VarInt::decode(buf)?.into_inner();
Ok(Self { sequence })
}
fn encode<B: BufMut>(&self, buf: &mut B) {
VarInt::from_u64(self.sequence)
.unwrap_or(VarInt::from_u32(0))
.encode(buf);
}
}
impl AddAddress {
pub fn encode_with_type<B: BufMut>(&self, buf: &mut B) {
VarInt::from_u64(FRAME_TYPE_ADD_ADDRESS)
.unwrap_or(VarInt::from_u32(0))
.encode(buf);
Codec::encode(self, buf);
}
}
impl PunchMeNow {
pub fn encode_with_type<B: BufMut>(&self, buf: &mut B) {
VarInt::from_u64(FRAME_TYPE_PUNCH_ME_NOW)
.unwrap_or(VarInt::from_u32(0))
.encode(buf);
Codec::encode(self, buf);
}
}
impl RemoveAddress {
pub fn encode_with_type<B: BufMut>(&self, buf: &mut B) {
VarInt::from_u64(FRAME_TYPE_REMOVE_ADDRESS)
.unwrap_or(VarInt::from_u32(0))
.encode(buf);
Codec::encode(self, buf);
}
}
#[cfg(test)]
mod tests {
use super::*;
use bytes::BytesMut;
fn test_socket_addr_v4() -> SocketAddr {
"192.168.1.100:9000".parse().expect("valid addr")
}
fn test_socket_addr_v6() -> SocketAddr {
"[::1]:9000".parse().expect("valid addr")
}
#[test]
fn test_add_address_udp_ipv4_roundtrip() {
let original = AddAddress::udp(42, 100, test_socket_addr_v4());
let mut buf = BytesMut::new();
Codec::encode(&original, &mut buf);
let decoded = AddAddress::decode(&mut buf.freeze()).expect("decode failed");
assert_eq!(decoded.sequence, 42);
assert_eq!(decoded.priority, 100);
assert_eq!(decoded.transport_type, TransportType::Udp);
assert_eq!(decoded.socket_addr(), Some(test_socket_addr_v4()));
}
#[test]
fn test_add_address_udp_ipv6_roundtrip() {
let original = AddAddress::udp(1, 50, test_socket_addr_v6());
let mut buf = BytesMut::new();
Codec::encode(&original, &mut buf);
let decoded = AddAddress::decode(&mut buf.freeze()).expect("decode failed");
assert_eq!(decoded.sequence, 1);
assert_eq!(decoded.transport_type, TransportType::Udp);
assert_eq!(decoded.socket_addr(), Some(test_socket_addr_v6()));
}
#[test]
fn test_add_address_ble_roundtrip() {
let device_id = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC];
let original = AddAddress::new(
10,
200,
TransportAddr::Ble {
device_id,
service_uuid: None,
},
);
let mut buf = BytesMut::new();
Codec::encode(&original, &mut buf);
let decoded = AddAddress::decode(&mut buf.freeze()).expect("decode failed");
assert_eq!(decoded.sequence, 10);
assert_eq!(decoded.priority, 200);
assert_eq!(decoded.transport_type, TransportType::Ble);
if let TransportAddr::Ble {
device_id: decoded_id,
service_uuid,
} = decoded.address
{
assert_eq!(decoded_id, device_id);
assert!(service_uuid.is_none());
} else {
panic!("Expected BLE address");
}
}
#[test]
fn test_add_address_ble_with_uuid_roundtrip() {
let device_id = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
let service_uuid = [
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
0x0F, 0x10,
];
let original = AddAddress::new(
5,
300,
TransportAddr::Ble {
device_id,
service_uuid: Some(service_uuid),
},
);
let mut buf = BytesMut::new();
Codec::encode(&original, &mut buf);
let decoded = AddAddress::decode(&mut buf.freeze()).expect("decode failed");
if let TransportAddr::Ble {
device_id: decoded_id,
service_uuid: decoded_uuid,
} = decoded.address
{
assert_eq!(decoded_id, device_id);
assert_eq!(decoded_uuid, Some(service_uuid));
} else {
panic!("Expected BLE address");
}
}
#[test]
fn test_add_address_lora_roundtrip() {
let device_addr = [0xDE, 0xAD, 0xBE, 0xEF];
let params = crate::transport::LoRaParams {
spreading_factor: 10,
bandwidth_khz: 250,
coding_rate: 6,
};
let original = AddAddress::new(
99,
500,
TransportAddr::LoRa {
device_addr,
params: params.clone(),
},
);
let mut buf = BytesMut::new();
Codec::encode(&original, &mut buf);
let decoded = AddAddress::decode(&mut buf.freeze()).expect("decode failed");
assert_eq!(decoded.sequence, 99);
assert_eq!(decoded.transport_type, TransportType::LoRa);
if let TransportAddr::LoRa {
device_addr: decoded_addr,
params: decoded_params,
} = decoded.address
{
assert_eq!(decoded_addr, device_addr);
assert_eq!(decoded_params.spreading_factor, 10);
assert_eq!(decoded_params.bandwidth_khz, 250);
assert_eq!(decoded_params.coding_rate, 6);
} else {
panic!("Expected LoRa address");
}
}
#[test]
fn test_add_address_serial_roundtrip() {
let original = AddAddress::new(
7,
50,
TransportAddr::Serial {
port: "/dev/ttyUSB0".to_string(),
},
);
let mut buf = BytesMut::new();
Codec::encode(&original, &mut buf);
let decoded = AddAddress::decode(&mut buf.freeze()).expect("decode failed");
assert_eq!(decoded.sequence, 7);
assert_eq!(decoded.transport_type, TransportType::Serial);
if let TransportAddr::Serial { port } = decoded.address {
assert_eq!(port, "/dev/ttyUSB0");
} else {
panic!("Expected Serial address");
}
}
#[test]
fn test_add_address_helper_methods() {
let socket_addr = test_socket_addr_v4();
let frame = AddAddress::udp(1, 100, socket_addr);
assert_eq!(frame.socket_addr(), Some(socket_addr));
let ble_frame = AddAddress::new(
2,
100,
TransportAddr::Ble {
device_id: [0; 6],
service_uuid: None,
},
);
assert_eq!(ble_frame.socket_addr(), None);
}
#[test]
fn test_punch_me_now_roundtrip() {
let original = PunchMeNow {
round: 3,
paired_with_sequence_number: 42,
address: test_socket_addr_v4(),
target_peer_id: None,
};
let mut buf = BytesMut::new();
Codec::encode(&original, &mut buf);
let decoded = PunchMeNow::decode(&mut buf.freeze()).expect("decode failed");
assert_eq!(decoded.round, 3);
assert_eq!(decoded.paired_with_sequence_number, 42);
assert_eq!(decoded.address, test_socket_addr_v4());
assert!(decoded.target_peer_id.is_none());
}
#[test]
fn test_punch_me_now_with_peer_id_roundtrip() {
let peer_id = [0x42u8; 32];
let original = PunchMeNow {
round: 5,
paired_with_sequence_number: 10,
address: test_socket_addr_v6(),
target_peer_id: Some(peer_id),
};
let mut buf = BytesMut::new();
Codec::encode(&original, &mut buf);
let decoded = PunchMeNow::decode(&mut buf.freeze()).expect("decode failed");
assert_eq!(decoded.round, 5);
assert_eq!(decoded.target_peer_id, Some(peer_id));
}
#[test]
fn test_remove_address_roundtrip() {
let original = RemoveAddress { sequence: 123 };
let mut buf = BytesMut::new();
Codec::encode(&original, &mut buf);
let decoded = RemoveAddress::decode(&mut buf.freeze()).expect("decode failed");
assert_eq!(decoded.sequence, 123);
}
#[test]
fn test_transport_type_wire_values() {
assert_eq!(TRANSPORT_TYPE_UDP, 0);
assert_eq!(TRANSPORT_TYPE_BLE, 1);
assert_eq!(TRANSPORT_TYPE_LORA, 2);
assert_eq!(TRANSPORT_TYPE_SERIAL, 3);
}
#[test]
fn test_frame_types() {
assert_eq!(FRAME_TYPE_ADD_ADDRESS, 0x3d7e90);
assert_eq!(FRAME_TYPE_PUNCH_ME_NOW, 0x3d7e91);
assert_eq!(FRAME_TYPE_REMOVE_ADDRESS, 0x3d7e92);
}
#[test]
fn test_capability_flags_empty() {
let flags = CapabilityFlags::empty();
assert_eq!(flags.to_raw(), 0);
assert!(!flags.supports_full_quic());
assert!(!flags.half_duplex());
assert!(!flags.broadcast());
assert!(!flags.metered());
assert!(!flags.power_constrained());
assert!(!flags.link_layer_acks());
assert_eq!(flags.mtu_tier(), 0);
assert_eq!(flags.bandwidth_tier(), 0);
assert_eq!(flags.latency_tier(), 0);
}
#[test]
fn test_capability_flags_individual_bits() {
let flags = CapabilityFlags::empty().with_supports_full_quic(true);
assert!(flags.supports_full_quic());
assert_eq!(flags.to_raw(), 1);
let flags = CapabilityFlags::empty().with_half_duplex(true);
assert!(flags.half_duplex());
assert_eq!(flags.to_raw(), 2);
let flags = CapabilityFlags::empty().with_broadcast(true);
assert!(flags.broadcast());
assert_eq!(flags.to_raw(), 4);
let flags = CapabilityFlags::empty().with_metered(true);
assert!(flags.metered());
assert_eq!(flags.to_raw(), 8);
let flags = CapabilityFlags::empty().with_power_constrained(true);
assert!(flags.power_constrained());
assert_eq!(flags.to_raw(), 16);
let flags = CapabilityFlags::empty().with_link_layer_acks(true);
assert!(flags.link_layer_acks());
assert_eq!(flags.to_raw(), 32);
}
#[test]
fn test_capability_flags_tiers() {
let flags = CapabilityFlags::empty().with_mtu_tier(0);
assert_eq!(flags.mtu_tier(), 0);
assert_eq!(flags.mtu_range(), (0, 499));
let flags = CapabilityFlags::empty().with_mtu_tier(1);
assert_eq!(flags.mtu_tier(), 1);
assert_eq!(flags.mtu_range(), (500, 1199));
let flags = CapabilityFlags::empty().with_mtu_tier(2);
assert_eq!(flags.mtu_tier(), 2);
assert_eq!(flags.mtu_range(), (1200, 4095));
let flags = CapabilityFlags::empty().with_mtu_tier(3);
assert_eq!(flags.mtu_tier(), 3);
assert_eq!(flags.mtu_range(), (4096, 65535));
let flags = CapabilityFlags::empty().with_bandwidth_tier(0);
assert_eq!(flags.bandwidth_tier(), 0);
let flags = CapabilityFlags::empty().with_bandwidth_tier(3);
assert_eq!(flags.bandwidth_tier(), 3);
let flags = CapabilityFlags::empty().with_latency_tier(0);
assert_eq!(flags.latency_tier(), 0);
assert_eq!(flags.latency_range().0, Duration::from_secs(2));
let flags = CapabilityFlags::empty().with_latency_tier(3);
assert_eq!(flags.latency_tier(), 3);
assert_eq!(flags.latency_range().1, Duration::from_millis(100));
}
#[test]
fn test_capability_flags_tier_clamping() {
let flags = CapabilityFlags::empty().with_mtu_tier(10);
assert_eq!(flags.mtu_tier(), 3);
let flags = CapabilityFlags::empty().with_bandwidth_tier(255);
assert_eq!(flags.bandwidth_tier(), 3);
let flags = CapabilityFlags::empty().with_latency_tier(100);
assert_eq!(flags.latency_tier(), 3);
}
#[test]
fn test_capability_flags_presets() {
let broadband = CapabilityFlags::broadband();
assert!(broadband.supports_full_quic());
assert!(broadband.broadcast());
assert!(!broadband.half_duplex());
assert!(!broadband.power_constrained());
assert_eq!(broadband.mtu_tier(), 2);
assert_eq!(broadband.bandwidth_tier(), 3);
assert_eq!(broadband.latency_tier(), 3);
let ble = CapabilityFlags::ble();
assert!(!ble.supports_full_quic());
assert!(ble.broadcast());
assert!(ble.power_constrained());
assert!(ble.link_layer_acks());
assert_eq!(ble.mtu_tier(), 0);
assert_eq!(ble.bandwidth_tier(), 2);
assert_eq!(ble.latency_tier(), 2);
let lora = CapabilityFlags::lora_long_range();
assert!(!lora.supports_full_quic());
assert!(lora.half_duplex());
assert!(lora.broadcast());
assert!(lora.power_constrained());
assert_eq!(lora.mtu_tier(), 0);
assert_eq!(lora.bandwidth_tier(), 0);
assert_eq!(lora.latency_tier(), 0);
}
#[test]
fn test_capability_flags_from_transport_capabilities() {
let caps = TransportCapabilities::broadband();
let flags = CapabilityFlags::from_capabilities(&caps);
assert!(flags.supports_full_quic());
assert!(!flags.half_duplex());
assert!(flags.broadcast());
assert!(!flags.metered());
assert!(!flags.power_constrained());
assert_eq!(flags.bandwidth_tier(), 3);
let caps = TransportCapabilities::ble();
let flags = CapabilityFlags::from_capabilities(&caps);
assert!(!flags.supports_full_quic()); assert!(flags.power_constrained());
assert!(flags.link_layer_acks());
assert_eq!(flags.bandwidth_tier(), 2);
let caps = TransportCapabilities::lora_long_range();
let flags = CapabilityFlags::from_capabilities(&caps);
assert!(!flags.supports_full_quic());
assert!(flags.half_duplex());
assert!(flags.broadcast());
assert!(flags.power_constrained());
assert_eq!(flags.bandwidth_tier(), 0); assert_eq!(flags.latency_tier(), 0); }
#[test]
fn test_capability_flags_roundtrip() {
let original = CapabilityFlags::empty()
.with_supports_full_quic(true)
.with_broadcast(true)
.with_mtu_tier(2)
.with_bandwidth_tier(3)
.with_latency_tier(1);
let raw = original.to_raw();
let decoded = CapabilityFlags::from_raw(raw);
assert_eq!(decoded.supports_full_quic(), original.supports_full_quic());
assert_eq!(decoded.broadcast(), original.broadcast());
assert_eq!(decoded.mtu_tier(), original.mtu_tier());
assert_eq!(decoded.bandwidth_tier(), original.bandwidth_tier());
assert_eq!(decoded.latency_tier(), original.latency_tier());
}
#[test]
fn test_add_address_with_capabilities_roundtrip() {
let caps = CapabilityFlags::broadband();
let original =
AddAddress::with_capabilities(42, 100, TransportAddr::Udp(test_socket_addr_v4()), caps);
assert!(original.has_capabilities());
assert_eq!(original.capability_flags(), Some(caps));
assert_eq!(original.supports_full_quic(), Some(true));
let mut buf = BytesMut::new();
Codec::encode(&original, &mut buf);
let decoded = AddAddress::decode(&mut buf.freeze()).expect("decode failed");
assert_eq!(decoded.sequence, 42);
assert_eq!(decoded.priority, 100);
assert!(decoded.has_capabilities());
assert_eq!(decoded.capability_flags(), Some(caps));
assert_eq!(decoded.supports_full_quic(), Some(true));
}
#[test]
fn test_add_address_without_capabilities_roundtrip() {
let original = AddAddress::udp(1, 50, test_socket_addr_v4());
assert!(!original.has_capabilities());
assert_eq!(original.capability_flags(), None);
assert_eq!(original.supports_full_quic(), None);
let mut buf = BytesMut::new();
Codec::encode(&original, &mut buf);
let decoded = AddAddress::decode(&mut buf.freeze()).expect("decode failed");
assert!(!decoded.has_capabilities());
assert_eq!(decoded.capability_flags(), None);
}
#[test]
fn test_add_address_from_transport_capabilities() {
let caps = TransportCapabilities::ble();
let original = AddAddress::from_capabilities(
10,
200,
TransportAddr::Ble {
device_id: [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC],
service_uuid: None,
},
&caps,
);
assert!(original.has_capabilities());
assert_eq!(original.supports_full_quic(), Some(false));
let mut buf = BytesMut::new();
Codec::encode(&original, &mut buf);
let decoded = AddAddress::decode(&mut buf.freeze()).expect("decode failed");
assert!(decoded.has_capabilities());
let flags = decoded.capability_flags().expect("expected flags");
assert!(!flags.supports_full_quic());
assert!(flags.power_constrained());
assert!(flags.link_layer_acks());
}
#[test]
fn test_add_address_ble_with_capabilities_roundtrip() {
let caps = CapabilityFlags::ble();
let device_id = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
let original = AddAddress::with_capabilities(
5,
300,
TransportAddr::Ble {
device_id,
service_uuid: None,
},
caps,
);
let mut buf = BytesMut::new();
Codec::encode(&original, &mut buf);
let decoded = AddAddress::decode(&mut buf.freeze()).expect("decode failed");
assert_eq!(decoded.transport_type, TransportType::Ble);
assert!(decoded.has_capabilities());
let flags = decoded.capability_flags().expect("expected flags");
assert!(flags.power_constrained());
assert_eq!(flags.mtu_tier(), 0);
}
#[test]
fn test_add_address_lora_with_capabilities_roundtrip() {
let caps = CapabilityFlags::lora_long_range();
let device_addr = [0xDE, 0xAD, 0xBE, 0xEF];
let params = crate::transport::LoRaParams {
spreading_factor: 12,
bandwidth_khz: 125,
coding_rate: 5,
};
let original = AddAddress::with_capabilities(
99,
500,
TransportAddr::LoRa {
device_addr,
params,
},
caps,
);
let mut buf = BytesMut::new();
Codec::encode(&original, &mut buf);
let decoded = AddAddress::decode(&mut buf.freeze()).expect("decode failed");
assert_eq!(decoded.transport_type, TransportType::LoRa);
assert!(decoded.has_capabilities());
let flags = decoded.capability_flags().expect("expected flags");
assert!(flags.half_duplex());
assert!(flags.power_constrained());
assert_eq!(flags.bandwidth_tier(), 0); assert_eq!(flags.latency_tier(), 0); }
}