use crate::*; /// A slice containing the link layer header (currently only Ethernet II and /// SLL are supported). #[derive(Clone, Debug, Eq, PartialEq)] pub enum LinkSlice<'a> { /// A slice containing an Ethernet II header. Ethernet2(Ethernet2Slice<'a>), /// A slice containing a Linux Cooked Capture v1 (SLL) header. LinuxSll(LinuxSllSlice<'a>), /// Ether payload without header. EtherPayload(EtherPayloadSlice<'a>), /// Sll payload without header. LinuxSllPayload(LinuxSllPayloadSlice<'a>), } impl<'a> LinkSlice<'a> { /// Convert the link slice to a header pub fn to_header(&self) -> Option { use LinkSlice::*; match self { Ethernet2(slice) => Some(LinkHeader::Ethernet2(slice.to_header())), LinuxSll(slice) => Some(LinkHeader::LinuxSll(slice.to_header())), EtherPayload(_) => None, LinuxSllPayload(_) => None, } } /// Returns the link layer ether payload (slice + ether type number). pub fn ether_payload(&self) -> Option> { use LinkSlice::*; match self { Ethernet2(s) => Some(s.payload().clone()), LinuxSll(s) => Some(EtherPayloadSlice::try_from(s.payload()).ok()?.clone()), EtherPayload(p) => Some(p.clone()), LinuxSllPayload(p) => Some(EtherPayloadSlice::try_from(p.clone()).ok()?), } } /// Returns the link layer sll payload (slice + link layer protocol type). pub fn sll_payload(&self) -> LinuxSllPayloadSlice<'a> { use LinkSlice::*; match self { Ethernet2(s) => LinuxSllPayloadSlice::from(s.payload().clone()), LinuxSll(s) => s.payload().clone(), EtherPayload(p) => LinuxSllPayloadSlice::from(p.clone()), LinuxSllPayload(p) => p.clone(), } } } #[cfg(test)] mod test { use super::*; use crate::test_gens::*; use alloc::{format, vec::Vec}; use proptest::prelude::*; proptest! { #[test] fn debug_clone_eq(ref eth in ethernet_2_unknown()) { let bytes = eth.to_bytes(); let e = Ethernet2Slice::from_slice_without_fcs(&bytes).unwrap(); let slice = LinkSlice::Ethernet2( e.clone() ); // clone & eq assert_eq!(slice.clone(), slice); // debug assert_eq!( format!("{:?}", slice), format!("Ethernet2({:?})", e), ); } } proptest! { #[test] fn to_header( ref eth in ethernet_2_unknown(), ref linux_sll in linux_sll_any() ) { { let bytes = eth.to_bytes(); let slice = LinkSlice::Ethernet2( Ethernet2Slice::from_slice_without_fcs(&bytes).unwrap() ); assert_eq!( slice.to_header(), Some(LinkHeader::Ethernet2(eth.clone())) ); } { let bytes = linux_sll.to_bytes(); let slice = LinkSlice::LinuxSll( LinuxSllSlice::from_slice(&bytes).unwrap() ); assert_eq!( slice.to_header(), Some(LinkHeader::LinuxSll(linux_sll.clone())) ); } { let slice = LinkSlice::EtherPayload(EtherPayloadSlice { ether_type: ether_type::IPV4, payload: &[] }); assert_eq!( slice.to_header(), None ); } { let slice = LinkSlice::LinuxSllPayload(LinuxSllPayloadSlice { protocol_type: LinuxSllProtocolType::EtherType(ether_type::IPV4), payload: &[] }); assert_eq!( slice.to_header(), None ); } } } proptest! { #[test] fn ether_payload( ref eth in ethernet_2_unknown(), ref linux_sll in linux_sll_any() ) { let p = [1,2,3,4]; { let mut bytes = Vec::with_capacity(Ethernet2Header::LEN + p.len()); bytes.extend_from_slice(ð.to_bytes()); bytes.extend_from_slice(&p); let slice = LinkSlice::Ethernet2( Ethernet2Slice::from_slice_without_fcs(&bytes).unwrap() ); assert_eq!( slice.ether_payload().unwrap(), EtherPayloadSlice{ ether_type: eth.ether_type, payload: &p } ); } { let slice = LinkSlice::EtherPayload(EtherPayloadSlice { ether_type: eth.ether_type, payload: &p }); assert_eq!( slice.ether_payload().unwrap(), EtherPayloadSlice{ ether_type: eth.ether_type, payload: &p } ); } { let mut bytes = Vec::with_capacity(LinuxSllHeader::LEN + p.len()); bytes.extend_from_slice(&linux_sll.to_bytes()); bytes.extend_from_slice(&p); let slice = LinkSlice::LinuxSll( LinuxSllSlice::from_slice(&bytes).unwrap() ); match linux_sll.protocol_type { LinuxSllProtocolType::EtherType(EtherType(v)) | LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType(v)) => { assert_eq!( slice.ether_payload().unwrap(), EtherPayloadSlice{ ether_type: EtherType(v), payload: &p } );} _ => { assert!(slice.ether_payload().is_none());} } } { let slice = LinkSlice::LinuxSllPayload(LinuxSllPayloadSlice { protocol_type: linux_sll.protocol_type, payload: &p }); match linux_sll.protocol_type { LinuxSllProtocolType::EtherType(EtherType(v)) | LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType(v)) => { assert_eq!( slice.ether_payload().unwrap(), EtherPayloadSlice{ ether_type: EtherType(v), payload: &p } );} _ => { assert!(slice.ether_payload().is_none());} } } } } }