internet 0.1.0

Network library for rust
Documentation
//! IEEE 802.3 Ethernet frame types and codecs.
//!
//! As defined in the IEEE 802.3 standard.
//!
//! ## Types
//!
//! * [Header] — the Ethernet frame header.
//! * [EtherType] — the EtherType field.
//! * [Address] — the MAC address.

use crate::{Buf, BufMut, BufResult, Codec, Cursor};

/// An Ethernet frame header.
///
/// As defined in the IEEE 802.3 standard.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Header {
    /// The destination MAC address.
    pub destination_address: Address,
    /// The source MAC address.
    pub source_address: Address,
    /// The length or type.
    pub length_or_type: EtherType,
}

impl Header {
    /// The size of the header in bytes.
    pub const SIZE: usize = 14;
}

impl Codec for Header {
    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
        self.destination_address.encode(writer, ())?;
        self.source_address.encode(writer, ())?;
        self.length_or_type.encode(writer, ())?;
        Ok(())
    }

    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
        let destination_address = Address::decode(reader, ())?;
        let source_address = Address::decode(reader, ())?;
        let length_or_type = EtherType::decode(reader, ())?;
        Ok(Self {
            destination_address,
            source_address,
            length_or_type,
        })
    }
}

/// An EtherType value.
///
/// Indicates the protocol encapsulated in the payload or the length of the payload.
/// Values <= 1500 indicate length, values >= 1536 indicate type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EtherType(pub u16);

impl EtherType {
    /// IPv4 protocol.
    pub const IPV4: Self = Self(0x0800);
    /// IPv6 protocol.
    pub const IPV6: Self = Self(0x86DD);
    /// Address Resolution Protocol.
    pub const ARP: Self = Self(0x0806);
    /// Reverse Address Resolution Protocol.
    pub const RARP: Self = Self(0x8035);
    /// VLAN-tagged frame (IEEE 802.1Q).
    pub const VLAN: Self = Self(0x8100);
    /// EAP over LAN (IEEE 802.1X).
    pub const EAPOL: Self = Self(0x888E);

    /// Returns true if this is a length field (value <= 1500).
    pub fn is_length(&self) -> bool {
        self.0 <= 1500
    }

    /// Returns true if this is a type field (value >= 1536).
    pub fn is_type(&self) -> bool {
        self.0 >= 1536
    }
}

impl Codec for EtherType {
    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
        self.0.encode(writer, ())
    }

    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
        Ok(Self(u16::decode(reader, ())?))
    }
}

/// A MAC address.
///
/// A 48-bit hardware address as defined in IEEE 802.3.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Address(pub [u8; 6]);

impl Address {
    /// The broadcast address (FF:FF:FF:FF:FF:FF).
    pub const BROADCAST: Self = Self([0xFF; 6]);

    /// The unspecified address (00:00:00:00:00:00).
    pub const UNSPECIFIED: Self = Self([0x00; 6]);

    /// Creates a new MAC address from bytes.
    pub fn new(bytes: [u8; 6]) -> Self {
        Self(bytes)
    }

    /// Returns true if this is a broadcast address.
    pub fn is_broadcast(&self) -> bool {
        self.0 == [0xFF; 6]
    }

    /// Returns true if this is a multicast address.
    pub fn is_multicast(&self) -> bool {
        (self.0[0] & 0x01) != 0
    }

    /// Returns true if this is a locally administered address.
    pub fn is_local(&self) -> bool {
        (self.0[0] & 0x02) != 0
    }
}

impl Codec for Address {
    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
        self.0.encode(writer, ())
    }

    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
        Ok(Self(<[u8; 6]>::decode(reader, ())?))
    }
}

#[cfg(test)]
mod tests {
    use core::fmt::Debug;

    use crate::{
        Codec, Cursor,
        ieee::ethernet::{Address, EtherType, Header},
    };

    fn codec_roundtrip<T: Codec<C> + Debug + Eq, C: Copy>(
        etalon_struct: T,
        etalon_bytes: &[u8],
        context: C,
    ) {
        let mut encoded_bytes = vec![];
        {
            let writer = &mut Cursor::new(&mut encoded_bytes);
            etalon_struct.encode(writer, context).unwrap();
        }
        assert_eq!(etalon_bytes, &encoded_bytes);

        let decoded_struct = {
            let reader = &mut Cursor::new(&mut encoded_bytes);
            T::decode(reader, context).unwrap()
        };
        assert_eq!(etalon_struct, decoded_struct);

        encoded_bytes.fill(0x00);
        {
            let writer = &mut Cursor::new(&mut encoded_bytes);
            decoded_struct.encode(writer, context).unwrap();
        }
        assert_eq!(etalon_bytes, &encoded_bytes);
    }

    #[test]
    fn address() {
        let addr = Address::new([0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E]);
        let bytes = &[0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E][..];
        codec_roundtrip(addr, bytes, ());

        assert!(!addr.is_broadcast());
        assert!(!addr.is_multicast());
        assert!(!addr.is_local());

        assert!(Address::BROADCAST.is_broadcast());
        assert!(Address::BROADCAST.is_multicast());
    }

    #[test]
    fn ether_type() {
        let ipv4 = EtherType::IPV4;
        let bytes = &[0x08, 0x00][..];
        codec_roundtrip(ipv4, bytes, ());

        assert!(ipv4.is_type());
        assert!(!ipv4.is_length());

        let length = EtherType(1500);
        assert!(length.is_length());
        assert!(!length.is_type());
    }

    #[test]
    fn header() {
        let etalon_bytes = &[
            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Destination: broadcast
            0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E, // Source: 00:1A:2B:3C:4D:5E
            0x08, 0x00, // Type: IPv4
        ];
        let etalon_struct = Header {
            destination_address: Address::BROADCAST,
            source_address: Address::new([0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E]),
            length_or_type: EtherType::IPV4,
        };
        codec_roundtrip(etalon_struct, etalon_bytes, ());
    }
}