| 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<LinkHeader> { |
| 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<EtherPayloadSlice<'a>> { |
| 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());} |
| } |
| } |
| } |
| } |
| } |