Skip to main content

packet_strata/packet/
ether.rs

1//! Ethernet frame header parser
2//!
3//! This module implements parsing for Ethernet II frames as defined in IEEE 802.3.
4//! Ethernet is the most widely used local area network (LAN) technology.
5//!
6//! # Ethernet II Header Format
7//!
8//! ```text
9//!  0                   1                   2                   3
10//!  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
11//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
12//! |                                                               |
13//! +                    Destination MAC Address                    +
14//! |                                                               |
15//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
16//! |                                                               |
17//! +                      Source MAC Address                       +
18//! |                                                               |
19//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
20//! |           EtherType           |
21//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
22//! ```
23//!
24//! # Key characteristics
25//!
26//! - Header size: 14 bytes (fixed)
27//! - MAC addresses: 6 bytes each
28//! - EtherType: 2 bytes (identifies encapsulated protocol)
29//! - Common EtherTypes: 0x0800 (IPv4), 0x86DD (IPv6), 0x0806 (ARP), 0x8100 (VLAN)
30//!
31//! # Examples
32//!
33//! ## Basic Ethernet parsing
34//!
35//! ```
36//! use packet_strata::packet::ether::EtherHeader;
37//! use packet_strata::packet::protocol::EtherProto;
38//! use packet_strata::packet::HeaderParser;
39//!
40//! // Ethernet frame with IPv4 payload
41//! let packet = vec![
42//!     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // Destination: broadcast
43//!     0x00, 0x11, 0x22, 0x33, 0x44, 0x55,  // Source MAC
44//!     0x08, 0x00,                          // EtherType: IPv4
45//!     // IPv4 payload follows...
46//! ];
47//!
48//! let (header, payload) = EtherHeader::from_bytes(&packet).unwrap();
49//! assert_eq!(header.protocol(), EtherProto::IPV4);
50//! assert_eq!(format!("{}", header.dest()), "ff:ff:ff:ff:ff:ff");
51//! assert_eq!(format!("{}", header.source()), "00:11:22:33:44:55");
52//! ```
53//!
54//! ## Ethernet with ARP payload
55//!
56//! ```
57//! use packet_strata::packet::ether::EtherHeader;
58//! use packet_strata::packet::protocol::EtherProto;
59//! use packet_strata::packet::HeaderParser;
60//!
61//! // Ethernet frame with ARP payload
62//! let packet = vec![
63//!     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // Destination: broadcast
64//!     0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E,  // Source MAC
65//!     0x08, 0x06,                          // EtherType: ARP
66//!     // ARP payload follows...
67//! ];
68//!
69//! let (header, payload) = EtherHeader::from_bytes(&packet).unwrap();
70//! assert_eq!(header.protocol(), EtherProto::ARP);
71//! assert_eq!(payload.len(), 0);
72//! ```
73
74#![allow(dead_code)]
75
76use core::fmt;
77use serde::{Deserialize, Serialize};
78use std::fmt::{Display, Formatter};
79use std::str::FromStr;
80use thiserror::Error;
81use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned, U16};
82
83use crate::packet::protocol::EtherProto;
84use crate::packet::{HeaderParser, PacketHeader};
85
86use std::ops::Deref;
87
88const ETH_ALEN: usize = 6; // Ethernet address length
89const ETH_HLEN: usize = 14; // Ethernet header length without VLAN
90const ETH_ZLEN: usize = 60; // Minimum Ethernet frame length without FCS
91const ETH_DATA_LEN: usize = 1500; // Maximum Ethernet payload length
92const ETH_FRAME_LEN: usize = 1514; // Maximum Ethernet frame length without FCS
93const VLAN_TAG_LEN: usize = 4; // VLAN tag length
94
95#[derive(
96    Debug,
97    Clone,
98    Copy,
99    PartialEq,
100    Eq,
101    PartialOrd,
102    Ord,
103    Hash,
104    FromBytes,
105    IntoBytes,
106    Immutable,
107    KnownLayout,
108    Serialize,
109    Deserialize,
110)]
111#[serde(into = "String")]
112#[serde(try_from = "String")]
113pub struct EthAddr([u8; ETH_ALEN]);
114
115impl Default for EthAddr {
116    fn default() -> Self {
117        EthAddr([0u8; ETH_ALEN])
118    }
119}
120
121impl Display for EthAddr {
122    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
123        write!(
124            f,
125            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
126            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
127        )
128    }
129}
130
131#[derive(Debug, Clone, Error)]
132pub enum EtherError {
133    #[error("Invalid Ethernet address format")]
134    InvalidAddressFormat,
135}
136
137impl FromStr for EthAddr {
138    type Err = EtherError;
139    fn from_str(s: &str) -> Result<Self, Self::Err> {
140        let bytes: Vec<u8> = s
141            .split(':')
142            .map(|part| u8::from_str_radix(part, 16).map_err(|_| EtherError::InvalidAddressFormat))
143            .collect::<Result<Vec<u8>, _>>()?;
144
145        if bytes.len() != ETH_ALEN {
146            return Err(EtherError::InvalidAddressFormat);
147        }
148
149        let mut addr = [0u8; ETH_ALEN];
150        addr.copy_from_slice(&bytes);
151        Ok(EthAddr(addr))
152    }
153}
154
155impl From<EthAddr> for String {
156    #[inline]
157    fn from(addr: EthAddr) -> Self {
158        addr.to_string()
159    }
160}
161
162impl TryFrom<String> for EthAddr {
163    type Error = EtherError;
164    #[inline]
165    fn try_from(s: String) -> Result<Self, Self::Error> {
166        EthAddr::from_str(&s)
167    }
168}
169
170#[repr(C, packed)]
171#[derive(FromBytes, IntoBytes, Unaligned, Immutable, KnownLayout, Debug, Clone, Copy)]
172pub struct EtherHeader {
173    dest: EthAddr,
174    source: EthAddr,
175    proto: EtherProto,
176}
177
178impl EtherHeader {
179    pub fn dest(&self) -> &EthAddr {
180        &self.dest
181    }
182
183    pub fn source(&self) -> &EthAddr {
184        &self.source
185    }
186
187    pub fn protocol(&self) -> EtherProto {
188        self.proto
189    }
190}
191
192#[repr(C)]
193#[derive(FromBytes, IntoBytes, Unaligned, Immutable, KnownLayout, Debug, Clone, Copy)]
194struct EtherHeaderFixed(EtherHeader);
195
196impl PacketHeader for EtherHeaderFixed {
197    const NAME: &'static str = "EtherHeaderFixed";
198    type InnerType = EtherProto;
199
200    #[inline]
201    fn inner_type(&self) -> Self::InnerType {
202        self.0.proto
203    }
204}
205
206impl HeaderParser for EtherHeaderFixed {
207    type Output<'a> = &'a EtherHeader;
208
209    #[inline]
210    fn into_view<'a>(header: &'a Self, _options: &'a [u8]) -> Self::Output<'a> {
211        &header.0
212    }
213}
214
215impl PacketHeader for EtherHeader {
216    const NAME: &'static str = "EtherHeader";
217    type InnerType = EtherProto;
218
219    #[inline]
220    fn inner_type(&self) -> Self::InnerType {
221        self.proto
222    }
223}
224
225impl HeaderParser for EtherHeader {
226    type Output<'a> = EtherHeaderVlan<'a>;
227
228    #[inline]
229    fn into_view<'a>(_: &'a Self, _: &'a [u8]) -> Self::Output<'a> {
230        unreachable!()
231    }
232
233    #[inline]
234    fn from_bytes<'a>(
235        buf: &'a [u8],
236    ) -> Result<(Self::Output<'a>, &'a [u8]), super::PacketHeaderError> {
237        let (eth_header, mut rest) = EtherHeaderFixed::from_bytes(buf)?;
238
239        let mut ethernet_header = EtherHeaderVlan::Standard(eth_header);
240
241        while ethernet_header.inner_type() == EtherProto::VLAN_8021Q {
242            let (vlan_header, vlan_rest) = Ether8021qHeader::from_bytes(rest)?;
243            rest = vlan_rest;
244
245            ethernet_header = match &ethernet_header {
246                EtherHeaderVlan::Standard(eth) => EtherHeaderVlan::VLAN8021Q(eth, vlan_header),
247                EtherHeaderVlan::VLAN8021Q(eth, vlan) => {
248                    EtherHeaderVlan::VLAN8021QNested(eth, vlan, vlan_header)
249                }
250                EtherHeaderVlan::VLAN8021QNested(_, _, _) => {
251                    return Err(super::PacketHeaderError::Other(
252                        "More than two nested VLAN tags are not supported",
253                    ));
254                }
255            };
256        }
257
258        Ok((ethernet_header, rest))
259    }
260}
261
262#[repr(C, packed)]
263#[derive(FromBytes, IntoBytes, Unaligned, Immutable, KnownLayout, Debug, Clone, Copy)]
264pub struct Ether8021qHeader {
265    tci: U16<BigEndian>,
266    proto: EtherProto,
267}
268
269impl Ether8021qHeader {
270    pub fn vlan_id(&self) -> u16 {
271        self.tci.get() & 0x0FFF
272    }
273
274    pub fn vlan_pcp(&self) -> u8 {
275        ((self.tci.get() >> 13) & 0x07) as u8
276    }
277
278    pub fn vlan_dei(&self) -> bool {
279        ((self.tci.get() >> 12) & 0x01) != 0
280    }
281}
282
283impl Display for Ether8021qHeader {
284    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
285        write!(
286            f,
287            "802.1Q vid={} pcp={} dei={} proto={}",
288            self.vlan_id(),
289            self.vlan_pcp(),
290            self.vlan_dei(),
291            self.proto
292        )
293    }
294}
295
296impl PacketHeader for Ether8021qHeader {
297    const NAME: &'static str = "Ether8021qHeader";
298    type InnerType = EtherProto;
299
300    #[inline]
301    fn inner_type(&self) -> Self::InnerType {
302        self.proto
303    }
304}
305
306impl HeaderParser for Ether8021qHeader {
307    type Output<'a> = &'a Ether8021qHeader;
308
309    #[inline]
310    fn into_view<'a>(header: &'a Self, _options: &'a [u8]) -> Self::Output<'a> {
311        header
312    }
313}
314
315#[derive(Debug, Clone)]
316pub enum EtherHeaderVlan<'a> {
317    Standard(&'a EtherHeader),
318    VLAN8021Q(&'a EtherHeader, &'a Ether8021qHeader),
319    VLAN8021QNested(&'a EtherHeader, &'a Ether8021qHeader, &'a Ether8021qHeader),
320}
321
322impl Display for EtherHeaderVlan<'_> {
323    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
324        match self {
325            EtherHeaderVlan::Standard(eth) => {
326                write!(
327                    f,
328                    "Ethernet {} -> {} proto={}",
329                    eth.source, eth.dest, eth.proto
330                )
331            }
332            EtherHeaderVlan::VLAN8021Q(eth, vlan) => {
333                write!(
334                    f,
335                    "Ethernet {} -> {} proto={} [{}]",
336                    eth.source, eth.dest, eth.proto, vlan
337                )
338            }
339            EtherHeaderVlan::VLAN8021QNested(eth, vlan1, vlan2) => {
340                write!(
341                    f,
342                    "Ethernet {} -> {} proto={} [{}] [{}]",
343                    eth.source, eth.dest, eth.proto, vlan1, vlan2
344                )
345            }
346        }
347    }
348}
349
350impl Deref for EtherHeaderVlan<'_> {
351    type Target = EtherHeader;
352
353    fn deref(&self) -> &Self::Target {
354        match self {
355            EtherHeaderVlan::Standard(eth) => eth,
356            EtherHeaderVlan::VLAN8021Q(eth, _) => eth,
357            EtherHeaderVlan::VLAN8021QNested(eth, _, _) => eth,
358        }
359    }
360}
361
362impl<'a> EtherHeaderVlan<'a> {
363    pub fn dest(&self) -> &EthAddr {
364        match self {
365            EtherHeaderVlan::Standard(eth) => &eth.dest,
366            EtherHeaderVlan::VLAN8021Q(eth, _) => &eth.dest,
367            EtherHeaderVlan::VLAN8021QNested(eth, _, _) => &eth.dest,
368        }
369    }
370
371    pub fn source(&self) -> &EthAddr {
372        match self {
373            EtherHeaderVlan::Standard(eth) => &eth.source,
374            EtherHeaderVlan::VLAN8021Q(eth, _) => &eth.source,
375            EtherHeaderVlan::VLAN8021QNested(eth, _, _) => &eth.source,
376        }
377    }
378
379    pub fn inner_type(&self) -> EtherProto {
380        match self {
381            EtherHeaderVlan::Standard(eth) => eth.proto,
382            EtherHeaderVlan::VLAN8021Q(_, vlan) => vlan.proto,
383            EtherHeaderVlan::VLAN8021QNested(_, _, vlan) => vlan.proto,
384        }
385    }
386}
387
388impl PacketHeader for EtherHeaderVlan<'_> {
389    const NAME: &'static str = "EtherHeaderVlan";
390    type InnerType = EtherProto;
391
392    #[inline]
393    fn inner_type(&self) -> Self::InnerType {
394        self.inner_type()
395    }
396}
397
398#[cfg(test)]
399mod tests {
400    use super::*;
401
402    #[test]
403    fn test_ether_header_parse_ipv4() {
404        let packet_bytes: [u8; 14] = [
405            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // dest MAC
406            0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, // source MAC
407            0x08, 0x00, // EtherType: IPv4
408        ];
409
410        // Parse the header using PacketHeader::from_bytes
411        let (header, remaining) =
412            EtherHeader::from_bytes(&packet_bytes).expect("Failed to parse EtherHeader");
413
414        // Verify the protocol is IPv4
415        assert_eq!(header.proto, EtherProto::IPV4);
416
417        // Verify MAC addresses
418        assert_eq!(header.dest.0, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
419        assert_eq!(header.source.0, [0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c]);
420
421        // Verify MAC addresses to_string()
422        assert_eq!(header.dest.to_string(), "01:02:03:04:05:06");
423        assert_eq!(header.source.to_string(), "07:08:09:0a:0b:0c");
424
425        // Verify remaining buffer is empty
426        assert_eq!(remaining.len(), 0);
427    }
428
429    #[test]
430    fn test_ether_header_parse_vlan() {
431        let packet_bytes: [u8; 18] = [
432            0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // dest MAC
433            0x11, 0x22, 0x33, 0x44, 0x55, 0x66, // source MAC
434            0x81, 0x00, // EtherType: 802.1Q VLAN
435            0x00, 0x2a, // TCI: VID=42 (0x002a), PCP=0, DEI=0
436            0x08, 0x00, // Inner EtherType: IPv4
437        ];
438
439        // Parse the Ethernet header - now automatically parses VLAN headers
440        let (eth_header_ext, remaining) =
441            EtherHeader::from_bytes(&packet_bytes).expect("Failed to parse EtherHeader");
442
443        // Verify MAC addresses using EtherHeaderVlan methods
444        assert_eq!(
445            eth_header_ext.dest().0,
446            [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]
447        );
448        assert_eq!(
449            eth_header_ext.source().0,
450            [0x11, 0x22, 0x33, 0x44, 0x55, 0x66]
451        );
452
453        // Verify MAC addresses to_string()
454        assert_eq!(eth_header_ext.dest().to_string(), "aa:bb:cc:dd:ee:ff");
455        assert_eq!(eth_header_ext.source().to_string(), "11:22:33:44:55:66");
456
457        // Verify the inner protocol (after VLAN) is IPv4
458        assert_eq!(eth_header_ext.inner_type(), EtherProto::IPV4);
459
460        // Verify remaining buffer is empty (VLAN header was consumed)
461        assert_eq!(remaining.len(), 0);
462
463        // Verify this is a VLAN8021Q variant
464        match eth_header_ext {
465            EtherHeaderVlan::VLAN8021Q(eth, vlan) => {
466                // Verify the outer ethernet protocol is 802.1Q VLAN
467                assert_eq!(eth.proto, EtherProto::VLAN_8021Q);
468
469                // Verify VLAN ID is 42
470                assert_eq!(vlan.vlan_id(), 42);
471
472                // Verify PCP (Priority Code Point) is 0
473                assert_eq!(vlan.vlan_pcp(), 0);
474
475                // Verify DEI (Drop Eligible Indicator) is false
476                assert!(!vlan.vlan_dei());
477
478                // Verify the inner protocol is IPv4
479                assert_eq!(vlan.proto, EtherProto::IPV4);
480
481                // Verify the Display implementation
482                let display_str = vlan.to_string();
483                assert!(display_str.contains("vid=42"));
484                assert!(display_str.contains("pcp=0"));
485                assert!(display_str.contains("dei=false"));
486            }
487            _ => panic!("Expected VLAN8021Q variant"),
488        }
489    }
490}