network_types/
eth.rs

1use core::mem;
2
3/// Ethernet header structure that appears at the beginning of every Ethernet frame.
4/// This structure represents the standard IEEE 802.3 Ethernet header format.
5#[repr(C, packed)]
6#[derive(Debug, Copy, Clone)]
7#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
8pub struct EthHdr {
9    /// Destination MAC address.
10    pub dst_addr: [u8; 6],
11    /// Source MAC address.
12    pub src_addr: [u8; 6],
13    /// Protocol which is encapsulated in the payload of the frame.
14    /// Indicates what type of data follows the Ethernet header (e.g., IPv4, IPv6, ARP)
15    pub ether_type: u16,
16}
17
18impl EthHdr {
19    pub const LEN: usize = mem::size_of::<EthHdr>();
20
21    /// Attempts to convert the raw ether_type field into an EtherType enum.
22    /// Returns either the corresponding EtherType variant or the raw value if unknown.
23    ///
24    /// # Returns
25    /// - `Ok(EtherType)` if a known protocol type
26    /// - `Err(u16)` if an unknown protocol type (returns the raw value)
27    pub fn ether_type(&self) -> Result<EtherType, u16> {
28        EtherType::try_from(self.ether_type)
29    }
30
31    /// Creates a new Ethernet header with the specified addresses and protocol type
32    ///
33    /// # Parameters
34    /// - `dst_addr`: The destination MAC address
35    /// - `src_addr`: The source MAC address
36    /// - `ether_type_enum`: The protocol type encapsulated in the payload
37    ///
38    /// # Returns
39    /// A new EthHdr structure initialized with the given values
40    pub fn new(dst_addr: [u8; 6], src_addr: [u8; 6], eth_type: EtherType) -> Self {
41        EthHdr {
42            dst_addr,
43            src_addr,
44            ether_type: eth_type.into(),
45        }
46    }
47}
48
49/// Protocol which is encapsulated in the payload of the Ethernet frame.
50/// These values represent the standard IEEE assigned protocol numbers
51#[repr(u16)]
52#[derive(PartialEq, Eq, Debug, Copy, Clone)]
53#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
54pub enum EtherType {
55    Loop = 0x0060_u16.to_be(),
56    Ipv4 = 0x0800_u16.to_be(),
57    Arp = 0x0806_u16.to_be(),
58    Ieee8021q = 0x8100_u16.to_be(),
59    Ipv6 = 0x86DD_u16.to_be(),
60    Ieee8021ad = 0x88A8_u16.to_be(),
61    Ieee8021MacSec = 0x88E5_u16.to_be(),
62    Ieee8021ah = 0x88E7_u16.to_be(),
63    Ieee8021mvrp = 0x88F5_u16.to_be(),
64    FibreChannel = 0x8906_u16.to_be(),
65    Infiniband = 0x8915_u16.to_be(),
66    LoopbackIeee8023 = 0x9000_u16.to_be(),
67    Ieee8021QinQ1 = 0x9100_u16.to_be(),
68    Ieee8021QinQ2 = 0x9200_u16.to_be(),
69    Ieee8021QinQ3 = 0x9300_u16.to_be(),
70}
71
72// This allows converting a u16 value into an EtherType enum variant.
73// This is useful when parsing headers.
74impl TryFrom<u16> for EtherType {
75    type Error = u16; // Return the unknown value itself as the error
76
77    fn try_from(value: u16) -> Result<Self, Self::Error> {
78        match value.to_be() {
79            0x0060_u16 => Ok(EtherType::Loop),
80            0x0800_u16 => Ok(EtherType::Ipv4),
81            0x0806_u16 => Ok(EtherType::Arp),
82            0x8100_u16 => Ok(EtherType::Ieee8021q),
83            0x86DD_u16 => Ok(EtherType::Ipv6),
84            0x88A8_u16 => Ok(EtherType::Ieee8021ad),
85            0x88E5_u16 => Ok(EtherType::Ieee8021MacSec),
86            0x88E7_u16 => Ok(EtherType::Ieee8021ah),
87            0x88F5_u16 => Ok(EtherType::Ieee8021mvrp),
88            0x8906_u16 => Ok(EtherType::FibreChannel),
89            0x8915_u16 => Ok(EtherType::Infiniband),
90            0x9000_u16 => Ok(EtherType::LoopbackIeee8023),
91            0x9100_u16 => Ok(EtherType::Ieee8021QinQ1),
92            0x9200_u16 => Ok(EtherType::Ieee8021QinQ2),
93            0x9300_u16 => Ok(EtherType::Ieee8021QinQ3),
94            _ => Err(value),
95        }
96    }
97}
98
99// This allows converting an EtherType enum variant back to its u16 representation.
100// This is useful when constructing headers.
101impl From<EtherType> for u16 {
102    fn from(ether_type: EtherType) -> Self {
103        ether_type as u16
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110    use core::mem;
111
112    // Test constants for MAC addresses
113    const TEST_DST_MAC: [u8; 6] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
114    const TEST_SRC_MAC: [u8; 6] = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
115
116    #[test]
117    fn test_ethhdr_len() {
118        assert_eq!(EthHdr::LEN, 14);
119        assert_eq!(mem::size_of::<EthHdr>(), 14);
120    }
121
122    #[test]
123    fn test_ethhdr_new() {
124        let eth_hdr = EthHdr::new(TEST_DST_MAC, TEST_SRC_MAC, EtherType::Ipv4);
125        assert_eq!(eth_hdr.dst_addr, TEST_DST_MAC);
126        assert_eq!(eth_hdr.src_addr, TEST_SRC_MAC);
127        let ether_type_value = eth_hdr.ether_type;
128        assert_eq!(ether_type_value, EtherType::Ipv4 as u16);
129        assert_eq!(ether_type_value, 0x0800_u16.to_be());
130    }
131
132    #[test]
133    fn test_ethhdr_ether_type_method_known() {
134        let eth_hdr = EthHdr {
135            dst_addr: TEST_DST_MAC,
136            src_addr: TEST_SRC_MAC,
137            ether_type: EtherType::Ipv6 as u16,
138        };
139        assert_eq!(eth_hdr.ether_type().unwrap(), EtherType::Ipv6);
140    }
141
142    #[test]
143    fn test_ethhdr_ether_type_method_unknown() {
144        let unknown_type_val = 0x1234_u16.to_be();
145        let eth_hdr = EthHdr {
146            dst_addr: TEST_DST_MAC,
147            src_addr: TEST_SRC_MAC,
148            ether_type: unknown_type_val,
149        };
150        assert_eq!(eth_hdr.ether_type().unwrap_err(), unknown_type_val);
151    }
152
153    #[test]
154    fn test_ethertype_try_from_u16_known() {
155        let ipv4_val = 0x0800_u16.to_be();
156        assert_eq!(EtherType::try_from(ipv4_val), Ok(EtherType::Ipv4));
157
158        let ipv6_val = 0x86DD_u16.to_be();
159        assert_eq!(EtherType::try_from(ipv6_val), Ok(EtherType::Ipv6));
160
161        let arp_val = 0x0806_u16.to_be();
162        assert_eq!(EtherType::try_from(arp_val), Ok(EtherType::Arp));
163    }
164
165    #[test]
166    fn test_ethertype_try_from_u16_unknown() {
167        let unknown_val = 0x1234_u16.to_be();
168        assert_eq!(EtherType::try_from(unknown_val), Err(unknown_val));
169    }
170
171    #[test]
172    fn test_u16_from_ethertype() {
173        assert_eq!(u16::from(EtherType::Ipv4), 0x0800_u16.to_be());
174        assert_eq!(u16::from(EtherType::Arp), 0x0806_u16.to_be());
175        assert_eq!(u16::from(EtherType::Ipv6), 0x86DD_u16.to_be());
176        assert_eq!(u16::from(EtherType::Loop), 0x0060_u16.to_be());
177    }
178
179    #[test]
180    fn test_ethertype_variants_unique_values() {
181        let all_types = [
182            EtherType::Loop,
183            EtherType::Ipv4,
184            EtherType::Arp,
185            EtherType::Ieee8021q,
186            EtherType::Ipv6,
187            EtherType::Ieee8021ad,
188            EtherType::Ieee8021MacSec,
189            EtherType::Ieee8021ah,
190            EtherType::Ieee8021mvrp,
191            EtherType::FibreChannel,
192            EtherType::Infiniband,
193            EtherType::LoopbackIeee8023,
194            EtherType::Ieee8021QinQ1,
195            EtherType::Ieee8021QinQ2,
196            EtherType::Ieee8021QinQ3,
197        ];
198
199        for i in 0..all_types.len() {
200            for j in (i + 1)..all_types.len() {
201                // Compare the u16 representation of each EtherType
202                let val_i = all_types[i] as u16;
203                let val_j = all_types[j] as u16;
204                assert_ne!(
205                    val_i, val_j,
206                    "Duplicate EtherType value found: {:?} and {:?} both have value {:#06x}",
207                    all_types[i], all_types[j], val_i
208                );
209            }
210        }
211    }
212}