use crate::network_port::{Bip6PortConfig, BipPortConfig, NetworkPortObject};
use bacnet_types::{MacAddr, enums::NetworkType, error::Error};
use core::net::{Ipv4Addr, Ipv6Addr};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NetworkPortEntry {
pub instance: u32,
pub network_type: u32,
pub network_number: u32,
pub mac: MacAddr,
pub ip: [u8; 4],
pub ipv6: [u8; 16],
pub udp_port: u16,
}
impl NetworkPortEntry {
pub fn bip(instance: u32, network_number: u32, ip: Ipv4Addr, udp_port: u16) -> Self {
let o = ip.octets();
let mut mac = MacAddr::new();
mac.extend_from_slice(&o);
mac.extend_from_slice(&udp_port.to_be_bytes());
Self {
instance,
network_type: NetworkType::IPV4.to_raw(),
network_number,
mac,
ip: o,
ipv6: [0; 16],
udp_port,
}
}
pub fn bip6(instance: u32, network_number: u32, ip: Ipv6Addr, udp_port: u16) -> Self {
Self::bip6_with_vmac(instance, network_number, ip, udp_port, [0; 3])
}
pub(super) fn bip6_with_vmac(
instance: u32,
network_number: u32,
ip: Ipv6Addr,
udp_port: u16,
vmac: [u8; 3],
) -> Self {
Self {
instance,
network_type: NetworkType::IPV6.to_raw(),
network_number,
mac: MacAddr::from_slice(&vmac),
ip: [0; 4],
ipv6: ip.octets(),
udp_port,
}
}
pub(super) fn build_object(&self) -> Result<NetworkPortObject, Error> {
let network_number = u16::try_from(self.network_number)
.map_err(|_| Error::Encoding("network number must be 0..65534".into()))?;
let name = format!("port-{}", self.instance);
let apdu_length = 1476;
if self.network_type == NetworkType::IPV4.to_raw() {
let [a, b, c, d] = self.ip;
let [hi, lo] = self.udp_port.to_be_bytes();
if self.mac.as_slice() != [a, b, c, d, hi, lo] {
return Err(Error::Encoding(
"B/IP MAC must match configured IPv4 address and UDP port".into(),
));
}
if self.ipv6 != [0; 16] {
return Err(Error::Encoding(
"a B/IP entry carries no IPv6 address".into(),
));
}
NetworkPortObject::new_bip(
self.instance,
name,
BipPortConfig {
network_number,
apdu_length,
ip_address: self.ip,
udp_port: self.udp_port,
..Default::default()
},
)
} else if self.network_type == NetworkType::IPV6.to_raw() {
NetworkPortObject::new_bip6(
self.instance,
name,
Bip6PortConfig {
network_number,
apdu_length,
ip_address: self.ipv6,
udp_port: self.udp_port,
vmac: self.vmac()?,
..Default::default()
},
)
} else {
NetworkPortObject::new_non_bip(
self.instance,
name,
NetworkType::from_raw(self.network_type),
network_number,
self.mac.clone(),
apdu_length,
)
}
}
pub fn sc(instance: u32, network_number: u32, vmac: [u8; 6]) -> Self {
Self {
instance,
network_type: NetworkType::VIRTUAL.to_raw(),
network_number,
mac: MacAddr::from_slice(&vmac),
ip: [0; 4],
ipv6: [0; 16],
udp_port: 0,
}
}
pub(super) fn vmac(&self) -> Result<[u8; 3], Error> {
if self.ip != [0; 4] {
return Err(Error::Encoding(
"a B/IPv6 entry carries no IPv4 address".into(),
));
}
<[u8; 3]>::try_from(self.mac.as_slice())
.map_err(|_| Error::Encoding("B/IPv6 MAC must be a 3-octet VMAC".into()))
}
}