use std::fmt::{Display, Formatter};
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
use crate::packet::protocol::EtherProto;
use crate::packet::{HeaderParser, PacketHeader};
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddressFamily {
Inet = 2,
Inet6Linux = 10,
Inet6Darwin = 30,
Inet6FreeBsd = 28,
Inet6OpenBsd = 24,
Unknown(u32),
}
impl From<u32> for AddressFamily {
fn from(value: u32) -> Self {
match value {
2 => AddressFamily::Inet,
10 => AddressFamily::Inet6Linux,
24 => AddressFamily::Inet6OpenBsd,
28 => AddressFamily::Inet6FreeBsd,
30 => AddressFamily::Inet6Darwin,
other => AddressFamily::Unknown(other),
}
}
}
impl AddressFamily {
pub fn is_ipv6(&self) -> bool {
matches!(
self,
AddressFamily::Inet6Linux
| AddressFamily::Inet6Darwin
| AddressFamily::Inet6FreeBsd
| AddressFamily::Inet6OpenBsd
)
}
pub fn to_ether_proto(&self) -> EtherProto {
match self {
AddressFamily::Inet => EtherProto::IPV4,
AddressFamily::Inet6Linux
| AddressFamily::Inet6Darwin
| AddressFamily::Inet6FreeBsd
| AddressFamily::Inet6OpenBsd => EtherProto::IPV6,
AddressFamily::Unknown(_) => EtherProto::from(0u16),
}
}
}
impl Display for AddressFamily {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
AddressFamily::Inet => write!(f, "AF_INET"),
AddressFamily::Inet6Linux => write!(f, "AF_INET6 (Linux)"),
AddressFamily::Inet6Darwin => write!(f, "AF_INET6 (Darwin)"),
AddressFamily::Inet6FreeBsd => write!(f, "AF_INET6 (FreeBSD)"),
AddressFamily::Inet6OpenBsd => write!(f, "AF_INET6 (OpenBSD)"),
AddressFamily::Unknown(v) => write!(f, "Unknown({})", v),
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
pub struct NullHeader {
family: [u8; 4],
}
impl NullHeader {
pub const SIZE: usize = 4;
#[inline]
pub fn family_raw_le(&self) -> u32 {
u32::from_le_bytes(self.family)
}
#[inline]
pub fn family_raw_be(&self) -> u32 {
u32::from_be_bytes(self.family)
}
#[inline]
pub fn address_family(&self) -> AddressFamily {
let le_value = self.family_raw_le();
let be_value = self.family_raw_be();
match le_value {
2 | 10 | 24 | 28 | 30 => AddressFamily::from(le_value),
_ => {
match be_value {
2 | 10 | 24 | 28 | 30 => AddressFamily::from(be_value),
_ => AddressFamily::from(le_value),
}
}
}
}
#[inline]
pub fn protocol(&self) -> EtherProto {
self.address_family().to_ether_proto()
}
}
impl PacketHeader for NullHeader {
const NAME: &'static str = "Null/Loopback";
type InnerType = EtherProto;
#[inline]
fn inner_type(&self) -> Self::InnerType {
self.protocol()
}
}
impl HeaderParser for NullHeader {
type Output<'a> = &'a NullHeader;
fn into_view<'a>(header: &'a Self, _options: &'a [u8]) -> Self::Output<'a> {
header
}
}
impl Display for NullHeader {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Null family={} (0x{:08x})",
self.address_family(),
self.family_raw_le()
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_null_header_size() {
assert_eq!(std::mem::size_of::<NullHeader>(), 4);
assert_eq!(NullHeader::SIZE, 4);
}
#[test]
fn test_null_header_ipv4_le() {
let packet = vec![0x02, 0x00, 0x00, 0x00, 0x45, 0x00];
let (header, payload) = NullHeader::from_bytes(&packet).unwrap();
assert_eq!(header.family_raw_le(), 2);
assert_eq!(header.address_family(), AddressFamily::Inet);
assert_eq!(header.protocol(), EtherProto::IPV4);
assert_eq!(payload.len(), 2);
}
#[test]
fn test_null_header_ipv6_darwin() {
let packet = vec![0x1e, 0x00, 0x00, 0x00, 0x60, 0x00];
let (header, payload) = NullHeader::from_bytes(&packet).unwrap();
assert_eq!(header.family_raw_le(), 30);
assert_eq!(header.address_family(), AddressFamily::Inet6Darwin);
assert!(header.address_family().is_ipv6());
assert_eq!(header.protocol(), EtherProto::IPV6);
assert_eq!(payload.len(), 2);
}
#[test]
fn test_null_header_ipv6_linux() {
let packet = vec![0x0a, 0x00, 0x00, 0x00];
let (header, _payload) = NullHeader::from_bytes(&packet).unwrap();
assert_eq!(header.family_raw_le(), 10);
assert_eq!(header.address_family(), AddressFamily::Inet6Linux);
assert!(header.address_family().is_ipv6());
assert_eq!(header.protocol(), EtherProto::IPV6);
}
#[test]
fn test_null_header_ipv6_freebsd() {
let packet = vec![0x1c, 0x00, 0x00, 0x00];
let (header, _payload) = NullHeader::from_bytes(&packet).unwrap();
assert_eq!(header.family_raw_le(), 28);
assert_eq!(header.address_family(), AddressFamily::Inet6FreeBsd);
assert!(header.address_family().is_ipv6());
}
#[test]
fn test_null_header_ipv6_openbsd() {
let packet = vec![0x18, 0x00, 0x00, 0x00];
let (header, _payload) = NullHeader::from_bytes(&packet).unwrap();
assert_eq!(header.family_raw_le(), 24);
assert_eq!(header.address_family(), AddressFamily::Inet6OpenBsd);
assert!(header.address_family().is_ipv6());
}
#[test]
fn test_null_header_ipv4_be() {
let packet = vec![0x00, 0x00, 0x00, 0x02];
let (header, _payload) = NullHeader::from_bytes(&packet).unwrap();
assert_eq!(header.address_family(), AddressFamily::Inet);
assert_eq!(header.protocol(), EtherProto::IPV4);
}
#[test]
fn test_null_header_unknown() {
let packet = vec![0xff, 0x00, 0x00, 0x00];
let (header, _payload) = NullHeader::from_bytes(&packet).unwrap();
assert!(matches!(
header.address_family(),
AddressFamily::Unknown(255)
));
assert_eq!(header.protocol(), EtherProto::from(0u16));
}
#[test]
fn test_null_header_too_short() {
let packet = vec![0x02, 0x00, 0x00];
let result = NullHeader::from_bytes(&packet);
assert!(result.is_err());
}
#[test]
fn test_null_header_display() {
let packet = vec![0x02, 0x00, 0x00, 0x00];
let (header, _) = NullHeader::from_bytes(&packet).unwrap();
let display = format!("{}", header);
assert!(display.contains("Null"));
assert!(display.contains("family="));
assert!(display.contains("AF_INET"));
}
#[test]
fn test_address_family_display() {
assert_eq!(format!("{}", AddressFamily::Inet), "AF_INET");
assert_eq!(format!("{}", AddressFamily::Inet6Linux), "AF_INET6 (Linux)");
assert_eq!(
format!("{}", AddressFamily::Inet6Darwin),
"AF_INET6 (Darwin)"
);
assert_eq!(format!("{}", AddressFamily::Unknown(99)), "Unknown(99)");
}
#[test]
fn test_packet_header_trait() {
let packet = vec![0x02, 0x00, 0x00, 0x00];
let (header, _) = NullHeader::from_bytes(&packet).unwrap();
assert_eq!(NullHeader::NAME, "Null/Loopback");
assert_eq!(header.inner_type(), EtherProto::IPV4);
}
}