1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! Ethernet packet.

use crate::memory::mbuf::Mbuf;
use crate::protocols::packet::{Packet, PacketHeader, PacketParseError};
use crate::utils::types::*;

use anyhow::{bail, Result};
use pnet::datalink::MacAddr;

const VLAN_802_1Q: u16 = 0x8100;
const VLAN_802_1AD: u16 = 0x88a8;

const TAG_SIZE: usize = 4;
const HDR_SIZE: usize = 14;
const HDR_SIZE_802_1Q: usize = HDR_SIZE + TAG_SIZE;
const HDR_SIZE_802_1AD: usize = HDR_SIZE_802_1Q + TAG_SIZE;

/// An Ethernet frame.
///
/// On networks that support virtual LANs, the frame may include a VLAN tag after the source MAC
/// address. Double-tagged frames (QinQ) are not yet supported.
#[derive(Debug)]
pub struct Ethernet<'a> {
    /// Fixed header.
    header: EthernetHeader,
    /// Offset to `header` from the start of `mbuf`.
    offset: usize,
    /// Packet buffer.
    mbuf: &'a Mbuf,
}

impl<'a> Ethernet<'a> {
    /// Returns the destination MAC address.
    #[inline]
    pub fn dst(&self) -> MacAddr {
        self.header.dst
    }

    /// Returns the source MAC address.
    #[inline]
    pub fn src(&self) -> MacAddr {
        self.header.src
    }

    /// Returns the encapsulated protocol identifier for untagged and single-tagged frames, and `0`
    /// for incorrectly fornatted and (not yet supported) double-tagged frames,.
    #[inline]
    pub fn ether_type(&self) -> u16 {
        self.next_header().unwrap_or(0) as u16
    }

    /// Returns the Tag Control Information field from a 802.1Q (single-tagged)
    /// frame, if available.
    pub fn tci(&self) -> Option<u16> {
        let ether_type: u16 = u16::from(self.header.ether_type);
        match ether_type {
            VLAN_802_1Q => {
                if let Ok(dot1q) = self.mbuf.get_data(HDR_SIZE) {
                    let dot1q: Dot1q = unsafe { *dot1q };
                    Some(dot1q.tci.into())
                } else {
                    None
                }
            }
            _ => None,
        }
    }
}

impl<'a> Packet<'a> for Ethernet<'a> {
    fn mbuf(&self) -> &Mbuf {
        self.mbuf
    }

    fn header_len(&self) -> usize {
        self.header.length()
    }

    fn next_header_offset(&self) -> usize {
        self.offset + self.header_len()
    }

    fn next_header(&self) -> Option<usize> {
        let ether_type: u16 = u16::from(self.header.ether_type);
        match ether_type {
            VLAN_802_1Q => {
                if let Ok(dot1q) = self.mbuf.get_data(HDR_SIZE) {
                    let dot1q: Dot1q = unsafe { *dot1q };
                    Some(u16::from(dot1q.ether_type).into())
                } else {
                    None
                }
            }
            VLAN_802_1AD => {
                // Unimplemented. TODO: support QinQ
                None
            }
            _ => Some(ether_type.into()),
        }
    }

    fn parse_from(outer: &'a impl Packet<'a>) -> Result<Self>
    where
        Self: Sized,
    {
        if let Ok(header) = outer.mbuf().get_data(0) {
            Ok(Ethernet {
                header: unsafe { *header },
                offset: 0,
                mbuf: outer.mbuf(),
            })
        } else {
            bail!(PacketParseError::InvalidRead)
        }
    }
}

/// Fixed portion of an Ethernet header.
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
struct EthernetHeader {
    dst: MacAddr,
    src: MacAddr,
    ether_type: u16be,
}

impl PacketHeader for EthernetHeader {
    fn length(&self) -> usize {
        match self.ether_type.into() {
            VLAN_802_1Q => HDR_SIZE_802_1Q,
            VLAN_802_1AD => HDR_SIZE_802_1AD,
            _ => HDR_SIZE,
        }
    }
}

/// 802.1Q tag control information and next EtherType.
///
/// ## Remarks
/// This is not a 801.1Q header. The first 16 bits of `Dot1q` is the TCI field and the second 16
/// bits is the EtherType of the encapsulated protocol.
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
struct Dot1q {
    tci: u16be,
    ether_type: u16be,
}

impl PacketHeader for Dot1q {
    /// The four bytes that make up the second byte of the 802.1Q header and the EtherType of the
    /// encapsulated protocol.
    fn length(&self) -> usize {
        TAG_SIZE
    }
}

// TODO: Implement QinQ.