use alloc::vec::Vec;
use crate::types::EtherType;
use crate::util::read_u16be;
pub const VLAN_HEADER_LEN: usize = 4;
pub const MAX_VLAN_DEPTH: usize = 2;
#[derive(Debug, Clone)]
pub struct VlanPacket<'a> {
buf: &'a [u8],
}
impl<'a> VlanPacket<'a> {
crate::parser_new!(VLAN_HEADER_LEN);
#[inline]
pub fn as_bytes(&self) -> &'a [u8] { self.buf }
#[inline]
pub fn pcp(&self) -> u8 {
self.buf[0] >> 5
}
#[inline]
pub fn dei(&self) -> bool {
self.buf[0] & 0x10 != 0
}
#[inline]
pub fn vid(&self) -> u16 {
read_u16be(&self.buf[..2]) & 0x0FFF
}
#[inline]
pub fn tpid(&self) -> EtherType {
EtherType::Unknown(read_u16be(&self.buf[..2]))
}
#[inline]
pub fn ethertype(&self) -> EtherType {
EtherType::from(read_u16be(&self.buf[2..4]))
}
#[inline]
pub fn raw_ethertype(&self) -> u16 {
read_u16be(&self.buf[2..4])
}
#[inline]
pub fn inner(&self) -> &'a [u8] {
&self.buf[VLAN_HEADER_LEN..]
}
}
pub fn parse_vlan_chain(buf: &[u8]) -> Option<(Vec<VlanPacket<'_>>, EtherType, &[u8])> {
let mut tags = Vec::new();
let mut remaining = buf;
for _ in 0..MAX_VLAN_DEPTH {
let vlan = VlanPacket::new(remaining)?;
let inner_et = vlan.raw_ethertype();
tags.push(vlan);
remaining = &remaining[VLAN_HEADER_LEN..];
if inner_et != 0x8100 && inner_et != 0x88A8 {
return Some((tags, EtherType::from(inner_et), remaining));
}
}
if tags.is_empty() {
None
} else {
let last_et = tags.last().unwrap().raw_ethertype();
Some((tags, EtherType::from(last_et), remaining))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_single_vlan_tag() {
let data = [
0x70, 0x64, 0x08, 0x00, ];
let vlan = VlanPacket::new(&data).unwrap();
assert_eq!(vlan.pcp(), 3);
assert!(vlan.dei());
assert_eq!(vlan.vid(), 100);
assert_eq!(vlan.ethertype(), EtherType::Ipv4);
}
#[test]
fn parse_vlan_tag_no_dei() {
let data = [
0x20, 0x0A, 0x08, 0x06, ];
let vlan = VlanPacket::new(&data).unwrap();
assert_eq!(vlan.pcp(), 1);
assert!(!vlan.dei());
assert_eq!(vlan.vid(), 10);
assert_eq!(vlan.ethertype(), EtherType::Arp);
}
#[test]
fn vlan_too_short() {
assert!(VlanPacket::new(&[]).is_none());
assert!(VlanPacket::new(&[0u8; 3]).is_none());
}
#[test]
fn vlan_chain_single() {
let data = [
0x00, 0x64, 0x08, 0x00, 0x45, ];
let (tags, final_et, payload) = parse_vlan_chain(&data).unwrap();
assert_eq!(tags.len(), 1);
assert_eq!(tags[0].vid(), 100);
assert_eq!(final_et, EtherType::Ipv4);
assert_eq!(payload, &[0x45]);
}
}