Thanks to visit codestin.com
Credit goes to docs.rs

etherparse/link/
macsec_header_slice.rs

1use crate::{
2    err::{Layer, LenError},
3    *,
4};
5
6/// Slice containing a MACsec header & next ether type (if possible).
7#[derive(Copy, Clone, Debug, Eq, PartialEq)]
8pub struct MacsecHeaderSlice<'a> {
9    pub(crate) slice: &'a [u8],
10}
11
12impl<'a> MacsecHeaderSlice<'a> {
13    /// Try creating a [`MacsecHeaderSlice`] from a slice containing the
14    /// MACsec header & next ether type.
15    pub fn from_slice(
16        slice: &'a [u8],
17    ) -> Result<MacsecHeaderSlice<'a>, err::macsec::HeaderSliceError> {
18        use err::macsec::{HeaderError::*, HeaderSliceError::*};
19
20        if slice.len() < 6 {
21            return Err(Len(LenError {
22                required_len: 6,
23                len: slice.len(),
24                len_source: LenSource::Slice,
25                layer: Layer::MacsecHeader,
26                layer_start_offset: 0,
27            }));
28        }
29
30        // SAFETY: Safe as the length was verified to be at least 6.
31        let tci_an = unsafe { slice.get_unchecked(0) };
32
33        // validate version
34        if 0 != tci_an & 0b1000_0000 {
35            return Err(Content(UnexpectedVersion));
36        }
37
38        // validate short_len is not 1 in the unmodified case
39        let unmodified = 0 == tci_an & 0b1100;
40        if unmodified {
41            // SAFETY: Safe as the length was verified to be at least 6.
42            let short_len = unsafe { slice.get_unchecked(1) & 0b0011_1111 };
43            // short len must be zero (unknown) or at least 2 in unmod
44            if short_len == 1 {
45                return Err(Content(InvalidUnmodifiedShortLen));
46            }
47        }
48
49        // get the encrypted, changed flag (check if ether_type can be parsed)
50        let required_len =
51            6 + if unmodified { 2 } else { 0 } + if 0 != tci_an & 0b10_0000 { 8 } else { 0 };
52
53        if slice.len() < required_len {
54            return Err(Len(LenError {
55                required_len,
56                len: slice.len(),
57                len_source: LenSource::Slice,
58                layer: Layer::MacsecHeader,
59                layer_start_offset: 0,
60            }));
61        }
62
63        Ok(MacsecHeaderSlice {
64            // SAFETY: Safe as the length was previously verified to be at least required_len.
65            slice: unsafe { core::slice::from_raw_parts(slice.as_ptr(), required_len) },
66        })
67    }
68
69    /// Slice containing the header & ether type of the next segment
70    /// if available.
71    #[inline]
72    pub fn slice(&self) -> &'a [u8] {
73        self.slice
74    }
75
76    /// Raw first byte of the mac sec header (containing TCI & AN).
77    #[inline]
78    pub fn tci_an_raw(&self) -> u8 {
79        // SAFETY: Slice access safe as length of the slice was
80        //         verified in the constructor to be at least 6.
81        unsafe { *self.slice.get_unchecked(0) }
82    }
83
84    /// End station identifier (TCI.ES flag).
85    #[inline]
86    pub fn endstation_id(&self) -> bool {
87        0 != (self.tci_an_raw() & 0b100_0000)
88    }
89
90    /// Ethernet passive optical network broadcast flag.
91    #[inline]
92    pub fn tci_scb(&self) -> bool {
93        0 != (self.tci_an_raw() & 0b1_0000)
94    }
95
96    /// Encryption flag, which indicates whether the user data is
97    /// encrypted (true = encrypted, TCI.E flag).
98    #[inline]
99    pub fn encrypted(&self) -> bool {
100        // SAFETY: Slice access safe as length of the slice was
101        //         verified in the constructor to be at least 6.
102        0 != (self.tci_an_raw() & 0b1000)
103    }
104
105    /// Flag for change text, set if the user data is modified.
106    #[inline]
107    pub fn userdata_changed(&self) -> bool {
108        // SAFETY: Slice access safe as length of the slice was
109        //         verified in the constructor to be at least 6.
110        0 != (self.tci_an_raw() & 0b100)
111    }
112
113    /// True if the payload was neither flagged as modified or encrypted.
114    #[inline]
115    pub fn is_unmodified(&self) -> bool {
116        // SAFETY: Slice access safe as length of the slice was
117        //         verified in the constructor to be at least 6.
118        0 == (self.tci_an_raw() & 0b1100)
119    }
120
121    /// Payload type (contains encryption, modification flag as
122    /// well as the next ether type if available)
123    #[inline]
124    pub fn ptype(&self) -> MacsecPType {
125        let e = self.encrypted();
126        let c = self.userdata_changed();
127        if e {
128            if c {
129                MacsecPType::Encrypted
130            } else {
131                MacsecPType::EncryptedUnmodified
132            }
133        } else if c {
134            MacsecPType::Modified
135        } else if 0 != (self.tci_an_raw() & 0b10_0000) {
136            // SAFETY: Slice access safe as length of the slice was
137            //         verified in the constructor to be at least 16
138            //         if 0b10_0000 is set and and 'c' and 'e' are not
139            //         set in the tci_an_raw.
140            MacsecPType::Unmodified(EtherType(u16::from_be_bytes(unsafe {
141                [*self.slice.get_unchecked(14), *self.slice.get_unchecked(15)]
142            })))
143        } else {
144            // SAFETY: Slice access safe as length of the slice was
145            //         verified in the constructor to be at least 8
146            //         if 0b10_0000 is not set and 'c' and 'e' are not
147            //         set in the tci_an_raw.
148            MacsecPType::Unmodified(EtherType(u16::from_be_bytes(unsafe {
149                [*self.slice.get_unchecked(6), *self.slice.get_unchecked(7)]
150            })))
151        }
152    }
153
154    /// Association number (identifies SAs).
155    #[inline]
156    pub fn an(&self) -> MacsecAn {
157        // SAFETY: MacSecAn conversion safe as bit-masked to only
158        //         contain 2 bits.
159        unsafe { MacsecAn::new_unchecked(self.tci_an_raw() & 0b11) }
160    }
161
162    /// Short length with reserved bits.
163    #[inline]
164    pub fn short_len(&self) -> MacsecShortLen {
165        // SAFETY: Slice access safe as length of the slice was
166        //         verified in the constructor to be at least 6.
167        //         MacsecSl conversion safe as bit-masked to contain
168        //         only 6 bits.
169        unsafe { MacsecShortLen::from_u8_unchecked(self.slice.get_unchecked(1) & 0b0011_1111) }
170    }
171
172    /// Packet number.
173    #[inline]
174    pub fn packet_nr(&self) -> u32 {
175        // SAFETY: Slice access safe as length of the slice was
176        //         verified in the constructor to be at least 6.
177        //         MacsecSl conversion safe as bit-masked.
178        u32::from_be_bytes(unsafe {
179            [
180                *self.slice.get_unchecked(2),
181                *self.slice.get_unchecked(3),
182                *self.slice.get_unchecked(4),
183                *self.slice.get_unchecked(5),
184            ]
185        })
186    }
187
188    /// True if the SCI bit is set in the TCI part of the SecTag header.
189    #[inline]
190    pub fn sci_present(&self) -> bool {
191        0 != (self.tci_an_raw() & 0b10_0000)
192    }
193
194    /// Secure channel identifier.
195    #[inline]
196    pub fn sci(&self) -> Option<u64> {
197        if self.sci_present() {
198            // SAFETY: Slice access safe as length of the slice was
199            //         verified in the constructor to be at least 14
200            //         if 0b10_0000 is set in the tci_an_raw.
201            Some(u64::from_be_bytes(unsafe {
202                [
203                    *self.slice.get_unchecked(6),
204                    *self.slice.get_unchecked(7),
205                    *self.slice.get_unchecked(8),
206                    *self.slice.get_unchecked(9),
207                    *self.slice.get_unchecked(10),
208                    *self.slice.get_unchecked(11),
209                    *self.slice.get_unchecked(12),
210                    *self.slice.get_unchecked(13),
211                ]
212            }))
213        } else {
214            None
215        }
216    }
217
218    /// Ether type of the data following the sec tag (only
219    /// available if not encrypted and userdata is not flagged
220    /// as modified).
221    #[inline]
222    pub fn next_ether_type(&self) -> Option<EtherType> {
223        if 0 != self.tci_an_raw() & 0b1100 {
224            None
225        } else if self.sci_present() {
226            // SAFETY: Slice access safe as length of the slice was
227            //         verified in the constructor to be at least 16
228            //         if 0b10_0000 is set and 0b1100 is not set in
229            //         the tci_an_raw.
230            Some(EtherType(u16::from_be_bytes(unsafe {
231                [*self.slice.get_unchecked(14), *self.slice.get_unchecked(15)]
232            })))
233        } else {
234            // SAFETY: Slice access safe as length of the slice was
235            //         verified in the constructor to be at least 8
236            //         if 0b10_0000 is not set and 0b1100 is not set in
237            //         the tci_an_raw.
238            Some(EtherType(u16::from_be_bytes(unsafe {
239                [*self.slice.get_unchecked(6), *self.slice.get_unchecked(7)]
240            })))
241        }
242    }
243
244    /// Length of the MACsec header (SecTag + next ether type if available).
245    #[inline]
246    pub fn header_len(&self) -> usize {
247        6 + if self.sci_present() { 8 } else { 0 } + if self.is_unmodified() { 2 } else { 0 }
248    }
249
250    /// Returns the required length of the payload (data after header +
251    /// next_ether_type if present) if possible.
252    ///
253    /// If the length cannot be determined (`short_len` is zero or less then
254    /// `2` when `ptype` `Unmodified`) `None` is returned.
255    #[inline]
256    pub fn expected_payload_len(&self) -> Option<usize> {
257        let sl = self.short_len().value() as usize;
258        if sl > 0 {
259            if 0 != self.tci_an_raw() & 0b1100 {
260                // no ether type (encrypted and/or modified payload)
261                Some(sl)
262            } else if sl < 2 {
263                None
264            } else {
265                Some(sl - 2)
266            }
267        } else {
268            None
269        }
270    }
271
272    /// Decodes all MacSecHeader values and returns them as a
273    /// [`crate::MacsecHeader`].
274    #[inline]
275    pub fn to_header(&self) -> MacsecHeader {
276        MacsecHeader {
277            ptype: self.ptype(),
278            endstation_id: self.endstation_id(),
279            scb: self.tci_scb(),
280            an: self.an(),
281            short_len: self.short_len(),
282            packet_nr: self.packet_nr(),
283            sci: self.sci(),
284        }
285    }
286}
287
288#[cfg(test)]
289mod test {
290    use super::*;
291    use crate::test_gens::*;
292    use arrayvec::ArrayVec;
293    use proptest::prelude::*;
294
295    proptest! {
296        #[test]
297        fn from_slice(
298            macsec in macsec_any(),
299            ether_type in ether_type_any(),
300            sci in any::<u64>()
301        ) {
302            use MacsecPType::*;
303            use err::macsec::*;
304
305            // variants
306            for ptype in [Unmodified(ether_type), Modified, Encrypted, EncryptedUnmodified] {
307                for has_sci in [false, true] {
308                    let mut macsec = macsec.clone();
309                    macsec.ptype = ptype;
310                    macsec.sci = if has_sci {
311                        Some(sci)
312                    } else {
313                        None
314                    };
315                    if matches!(ptype, MacsecPType::Unmodified(_)) && macsec.short_len.value() == 1 {
316                        macsec.short_len = MacsecShortLen::ZERO;
317                    }
318
319                    // ok case
320                    {
321                        let mut bytes = ArrayVec::<u8, { MacsecHeader::MAX_LEN + 1 }>::new();
322                        bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap();
323                        bytes.try_extend_from_slice(&[1]).unwrap();
324                        let m = MacsecHeaderSlice::from_slice(&bytes).unwrap();
325                        assert_eq!(m.to_header(), macsec);
326                        assert_eq!(m.slice(), &bytes[..bytes.len() - 1]);
327                    }
328
329                    // version error
330                    {
331                        let mut bytes = ArrayVec::<u8, { MacsecHeader::MAX_LEN + 1 }>::new();
332                        bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap();
333                        bytes.try_extend_from_slice(&[1]).unwrap();
334
335                        // version bit
336                        bytes[0] = bytes[0] | 0b1000_0000;
337
338                        let m = MacsecHeaderSlice::from_slice(&bytes);
339                        assert_eq!(m, Err(HeaderSliceError::Content(HeaderError::UnexpectedVersion)));
340                    }
341
342                    // short len error
343                    if matches!(ptype, MacsecPType::Unmodified(_)) {
344                        let mut macsec = macsec.clone();
345                        macsec.short_len = MacsecShortLen::try_from_u8(1).unwrap();
346                        let mut bytes = ArrayVec::<u8, { MacsecHeader::MAX_LEN + 1 }>::new();
347                        bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap();
348                        bytes.try_extend_from_slice(&[1]).unwrap();
349
350                        let m = MacsecHeaderSlice::from_slice(&bytes);
351                        assert_eq!(m, Err(HeaderSliceError::Content(HeaderError::InvalidUnmodifiedShortLen)));
352                    }
353
354                    // len error
355                    for len in 0..macsec.header_len() {
356                        let mut bytes = ArrayVec::<u8, { MacsecHeader::MAX_LEN + 1 }>::new();
357                        bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap();
358                        bytes.try_extend_from_slice(&[1]).unwrap();
359
360                        let m = MacsecHeaderSlice::from_slice(&bytes[..len]);
361                        assert_eq!(
362                            m,
363                            Err(HeaderSliceError::Len(err::LenError{
364                                required_len: if len < 6 {
365                                    6
366                                } else {
367                                    macsec.header_len()
368                                },
369                                len,
370                                len_source: LenSource::Slice,
371                                layer: Layer::MacsecHeader,
372                                layer_start_offset: 0,
373                            }))
374                        );
375                    }
376                }
377            }
378        }
379    }
380
381    proptest! {
382        #[test]
383        fn expected_payload_len(
384            header in macsec_any(),
385            ether_type in ether_type_any(),
386            valid_unmodified_len in 2u8..=MacsecShortLen::MAX_U8,
387            valid_modified_len in 1u8..=MacsecShortLen::MAX_U8
388        ) {
389            // unmodified, payload len (non zero or one)
390            {
391                let mut header = header.clone();
392                header.ptype = MacsecPType::Unmodified(ether_type);
393                header.short_len = MacsecShortLen::try_from_u8(valid_unmodified_len).unwrap();
394                let bytes = header.to_bytes();
395                let slice = MacsecHeaderSlice::from_slice(&bytes).unwrap();
396                assert_eq!(Some(valid_unmodified_len as usize - 2), slice.expected_payload_len());
397            }
398
399            // unmodified, unknown len
400            for short_len in 0..2u8 {
401                let mut header = header.clone();
402                header.ptype = MacsecPType::Unmodified(ether_type);
403                header.short_len = MacsecShortLen::try_from_u8(short_len).unwrap();
404                let bytes = header.to_bytes();
405                let slice = MacsecHeaderSlice{ slice: &bytes };
406                assert_eq!(None, slice.expected_payload_len());
407            }
408
409            // modified, valid payload len (non zero)
410            for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] {
411                let mut header = header.clone();
412                header.ptype = ptype;
413                header.short_len = MacsecShortLen::try_from_u8(valid_modified_len).unwrap();
414                let bytes = header.to_bytes();
415                let slice = MacsecHeaderSlice::from_slice(&bytes).unwrap();
416                assert_eq!(Some(valid_modified_len as usize), slice.expected_payload_len());
417            }
418
419            // modified, unknown len
420            for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] {
421                let mut header = header.clone();
422                header.ptype = ptype;
423                header.short_len = MacsecShortLen::ZERO;
424                let bytes = header.to_bytes();
425                let slice = MacsecHeaderSlice::from_slice(&bytes).unwrap();
426                assert_eq!(None, slice.expected_payload_len());
427            }
428        }
429    }
430}