Skip to main content

retina_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<'a> Ethernet<'a> {
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
53impl<'a> Packet<'a> for Ethernet<'a> {
54    fn mbuf(&self) -> &Mbuf {
55        self.mbuf
56    }
57
58    fn header_len(&self) -> usize {
59        self.header.length()
60    }
61
62    fn next_header_offset(&self) -> usize {
63        self.offset + self.header_len()
64    }
65
66    fn next_header(&self) -> Option<usize> {
67        let ether_type: u16 = u16::from(self.header.ether_type);
68        match ether_type {
69            VLAN_802_1Q => {
70                if let Ok(dot1q) = self.mbuf.get_data(HDR_SIZE) {
71                    let dot1q: Dot1q = unsafe { *dot1q };
72                    Some(u16::from(dot1q.ether_type).into())
73                } else {
74                    None
75                }
76            }
77            VLAN_802_1AD => {
78                // Unimplemented. TODO: support QinQ
79                None
80            }
81            _ => Some(ether_type.into()),
82        }
83    }
84
85    fn parse_from(outer: &'a impl Packet<'a>) -> Result<Self>
86    where
87        Self: Sized,
88    {
89        if let Ok(header) = outer.mbuf().get_data(0) {
90            Ok(Ethernet {
91                header: unsafe { *header },
92                offset: 0,
93                mbuf: outer.mbuf(),
94            })
95        } else {
96            bail!(PacketParseError::InvalidRead)
97        }
98    }
99}
100
101/// Fixed portion of an Ethernet header.
102#[derive(Debug, Clone, Copy)]
103#[repr(C, packed)]
104struct EthernetHeader {
105    dst: MacAddr,
106    src: MacAddr,
107    ether_type: u16be,
108}
109
110impl PacketHeader for EthernetHeader {
111    fn length(&self) -> usize {
112        match self.ether_type.into() {
113            VLAN_802_1Q => HDR_SIZE_802_1Q,
114            VLAN_802_1AD => HDR_SIZE_802_1AD,
115            _ => HDR_SIZE,
116        }
117    }
118}
119
120/// 802.1Q tag control information and next EtherType.
121///
122/// ## Remarks
123/// This is not a 801.1Q header. The first 16 bits of `Dot1q` is the TCI field and the second 16
124/// bits is the EtherType of the encapsulated protocol.
125#[derive(Debug, Clone, Copy)]
126#[repr(C, packed)]
127struct Dot1q {
128    tci: u16be,
129    ether_type: u16be,
130}
131
132impl PacketHeader for Dot1q {
133    /// The four bytes that make up the second byte of the 802.1Q header and the EtherType of the
134    /// encapsulated protocol.
135    fn length(&self) -> usize {
136        TAG_SIZE
137    }
138}
139
140// TODO: Implement QinQ.