//! Ethernet frame, copied from SmolTCP, //! [here](https://github.com/smoltcp-rs/smoltcp/blob/53caf70f640d5ccb3cd1492e1cb178bc7dfa3cdd/src/wire/ethernet.rs) //! at time of writing. //! //! Then drastically modified/stripped down to suit EtherCrab's needs. use core::fmt; use crate::error::{Error, PduError}; /// A six-octet Ethernet II address. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] pub struct EthernetAddress(pub [u8; 6]); impl EthernetAddress { /// The broadcast address. pub const BROADCAST: EthernetAddress = EthernetAddress([0xff; 6]); /// Construct an Ethernet address from a sequence of octets, in big-endian. /// /// # Panics /// The function panics if `data` is not six octets long. pub fn from_bytes(data: &[u8]) -> EthernetAddress { let mut bytes = [0; 6]; bytes.copy_from_slice(data); EthernetAddress(bytes) } /// Return an Ethernet address as a sequence of octets, in big-endian. pub const fn as_bytes(&self) -> &[u8] { &self.0 } } impl fmt::Display for EthernetAddress { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let bytes = self.0; write!( f, "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5] ) } } #[cfg(feature = "defmt")] impl defmt::Format for EthernetAddress { fn format(&self, fmt: defmt::Formatter) { let bytes = self.0; defmt::write!( fmt, "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5] ) } } /// A read/write wrapper around an Ethernet II frame buffer. #[derive(Debug, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct EthernetFrame> { buffer: T, } mod field { use core::ops::{Range, RangeFrom}; pub const DESTINATION: Range = 0..6; pub const SOURCE: Range = 6..12; pub const ETHERTYPE: Range = 12..14; pub const PAYLOAD: RangeFrom = 14..; } /// The Ethernet header length pub const ETHERNET_HEADER_LEN: usize = field::PAYLOAD.start; impl> EthernetFrame { /// Imbue a raw octet buffer with Ethernet frame structure. pub const fn new_unchecked(buffer: T) -> EthernetFrame { EthernetFrame { buffer } } /// Shorthand for a combination of [new_unchecked] and [check_len]. /// /// [new_unchecked]: #method.new_unchecked /// [check_len]: #method.check_len pub fn new_checked(buffer: T) -> Result, Error> { let packet = Self::new_unchecked(buffer); packet.check_len()?; Ok(packet) } /// Ensure that no accessor method will panic if called. /// Returns `Err(Error)` if the buffer is too short. pub fn check_len(&self) -> Result<(), Error> { let len = self.buffer.as_ref().len(); if len < ETHERNET_HEADER_LEN { Err(Error::Pdu(PduError::Ethernet)) } else { Ok(()) } } /// Consumes the frame, returning the underlying buffer. pub fn into_inner(self) -> T { self.buffer } /// Return the length of a frame header. pub const fn header_len() -> usize { ETHERNET_HEADER_LEN } /// Return the length of a buffer required to hold a packet with the payload /// of a given length. pub const fn buffer_len(payload_len: usize) -> usize { ETHERNET_HEADER_LEN + payload_len } /// Return the destination address field. #[inline] pub fn dst_addr(&self) -> EthernetAddress { let data = self.buffer.as_ref(); EthernetAddress::from_bytes(&data[field::DESTINATION]) } /// Return the source address field. #[inline] pub fn src_addr(&self) -> EthernetAddress { let data = self.buffer.as_ref(); EthernetAddress::from_bytes(&data[field::SOURCE]) } /// Return the EtherType field, without checking for 802.1Q. #[inline] pub fn ethertype(&self) -> u16 { let data = self.buffer.as_ref(); // Ethernet is big-endian data.get(field::ETHERTYPE) .map(|res| u16::from_be_bytes(res.try_into().unwrap())) // EtherCrab only really cares whether the ethertype is 0x88a4, so defaulting to zero on // unparseable ethertypes is fine here (imo, lol) .unwrap_or(0) } } impl<'a, T: AsRef<[u8]> + ?Sized> EthernetFrame<&'a T> { /// Return a pointer to the payload, without checking for 802.1Q. #[inline] pub fn payload(&self) -> &'a [u8] { let data = self.buffer.as_ref(); &data[field::PAYLOAD] } } impl + AsMut<[u8]>> EthernetFrame { /// Set the destination address field. #[inline] pub fn set_dst_addr(&mut self, value: EthernetAddress) { let data = self.buffer.as_mut(); data[field::DESTINATION].copy_from_slice(value.as_bytes()) } /// Set the source address field. #[inline] pub fn set_src_addr(&mut self, value: EthernetAddress) { let data = self.buffer.as_mut(); data[field::SOURCE].copy_from_slice(value.as_bytes()) } /// Set the EtherType field. #[inline] pub fn set_ethertype(&mut self, value: u16) { let data = self.buffer.as_mut(); data[field::ETHERTYPE].copy_from_slice(&value.to_be_bytes()); } /// Return a mutable pointer to the payload. #[inline] pub fn payload_mut(&mut self) -> &mut [u8] { let data = self.buffer.as_mut(); &mut data[field::PAYLOAD] } } impl> AsRef<[u8]> for EthernetFrame { fn as_ref(&self) -> &[u8] { self.buffer.as_ref() } } impl> fmt::Display for EthernetFrame { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "EthernetII src={} dst={} type={}", self.src_addr(), self.dst_addr(), self.ethertype() ) } }