use core::convert::TryInto;
use crate::{Error, Result};
#[allow(non_snake_case)]
pub mod EtherType {
pub const ARP: u16 = 0x0806;
pub const IPV4: u16 = 0x0800;
pub const IPV6: u16 = 0x86DD;
pub const DOT1Q: u16 = 0x8100;
pub const TEB: u16 = 0x6558;
}
#[derive(Debug, Copy, Clone)]
pub struct EthernetPdu<'a> {
buffer: &'a [u8],
}
#[derive(Debug, Copy, Clone)]
pub enum Ethernet<'a> {
Raw(&'a [u8]),
Arp(super::ArpPdu<'a>),
Ipv4(super::Ipv4Pdu<'a>),
Ipv6(super::Ipv6Pdu<'a>),
}
impl<'a> EthernetPdu<'a> {
pub fn new(buffer: &'a [u8]) -> Result<Self> {
if buffer.len() < 14 {
return Err(Error::Truncated);
}
let pdu = EthernetPdu { buffer };
if pdu.tpid() == EtherType::DOT1Q && buffer.len() < 18 {
return Err(Error::Truncated);
}
if pdu.ethertype() < 0x0600 {
return Err(Error::Malformed);
}
Ok(pdu)
}
pub fn buffer(&'a self) -> &'a [u8] {
self.buffer
}
pub fn into_buffer(self) -> &'a [u8] {
self.buffer
}
pub fn as_bytes(&'a self) -> &'a [u8] {
self.clone().into_bytes()
}
pub fn into_bytes(self) -> &'a [u8] {
&self.buffer[0..self.computed_ihl()]
}
pub fn inner(&'a self) -> Result<Ethernet<'a>> {
self.clone().into_inner()
}
pub fn into_inner(self) -> Result<Ethernet<'a>> {
let rest = &self.buffer[self.computed_ihl()..];
Ok(match self.ethertype() {
EtherType::ARP => Ethernet::Arp(super::ArpPdu::new(rest)?),
EtherType::IPV4 => Ethernet::Ipv4(super::Ipv4Pdu::new(rest)?),
EtherType::IPV6 => Ethernet::Ipv6(super::Ipv6Pdu::new(rest)?),
_ => Ethernet::Raw(rest),
})
}
pub fn computed_ihl(&'a self) -> usize {
match self.tpid() {
EtherType::DOT1Q => 18,
_ => 14,
}
}
pub fn source_address(&'a self) -> [u8; 6] {
let mut source_address = [0u8; 6];
source_address.copy_from_slice(&self.buffer[6..12]);
source_address
}
pub fn destination_address(&'a self) -> [u8; 6] {
let mut destination_address = [0u8; 6];
destination_address.copy_from_slice(&self.buffer[0..6]);
destination_address
}
pub fn tpid(&'a self) -> u16 {
u16::from_be_bytes(self.buffer[12..=13].try_into().unwrap())
}
pub fn ethertype(&'a self) -> u16 {
match self.tpid() {
EtherType::DOT1Q => u16::from_be_bytes(self.buffer[16..=17].try_into().unwrap()),
ethertype => ethertype,
}
}
pub fn vlan(&'a self) -> Option<u16> {
match self.tpid() {
EtherType::DOT1Q => Some(u16::from_be_bytes(self.buffer[14..=15].try_into().unwrap()) & 0x0FFF),
_ => None,
}
}
pub fn vlan_pcp(&'a self) -> Option<u8> {
match self.tpid() {
EtherType::DOT1Q => Some((self.buffer[14] & 0xE0) >> 5),
_ => None,
}
}
pub fn vlan_dei(&'a self) -> Option<bool> {
match self.tpid() {
EtherType::DOT1Q => Some(((self.buffer[14] & 0x10) >> 4) > 0),
_ => None,
}
}
}