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