Skip to main content

wireforge_core/
ether.rs

1//! Ethernet II frame parser and builder.
2
3use alloc::vec::Vec;
4
5use crate::types::EtherType;
6use crate::util::{read_u16be, write_u16be};
7
8/// Minimum Ethernet II header length (dst MAC + src MAC + ethertype).
9pub const ETHER_HEADER_LEN: usize = 14;
10
11/// Zero-copy Ethernet II frame parser.
12///
13/// Holds a reference to the underlying byte slice. All field accessors
14/// are inline offset reads — no allocation.
15#[derive(Debug, Clone)]
16pub struct EthernetPacket<'a> {
17    buf: &'a [u8],
18}
19
20impl<'a> EthernetPacket<'a> {
21    /// Attempt to parse an Ethernet II frame from raw bytes.
22    ///
23    /// Returns `None` if `buf` is shorter than [`ETHER_HEADER_LEN`] (14 bytes).
24    #[inline]
25    pub fn new(buf: &'a [u8]) -> Option<Self> {
26        if buf.len() < ETHER_HEADER_LEN {
27            return None;
28        }
29        Some(Self { buf })
30    }
31
32    /// Destination MAC address.
33    #[inline]
34    pub fn dst_mac(&self) -> [u8; 6] {
35        self.buf[..6].try_into().unwrap()
36    }
37
38    /// Source MAC address.
39    #[inline]
40    pub fn src_mac(&self) -> [u8; 6] {
41        self.buf[6..12].try_into().unwrap()
42    }
43
44    /// EtherType field.
45    #[inline]
46    pub fn ethertype(&self) -> EtherType {
47        EtherType::from(read_u16be(&self.buf[12..14]))
48    }
49
50    /// Raw ethertype value.
51    #[inline]
52    pub fn raw_ethertype(&self) -> u16 {
53        read_u16be(&self.buf[12..14])
54    }
55
56    /// Payload following the Ethernet header.
57    #[inline]
58    pub fn payload(&self) -> &'a [u8] {
59        &self.buf[ETHER_HEADER_LEN..]
60    }
61}
62
63// ---------------------------------------------------------------------------
64// Builder
65// ---------------------------------------------------------------------------
66
67/// Builder for constructing Ethernet II frames.
68#[derive(Debug, Clone)]
69pub struct EthernetPacketBuilder {
70    buf: [u8; ETHER_HEADER_LEN],
71    payload: Option<Vec<u8>>,
72}
73
74impl Default for EthernetPacketBuilder {
75    fn default() -> Self {
76        Self::new()
77    }
78}
79
80impl EthernetPacketBuilder {
81    pub fn new() -> Self {
82        Self {
83            buf: [0u8; ETHER_HEADER_LEN],
84            payload: None,
85        }
86    }
87
88    pub fn dst_mac(mut self, mac: [u8; 6]) -> Self {
89        self.buf[..6].copy_from_slice(&mac);
90        self
91    }
92
93    pub fn src_mac(mut self, mac: [u8; 6]) -> Self {
94        self.buf[6..12].copy_from_slice(&mac);
95        self
96    }
97
98    pub fn ethertype(mut self, et: EtherType) -> Self {
99        write_u16be(&mut self.buf[12..14], et.into());
100        self
101    }
102
103    pub fn raw_ethertype(mut self, et: u16) -> Self {
104        write_u16be(&mut self.buf[12..14], et);
105        self
106    }
107
108    pub fn payload(mut self, data: &[u8]) -> Self {
109        self.payload = Some(data.to_vec());
110        self
111    }
112
113    /// Assemble the complete Ethernet frame.
114    pub fn build(self) -> Vec<u8> {
115        let mut frame = self.buf.to_vec();
116        if let Some(ref p) = self.payload {
117            frame.extend_from_slice(p);
118        }
119        frame
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    // A real Ethernet frame: IPv4 packet
128    const SAMPLE_FRAME: &[u8] = &[
129        0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E, // dst
130        0x0A, 0x1B, 0x2C, 0x3D, 0x4E, 0x5F, // src
131        0x08, 0x00, // ethertype (IPv4)
132        0x45, // ... payload
133    ];
134
135    #[test]
136    fn parse_valid_frame() {
137        let pkt = EthernetPacket::new(SAMPLE_FRAME).unwrap();
138        assert_eq!(pkt.dst_mac(), [0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E]);
139        assert_eq!(pkt.src_mac(), [0x0A, 0x1B, 0x2C, 0x3D, 0x4E, 0x5F]);
140        assert_eq!(pkt.ethertype(), EtherType::Ipv4);
141    }
142
143    #[test]
144    fn parse_too_short() {
145        assert!(EthernetPacket::new(&[0u8; 13]).is_none());
146        assert!(EthernetPacket::new(&[]).is_none());
147    }
148
149    #[test]
150    fn parse_minimum_frame() {
151        let pkt = EthernetPacket::new(&[0u8; 14]).unwrap();
152        assert_eq!(pkt.ethertype(), EtherType::Unknown(0));
153        assert_eq!(pkt.payload().len(), 0);
154    }
155
156    #[test]
157    fn payload_slice() {
158        let pkt = EthernetPacket::new(SAMPLE_FRAME).unwrap();
159        assert_eq!(pkt.payload(), &[0x45]);
160    }
161
162    #[test]
163    fn build_and_parse_roundtrip() {
164        let frame = EthernetPacketBuilder::new()
165            .dst_mac([0x11, 0x22, 0x33, 0x44, 0x55, 0x66])
166            .src_mac([0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF])
167            .ethertype(EtherType::Arp)
168            .payload(&[0x01, 0x02, 0x03])
169            .build();
170
171        let pkt = EthernetPacket::new(&frame).unwrap();
172        assert_eq!(pkt.dst_mac(), [0x11, 0x22, 0x33, 0x44, 0x55, 0x66]);
173        assert_eq!(pkt.src_mac(), [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
174        assert_eq!(pkt.ethertype(), EtherType::Arp);
175        assert_eq!(pkt.payload(), &[0x01, 0x02, 0x03]);
176    }
177
178    #[test]
179    fn ethertype_arp() {
180        let frame = [
181            0xFFu8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // broadcast
182            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
183            0x08, 0x06, // ARP
184        ];
185        let pkt = EthernetPacket::new(&frame).unwrap();
186        assert_eq!(pkt.ethertype(), EtherType::Arp);
187    }
188}