use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
use std::time::Duration;
use crate::protocol::dds::message::Message;
use crate::protocol::dds::types::locator::Locator;
use crate::protocol::dds::{parse_message, serialize_message};
use super::error::TransportError;
use super::locator::locator_to_socket_addr;
pub const SPDP_MULTICAST_IPV4: [u8; 4] = [239, 255, 0, 1];
pub fn metatraffic_multicast_port(domain_id: u16) -> u16 {
7400u16.saturating_add(250u16.saturating_mul(domain_id))
}
pub fn metatraffic_unicast_port(domain_id: u16, participant_id: u16) -> u16 {
7400u16
.saturating_add(250u16.saturating_mul(domain_id))
.saturating_add(10)
.saturating_add(2u16.saturating_mul(participant_id))
}
pub fn user_traffic_multicast_port(domain_id: u16) -> u16 {
7400u16
.saturating_add(250u16.saturating_mul(domain_id))
.saturating_add(1)
}
pub fn user_traffic_unicast_port(domain_id: u16, participant_id: u16) -> u16 {
7400u16
.saturating_add(250u16.saturating_mul(domain_id))
.saturating_add(11)
.saturating_add(2u16.saturating_mul(participant_id))
}
pub struct TransportConfig {
pub bind_addr: SocketAddr,
pub multicast_group: Option<[u8; 4]>,
pub multicast_interface: Option<[u8; 4]>,
pub ttl: u32,
pub read_timeout: Option<Duration>,
}
impl TransportConfig {
pub fn unicast(bind_addr: SocketAddr) -> Self {
Self {
bind_addr,
multicast_group: None,
multicast_interface: None,
ttl: 1,
read_timeout: None,
}
}
pub fn multicast(port: u16, group: [u8; 4]) -> Self {
Self {
bind_addr: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port)),
multicast_group: Some(group),
multicast_interface: None,
ttl: 1,
read_timeout: None,
}
}
}
pub struct UdpTransport {
socket: UdpSocket,
}
impl UdpTransport {
pub fn new(config: TransportConfig) -> Result<Self, TransportError> {
let socket = UdpSocket::bind(config.bind_addr)?;
socket.set_ttl(config.ttl)?;
if let Some(timeout) = config.read_timeout {
socket.set_read_timeout(Some(timeout))?;
}
if let Some(group) = config.multicast_group {
let iface = config.multicast_interface.unwrap_or([0, 0, 0, 0]);
socket.join_multicast_v4(&Ipv4Addr::from(group), &Ipv4Addr::from(iface))?;
}
Ok(Self { socket })
}
pub fn send_to(&self, msg: &Message<'_>, locator: &Locator) -> Result<usize, TransportError> {
let addr = locator_to_socket_addr(locator).ok_or(TransportError::InvalidLocator)?;
let mut buf = [0u8; 65535];
let n = serialize_message(msg, &mut buf)?;
Ok(self.socket.send_to(&buf[..n], addr)?)
}
pub fn recv_into<'buf>(
&self,
buf: &'buf mut [u8],
) -> Result<(Message<'buf>, SocketAddr), TransportError> {
let (n, addr) = self.socket.recv_from(buf)?;
let msg = parse_message(&buf[..n])?;
Ok((msg, addr))
}
pub fn set_read_timeout(&self, dur: Option<Duration>) -> Result<(), TransportError> {
Ok(self.socket.set_read_timeout(dur)?)
}
pub fn from_socket(socket: UdpSocket) -> Self {
Self { socket }
}
pub fn local_addr(&self) -> Result<SocketAddr, TransportError> {
Ok(self.socket.local_addr()?)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::protocol::dds::byte_cursor::Endianness;
use crate::protocol::dds::message::submessage::Heartbeat;
use crate::protocol::dds::message::{Message, MessageHeader, Submessage};
use crate::protocol::dds::types::guid::{
GuidPrefix, ENTITYID_UNKNOWN, PROTOCOL_VERSION_2_3, VENDOR_ID_OXICTL,
};
use crate::protocol::dds::types::sequence::SequenceNumber;
fn make_test_header() -> MessageHeader {
MessageHeader {
version: PROTOCOL_VERSION_2_3,
vendor_id: VENDOR_ID_OXICTL,
guid_prefix: GuidPrefix([0xABu8; 12]),
}
}
#[test]
fn port_formulas_domain0() {
assert_eq!(metatraffic_multicast_port(0), 7400);
assert_eq!(metatraffic_unicast_port(0, 0), 7410);
assert_eq!(metatraffic_unicast_port(0, 1), 7412);
assert_eq!(user_traffic_multicast_port(0), 7401);
assert_eq!(user_traffic_unicast_port(0, 0), 7411);
assert_eq!(user_traffic_unicast_port(0, 1), 7413);
}
#[test]
fn port_formulas_domain1() {
assert_eq!(metatraffic_multicast_port(1), 7650);
assert_eq!(metatraffic_unicast_port(1, 0), 7660);
assert_eq!(user_traffic_multicast_port(1), 7651);
}
#[test]
fn port_formulas_saturation() {
let _ = metatraffic_multicast_port(u16::MAX);
let _ = metatraffic_unicast_port(u16::MAX, u16::MAX);
let _ = user_traffic_multicast_port(u16::MAX);
let _ = user_traffic_unicast_port(u16::MAX, u16::MAX);
}
#[test]
fn send_recv_loopback() {
let bind_a = SocketAddr::from(([127, 0, 0, 1], 0));
let bind_b = SocketAddr::from(([127, 0, 0, 1], 0));
let cfg_a = TransportConfig::unicast(bind_a);
let cfg_b = TransportConfig {
read_timeout: Some(Duration::from_millis(500)),
..TransportConfig::unicast(bind_b)
};
let a = UdpTransport::new(cfg_a).unwrap();
let b = UdpTransport::new(cfg_b).unwrap();
let b_addr = b.local_addr().unwrap();
let b_locator = Locator::udp_v4(b_addr.port() as u32, [127, 0, 0, 1]);
let hb = Heartbeat {
endianness: Endianness::Little,
final_flag: true,
liveliness_flag: false,
group_info_flag: false,
reader_id: ENTITYID_UNKNOWN,
writer_id: ENTITYID_UNKNOWN,
first_sn: SequenceNumber::new(1),
last_sn: SequenceNumber::new(10),
count: 1,
};
let mut subs: heapless::Vec<Submessage<'_>, 64> = heapless::Vec::new();
subs.push(Submessage::Heartbeat(hb)).unwrap();
let msg = Message {
header: make_test_header(),
submessages: subs,
};
a.send_to(&msg, &b_locator).unwrap();
let mut buf = [0u8; 65535];
let (received, _sender) = b.recv_into(&mut buf).unwrap();
assert_eq!(received.header.guid_prefix, GuidPrefix([0xABu8; 12]));
assert_eq!(received.submessages.len(), 1);
assert!(matches!(&received.submessages[0], Submessage::Heartbeat(h) if h.count == 1));
}
#[test]
fn from_socket_local_addr() {
let sock = UdpSocket::bind("127.0.0.1:0").unwrap();
let transport = UdpTransport::from_socket(sock);
assert!(transport.local_addr().is_ok());
}
}