use crate::dpdk::BufferError;
use crate::net::MacAddr;
use crate::packets::types::u16be;
use crate::packets::{Internal, Packet};
use crate::{ensure, Mbuf, SizeOf};
use anyhow::Result;
use std::fmt;
use std::ptr::NonNull;
const ETH_HEADER_SIZE: usize = 14;
const VLAN_802_1Q: u16 = 0x8100;
const VLAN_802_1AD: u16 = 0x88a8;
pub struct Ethernet {
envelope: Mbuf,
header: NonNull<EthernetHeader>,
offset: usize,
}
impl Ethernet {
#[inline]
fn header(&self) -> &EthernetHeader {
unsafe { self.header.as_ref() }
}
#[inline]
fn header_mut(&mut self) -> &mut EthernetHeader {
unsafe { self.header.as_mut() }
}
#[inline]
pub fn src(&self) -> MacAddr {
self.header().src
}
#[inline]
pub fn set_src(&mut self, src: MacAddr) {
self.header_mut().src = src
}
#[inline]
pub fn dst(&self) -> MacAddr {
self.header().dst
}
#[inline]
pub fn set_dst(&mut self, dst: MacAddr) {
self.header_mut().dst = dst
}
#[inline]
fn vlan_marker(&self) -> u16 {
unsafe { self.header().chunk.ether_type.into() }
}
#[inline]
pub fn ether_type(&self) -> EtherType {
let header = self.header();
let ether_type = unsafe {
match self.vlan_marker() {
VLAN_802_1Q => header.chunk.dot1q.ether_type,
VLAN_802_1AD => header.chunk.qinq.ether_type,
_ => header.chunk.ether_type,
}
};
EtherType::new(ether_type.into())
}
#[inline]
pub fn set_ether_type(&mut self, ether_type: EtherType) {
let ether_type = ether_type.0.into();
match self.vlan_marker() {
VLAN_802_1Q => self.header_mut().chunk.dot1q.ether_type = ether_type,
VLAN_802_1AD => self.header_mut().chunk.qinq.ether_type = ether_type,
_ => self.header_mut().chunk.ether_type = ether_type,
}
}
#[inline]
pub fn is_dot1q(&self) -> bool {
self.vlan_marker() == VLAN_802_1Q
}
#[inline]
pub fn is_qinq(&self) -> bool {
self.vlan_marker() == VLAN_802_1AD
}
#[inline]
pub fn swap_addresses(&mut self) {
let src = self.src();
let dst = self.dst();
self.set_src(dst);
self.set_dst(src);
}
}
impl fmt::Debug for Ethernet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ethernet")
.field("src", &format!("{}", self.src()))
.field("dst", &format!("{}", self.dst()))
.field("ether_type", &format!("{}", self.ether_type()))
.field("vlan", &(self.is_dot1q() || self.is_qinq()))
.field("$offset", &self.offset())
.field("$len", &self.len())
.field("$header_len", &self.header_len())
.finish()
}
}
impl Packet for Ethernet {
type Envelope = Mbuf;
#[inline]
fn envelope(&self) -> &Self::Envelope {
&self.envelope
}
#[inline]
fn envelope_mut(&mut self) -> &mut Self::Envelope {
&mut self.envelope
}
#[inline]
fn offset(&self) -> usize {
self.offset
}
#[inline]
fn header_len(&self) -> usize {
if self.is_dot1q() {
EthernetHeader::size_of() + VlanTag::size_of()
} else if self.is_qinq() {
EthernetHeader::size_of() + VlanTag::size_of() * 2
} else {
EthernetHeader::size_of()
}
}
#[inline]
unsafe fn clone(&self, internal: Internal) -> Self {
Ethernet {
envelope: self.envelope.clone(internal),
header: self.header,
offset: self.offset,
}
}
#[inline]
fn try_parse(envelope: Self::Envelope, _internal: Internal) -> Result<Self> {
let mbuf = envelope.mbuf();
let offset = envelope.payload_offset();
let header = mbuf.read_data(offset)?;
let packet = Ethernet {
envelope,
header,
offset,
};
ensure!(
packet.mbuf().data_len() >= packet.header_len(),
BufferError::OutOfBuffer(packet.header_len(), packet.mbuf().data_len())
);
Ok(packet)
}
#[inline]
fn try_push(mut envelope: Self::Envelope, _internal: Internal) -> Result<Self> {
let offset = envelope.payload_offset();
let mbuf = envelope.mbuf_mut();
mbuf.extend(offset, ETH_HEADER_SIZE)?;
let _ = mbuf.write_data_slice(offset, &[0; ETH_HEADER_SIZE])?;
let header = mbuf.read_data(offset)?;
Ok(Ethernet {
envelope,
header,
offset,
})
}
#[inline]
fn deparse(self) -> Self::Envelope {
self.envelope
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
#[repr(C, packed)]
pub struct EtherType(pub u16);
impl EtherType {
pub fn new(value: u16) -> Self {
EtherType(value)
}
}
#[allow(non_snake_case)]
#[allow(non_upper_case_globals)]
pub mod EtherTypes {
use super::EtherType;
pub const Arp: EtherType = EtherType(0x0806);
pub const Ipv4: EtherType = EtherType(0x0800);
pub const Ipv6: EtherType = EtherType(0x86DD);
}
impl fmt::Display for EtherType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match *self {
EtherTypes::Arp => "ARP".to_string(),
EtherTypes::Ipv4 => "IPv4".to_string(),
EtherTypes::Ipv6 => "IPv6".to_string(),
_ => {
let t = self.0;
format!("0x{:04x}", t)
}
}
)
}
}
#[derive(Clone, Copy, Debug, Default, SizeOf)]
#[repr(C, packed)]
struct VlanTag {
tpid: u16be,
tci: u16be,
}
#[allow(clippy::trivially_copy_pass_by_ref)]
impl VlanTag {
#[allow(dead_code)]
#[inline]
fn tag_id(&self) -> u16 {
self.tpid.into()
}
#[allow(dead_code)]
#[inline]
fn priority(&self) -> u8 {
let tci: u16 = self.tci.into();
(tci >> 13) as u8
}
#[allow(dead_code)]
#[inline]
fn drop_eligible(&self) -> bool {
self.tci & u16be::from(0x1000) > u16be::MIN
}
#[allow(dead_code)]
#[inline]
fn identifier(&self) -> u16 {
(self.tci & u16be::from(0x0fff)).into()
}
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C, packed)]
struct Dot1q {
tag: VlanTag,
ether_type: u16be,
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(C, packed)]
struct Qinq {
stag: VlanTag,
ctag: VlanTag,
ether_type: u16be,
}
#[allow(missing_debug_implementations)]
#[derive(Clone, Copy)]
#[repr(C, packed)]
union Chunk {
ether_type: u16be,
dot1q: Dot1q,
qinq: Qinq,
}
impl Default for Chunk {
fn default() -> Chunk {
Chunk {
ether_type: u16be::default(),
}
}
}
#[allow(missing_debug_implementations)]
#[derive(Clone, Copy, Default)]
#[repr(C, packed)]
struct EthernetHeader {
dst: MacAddr,
src: MacAddr,
chunk: Chunk,
}
impl SizeOf for EthernetHeader {
#[inline]
fn size_of() -> usize {
ETH_HEADER_SIZE
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::testils::byte_arrays::{IPV4_UDP_PACKET, VLAN_DOT1Q_PACKET, VLAN_QINQ_PACKET};
#[test]
fn size_of_ethernet_header() {
assert_eq!(14, EthernetHeader::size_of());
}
#[test]
fn ether_type_to_string() {
assert_eq!("ARP", EtherTypes::Arp.to_string());
assert_eq!("IPv4", EtherTypes::Ipv4.to_string());
assert_eq!("IPv6", EtherTypes::Ipv6.to_string());
assert_eq!("0x0000", EtherType::new(0).to_string());
}
#[capsule::test]
fn parse_ethernet_packet() {
let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap();
let ethernet = packet.parse::<Ethernet>().unwrap();
assert_eq!("00:00:00:00:00:01", ethernet.dst().to_string());
assert_eq!("00:00:00:00:00:02", ethernet.src().to_string());
assert_eq!(EtherTypes::Ipv4, ethernet.ether_type());
}
#[capsule::test]
fn parse_dot1q_packet() {
let packet = Mbuf::from_bytes(&VLAN_DOT1Q_PACKET).unwrap();
let ethernet = packet.parse::<Ethernet>().unwrap();
assert_eq!("00:00:00:00:00:01", ethernet.dst().to_string());
assert_eq!("00:00:00:00:00:02", ethernet.src().to_string());
assert!(ethernet.is_dot1q());
assert_eq!(EtherTypes::Arp, ethernet.ether_type());
assert_eq!(18, ethernet.header_len());
}
#[capsule::test]
fn parse_qinq_packet() {
let packet = Mbuf::from_bytes(&VLAN_QINQ_PACKET).unwrap();
let ethernet = packet.parse::<Ethernet>().unwrap();
assert_eq!("00:00:00:00:00:01", ethernet.dst().to_string());
assert_eq!("00:00:00:00:00:02", ethernet.src().to_string());
assert!(ethernet.is_qinq());
assert_eq!(EtherTypes::Arp, ethernet.ether_type());
assert_eq!(22, ethernet.header_len());
}
#[capsule::test]
fn swap_addresses() {
let packet = Mbuf::from_bytes(&IPV4_UDP_PACKET).unwrap();
let mut ethernet = packet.parse::<Ethernet>().unwrap();
ethernet.swap_addresses();
assert_eq!("00:00:00:00:00:02", ethernet.dst().to_string());
assert_eq!("00:00:00:00:00:01", ethernet.src().to_string());
}
#[capsule::test]
fn push_ethernet_packet() {
let packet = Mbuf::new().unwrap();
let ethernet = packet.push::<Ethernet>().unwrap();
assert_eq!(EthernetHeader::size_of(), ethernet.len());
}
#[capsule::test]
fn bug_push_ethernet_corrupts_buffer() {
let data = [255u8; 8];
let packet = Mbuf::from_bytes(&data).unwrap();
let ethernet = packet.push::<Ethernet>().unwrap();
let overflow = ethernet.mbuf().read_data_slice::<u8>(14, 8).unwrap();
assert_eq!(&data, unsafe { overflow.as_ref() });
}
}