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