use crate::{err, ArpHardwareId, LinuxSllHeaderSlice, LinuxSllPacketType, LinuxSllProtocolType};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LinuxSllHeader {
pub packet_type: LinuxSllPacketType,
pub arp_hrd_type: ArpHardwareId,
pub sender_address_valid_length: u16,
pub sender_address: [u8; 8],
pub protocol_type: LinuxSllProtocolType,
}
impl LinuxSllHeader {
pub const LEN: usize = 16;
#[inline]
pub fn from_slice(
slice: &[u8],
) -> Result<(LinuxSllHeader, &[u8]), err::linux_sll::HeaderSliceError> {
Ok((
LinuxSllHeaderSlice::from_slice(slice)?.to_header(),
&slice[LinuxSllHeader::LEN..],
))
}
#[inline]
pub fn from_bytes(bytes: [u8; 16]) -> Result<LinuxSllHeader, err::linux_sll::HeaderError> {
let packet_type = LinuxSllPacketType::try_from(u16::from_be_bytes([bytes[0], bytes[1]]))?;
let arp_hrd_type = ArpHardwareId::from(u16::from_be_bytes([bytes[2], bytes[3]]));
let sender_address_valid_length = u16::from_be_bytes([bytes[4], bytes[5]]);
let sender_address = [
bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13],
];
let protocol_type = LinuxSllProtocolType::try_from((
arp_hrd_type,
u16::from_be_bytes([bytes[14], bytes[15]]),
))?;
Ok(LinuxSllHeader {
packet_type,
arp_hrd_type,
sender_address_valid_length,
sender_address,
protocol_type,
})
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn read<T: std::io::Read + std::io::Seek + Sized>(
reader: &mut T,
) -> Result<LinuxSllHeader, err::ReadError> {
let buffer = {
let mut buffer = [0; LinuxSllHeader::LEN];
reader.read_exact(&mut buffer)?;
buffer
};
Ok(
unsafe { LinuxSllHeaderSlice::from_slice_unchecked(&buffer) }.to_header(),
)
}
pub fn write_to_slice<'a>(
&self,
slice: &'a mut [u8],
) -> Result<&'a mut [u8], err::SliceWriteSpaceError> {
if slice.len() < LinuxSllHeader::LEN {
Err(err::SliceWriteSpaceError {
required_len: LinuxSllHeader::LEN,
len: slice.len(),
layer: err::Layer::LinuxSllHeader,
layer_start_offset: 0,
})
} else {
slice[..LinuxSllHeader::LEN].copy_from_slice(&self.to_bytes());
Ok(&mut slice[LinuxSllHeader::LEN..])
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[inline]
pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
writer.write_all(&self.to_bytes())
}
#[inline]
pub fn header_len(&self) -> usize {
Self::LEN
}
#[inline]
pub fn to_bytes(&self) -> [u8; Self::LEN] {
let packet_type_be = u16::from(self.packet_type).to_be_bytes();
let arp_hrd_type_be = u16::from(self.arp_hrd_type).to_be_bytes();
let sender_address_valid_length_be = self.sender_address_valid_length.to_be_bytes();
let sender_address_be = self.sender_address;
let protocol_type_be = u16::from(self.protocol_type).to_be_bytes();
[
packet_type_be[0],
packet_type_be[1],
arp_hrd_type_be[0],
arp_hrd_type_be[1],
sender_address_valid_length_be[0],
sender_address_valid_length_be[1],
sender_address_be[0],
sender_address_be[1],
sender_address_be[2],
sender_address_be[3],
sender_address_be[4],
sender_address_be[5],
sender_address_be[6],
sender_address_be[7],
protocol_type_be[0],
protocol_type_be[1],
]
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{test_gens::*, LenSource};
use alloc::{format, vec::Vec};
use proptest::prelude::*;
use std::io::{Cursor, ErrorKind};
proptest! {
#[test]
fn from_slice(
input in linux_sll_any(),
dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
) {
let mut buffer: Vec<u8> = Vec::with_capacity(LinuxSllHeader::LEN + dummy_data.len());
input.write(&mut buffer).unwrap();
buffer.extend(&dummy_data[..]);
{
let (result, rest) = LinuxSllHeader::from_slice(&buffer[..]).unwrap();
assert_eq!(input, result);
assert_eq!(&buffer[16..], rest);
}
for len in 0..=13 {
assert_eq!(
LinuxSllHeader::from_slice(&buffer[..len]).unwrap_err(),
err::linux_sll::HeaderSliceError::Len(err::LenError{
required_len: LinuxSllHeader::LEN,
len: len,
len_source: LenSource::Slice,
layer: err::Layer::LinuxSllHeader,
layer_start_offset: 0,
})
);
}
}
}
proptest! {
#[test]
fn from_bytes(input in linux_sll_any()) {
assert_eq!(
input,
LinuxSllHeader::from_bytes(input.to_bytes()).unwrap()
);
}
}
proptest! {
#[test]
fn read(
input in linux_sll_any(),
dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
) {
let mut buffer = Vec::with_capacity(LinuxSllHeader::LEN + dummy_data.len());
input.write(&mut buffer).unwrap();
buffer.extend(&dummy_data[..]);
{
let mut cursor = Cursor::new(&buffer);
let result = LinuxSllHeader::read(&mut cursor).unwrap();
assert_eq!(input, result);
assert_eq!(cursor.position(), u64::try_from(LinuxSllHeader::LEN).unwrap());
}
for len in 0..=13 {
let mut cursor = Cursor::new(&buffer[0..len]);
assert_eq!(
LinuxSllHeader::read(&mut cursor)
.unwrap_err()
.io().unwrap()
.kind(),
ErrorKind::UnexpectedEof
);
}
}
}
proptest! {
#[test]
fn write_to_slice(input in linux_sll_any()) {
{
let mut buffer: [u8;LinuxSllHeader::LEN] = [0;LinuxSllHeader::LEN];
input.write_to_slice(&mut buffer).unwrap();
assert_eq!(buffer, input.to_bytes());
}
for len in 0..14 {
let mut buffer: [u8;LinuxSllHeader::LEN] = [0;LinuxSllHeader::LEN];
assert_eq!(
err::SliceWriteSpaceError {
required_len: LinuxSllHeader::LEN,
len,
layer: err::Layer::LinuxSllHeader,
layer_start_offset: 0,
},
input.write_to_slice(&mut buffer[..len]).unwrap_err()
);
}
}
}
proptest! {
#[test]
fn write(input in linux_sll_any()) {
{
let mut buffer: Vec<u8> = Vec::with_capacity(LinuxSllHeader::LEN);
input.write(&mut buffer).unwrap();
assert_eq!(&buffer[..], &input.to_bytes());
}
for len in 0..8 {
let mut buffer = [0u8;8];
let mut writer = Cursor::new(&mut buffer[..len]);
assert!(input.write(&mut writer).is_err());
}
}
}
proptest! {
#[test]
fn header_len(input in linux_sll_any()) {
assert_eq!(input.header_len(), LinuxSllHeader::LEN);
}
}
proptest! {
#[test]
fn to_bytes(input in linux_sll_any()) {
let packet_type_be = u16::from(input.packet_type).to_be_bytes();
let arp_hrd_type_be = u16::from(input.arp_hrd_type).to_be_bytes();
let sender_address_valid_length_be = input.sender_address_valid_length.to_be_bytes();
let sender_address_be = input.sender_address;
let protocol_type_be = u16::from(input.protocol_type).to_be_bytes();
assert_eq!(
input.to_bytes(),
[
packet_type_be[0],
packet_type_be[1],
arp_hrd_type_be[0],
arp_hrd_type_be[1],
sender_address_valid_length_be[0],
sender_address_valid_length_be[1],
sender_address_be[0],
sender_address_be[1],
sender_address_be[2],
sender_address_be[3],
sender_address_be[4],
sender_address_be[5],
sender_address_be[6],
sender_address_be[7],
protocol_type_be[0],
protocol_type_be[1],
]
);
}
}
proptest! {
#[test]
fn clone_eq(input in linux_sll_any()) {
assert_eq!(input, input.clone());
}
}
proptest! {
#[test]
fn dbg(input in linux_sll_any()) {
assert_eq!(
&format!(
"LinuxSllHeader {{ packet_type: {:?}, arp_hrd_type: {:?}, sender_address_valid_length: {:?}, sender_address: {:?}, protocol_type: {:?} }}",
input.packet_type,
input.arp_hrd_type,
input.sender_address_valid_length,
input.sender_address,
input.protocol_type,
),
&format!("{:?}", input)
);
}
}
}