Skip to main content

network_types/
eth.rs

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