use crate::{
err::{self, Layer},
ArpHardwareId, LenSource, LinuxSllHeader, LinuxSllHeaderSlice, LinuxSllPacketType,
LinuxSllPayloadSlice, LinuxSllProtocolType,
};
#[derive(Clone, Eq, PartialEq)]
pub struct LinuxSllSlice<'a> {
header_slice: LinuxSllHeaderSlice<'a>,
header_and_payload_slice: &'a [u8],
}
impl<'a> LinuxSllSlice<'a> {
pub fn from_slice(
slice: &'a [u8],
) -> Result<LinuxSllSlice<'a>, err::linux_sll::HeaderSliceError> {
if slice.len() < LinuxSllHeader::LEN {
return Err(err::linux_sll::HeaderSliceError::Len(err::LenError {
required_len: LinuxSllHeader::LEN,
len: slice.len(),
len_source: LenSource::Slice,
layer: Layer::LinuxSllHeader,
layer_start_offset: 0,
}));
}
match LinuxSllHeaderSlice::from_slice(&slice[0..LinuxSllHeader::LEN]) {
Err(err) => Err(err),
Ok(header_slice) => Ok(LinuxSllSlice {
header_slice,
header_and_payload_slice: slice,
}),
}
}
#[inline]
pub fn slice(&self) -> &'a [u8] {
self.header_and_payload_slice
}
#[inline]
pub fn packet_type(&self) -> LinuxSllPacketType {
self.header_slice.packet_type()
}
#[inline]
pub fn arp_hardware_type(&self) -> ArpHardwareId {
self.header_slice.arp_hardware_type()
}
#[inline]
pub fn sender_address_valid_length(&self) -> u16 {
self.header_slice.sender_address_valid_length()
}
#[inline]
pub fn sender_address_full(&self) -> [u8; 8] {
self.header_slice.sender_address_full()
}
#[inline]
pub fn sender_address(&self) -> &'a [u8] {
self.header_slice.sender_address()
}
#[inline]
pub fn protocol_type(&self) -> LinuxSllProtocolType {
self.header_slice.protocol_type()
}
pub fn to_header(&self) -> LinuxSllHeader {
LinuxSllHeader {
packet_type: self.packet_type(),
arp_hrd_type: self.arp_hardware_type(),
sender_address_valid_length: self.sender_address_valid_length(),
sender_address: self.sender_address_full(),
protocol_type: self.protocol_type(),
}
}
pub fn header_slice(&self) -> &[u8] {
self.header_slice.slice()
}
#[inline]
pub fn payload(&self) -> LinuxSllPayloadSlice<'a> {
LinuxSllPayloadSlice {
protocol_type: self.protocol_type(),
payload: self.payload_slice(),
}
}
#[inline]
pub fn payload_slice(&self) -> &'a [u8] {
unsafe {
core::slice::from_raw_parts(
self.header_and_payload_slice
.as_ptr()
.add(LinuxSllHeader::LEN),
self.header_and_payload_slice.len() - LinuxSllHeader::LEN,
)
}
}
#[inline]
pub const fn header_len(&self) -> usize {
LinuxSllHeader::LEN
}
}
impl core::fmt::Debug for LinuxSllSlice<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("LinuxSllSlice")
.field("header", &self.to_header())
.field("payload", &self.payload())
.finish()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::test_gens::*;
use alloc::{format, vec::Vec};
use proptest::prelude::*;
proptest! {
#[test]
fn debug_clone_eq(
linux_sll in linux_sll_any()
) {
let payload: [u8;8] = [1,2,3,4,5,6,7,8];
let mut data = Vec::with_capacity(
linux_sll.header_len() +
payload.len()
);
data.extend_from_slice(&linux_sll.to_bytes());
data.extend_from_slice(&payload);
let slice = LinuxSllSlice::from_slice(&data).unwrap();
prop_assert_eq!(
format!("{:?}", slice),
format!(
"LinuxSllSlice {{ header: {:?}, payload: {:?} }}",
slice.to_header(),
slice.payload(),
)
);
prop_assert_eq!(slice.clone(), slice);
}
}
proptest! {
#[test]
fn getters(linux_sll in linux_sll_any()) {
let payload: [u8;8] = [1,2,3,4,5,6,7,8];
let mut data = Vec::with_capacity(
linux_sll.header_len() +
payload.len()
);
data.extend_from_slice(&linux_sll.to_bytes());
data.extend_from_slice(&payload);
let slice = LinuxSllSlice::from_slice(&data).unwrap();
assert_eq!(linux_sll.packet_type, slice.packet_type());
assert_eq!(linux_sll.arp_hrd_type, slice.arp_hardware_type());
assert_eq!(linux_sll.sender_address_valid_length, slice.sender_address_valid_length());
assert_eq!(linux_sll.sender_address, slice.sender_address_full());
assert_eq!(&linux_sll.sender_address[..usize::from(linux_sll.sender_address_valid_length)], slice.sender_address());
assert_eq!(linux_sll.protocol_type, slice.protocol_type());
assert_eq!(&payload, slice.payload_slice());
assert_eq!(
LinuxSllPayloadSlice{
payload: &payload,
protocol_type: linux_sll.protocol_type,
},
slice.payload()
);
assert_eq!(linux_sll, slice.to_header());
assert_eq!(&data, slice.slice());
assert_eq!(&data[..LinuxSllHeader::LEN], slice.header_slice());
}
}
proptest! {
#[test]
fn from_slice(linux_sll in linux_sll_any()) {
let payload: [u8;10] = [1,2,3,4,5,6,7,8,9,10];
let data = {
let mut data = Vec::with_capacity(
linux_sll.header_len() +
payload.len()
);
data.extend_from_slice(&linux_sll.to_bytes());
data.extend_from_slice(&payload);
data
};
{
let slice = LinuxSllSlice::from_slice(&data).unwrap();
assert_eq!(slice.to_header(), linux_sll);
assert_eq!(slice.payload_slice(), &payload);
}
{
let slice = LinuxSllSlice::from_slice(&data[..LinuxSllHeader::LEN]).unwrap();
assert_eq!(slice.to_header(), linux_sll);
assert_eq!(slice.payload_slice(), &[]);
}
for len in 0..LinuxSllHeader::LEN {
assert_eq!(
LinuxSllSlice::from_slice(&data[..len]).unwrap_err(),
err::linux_sll::HeaderSliceError::Len(err::LenError{
required_len: LinuxSllHeader::LEN,
len,
len_source: LenSource::Slice,
layer: Layer::LinuxSllHeader,
layer_start_offset: 0
})
);
}
}
}
}