#[cfg(feature = "std")]
use std::sync::{Arc, Mutex};
#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};
use crate::datalink::{DataLink, DataLinkAddress, DataLinkError, DataLinkType, Result};
pub const ETHERNET_BROADCAST_MAC: [u8; 6] = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
pub const BACNET_ETHERNET_TYPE: u16 = 0x82DC;
pub const BACNET_LLC_HEADER: [u8; 3] = [0x82, 0x82, 0x03];
pub const MIN_ETHERNET_FRAME_SIZE: usize = 60;
pub const MAX_ETHERNET_FRAME_SIZE: usize = 1514;
pub const ETHERNET_HEADER_SIZE: usize = 14;
pub const LLC_HEADER_SIZE: usize = 3;
pub const BACNET_ETHERNET_HEADER_SIZE: usize = ETHERNET_HEADER_SIZE + LLC_HEADER_SIZE;
#[derive(Debug, Clone)]
pub struct EthernetFrame {
pub dest_mac: [u8; 6],
pub src_mac: [u8; 6],
pub ether_type: u16,
pub llc_header: [u8; 3],
pub payload: Vec<u8>,
}
impl EthernetFrame {
pub fn new(dest_mac: [u8; 6], src_mac: [u8; 6], npdu: Vec<u8>) -> Self {
Self {
dest_mac,
src_mac,
ether_type: BACNET_ETHERNET_TYPE,
llc_header: BACNET_LLC_HEADER,
payload: npdu,
}
}
pub fn broadcast(src_mac: [u8; 6], npdu: Vec<u8>) -> Self {
Self::new(ETHERNET_BROADCAST_MAC, src_mac, npdu)
}
pub fn encode(&self) -> Vec<u8> {
let mut frame = Vec::with_capacity(BACNET_ETHERNET_HEADER_SIZE + self.payload.len());
frame.extend_from_slice(&self.dest_mac);
frame.extend_from_slice(&self.src_mac);
frame.extend_from_slice(&self.ether_type.to_be_bytes());
frame.extend_from_slice(&self.llc_header);
frame.extend_from_slice(&self.payload);
while frame.len() < MIN_ETHERNET_FRAME_SIZE {
frame.push(0);
}
frame
}
pub fn decode(data: &[u8]) -> Result<Self> {
if data.len() < BACNET_ETHERNET_HEADER_SIZE {
return Err(DataLinkError::InvalidFrame);
}
let mut dest_mac = [0u8; 6];
dest_mac.copy_from_slice(&data[0..6]);
let mut src_mac = [0u8; 6];
src_mac.copy_from_slice(&data[6..12]);
let ether_type = u16::from_be_bytes([data[12], data[13]]);
if ether_type != BACNET_ETHERNET_TYPE {
return Err(DataLinkError::InvalidFrame);
}
let mut llc_header = [0u8; 3];
llc_header.copy_from_slice(&data[14..17]);
if llc_header != BACNET_LLC_HEADER {
return Err(DataLinkError::InvalidFrame);
}
let payload = data[17..].to_vec();
Ok(Self {
dest_mac,
src_mac,
ether_type,
llc_header,
payload,
})
}
pub fn is_broadcast(&self) -> bool {
self.dest_mac == ETHERNET_BROADCAST_MAC
}
pub fn is_multicast(&self) -> bool {
self.dest_mac[0] & 0x01 == 0x01
}
}
#[cfg(feature = "std")]
pub struct EthernetDataLink {
local_mac: [u8; 6],
_interface: String,
rx_buffer: Arc<Mutex<Vec<(EthernetFrame, DataLinkAddress)>>>,
_running: Arc<Mutex<bool>>,
}
#[cfg(feature = "std")]
impl EthernetDataLink {
pub fn new(interface: &str, local_mac: [u8; 6]) -> Result<Self> {
let rx_buffer = Arc::new(Mutex::new(Vec::new()));
let running = Arc::new(Mutex::new(true));
Ok(Self {
local_mac,
_interface: interface.to_string(),
rx_buffer,
_running: running,
})
}
fn send_ethernet_frame(&self, frame: &EthernetFrame) -> Result<()> {
let encoded = frame.encode();
if encoded.len() > MAX_ETHERNET_FRAME_SIZE {
return Err(DataLinkError::InvalidFrame);
}
println!(
"Sending Ethernet frame: {} bytes to {:02X?}",
encoded.len(),
frame.dest_mac
);
Ok(())
}
#[cfg(test)]
pub fn simulate_receive(&self, frame: EthernetFrame, source: DataLinkAddress) {
let mut buffer = self.rx_buffer.lock().unwrap();
buffer.push((frame, source));
}
}
#[cfg(feature = "std")]
impl DataLink for EthernetDataLink {
fn send_frame(&mut self, frame: &[u8], dest: &DataLinkAddress) -> Result<()> {
let dest_mac = match dest {
DataLinkAddress::Ethernet(mac) => *mac,
DataLinkAddress::Broadcast => ETHERNET_BROADCAST_MAC,
_ => {
return Err(DataLinkError::AddressError(
"Invalid address type for Ethernet".into(),
))
}
};
let eth_frame = EthernetFrame::new(dest_mac, self.local_mac, frame.to_vec());
self.send_ethernet_frame(ð_frame)
}
fn receive_frame(&mut self) -> Result<(Vec<u8>, DataLinkAddress)> {
let mut buffer = self.rx_buffer.lock().unwrap();
if let Some((frame, source)) = buffer.pop() {
Ok((frame.payload, source))
} else {
Err(DataLinkError::InvalidFrame)
}
}
fn link_type(&self) -> DataLinkType {
DataLinkType::Ethernet
}
fn local_address(&self) -> DataLinkAddress {
DataLinkAddress::Ethernet(self.local_mac)
}
}
pub fn parse_mac_address(mac_str: &str) -> Result<[u8; 6]> {
let parts: Vec<&str> = mac_str.split(':').collect();
if parts.len() != 6 {
return Err(DataLinkError::AddressError(
"Invalid MAC address format".into(),
));
}
let mut mac = [0u8; 6];
for (i, part) in parts.iter().enumerate() {
mac[i] = u8::from_str_radix(part, 16)
.map_err(|_| DataLinkError::AddressError("Invalid MAC address hex".into()))?;
}
Ok(mac)
}
pub fn format_mac_address(mac: &[u8; 6]) -> String {
format!(
"{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
)
}
pub fn validate_ethernet_frame(data: &[u8]) -> Result<()> {
if data.len() < BACNET_ETHERNET_HEADER_SIZE {
return Err(DataLinkError::InvalidFrame);
}
if data.len() > MAX_ETHERNET_FRAME_SIZE {
return Err(DataLinkError::InvalidFrame);
}
let ether_type = u16::from_be_bytes([data[12], data[13]]);
if ether_type != BACNET_ETHERNET_TYPE {
return Err(DataLinkError::InvalidFrame);
}
if data.len() >= 17 && data[14..17] != BACNET_LLC_HEADER {
return Err(DataLinkError::InvalidFrame);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ethernet_frame_encode_decode() {
let dest_mac = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
let src_mac = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
let npdu = vec![0x01, 0x02, 0x03, 0x04];
let frame = EthernetFrame::new(dest_mac, src_mac, npdu.clone());
let encoded = frame.encode();
assert!(encoded.len() >= MIN_ETHERNET_FRAME_SIZE);
let decoded = EthernetFrame::decode(&encoded).unwrap();
assert_eq!(decoded.dest_mac, dest_mac);
assert_eq!(decoded.src_mac, src_mac);
assert_eq!(decoded.ether_type, BACNET_ETHERNET_TYPE);
assert_eq!(decoded.llc_header, BACNET_LLC_HEADER);
assert!(decoded.payload.starts_with(&npdu));
}
#[test]
fn test_broadcast_frame() {
let src_mac = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
let npdu = vec![0x01, 0x02, 0x03, 0x04];
let frame = EthernetFrame::broadcast(src_mac, npdu);
assert_eq!(frame.dest_mac, ETHERNET_BROADCAST_MAC);
assert!(frame.is_broadcast());
assert!(frame.is_multicast());
}
#[test]
fn test_mac_address_parsing() {
let mac_str = "00:11:22:33:44:55";
let mac = parse_mac_address(mac_str).unwrap();
assert_eq!(mac, [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
let formatted = format_mac_address(&mac);
assert_eq!(formatted, "00:11:22:33:44:55");
assert!(parse_mac_address("invalid").is_err());
assert!(parse_mac_address("00:11:22:33:44").is_err());
assert!(parse_mac_address("00:11:22:33:44:GG").is_err());
}
#[test]
fn test_frame_validation() {
let dest_mac = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
let src_mac = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
let npdu = vec![0x01, 0x02, 0x03, 0x04];
let frame = EthernetFrame::new(dest_mac, src_mac, npdu);
let encoded = frame.encode();
assert!(validate_ethernet_frame(&encoded).is_ok());
assert!(validate_ethernet_frame(&[]).is_err()); assert!(validate_ethernet_frame(&encoded[..16]).is_err());
let mut bad_frame = encoded.clone();
bad_frame[12] = 0x08;
bad_frame[13] = 0x00;
assert!(validate_ethernet_frame(&bad_frame).is_err());
}
#[cfg(feature = "std")]
#[test]
fn test_ethernet_datalink() {
let local_mac = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
let mut datalink = EthernetDataLink::new("eth0", local_mac).unwrap();
assert_eq!(datalink.link_type(), DataLinkType::Ethernet);
assert_eq!(
datalink.local_address(),
DataLinkAddress::Ethernet(local_mac)
);
let dest_mac = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
let npdu = vec![0x01, 0x02, 0x03, 0x04];
let result = datalink.send_frame(&npdu, &DataLinkAddress::Ethernet(dest_mac));
assert!(result.is_ok());
let result = datalink.send_frame(&npdu, &DataLinkAddress::Broadcast);
assert!(result.is_ok());
}
}