zero_packet/datalink/
ethernet.rs

1use crate::misc::bytes_to_mac;
2use core::{fmt, str::from_utf8};
3
4/// The minimum length of an Ethernet frame header in bytes.
5pub const ETHERNET_MIN_HEADER_LENGTH: usize = 14;
6
7/// The minimum length of an Ethernet frame in bytes.
8pub const ETHERNET_MIN_FRAME_LENGTH: usize = 64;
9
10/// The length of a single VLAN tag in bytes.
11pub const VLAN_TAG_LENGTH: usize = 4;
12
13/// Indicates VLAN tagging.
14pub const ETHERTYPE_VLAN: u16 = 0x8100;
15
16/// Indicates double VLAN tagging (Q-in-Q).
17pub const ETHERTYPE_QINQ: u16 = 0x88A8;
18
19/// Writes Ethernet header fields.
20pub struct EthernetWriter<'a> {
21    pub bytes: &'a mut [u8],
22    header_len: usize,
23}
24
25impl<'a> EthernetWriter<'a> {
26    /// Creates a new `EthernetWriter` from the given slice.
27    #[inline]
28    pub fn new(bytes: &'a mut [u8]) -> Result<Self, &'static str> {
29        if bytes.len() < ETHERNET_MIN_HEADER_LENGTH {
30            return Err("Slice is too short to contain an Ethernet frame.");
31        }
32
33        Ok(Self {
34            bytes,
35            header_len: ETHERNET_MIN_HEADER_LENGTH,
36        })
37    }
38
39    /// Returns the header length in bytes.
40    #[inline]
41    pub fn header_len(&self) -> usize {
42        self.header_len
43    }
44
45    /// Sets the destination MAC address field.
46    #[inline]
47    pub fn set_dest_mac(&mut self, dest: &[u8; 6]) {
48        self.bytes[0] = dest[0];
49        self.bytes[1] = dest[1];
50        self.bytes[2] = dest[2];
51        self.bytes[3] = dest[3];
52        self.bytes[4] = dest[4];
53        self.bytes[5] = dest[5];
54    }
55
56    /// Sets the source MAC address field.
57    #[inline]
58    pub fn set_src_mac(&mut self, src: &[u8; 6]) {
59        self.bytes[6] = src[0];
60        self.bytes[7] = src[1];
61        self.bytes[8] = src[2];
62        self.bytes[9] = src[3];
63        self.bytes[10] = src[4];
64        self.bytes[11] = src[5];
65    }
66
67    /// Sets the EtherType field.
68    ///
69    /// EtherType indicates which protocol is encapsulated in the payload.
70    #[inline]
71    pub fn set_ethertype(&mut self, ethertype: u16) {
72        let offset = self.header_len - ETHERNET_MIN_HEADER_LENGTH;
73        self.bytes[12 + offset] = (ethertype >> 8) as u8;
74        self.bytes[13 + offset] = (ethertype & 0xFF) as u8;
75    }
76
77    /// Sets a VLAN tag.
78    ///
79    /// Optionally present in the Ethernet frame header.
80    ///
81    /// Increases the header length by VLAN_TAG_LENGTH.
82    #[inline]
83    pub fn set_vlan_tag(&mut self, tpid: u16, tci: u16) -> Result<(), &'static str> {
84        if self.bytes.len() < self.header_len + VLAN_TAG_LENGTH {
85            return Err("Slice is too short to contain VLAN tagging.");
86        }
87
88        self.bytes[12] = (tpid >> 8) as u8;
89        self.bytes[13] = (tpid & 0xFF) as u8;
90        self.bytes[14] = (tci >> 8) as u8;
91        self.bytes[15] = (tci & 0xFF) as u8;
92
93        self.header_len += VLAN_TAG_LENGTH;
94
95        Ok(())
96    }
97
98    /// Sets a double VLAN tag (Q-in-Q).
99    ///
100    /// Optionally present in the Ethernet frame header.
101    ///
102    /// Increases the header length by 2 * VLAN_TAG_LENGTH.
103    #[inline]
104    pub fn set_double_vlan_tag(
105        &mut self,
106        outer_tpid: u16,
107        outer_tci: u16,
108        inner_tpid: u16,
109        inner_tci: u16,
110    ) -> Result<(), &'static str> {
111        if self.bytes.len() < self.header_len + 2 * VLAN_TAG_LENGTH {
112            return Err("Slice is too short to contain double VLAN tagging.");
113        }
114
115        self.bytes[12] = (outer_tpid >> 8) as u8;
116        self.bytes[13] = (outer_tpid & 0xFF) as u8;
117        self.bytes[14] = (outer_tci >> 8) as u8;
118        self.bytes[15] = (outer_tci & 0xFF) as u8;
119
120        self.bytes[16] = (inner_tpid >> 8) as u8;
121        self.bytes[17] = (inner_tpid & 0xFF) as u8;
122        self.bytes[18] = (inner_tci >> 8) as u8;
123        self.bytes[19] = (inner_tci & 0xFF) as u8;
124
125        self.header_len += 2 * VLAN_TAG_LENGTH;
126
127        Ok(())
128    }
129}
130
131/// Reads Ethernet header fields.
132#[derive(PartialEq)]
133pub struct EthernetReader<'a> {
134    pub bytes: &'a [u8],
135    header_len: usize,
136}
137
138impl<'a> EthernetReader<'a> {
139    /// Creates a new `EthernetReader` from the given slice.
140    #[inline]
141    pub fn new(bytes: &'a [u8]) -> Result<Self, &'static str> {
142        if bytes.len() < ETHERNET_MIN_HEADER_LENGTH {
143            return Err("Slice is too short to contain an Ethernet frame.");
144        }
145
146        let header_len = Self::calculate_header_len(bytes)?;
147
148        Ok(Self { bytes, header_len })
149    }
150
151    /// Calculates the header length of an Ethernet frame.
152    ///
153    /// Depending on the presence of VLAN tagging, the header length may vary.
154    #[inline]
155    pub fn calculate_header_len(bytes: &[u8]) -> Result<usize, &'static str> {
156        // EtherType field.
157        match ((bytes[12] as u16) << 8) | (bytes[13] as u16) {
158            ETHERTYPE_VLAN => {
159                if bytes.len() < ETHERNET_MIN_HEADER_LENGTH + VLAN_TAG_LENGTH {
160                    return Err("Slice is too short to contain VLAN tagging.");
161                }
162
163                Ok(ETHERNET_MIN_HEADER_LENGTH + VLAN_TAG_LENGTH)
164            }
165            ETHERTYPE_QINQ => {
166                if bytes.len() < ETHERNET_MIN_HEADER_LENGTH + 2 * VLAN_TAG_LENGTH {
167                    return Err("Slice is too short to contain double VLAN tagging.");
168                }
169
170                // Next EtherType field.
171                if ((bytes[16] as u16) << 8) | (bytes[17] as u16) != ETHERTYPE_VLAN {
172                    return Err("Invalid double VLAN tag.");
173                }
174
175                Ok(ETHERNET_MIN_HEADER_LENGTH + 2 * VLAN_TAG_LENGTH)
176            }
177            _ => Ok(ETHERNET_MIN_HEADER_LENGTH),
178        }
179    }
180
181    /// Checks if the Ethernet frame is VLAN tagged.
182    #[inline]
183    pub fn is_vlan_tagged(bytes: &[u8]) -> bool {
184        ((bytes[12] as u16) << 8) | (bytes[13] as u16) == ETHERTYPE_VLAN
185    }
186
187    /// Checks if the Ethernet frame is double VLAN tagged (Q-in-Q).
188    #[inline]
189    pub fn is_vlan_double_tagged(bytes: &[u8]) -> bool {
190        ((bytes[12] as u16) << 8) | (bytes[13] as u16) == ETHERTYPE_QINQ
191    }
192
193    /// Returns the destination MAC address field.
194    #[inline]
195    pub fn dest_mac(&self) -> &[u8] {
196        &self.bytes[0..6]
197    }
198
199    /// Returns the source MAC address field.
200    #[inline]
201    pub fn src_mac(&self) -> &[u8] {
202        &self.bytes[6..12]
203    }
204
205    /// Returns the EtherType field field.
206    ///
207    /// Indicates which network protocol is encapsulated.
208    #[inline]
209    pub fn ethertype(&self) -> u16 {
210        let offset = self.header_len - ETHERNET_MIN_HEADER_LENGTH;
211        ((self.bytes[12 + offset] as u16) << 8) | (self.bytes[13 + offset] as u16)
212    }
213
214    /// Returns the VLAN tag.
215    ///
216    /// Optionally present in the Ethernet frame header.
217    #[inline]
218    pub fn vlan_tag(&self) -> Option<(u16, u16)> {
219        if !Self::is_vlan_tagged(self.bytes) {
220            return None;
221        }
222
223        let tpid = ((self.bytes[12] as u16) << 8) | (self.bytes[13] as u16);
224        let tci = ((self.bytes[14] as u16) << 8) | (self.bytes[15] as u16);
225
226        Some((tpid, tci))
227    }
228
229    /// Returns the double VLAN tag (Q-in-Q).
230    ///
231    /// Optionally present in the Ethernet frame header.
232    #[inline]
233    pub fn double_vlan_tag(&self) -> Option<((u16, u16), (u16, u16))> {
234        if !Self::is_vlan_double_tagged(self.bytes) {
235            return None;
236        }
237
238        let outer_tpid = ((self.bytes[12] as u16) << 8) | (self.bytes[13] as u16);
239        let outer_tci = ((self.bytes[14] as u16) << 8) | (self.bytes[15] as u16);
240        let inner_tpid = ((self.bytes[16] as u16) << 8) | (self.bytes[17] as u16);
241        let inner_tci = ((self.bytes[18] as u16) << 8) | (self.bytes[19] as u16);
242
243        Some(((outer_tpid, outer_tci), (inner_tpid, inner_tci)))
244    }
245
246    /// Returns the header length in bytes.
247    #[inline]
248    pub const fn header_len(&self) -> usize {
249        self.header_len
250    }
251
252    /// Returns a reference to the header.
253    #[inline]
254    pub fn header(&self) -> &'a [u8] {
255        &self.bytes[..self.header_len]
256    }
257
258    /// Returns a reference to the payload.
259    #[inline]
260    pub fn payload(&self) -> &[u8] {
261        &self.bytes[self.header_len..]
262    }
263}
264
265impl fmt::Debug for EthernetReader<'_> {
266    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267        let mut d_buf = [0u8; 18];
268        let d_len = bytes_to_mac(self.dest_mac(), &mut d_buf);
269        let d_hex = from_utf8(&d_buf[..d_len]).unwrap();
270        let mut s_buf = [0u8; 18];
271        let s_len = bytes_to_mac(self.src_mac(), &mut s_buf);
272        let s_hex = from_utf8(&s_buf[..s_len]).unwrap();
273        f.debug_struct("EthernetFrame")
274            .field("dest_mac", &d_hex)
275            .field("src_mac", &s_hex)
276            .field("ethertype", &self.ethertype())
277            .finish()
278    }
279}
280
281#[cfg(test)]
282mod tests {
283    use super::*;
284
285    #[test]
286    fn getters_and_setters() {
287        // Raw packet.
288        let mut bytes = [0u8; 14];
289
290        // Random values.
291        let src = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05];
292        let dest = [0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b];
293        let ethertype = 2048;
294
295        // Create an Ethernet frame writer.
296        let mut writer = EthernetWriter::new(&mut bytes).unwrap();
297
298        // Set the fields.
299        writer.set_src_mac(&src);
300        writer.set_dest_mac(&dest);
301        writer.set_ethertype(ethertype);
302
303        // Create an Ethernet frame reader.
304        let reader = EthernetReader::new(&bytes).unwrap();
305
306        // Ensure the fields are set and retrieved correctly.
307        assert_eq!(reader.src_mac(), src);
308        assert_eq!(reader.dest_mac(), dest);
309        assert_eq!(reader.ethertype(), ethertype);
310    }
311}