use core::fmt;
use super::as_header;
use crate::error::{Error, Result};
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[repr(C)]
pub struct EthernetII {
dst: EtherAddr,
src: EtherAddr,
ty: EtherTypeRepr,
}
impl EthernetII {
#[inline]
pub fn split_header(bytes: &[u8]) -> Result<(&Self, &[u8])> {
let (header, payload) = as_header!(EthernetII, bytes)?;
header.ty.check()?;
Ok((header, payload))
}
#[inline]
pub fn source(&self) -> EtherAddr {
self.src
}
#[inline]
pub fn destination(&self) -> EtherAddr {
self.dst
}
#[inline]
pub fn ethertype(&self) -> EtherType {
self.ty.get()
}
}
impl fmt::Display for EthernetII {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"EthernetII src: {}, dst: {}, type: {}",
self.src, self.dst, self.ty
)
}
}
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
#[repr(transparent)]
pub struct EtherAddr([u8; 6]);
impl EtherAddr {
pub const BROADCAST: EtherAddr = EtherAddr([0xFF; 6]);
#[inline]
pub fn new(bytes: [u8; 6]) -> Self {
Self(bytes)
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.0.as_ref()
}
#[inline]
pub const fn is_unicast(&self) -> bool {
self.0[0] & 0x01 == 0
}
#[inline]
pub const fn is_multicast(&self) -> bool {
!self.is_unicast()
}
#[inline]
pub const fn is_broadcast(&self) -> bool {
self.0[0] == 0xFF
&& self.0[1] == 0xFF
&& self.0[2] == 0xFF
&& self.0[3] == 0xFF
&& self.0[4] == 0xFF
&& self.0[5] == 0xFF
}
#[inline]
pub const fn is_universal(&self) -> bool {
self.0[0] & 0x02 == 0
}
#[inline]
pub const fn is_local(&self) -> bool {
!self.is_universal()
}
}
impl fmt::Display for EtherAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let bytes = self.0;
write!(
f,
"{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]
)
}
}
#[non_exhaustive]
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
#[repr(u16)]
pub enum EtherType {
Ipv4 = 0x0800,
Arp = 0x0806,
Ipv6 = 0x86DD,
}
impl From<EtherType> for u16 {
#[inline]
fn from(val: EtherType) -> Self {
val as u16
}
}
impl TryFrom<u16> for EtherType {
type Error = Error;
#[inline]
fn try_from(value: u16) -> Result<Self> {
match value {
value if value == Self::Ipv4 as u16 => Ok(Self::Ipv4),
value if value == Self::Arp as u16 => Ok(Self::Arp),
value if value == Self::Ipv6 as u16 => Ok(Self::Ipv6),
_ => Err(Error::Unsupported),
}
}
}
impl fmt::Display for EtherType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self, f)
}
}
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[repr(transparent)]
struct EtherTypeRepr([u8; 2]);
impl EtherTypeRepr {
const IPV4: EtherTypeRepr = EtherTypeRepr(u16::to_be_bytes(EtherType::Ipv4 as u16));
const ARP: EtherTypeRepr = EtherTypeRepr(u16::to_be_bytes(EtherType::Arp as u16));
const IPV6: EtherTypeRepr = EtherTypeRepr(u16::to_be_bytes(EtherType::Ipv6 as u16));
#[inline]
const fn check(&self) -> Result<()> {
match *self {
Self::IPV4 | Self::ARP | Self::IPV6 => Ok(()),
_ => Err(Error::Unsupported),
}
}
#[inline]
const fn get(&self) -> EtherType {
match *self {
Self::IPV4 => EtherType::Ipv4,
Self::ARP => EtherType::Arp,
Self::IPV6 => EtherType::Ipv6,
_ => unreachable!(),
}
}
}
impl From<EtherType> for EtherTypeRepr {
#[inline]
fn from(value: EtherType) -> Self {
EtherTypeRepr(u16::to_be_bytes(value as u16))
}
}
impl fmt::Display for EtherTypeRepr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.get(), f)
}
}
#[cfg(test)]
mod tests {
use crate::error::Error;
use super::*;
#[test]
fn short_header() {
let bytes = [0; 13];
assert_eq!(
EthernetII::split_header(&bytes).unwrap_err(),
Error::Truncated
);
}
#[test]
fn invalid_ethertype() {
let bytes = [0; 14];
assert_eq!(
EthernetII::split_header(&bytes).unwrap_err(),
Error::Unsupported
);
}
#[test]
fn valid_ethertypes() {
let bytes = [&[0; 12][..], &[0x08, 0x00][..]].concat();
let (header, _) = EthernetII::split_header(&bytes).unwrap();
assert_eq!(header.ethertype(), EtherType::Ipv4);
let bytes = [&[0; 12][..], &[0x08, 0x06][..]].concat();
let (header, _) = EthernetII::split_header(&bytes).unwrap();
assert_eq!(header.ethertype(), EtherType::Arp);
let bytes = [&[0; 12][..], &[0x86, 0xDD][..]].concat();
let (header, _) = EthernetII::split_header(&bytes).unwrap();
assert_eq!(header.ethertype(), EtherType::Ipv6);
}
#[test]
fn ether_addr() {
let mut addr = EtherAddr([0xFF; 6]);
assert!(addr.is_broadcast());
assert_eq!((true, false), (addr.is_local(), addr.is_universal()));
assert_eq!((true, false), (addr.is_multicast(), addr.is_unicast()));
addr.0[0] = 0x0EF;
assert!(!addr.is_broadcast());
assert_eq!((true, false), (addr.is_local(), addr.is_universal()));
assert_eq!((true, false), (addr.is_multicast(), addr.is_unicast()));
addr.0[0] = 0x0FE;
assert!(!addr.is_broadcast());
assert_eq!((true, false), (addr.is_local(), addr.is_universal()));
assert_eq!((false, true), (addr.is_multicast(), addr.is_unicast()));
addr.0[0] = 0x0FD;
assert!(!addr.is_broadcast());
assert_eq!((false, true), (addr.is_local(), addr.is_universal()));
assert_eq!((true, false), (addr.is_multicast(), addr.is_unicast()));
addr.0[0] = 0xFC;
assert!(!addr.is_broadcast());
assert_eq!((false, true), (addr.is_local(), addr.is_universal()));
assert_eq!((false, true), (addr.is_multicast(), addr.is_unicast()));
}
}