use core::fmt;
use std::fmt::{Display, Formatter};
use zerocopy::byteorder::{BigEndian, U16, U32};
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
use crate::packet::protocol::EtherProto;
use crate::packet::{HeaderParser, PacketHeader};
#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SllPacketType {
Host = 0,
Broadcast = 1,
Multicast = 2,
OtherHost = 3,
Outgoing = 4,
LoopbackOutgoing = 5,
FastRoute = 6,
Unknown(u16),
}
impl From<u16> for SllPacketType {
fn from(value: u16) -> Self {
match value {
0 => SllPacketType::Host,
1 => SllPacketType::Broadcast,
2 => SllPacketType::Multicast,
3 => SllPacketType::OtherHost,
4 => SllPacketType::Outgoing,
5 => SllPacketType::LoopbackOutgoing,
6 => SllPacketType::FastRoute,
v => SllPacketType::Unknown(v),
}
}
}
impl Display for SllPacketType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
SllPacketType::Host => write!(f, "Host"),
SllPacketType::Broadcast => write!(f, "Broadcast"),
SllPacketType::Multicast => write!(f, "Multicast"),
SllPacketType::OtherHost => write!(f, "OtherHost"),
SllPacketType::Outgoing => write!(f, "Outgoing"),
SllPacketType::LoopbackOutgoing => write!(f, "LoopbackOutgoing"),
SllPacketType::FastRoute => write!(f, "FastRoute"),
SllPacketType::Unknown(v) => write!(f, "Unknown({})", v),
}
}
}
#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArphrdType {
Ether = 1,
Ieee80211 = 801,
Ieee80211Radiotap = 803,
Loopback = 772,
Ppp = 512,
Unknown(u16),
}
impl From<u16> for ArphrdType {
fn from(value: u16) -> Self {
match value {
1 => ArphrdType::Ether,
801 => ArphrdType::Ieee80211,
803 => ArphrdType::Ieee80211Radiotap,
772 => ArphrdType::Loopback,
512 => ArphrdType::Ppp,
v => ArphrdType::Unknown(v),
}
}
}
impl Display for ArphrdType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
ArphrdType::Ether => write!(f, "Ethernet"),
ArphrdType::Ieee80211 => write!(f, "IEEE802.11"),
ArphrdType::Ieee80211Radiotap => write!(f, "IEEE802.11+Radiotap"),
ArphrdType::Loopback => write!(f, "Loopback"),
ArphrdType::Ppp => write!(f, "PPP"),
ArphrdType::Unknown(v) => write!(f, "Unknown({})", v),
}
}
}
#[repr(C, packed)]
#[derive(FromBytes, IntoBytes, Unaligned, Immutable, KnownLayout, Debug, Clone, Copy)]
pub struct SllHeader {
packet_type: U16<BigEndian>,
arphrd_type: U16<BigEndian>,
ll_addr_len: U16<BigEndian>,
ll_addr: [u8; 8],
protocol: EtherProto,
}
impl SllHeader {
#[inline]
pub fn packet_type(&self) -> SllPacketType {
SllPacketType::from(self.packet_type.get())
}
#[inline]
pub fn packet_type_raw(&self) -> u16 {
self.packet_type.get()
}
#[inline]
pub fn arphrd_type(&self) -> ArphrdType {
ArphrdType::from(self.arphrd_type.get())
}
#[inline]
pub fn arphrd_type_raw(&self) -> u16 {
self.arphrd_type.get()
}
#[inline]
pub fn ll_addr_len(&self) -> u16 {
self.ll_addr_len.get()
}
#[inline]
pub fn ll_addr(&self) -> &[u8] {
let len = std::cmp::min(self.ll_addr_len.get() as usize, 8);
&self.ll_addr[..len]
}
#[inline]
pub fn ll_addr_raw(&self) -> &[u8; 8] {
&self.ll_addr
}
#[inline]
pub fn protocol(&self) -> EtherProto {
self.protocol
}
}
impl PacketHeader for SllHeader {
const NAME: &'static str = "SllHeader";
type InnerType = EtherProto;
#[inline]
fn inner_type(&self) -> Self::InnerType {
self.protocol
}
}
impl HeaderParser for SllHeader {
type Output<'a> = &'a SllHeader;
#[inline]
fn into_view<'a>(header: &'a Self, _options: &'a [u8]) -> Self::Output<'a> {
header
}
}
impl Display for SllHeader {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"SLL type={} hw={} proto={}",
self.packet_type(),
self.arphrd_type(),
self.protocol()
)?;
let addr_len = self.ll_addr_len() as usize;
if addr_len > 0 && addr_len <= 8 {
write!(f, " addr=")?;
for (i, byte) in self.ll_addr[..addr_len].iter().enumerate() {
if i > 0 {
write!(f, ":")?;
}
write!(f, "{:02x}", byte)?;
}
}
Ok(())
}
}
#[repr(C, packed)]
#[derive(FromBytes, IntoBytes, Unaligned, Immutable, KnownLayout, Debug, Clone, Copy)]
pub struct Sllv2Header {
protocol: EtherProto,
reserved: U16<BigEndian>,
interface_index: U32<BigEndian>,
arphrd_type: U16<BigEndian>,
packet_type: u8,
ll_addr_len: u8,
ll_addr: [u8; 8],
}
impl Sllv2Header {
#[inline]
pub fn protocol(&self) -> EtherProto {
self.protocol
}
#[inline]
pub fn interface_index(&self) -> u32 {
self.interface_index.get()
}
#[inline]
pub fn arphrd_type(&self) -> ArphrdType {
ArphrdType::from(self.arphrd_type.get())
}
#[inline]
pub fn arphrd_type_raw(&self) -> u16 {
self.arphrd_type.get()
}
#[inline]
pub fn packet_type(&self) -> SllPacketType {
SllPacketType::from(self.packet_type as u16)
}
#[inline]
pub fn packet_type_raw(&self) -> u8 {
self.packet_type
}
#[inline]
pub fn ll_addr_len(&self) -> u8 {
self.ll_addr_len
}
#[inline]
pub fn ll_addr(&self) -> &[u8] {
let len = std::cmp::min(self.ll_addr_len as usize, 8);
&self.ll_addr[..len]
}
#[inline]
pub fn ll_addr_raw(&self) -> &[u8; 8] {
&self.ll_addr
}
}
impl PacketHeader for Sllv2Header {
const NAME: &'static str = "Sllv2Header";
type InnerType = EtherProto;
#[inline]
fn inner_type(&self) -> Self::InnerType {
self.protocol
}
}
impl HeaderParser for Sllv2Header {
type Output<'a> = &'a Sllv2Header;
#[inline]
fn into_view<'a>(header: &'a Self, _options: &'a [u8]) -> Self::Output<'a> {
header
}
}
impl Display for Sllv2Header {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"SLLv2 if={} type={} hw={} proto={}",
self.interface_index(),
self.packet_type(),
self.arphrd_type(),
self.protocol()
)?;
let addr_len = self.ll_addr_len as usize;
if addr_len > 0 && addr_len <= 8 {
write!(f, " addr=")?;
for (i, byte) in self.ll_addr[..addr_len].iter().enumerate() {
if i > 0 {
write!(f, ":")?;
}
write!(f, "{:02x}", byte)?;
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem;
#[test]
fn test_sll_header_size() {
assert_eq!(mem::size_of::<SllHeader>(), 16);
assert_eq!(SllHeader::FIXED_LEN, 16);
}
#[test]
fn test_sllv2_header_size() {
assert_eq!(mem::size_of::<Sllv2Header>(), 20);
assert_eq!(Sllv2Header::FIXED_LEN, 20);
}
#[test]
fn test_sll_header_parse() {
let packet: [u8; 16] = [
0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x00, 0x08, 0x00, ];
let (header, remaining) =
SllHeader::from_bytes(&packet).expect("Failed to parse SllHeader");
assert_eq!(header.packet_type(), SllPacketType::Host);
assert_eq!(header.arphrd_type(), ArphrdType::Ether);
assert_eq!(header.ll_addr_len(), 6);
assert_eq!(header.ll_addr(), &[0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]);
assert_eq!(header.protocol(), EtherProto::IPV4);
assert_eq!(remaining.len(), 0);
}
#[test]
fn test_sll_header_outgoing() {
let packet: [u8; 16] = [
0x00, 0x04, 0x00, 0x01, 0x00, 0x06, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x00, 0x00, 0x86, 0xdd, ];
let (header, _) = SllHeader::from_bytes(&packet).expect("Failed to parse SllHeader");
assert_eq!(header.packet_type(), SllPacketType::Outgoing);
assert_eq!(header.protocol(), EtherProto::IPV6);
}
#[test]
fn test_sllv2_header_parse() {
let packet: [u8; 20] = [
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x06, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x00, ];
let (header, remaining) =
Sllv2Header::from_bytes(&packet).expect("Failed to parse Sllv2Header");
assert_eq!(header.protocol(), EtherProto::IPV4);
assert_eq!(header.interface_index(), 2);
assert_eq!(header.arphrd_type(), ArphrdType::Ether);
assert_eq!(header.packet_type(), SllPacketType::Host);
assert_eq!(header.ll_addr_len(), 6);
assert_eq!(header.ll_addr(), &[0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]);
assert_eq!(remaining.len(), 0);
}
#[test]
fn test_sllv2_header_with_payload() {
let mut packet = Vec::new();
packet.extend_from_slice(&[
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x04, 0x06, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x00, 0x00, ]);
packet.extend_from_slice(b"test payload");
let (header, remaining) =
Sllv2Header::from_bytes(&packet).expect("Failed to parse Sllv2Header");
assert_eq!(header.protocol(), EtherProto::IPV4);
assert_eq!(header.interface_index(), 5);
assert_eq!(header.packet_type(), SllPacketType::Outgoing);
assert_eq!(remaining, b"test payload");
}
#[test]
fn test_sll_display() {
let packet: [u8; 16] = [
0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x00, 0x08, 0x00, ];
let (header, _) = SllHeader::from_bytes(&packet).unwrap();
let display = format!("{}", header);
assert!(display.contains("SLL"));
assert!(display.contains("Host"));
assert!(display.contains("Ethernet"));
assert!(display.contains("aa:bb:cc:dd:ee:ff"));
}
#[test]
fn test_sllv2_display() {
let packet: [u8; 20] = [
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x06, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x00, 0x00,
];
let (header, _) = Sllv2Header::from_bytes(&packet).unwrap();
let display = format!("{}", header);
assert!(display.contains("SLLv2"));
assert!(display.contains("if=3"));
assert!(display.contains("Host"));
assert!(display.contains("11:22:33:44:55:66"));
}
#[test]
fn test_packet_type_display() {
assert_eq!(format!("{}", SllPacketType::Host), "Host");
assert_eq!(format!("{}", SllPacketType::Broadcast), "Broadcast");
assert_eq!(format!("{}", SllPacketType::Multicast), "Multicast");
assert_eq!(format!("{}", SllPacketType::OtherHost), "OtherHost");
assert_eq!(format!("{}", SllPacketType::Outgoing), "Outgoing");
assert_eq!(format!("{}", SllPacketType::Unknown(99)), "Unknown(99)");
}
#[test]
fn test_arphrd_type_display() {
assert_eq!(format!("{}", ArphrdType::Ether), "Ethernet");
assert_eq!(format!("{}", ArphrdType::Loopback), "Loopback");
assert_eq!(format!("{}", ArphrdType::Ieee80211), "IEEE802.11");
assert_eq!(format!("{}", ArphrdType::Unknown(999)), "Unknown(999)");
}
#[test]
fn test_sll_too_short() {
let packet: [u8; 10] = [0; 10];
let result = SllHeader::from_bytes(&packet);
assert!(result.is_err());
}
#[test]
fn test_sllv2_too_short() {
let packet: [u8; 15] = [0; 15];
let result = Sllv2Header::from_bytes(&packet);
assert!(result.is_err());
}
#[test]
fn test_sll_loopback() {
let packet: [u8; 16] = [
0x00, 0x04, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, ];
let (header, _) = SllHeader::from_bytes(&packet).expect("Failed to parse");
assert_eq!(header.arphrd_type(), ArphrdType::Loopback);
assert_eq!(header.ll_addr_len(), 0);
assert_eq!(header.ll_addr(), &[]);
}
}