1 use crate::*;
2 
3 /// A slice containing the link layer header (currently only Ethernet II and
4 /// SLL are supported).
5 #[derive(Clone, Debug, Eq, PartialEq)]
6 pub enum LinkSlice<'a> {
7     /// A slice containing an Ethernet II header.
8     Ethernet2(Ethernet2Slice<'a>),
9 
10     /// A slice containing a Linux Cooked Capture v1 (SLL) header.
11     LinuxSll(LinuxSllSlice<'a>),
12 
13     /// Ether payload without header.
14     EtherPayload(EtherPayloadSlice<'a>),
15 
16     /// Sll payload without header.
17     LinuxSllPayload(LinuxSllPayloadSlice<'a>),
18 }
19 
20 impl<'a> LinkSlice<'a> {
21     /// Convert the link slice to a header
to_header(&self) -> Option<LinkHeader>22     pub fn to_header(&self) -> Option<LinkHeader> {
23         use LinkSlice::*;
24         match self {
25             Ethernet2(slice) => Some(LinkHeader::Ethernet2(slice.to_header())),
26             LinuxSll(slice) => Some(LinkHeader::LinuxSll(slice.to_header())),
27             EtherPayload(_) => None,
28             LinuxSllPayload(_) => None,
29         }
30     }
31 
32     /// Returns the link layer ether payload (slice + ether type number).
ether_payload(&self) -> Option<EtherPayloadSlice<'a>>33     pub fn ether_payload(&self) -> Option<EtherPayloadSlice<'a>> {
34         use LinkSlice::*;
35         match self {
36             Ethernet2(s) => Some(s.payload().clone()),
37             LinuxSll(s) => Some(EtherPayloadSlice::try_from(s.payload()).ok()?.clone()),
38             EtherPayload(p) => Some(p.clone()),
39             LinuxSllPayload(p) => Some(EtherPayloadSlice::try_from(p.clone()).ok()?),
40         }
41     }
42 
43     /// Returns the link layer sll payload (slice + link layer protocol type).
sll_payload(&self) -> LinuxSllPayloadSlice<'a>44     pub fn sll_payload(&self) -> LinuxSllPayloadSlice<'a> {
45         use LinkSlice::*;
46         match self {
47             Ethernet2(s) => LinuxSllPayloadSlice::from(s.payload().clone()),
48             LinuxSll(s) => s.payload().clone(),
49             EtherPayload(p) => LinuxSllPayloadSlice::from(p.clone()),
50             LinuxSllPayload(p) => p.clone(),
51         }
52     }
53 }
54 
55 #[cfg(test)]
56 mod test {
57     use super::*;
58     use crate::test_gens::*;
59     use alloc::{format, vec::Vec};
60     use proptest::prelude::*;
61 
62     proptest! {
63         #[test]
64         fn debug_clone_eq(ref eth in ethernet_2_unknown()) {
65             let bytes = eth.to_bytes();
66             let e = Ethernet2Slice::from_slice_without_fcs(&bytes).unwrap();
67             let slice = LinkSlice::Ethernet2(
68                 e.clone()
69             );
70 
71             // clone & eq
72             assert_eq!(slice.clone(), slice);
73 
74             // debug
75             assert_eq!(
76                 format!("{:?}", slice),
77                 format!("Ethernet2({:?})", e),
78             );
79         }
80     }
81 
82     proptest! {
83         #[test]
84         fn to_header(
85             ref eth in ethernet_2_unknown(),
86             ref linux_sll in linux_sll_any()
87         ) {
88             {
89                 let bytes = eth.to_bytes();
90                 let slice = LinkSlice::Ethernet2(
91                     Ethernet2Slice::from_slice_without_fcs(&bytes).unwrap()
92                 );
93                 assert_eq!(
94                     slice.to_header(),
95                     Some(LinkHeader::Ethernet2(eth.clone()))
96                 );
97             }
98             {
99                 let bytes = linux_sll.to_bytes();
100                 let slice = LinkSlice::LinuxSll(
101                     LinuxSllSlice::from_slice(&bytes).unwrap()
102                 );
103                 assert_eq!(
104                     slice.to_header(),
105                     Some(LinkHeader::LinuxSll(linux_sll.clone()))
106                 );
107             }
108             {
109                 let slice = LinkSlice::EtherPayload(EtherPayloadSlice {
110                     ether_type: ether_type::IPV4,
111                     payload: &[]
112                 });
113                 assert_eq!(
114                     slice.to_header(),
115                     None
116                 );
117             }
118             {
119                 let slice = LinkSlice::LinuxSllPayload(LinuxSllPayloadSlice {
120                     protocol_type: LinuxSllProtocolType::EtherType(ether_type::IPV4),
121                     payload: &[]
122                 });
123                 assert_eq!(
124                     slice.to_header(),
125                     None
126                 );
127             }
128         }
129     }
130 
131     proptest! {
132         #[test]
133         fn ether_payload(
134             ref eth in ethernet_2_unknown(),
135             ref linux_sll in linux_sll_any()
136         ) {
137             let p = [1,2,3,4];
138             {
139                 let mut bytes = Vec::with_capacity(Ethernet2Header::LEN + p.len());
140                 bytes.extend_from_slice(&eth.to_bytes());
141                 bytes.extend_from_slice(&p);
142                 let slice = LinkSlice::Ethernet2(
143                     Ethernet2Slice::from_slice_without_fcs(&bytes).unwrap()
144                 );
145                 assert_eq!(
146                     slice.ether_payload().unwrap(),
147                     EtherPayloadSlice{ ether_type: eth.ether_type, payload: &p }
148                 );
149             }
150             {
151                 let slice = LinkSlice::EtherPayload(EtherPayloadSlice {
152                     ether_type: eth.ether_type,
153                     payload: &p
154                 });
155                 assert_eq!(
156                     slice.ether_payload().unwrap(),
157                     EtherPayloadSlice{ ether_type: eth.ether_type, payload: &p }
158                 );
159             }
160             {
161                 let mut bytes = Vec::with_capacity(LinuxSllHeader::LEN + p.len());
162                 bytes.extend_from_slice(&linux_sll.to_bytes());
163                 bytes.extend_from_slice(&p);
164                 let slice = LinkSlice::LinuxSll(
165                     LinuxSllSlice::from_slice(&bytes).unwrap()
166                 );
167                 match linux_sll.protocol_type {
168                     LinuxSllProtocolType::EtherType(EtherType(v)) | LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType(v)) => { assert_eq!(
169                             slice.ether_payload().unwrap(),
170                             EtherPayloadSlice{ ether_type: EtherType(v), payload: &p }
171                     );}
172                     _ => { assert!(slice.ether_payload().is_none());}
173                 }
174             }
175             {
176                 let slice = LinkSlice::LinuxSllPayload(LinuxSllPayloadSlice {
177                     protocol_type: linux_sll.protocol_type,
178                     payload: &p
179                 });
180                 match linux_sll.protocol_type {
181                     LinuxSllProtocolType::EtherType(EtherType(v)) | LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType(v)) => { assert_eq!(
182                         slice.ether_payload().unwrap(),
183                             EtherPayloadSlice{ ether_type: EtherType(v), payload: &p }
184                     );}
185                     _ => { assert!(slice.ether_payload().is_none());}
186                 }
187             }
188         }
189     }
190 }
191