use std::fmt::{Debug, Display};
use std::str::FromStr;
use std::{array, io};
use crate::Interface;
use pkts_common::Buffer;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum L2Protocol {
All,
Ip,
Other(u16),
}
impl From<u16> for L2Protocol {
#[inline]
fn from(value: u16) -> Self {
match value as i32 {
libc::ETH_P_ALL => L2Protocol::All,
libc::ETH_P_IP => L2Protocol::Ip,
_ => L2Protocol::Other(value),
}
}
}
impl From<L2Protocol> for u16 {
#[inline]
fn from(value: L2Protocol) -> Self {
match value {
L2Protocol::All => libc::ETH_P_ALL as u16,
L2Protocol::Ip => libc::ETH_P_IP as u16,
L2Protocol::Other(val) => val,
}
}
}
pub trait L2Addr: TryFrom<libc::sockaddr_ll> {
fn protocol(&self) -> L2Protocol;
fn interface(&self) -> Interface;
fn set_interface(&mut self, iface: Interface);
fn to_sockaddr(&self) -> io::Result<libc::sockaddr_ll>;
}
#[derive(Debug)]
pub struct AddrConversionError {
reason: &'static str,
}
impl AddrConversionError {
fn new(reason: &'static str) -> Self {
Self { reason }
}
#[inline]
pub fn as_str(&self) -> &str {
self.reason
}
}
pub struct MacAddr {
addr: [u8; 6],
}
impl From<[u8; 6]> for MacAddr {
#[inline]
fn from(value: [u8; 6]) -> Self {
Self { addr: value }
}
}
impl From<MacAddr> for [u8; 6] {
#[inline]
fn from(value: MacAddr) -> Self {
value.addr
}
}
impl Debug for MacAddr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MacAddress")
.field(
"addr",
&format!(
"{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
self.addr[0],
self.addr[1],
self.addr[2],
self.addr[3],
self.addr[4],
self.addr[5]
),
)
.finish()
}
}
impl Display for MacAddr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
self.addr[0], self.addr[1], self.addr[2], self.addr[3], self.addr[4], self.addr[5]
)
}
}
impl FromStr for MacAddr {
type Err = AddrConversionError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut addr = [0u8; 6];
let mut addr_idx = 0;
if let Some(delim @ (b':' | b'-')) = s.as_bytes().get(2) {
if s.bytes().len() != 17 {
return Err(AddrConversionError::new("invalid length MAC address"));
}
for (idx, mut b) in s.bytes().enumerate() {
let mod3_idx = idx % 3;
if (mod3_idx) == 2 {
if b != *delim {
return Err(AddrConversionError::new(
"invalid character in MAC address: expected colon/dash",
));
}
addr_idx += 1;
} else {
b = match b {
b'0'..=b'9' => b - b'0',
b'a'..=b'f' => 10 + (b - b'a'),
b'A'..=b'F' => 10 + (b - b'A'),
_ => {
return Err(AddrConversionError::new(
"invalid character in MAC address: expected hexadecimal value",
))
}
};
if mod3_idx == 0 {
b <<= 4;
}
addr[addr_idx] |= b;
}
}
} else if let Some(b'.') = s.as_bytes().get(4) {
if s.bytes().len() != 14 {
return Err(AddrConversionError::new("invalid length MAC address"));
}
for (idx, mut b) in s.bytes().enumerate() {
let mod5_idx = idx % 5;
if (mod5_idx) == 4 {
if b != b'.' {
return Err(AddrConversionError::new("invalid character in MAC address: expected '.' after four hexadecimal values"));
}
} else {
b = match b {
b'0'..=b'9' => b - b'0',
b'a'..=b'f' => 10 + (b - b'a'),
b'A'..=b'F' => 10 + (b - b'A'),
_ => {
return Err(AddrConversionError::new(
"invalid character in MAC address: expected hexadecimal value",
))
}
};
if mod5_idx & 0b1 == 0 {
addr[addr_idx] = b << 4;
} else {
addr[addr_idx] |= b;
addr_idx += 1;
}
}
}
} else {
if s.bytes().len() != 12 {
return Err(AddrConversionError::new("invalid length MAC address"));
}
for (idx, mut b) in s.bytes().enumerate() {
b = match b {
b'0'..=b'9' => b - b'0',
b'a'..=b'f' => 10 + (b - b'a'),
b'A'..=b'F' => 10 + (b - b'A'),
_ => {
return Err(AddrConversionError::new(
"invalid character in MAC address: expected hexadecimal value",
))
}
};
let even_bit = (idx & 0b1) == 0;
if even_bit {
addr[addr_idx] = b << 4;
} else {
addr[addr_idx] |= b;
addr_idx += 1;
}
}
}
Ok(Self { addr })
}
}
pub struct L2AddrAll {
iface: Interface,
}
impl L2AddrAll {
#[inline]
pub fn interface(&self) -> Interface {
self.iface
}
}
pub struct L2AddrIp {
addr: MacAddr,
iface: Interface,
}
impl TryFrom<libc::sockaddr_ll> for L2AddrIp {
type Error = io::Error;
#[inline]
fn try_from(value: libc::sockaddr_ll) -> Result<Self, Self::Error> {
if value.sll_family != libc::AF_PACKET as u16 {
return Err(io::Error::new(
io::ErrorKind::Unsupported,
"unrecognized address family (not AF_PACKET)",
));
}
if value.sll_protocol != libc::ETH_P_IP as u16 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"sockaddr link-layer protocol did not match type (expected {}, was {})",
libc::ETH_P_IP as u16,
value.sll_protocol
),
));
}
if value.sll_halen != 6 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid sockaddr link-layer address length",
));
}
let addr: [u8; 6] = array::from_fn(|i| value.sll_addr[i]);
Ok(L2AddrIp {
addr: MacAddr { addr },
iface: Interface::from_index(value.sll_ifindex as u32)?,
})
}
}
impl L2Addr for L2AddrIp {
#[inline]
fn protocol(&self) -> L2Protocol {
L2Protocol::Ip
}
#[inline]
fn interface(&self) -> Interface {
self.iface
}
#[inline]
fn set_interface(&mut self, iface: Interface) {
self.iface = iface;
}
fn to_sockaddr(&self) -> io::Result<libc::sockaddr_ll> {
let sll_addr = array::from_fn(|i| *self.addr.addr.get(i).unwrap_or(&0));
Ok(libc::sockaddr_ll {
sll_family: libc::AF_PACKET as u16,
sll_protocol: libc::ETH_P_IP as u16,
sll_ifindex: self.iface.index()? as i32,
sll_hatype: 0,
sll_pkttype: 0,
sll_halen: 6,
sll_addr,
})
}
}
pub enum L2AddrAny {
Ip(L2AddrIp),
Other(L2AddrUnspec),
}
impl TryFrom<libc::sockaddr_ll> for L2AddrAny {
type Error = io::Error;
#[inline]
fn try_from(value: libc::sockaddr_ll) -> Result<Self, Self::Error> {
Ok(match value.sll_protocol as i32 {
libc::ETH_P_IP => Self::Ip(L2AddrIp::try_from(value)?),
_ => Self::Other(L2AddrUnspec::try_from(value)?),
})
}
}
impl L2Addr for L2AddrAny {
#[inline]
fn protocol(&self) -> L2Protocol {
match self {
L2AddrAny::Ip(addr_ip) => addr_ip.protocol(),
L2AddrAny::Other(addr_other) => addr_other.protocol(),
}
}
#[inline]
fn interface(&self) -> Interface {
match self {
L2AddrAny::Ip(addr_ip) => addr_ip.interface(),
L2AddrAny::Other(addr_other) => addr_other.interface(),
}
}
#[inline]
fn set_interface(&mut self, iface: Interface) {
match self {
L2AddrAny::Ip(addr_ip) => addr_ip.set_interface(iface),
L2AddrAny::Other(addr_other) => addr_other.set_interface(iface),
}
}
#[inline]
fn to_sockaddr(&self) -> io::Result<libc::sockaddr_ll> {
match self {
L2AddrAny::Ip(addr_ip) => addr_ip.to_sockaddr(),
L2AddrAny::Other(addr_other) => addr_other.to_sockaddr(),
}
}
}
pub struct L2AddrUnspec {
addr: Buffer<u8, 8>,
iface: Interface,
protocol: L2Protocol,
}
impl TryFrom<libc::sockaddr_ll> for L2AddrUnspec {
type Error = io::Error;
fn try_from(value: libc::sockaddr_ll) -> Result<Self, Self::Error> {
if value.sll_family != libc::AF_PACKET as u16 {
return Err(io::Error::new(
io::ErrorKind::Unsupported,
"unrecognized address family (not AF_PACKET)",
));
}
let mut addr = Buffer::new();
match value.sll_addr.get(..value.sll_halen as usize) {
None => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid sockaddr link-layer address length",
))
}
Some(s) => addr.append(s),
}
Ok(L2AddrUnspec {
addr,
iface: Interface::from_index(value.sll_ifindex as u32)?,
protocol: value.sll_protocol.into(),
})
}
}
impl L2Addr for L2AddrUnspec {
#[inline]
fn protocol(&self) -> L2Protocol {
self.protocol
}
#[inline]
fn interface(&self) -> Interface {
self.iface
}
#[inline]
fn set_interface(&mut self, iface: Interface) {
self.iface = iface;
}
fn to_sockaddr(&self) -> io::Result<libc::sockaddr_ll> {
let sll_addr = array::from_fn(|i| *self.addr.as_slice().get(i).unwrap_or(&0));
Ok(libc::sockaddr_ll {
sll_family: libc::AF_PACKET as u16,
sll_protocol: libc::ETH_P_IP as u16,
sll_ifindex: self.iface.index()? as i32,
sll_hatype: 0,
sll_pkttype: 0,
sll_halen: 6,
sll_addr,
})
}
}