Skip to main content

iris_core/protocols/packet/
ethernet.rs

1//! Ethernet packet.
2
3use crate::memory::mbuf::Mbuf;
4use crate::protocols::packet::{Packet, PacketHeader, PacketParseError};
5use crate::utils::types::*;
6
7use anyhow::{bail, Result};
8use pnet::datalink::MacAddr;
9
10const VLAN_802_1Q: u16 = 0x8100;
11const VLAN_802_1AD: u16 = 0x88a8;
12
13const TAG_SIZE: usize = 4;
14const HDR_SIZE: usize = 14;
15const HDR_SIZE_802_1Q: usize = HDR_SIZE + TAG_SIZE;
16const HDR_SIZE_802_1AD: usize = HDR_SIZE_802_1Q + TAG_SIZE;
17
18/// An Ethernet frame.
19///
20/// On networks that support virtual LANs, the frame may include a VLAN tag after the source MAC
21/// address. Double-tagged frames (QinQ) are not yet supported.
22#[derive(Debug)]
23pub struct Ethernet<'a> {
24    /// Fixed header.
25    header: EthernetHeader,
26    /// Offset to `header` from the start of `mbuf`.
27    offset: usize,
28    /// Packet buffer.
29    mbuf: &'a Mbuf,
30}
31
32impl Ethernet<'_> {
33    /// Returns the destination MAC address.
34    #[inline]
35    pub fn dst(&self) -> MacAddr {
36        self.header.dst
37    }
38
39    /// Returns the source MAC address.
40    #[inline]
41    pub fn src(&self) -> MacAddr {
42        self.header.src
43    }
44
45    /// Returns the encapsulated protocol identifier for untagged and single-tagged frames, and `0`
46    /// for incorrectly fornatted and (not yet supported) double-tagged frames,.
47    #[inline]
48    pub fn ether_type(&self) -> u16 {
49        self.next_header().unwrap_or(0) as u16
50    }
51
52    /// Returns the Tag Control Information field from a 802.1Q (single-tagged)
53    /// frame, if available.
54    pub fn tci(&self) -> Option<u16> {
55        let ether_type: u16 = u16::from(self.header.ether_type);
56        match ether_type {
57            VLAN_802_1Q => {
58                if let Ok(dot1q) = self.mbuf.get_data(HDR_SIZE) {
59                    let dot1q: Dot1q = unsafe { *dot1q };
60                    Some(dot1q.tci.into())
61                } else {
62                    None
63                }
64            }
65            _ => None,
66        }
67    }
68}
69
70impl<'a> Packet<'a> for Ethernet<'a> {
71    fn mbuf(&self) -> &Mbuf {
72        self.mbuf
73    }
74
75    fn header_len(&self) -> usize {
76        self.header.length()
77    }
78
79    fn next_header_offset(&self) -> usize {
80        self.offset + self.header_len()
81    }
82
83    fn next_header(&self) -> Option<usize> {
84        let ether_type: u16 = u16::from(self.header.ether_type);
85        match ether_type {
86            VLAN_802_1Q => {
87                if let Ok(dot1q) = self.mbuf.get_data(HDR_SIZE) {
88                    let dot1q: Dot1q = unsafe { *dot1q };
89                    Some(u16::from(dot1q.ether_type).into())
90                } else {
91                    None
92                }
93            }
94            VLAN_802_1AD => {
95                // Unimplemented. NICE-TO-HAVE: support QinQ
96                None
97            }
98            _ => Some(ether_type.into()),
99        }
100    }
101
102    fn parse_from(outer: &'a impl Packet<'a>) -> Result<Self>
103    where
104        Self: Sized,
105    {
106        if let Ok(header) = outer.mbuf().get_data(0) {
107            Ok(Ethernet {
108                header: unsafe { *header },
109                offset: 0,
110                mbuf: outer.mbuf(),
111            })
112        } else {
113            bail!(PacketParseError::InvalidRead)
114        }
115    }
116}
117
118/// Fixed portion of an Ethernet header.
119#[derive(Debug, Clone, Copy)]
120#[repr(C, packed)]
121struct EthernetHeader {
122    dst: MacAddr,
123    src: MacAddr,
124    ether_type: u16be,
125}
126
127impl PacketHeader for EthernetHeader {
128    fn length(&self) -> usize {
129        match self.ether_type.into() {
130            VLAN_802_1Q => HDR_SIZE_802_1Q,
131            VLAN_802_1AD => HDR_SIZE_802_1AD,
132            _ => HDR_SIZE,
133        }
134    }
135}
136
137/// 802.1Q tag control information and next EtherType.
138///
139/// ## Remarks
140/// This is not a 801.1Q header. The first 16 bits of `Dot1q` is the TCI field and the second 16
141/// bits is the EtherType of the encapsulated protocol.
142#[derive(Debug, Clone, Copy)]
143#[repr(C, packed)]
144struct Dot1q {
145    tci: u16be,
146    ether_type: u16be,
147}
148
149impl PacketHeader for Dot1q {
150    /// The four bytes that make up the second byte of the 802.1Q header and the EtherType of the
151    /// encapsulated protocol.
152    fn length(&self) -> usize {
153        TAG_SIZE
154    }
155}
156
157// NICE-TO-HAVE: Implement QinQ.