ipsuite 0.0.1

[WIP] Collection of Types for the Internet Protocol Suite
Documentation
use core::{fmt, mem};

use zerocopy::{
    FromBytes, Immutable, IntoBytes, KnownLayout, SizeError, Unaligned, network_endian,
};

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EthernetPduError {
    CastError,
}

impl From<SizeError<&[u8], EthernetPdu>> for EthernetPduError {
    #[inline]
    fn from(_err: SizeError<&[u8], EthernetPdu>) -> Self {
        EthernetPduError::CastError
    }
}

impl From<SizeError<&mut [u8], EthernetPdu>> for EthernetPduError {
    #[inline]
    fn from(_err: SizeError<&mut [u8], EthernetPdu>) -> Self {
        EthernetPduError::CastError
    }
}

#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(C, packed)]
pub struct EthernetPdu {
    pub header: EthernetHeader,
    pub payload: [u8],
}

impl EthernetPdu {
    #[inline]
    pub fn from_bytes(buf: &[u8]) -> Result<&Self, EthernetPduError> {
        EthernetPdu::ref_from_bytes(buf)
            .map_err(SizeError::from)
            .map_err(Into::into)
    }

    #[inline]
    pub fn from_bytes_mut(buf: &mut [u8]) -> Result<&mut Self, EthernetPduError> {
        EthernetPdu::mut_from_bytes(buf)
            .map_err(SizeError::from)
            .map_err(Into::into)
    }

    #[inline]
    pub fn as_parts(&self) -> Result<(&EthernetHeader, &[u8]), EthernetPduError> {
        Ok((&self.header, &self.payload))
    }

    #[inline]
    pub fn as_mut_parts(&mut self) -> Result<(&mut EthernetHeader, &mut [u8]), EthernetPduError> {
        Ok((&mut self.header, &mut self.payload))
    }
}

impl fmt::Debug for EthernetPdu {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("EthernetPdu")
            .field("header", &self.header)
            .field("payload", &self.payload.len())
            .finish()
    }
}

#[derive(Copy, Clone, Debug, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(C, packed)]
pub struct EthernetHeader {
    pub dst: MacAddress,
    pub src: MacAddress,
    pub ethertype: EtherType,
}

impl EthernetHeader {
    pub const SIZE: usize = mem::size_of::<Self>();
}

impl fmt::Display for EthernetHeader {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "src={} dst={} type={}",
            self.src, self.dst, self.ethertype
        )
    }
}

#[derive(
    Copy,
    Clone,
    FromBytes,
    IntoBytes,
    KnownLayout,
    Immutable,
    Unaligned,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
)]
#[repr(transparent)]
pub struct MacAddress(pub [u8; 6]);

impl From<[u8; 6]> for MacAddress {
    #[inline]
    fn from(value: [u8; 6]) -> Self {
        MacAddress(value)
    }
}

impl fmt::Debug for MacAddress {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let b = self.0;
        write!(
            f,
            "MacAddress({:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x})",
            b[0], b[1], b[2], b[3], b[4], b[5]
        )
    }
}

impl fmt::Display for MacAddress {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let b = self.0;
        write!(
            f,
            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
            b[0], b[1], b[2], b[3], b[4], b[5]
        )
    }
}

#[derive(
    Copy,
    Clone,
    Debug,
    FromBytes,
    IntoBytes,
    KnownLayout,
    Immutable,
    Unaligned,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
)]
#[repr(transparent)]
pub struct EtherType(pub network_endian::U16);

impl EtherType {
    pub const IPV4: Self = Self(network_endian::U16::new(0x0800));
    pub const ARP: Self = Self(network_endian::U16::new(0x0806));
    pub const IPV6: Self = Self(network_endian::U16::new(0x86DD));

    #[inline]
    #[must_use]
    pub const fn name(self) -> Option<&'static str> {
        Some(match self {
            Self::IPV4 => "IPV4",
            Self::ARP => "ARP",
            Self::IPV6 => "IPV6",
            _ => return None,
        })
    }
}

impl fmt::Display for EtherType {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(name) = self.name() {
            f.write_str(name)
        } else {
            write!(f, "0x{:04X}", self.0)
        }
    }
}