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

etherparse/link/
macsec_header.rs

1use crate::*;
2use arrayvec::ArrayVec;
3
4/// MACsec SecTag header (present at the start of a
5/// packet capsuled with MACsec).
6#[derive(Clone, Debug, Eq, PartialEq)]
7pub struct MacsecHeader {
8    /// Payload type (contains encryption, modification flag as
9    /// well as the next ether type if available)
10    pub ptype: MacsecPType,
11
12    /// End station identifier (TCI.ES flag).
13    pub endstation_id: bool,
14
15    /// Ethernet passive optical network broadcast flag.
16    pub scb: bool,
17
18    /// Association number (identifies SAs).
19    pub an: MacsecAn,
20
21    /// Short length with reserved bits.
22    pub short_len: MacsecShortLen,
23
24    /// Packet number.
25    pub packet_nr: u32,
26
27    /// Secure channel identifier.
28    pub sci: Option<u64>,
29}
30
31impl MacsecHeader {
32    /// Minimum length of an MacSec header in bytes/octets.
33    pub const MIN_LEN: usize = 6;
34
35    /// Maximum length of an MacSec header (including ether type of payload) in bytes/octets.
36    pub const MAX_LEN: usize = 16;
37
38    /// Encryption flag, which indicates whether the user data is
39    /// encrypted (true = encrypted, TCI.E flag).
40    #[inline]
41    pub fn encrypted(&self) -> bool {
42        use MacsecPType::*;
43        matches!(self.ptype, Encrypted | EncryptedUnmodified)
44    }
45
46    /// Flag for change text, set if the user data is modified.
47    pub fn userdata_changed(&self) -> bool {
48        use MacsecPType::*;
49        matches!(self.ptype, Encrypted | Modified)
50    }
51
52    /// Ether type of the data following the mac sec tag.
53    pub fn next_ether_type(&self) -> Option<EtherType> {
54        if let MacsecPType::Unmodified(re) = self.ptype {
55            Some(re)
56        } else {
57            None
58        }
59    }
60
61    /// Try creating a [`MacsecHeaderSlice`] from a slice containing the
62    /// MACsec header & next ether type.
63    pub fn from_slice(slice: &[u8]) -> Result<MacsecHeader, err::macsec::HeaderSliceError> {
64        MacsecHeaderSlice::from_slice(slice).map(|v| v.to_header())
65    }
66
67    /// Serialize the mac sec header.
68    pub fn to_bytes(&self) -> ArrayVec<u8, { MacsecHeader::MAX_LEN }> {
69        // tci-an is composed of:
70        //       ---------------------------------------
71        //       | v | es | sc | scp | e | c | an | an |
72        //       ---------------------------------------
73        // bits    8   7    6     5    4   3    2    1
74        //
75        // - version (0)
76        // - es (end station identifier bit)
77        // - sc (SCI present bit)
78        // - scp (Ethernet passive optical network broadcast bit)
79        // - e (encryption bit)
80        // - c (user data change bit)
81        // - an (Association number) [2 bits]
82        let tci_an = (self.an.value() & 0b11)
83            | if self.userdata_changed() { 0b100 } else { 0 }
84            | if self.encrypted() { 0b1000 } else { 0 }
85            | if self.scb { 0b1_0000 } else { 0 }
86            | if self.sci.is_some() { 0b10_0000 } else { 0 }
87            | if self.endstation_id { 0b100_0000 } else { 0 };
88        let pn_be = self.packet_nr.to_be_bytes();
89        let sci_be = self.sci.unwrap_or(0).to_be_bytes();
90        let et_be = if let MacsecPType::Unmodified(e) = self.ptype {
91            e.0
92        } else {
93            0
94        }
95        .to_be_bytes();
96        let mut result: ArrayVec<u8, { MacsecHeader::MAX_LEN }> = if self.sci.is_some() {
97            [
98                tci_an,
99                self.short_len.value() & 0b0011_1111,
100                pn_be[0],
101                pn_be[1],
102                pn_be[2],
103                pn_be[3],
104                sci_be[0],
105                sci_be[1],
106                sci_be[2],
107                sci_be[3],
108                sci_be[4],
109                sci_be[5],
110                sci_be[6],
111                sci_be[7],
112                et_be[0],
113                et_be[1],
114            ]
115        } else {
116            [
117                tci_an,
118                self.short_len.value() & 0b0011_1111,
119                pn_be[0],
120                pn_be[1],
121                pn_be[2],
122                pn_be[3],
123                et_be[0],
124                et_be[1],
125                0,
126                0,
127                0,
128                0,
129                0,
130                0,
131                0,
132                0,
133            ]
134        }
135        .into();
136        // SAFETY: Safe as the maximum size of 16 can not be exceeded.
137        unsafe {
138            result.set_len(
139                6 + if self.sci.is_some() { 8 } else { 0 }
140                    + if matches!(self.ptype, MacsecPType::Unmodified(_)) {
141                        2
142                    } else {
143                        0
144                    },
145            );
146        }
147        result
148    }
149
150    /// Try reading a MACsec header from the position of the reader.
151    #[cfg(feature = "std")]
152    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
153    pub fn read<T: std::io::Read + Sized>(
154        reader: &mut T,
155    ) -> Result<MacsecHeader, err::macsec::HeaderReadError> {
156        use err::macsec::HeaderError::*;
157        use err::macsec::HeaderReadError::*;
158
159        let mut bytes = [0; MacsecHeader::MAX_LEN];
160        reader.read_exact(&mut bytes[..6]).map_err(Io)?;
161
162        // check version bit
163        let tci_an = bytes[0];
164        if 0 != tci_an & 0b1000_0000 {
165            return Err(Content(UnexpectedVersion));
166        }
167
168        // validate short_len is not 1 in the unmodified case
169        let unmodified = 0 == tci_an & 0b1100;
170        if unmodified {
171            // SAFETY: Safe as the length was verified to be at least 6.
172            let short_len = bytes[1] & 0b0011_1111;
173            // short len must be zero (unknown) or at least 2 in case of an
174            // unmodified payload
175            if short_len == 1 {
176                return Err(Content(InvalidUnmodifiedShortLen));
177            }
178        }
179
180        // get the encrypted, changed flag (check if ether_type can be parsed)
181        let required_len =
182            6 + if unmodified { 2 } else { 0 } + if 0 != tci_an & 0b10_0000 { 8 } else { 0 };
183
184        if required_len > 6 {
185            reader.read_exact(&mut bytes[6..required_len]).map_err(Io)?;
186        }
187
188        Ok(MacsecHeaderSlice {
189            slice: &bytes[..required_len],
190        }
191        .to_header())
192    }
193
194    /// Writes a given MACsec header to the current position (SecTag & next
195    /// ether type if available).
196    #[cfg(feature = "std")]
197    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
198    pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
199        writer.write_all(&self.to_bytes())
200    }
201
202    /// Length of the MACsec header (SecTag + next ether type if available).
203    #[inline]
204    pub fn header_len(&self) -> usize {
205        6 + if self.sci.is_some() { 8 } else { 0 }
206            + if matches!(self.ptype, MacsecPType::Unmodified(_)) {
207                2
208            } else {
209                0
210            }
211    }
212
213    /// Returns the required length of the payload (data after header +
214    /// next_ether_type if present) if possible.
215    ///
216    /// If the length cannot be determined (`short_len` is zero or less then
217    /// `2` when `ptype` `Unmodified`) `None` is returned.
218    #[inline]
219    pub fn expected_payload_len(&self) -> Option<usize> {
220        let sl = self.short_len.value() as usize;
221        if sl > 0 {
222            if matches!(self.ptype, MacsecPType::Unmodified(_)) {
223                if sl < 2 {
224                    None
225                } else {
226                    Some(sl - 2)
227                }
228            } else {
229                // no ether type (encrypted and/or modified payload)
230                Some(sl)
231            }
232        } else {
233            None
234        }
235    }
236
237    /// Set the `short_len` field based on the given payload byte len
238    /// (payload len excluding the ether_type if `ptype` `Unmodified`)
239    /// based on the current `ptype`.
240    #[inline]
241    pub fn set_payload_len(&mut self, payload_len: usize) {
242        if matches!(self.ptype, MacsecPType::Unmodified(_)) {
243            if payload_len > MacsecShortLen::MAX_USIZE - 2 {
244                self.short_len = MacsecShortLen::ZERO;
245            } else {
246                // SAFETY: Safe as payload_len + 2 <= MacsecShortLen::MAX_USIZE
247                //         is guaranteed after the if above.
248                self.short_len =
249                    unsafe { MacsecShortLen::from_u8_unchecked(payload_len as u8 + 2) };
250            }
251        } else if payload_len > MacsecShortLen::MAX_USIZE {
252            self.short_len = MacsecShortLen::ZERO;
253        } else {
254            // SAFETY: Safe as payload_len + 2 <= MacsecShortLen::MAX_USIZE
255            //         is guaranteed after the if above.
256            self.short_len = unsafe { MacsecShortLen::from_u8_unchecked(payload_len as u8) };
257        }
258    }
259}
260
261#[cfg(test)]
262mod test {
263    use super::*;
264    use crate::test_gens::*;
265    use proptest::prelude::*;
266    use std::io::Cursor;
267
268    proptest! {
269        #[test]
270        fn from_slice_to_bytes(
271            header in macsec_any()
272        ) {
273            let mut header = header.clone();
274            if matches!(header.ptype, MacsecPType::Unmodified(_)) && header.short_len.value() == 1 {
275                header.short_len = MacsecShortLen::ZERO;
276            }
277            let bytes = header.to_bytes();
278            let actual = MacsecHeader::from_slice(&bytes);
279            assert_eq!(actual, Ok(header.clone()));
280        }
281    }
282
283    proptest! {
284        #[test]
285        fn getter(
286            macsec in macsec_any(),
287            ethertype in ether_type_any(),
288        ) {
289
290            let tests = [
291                // ptype, encrypted, userdata_changed, next_ether_type
292                (MacsecPType::Unmodified(ethertype), false, false, Some(ethertype)),
293                (MacsecPType::Modified, false, true, None),
294                (MacsecPType::Encrypted, true, true, None),
295                (MacsecPType::EncryptedUnmodified, true, false, None),
296            ];
297
298            for test in tests {
299                let mut macsec = macsec.clone();
300                macsec.ptype = test.0;
301
302                assert_eq!(test.1, macsec.encrypted());
303                assert_eq!(test.2, macsec.userdata_changed());
304                assert_eq!(test.3, macsec.next_ether_type());
305            }
306        }
307    }
308
309    proptest! {
310        #[test]
311        fn header_len(
312            macsec in macsec_any(),
313            ethertype in ether_type_any(),
314            sci in any::<u64>(),
315        ) {
316            // no ethertype
317            for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] {
318                // no sci
319                {
320                    let mut macsec = macsec.clone();
321                    macsec.ptype = ptype;
322                    macsec.sci = None;
323                    assert_eq!(6, macsec.header_len());
324                }
325                // with sci
326                {
327                    let mut macsec = macsec.clone();
328                    macsec.ptype = ptype;
329                    macsec.sci = Some(sci);
330                    assert_eq!(14, macsec.header_len());
331                }
332            }
333
334            // with ethertype
335            // no sci
336            {
337                let mut macsec = macsec.clone();
338                macsec.ptype = MacsecPType::Unmodified(ethertype);
339                macsec.sci = None;
340                assert_eq!(8, macsec.header_len());
341            }
342            // with sci
343            {
344                let mut macsec = macsec.clone();
345                macsec.ptype = MacsecPType::Unmodified(ethertype);
346                macsec.sci = Some(sci);
347                assert_eq!(16, macsec.header_len());
348            }
349        }
350    }
351
352    proptest! {
353        #[test]
354        fn read(
355            macsec in macsec_any(),
356            ether_type in ether_type_any(),
357            sci in any::<u64>()
358        ) {
359            use MacsecPType::*;
360            use err::macsec::*;
361
362            // variants
363            for ptype in [Unmodified(ether_type), Modified, Encrypted, EncryptedUnmodified] {
364                for has_sci in [false, true] {
365                    let mut macsec = macsec.clone();
366                    macsec.ptype = ptype;
367                    macsec.sci = if has_sci {
368                        Some(sci)
369                    } else {
370                        None
371                    };
372                    if matches!(ptype, MacsecPType::Unmodified(_)) && macsec.short_len.value() == 1 {
373                        macsec.short_len = MacsecShortLen::ZERO;
374                    }
375
376                    // ok case
377                    {
378                        let mut bytes = ArrayVec::<u8, { MacsecHeader::MAX_LEN + 1 }>::new();
379                        bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap();
380                        let mut cursor = Cursor::new(&bytes);
381                        let m = MacsecHeader::read(&mut cursor).unwrap();
382                        assert_eq!(m, macsec);
383                        assert_eq!(macsec.header_len() as u64, cursor.position());
384                    }
385
386                    // version error
387                    {
388                        let mut bytes = ArrayVec::<u8, { MacsecHeader::MAX_LEN + 1 }>::new();
389                        bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap();
390                        bytes.try_extend_from_slice(&[1]).unwrap();
391
392                        // version bit
393                        bytes[0] = bytes[0] | 0b1000_0000;
394
395                        let mut cursor = Cursor::new(&bytes);
396                        let m = MacsecHeader::read(&mut cursor).unwrap_err();
397                        assert_eq!(m.content_error(), Some(HeaderError::UnexpectedVersion));
398                    }
399
400                    // short len error
401                    if matches!(ptype, MacsecPType::Unmodified(_)) {
402                        let mut macsec = macsec.clone();
403                        macsec.short_len = MacsecShortLen::try_from_u8(1).unwrap();
404                        let mut bytes = ArrayVec::<u8, { MacsecHeader::MAX_LEN + 1 }>::new();
405                        bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap();
406
407                        let mut cursor = Cursor::new(&bytes);
408                        let m = MacsecHeader::read(&mut cursor).unwrap_err();
409                        assert_eq!(m.content_error(), Some(HeaderError::InvalidUnmodifiedShortLen));
410                    }
411
412                    // len error
413                    for len in 0..macsec.header_len() {
414                        let mut bytes = ArrayVec::<u8, { MacsecHeader::MAX_LEN + 1 }>::new();
415                        bytes.try_extend_from_slice(&macsec.to_bytes()).unwrap();
416
417                        let mut cursor = Cursor::new(&bytes[..len]);
418                        let m = MacsecHeader::read(&mut cursor);
419                        assert!(m.unwrap_err().io_error().is_some());
420                    }
421                }
422            }
423        }
424    }
425
426    proptest! {
427        #[test]
428        fn write(
429            header in macsec_any()
430        ) {
431            // ok case
432            {
433                let mut buffer = ArrayVec::<u8, {MacsecHeader::MAX_LEN}>::new();
434                header.write(&mut buffer).unwrap();
435                assert_eq!(&buffer, &header.to_bytes());
436            }
437            // not enough memory
438            {
439                let mut buffer = [0u8;MacsecHeader::MAX_LEN];
440                let mut cursor = Cursor::new(&mut buffer[..header.header_len() - 1]);
441                header.write(&mut cursor).unwrap_err();
442            }
443        }
444    }
445
446    proptest! {
447        #[test]
448        fn expected_payload_len(
449            header in macsec_any(),
450            ether_type in ether_type_any(),
451            valid_unmodified_len in 2u8..=MacsecShortLen::MAX_U8,
452            valid_modified_len in 1u8..=MacsecShortLen::MAX_U8
453        ) {
454            // unmodified, payload len (non zero or one)
455            {
456                let mut header = header.clone();
457                header.ptype = MacsecPType::Unmodified(ether_type);
458                header.short_len = MacsecShortLen::try_from_u8(valid_unmodified_len).unwrap();
459                assert_eq!(Some(valid_unmodified_len as usize - 2), header.expected_payload_len());
460            }
461
462            // unmodified, unknown len
463            for short_len in 0..2u8 {
464                let mut header = header.clone();
465                header.ptype = MacsecPType::Unmodified(ether_type);
466                header.short_len = MacsecShortLen::try_from_u8(short_len).unwrap();
467                assert_eq!(None, header.expected_payload_len());
468            }
469
470            // modified, valid payload len (non zero)
471            for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] {
472                let mut header = header.clone();
473                header.ptype = ptype;
474                header.short_len = MacsecShortLen::try_from_u8(valid_modified_len).unwrap();
475                assert_eq!(Some(valid_modified_len as usize), header.expected_payload_len());
476            }
477
478            // modified, unknown len
479            for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] {
480                let mut header = header.clone();
481                header.ptype = ptype;
482                header.short_len = MacsecShortLen::ZERO;
483                assert_eq!(None, header.expected_payload_len());
484            }
485        }
486    }
487
488    proptest! {
489        #[test]
490        fn set_payload_len(
491            header in macsec_any(),
492            ether_type in ether_type_any(),
493            valid_unmodified_len in 0..=(MacsecShortLen::MAX_USIZE - 2),
494            invalid_unmodified_len in (MacsecShortLen::MAX_USIZE - 1)..=usize::MAX,
495            valid_modified_len in 1..=MacsecShortLen::MAX_USIZE,
496            invalid_modified_len in (MacsecShortLen::MAX_USIZE + 1)..=usize::MAX
497        ) {
498            // unmodified, payload len (non zero or one)
499            {
500                let mut header = header.clone();
501                header.ptype = MacsecPType::Unmodified(ether_type);
502                header.set_payload_len(valid_unmodified_len);
503                assert_eq!(header.short_len.value() as usize, valid_unmodified_len + 2);
504            }
505
506            // unmodified, invalid len
507            {
508                let mut header = header.clone();
509                header.ptype = MacsecPType::Unmodified(ether_type);
510                header.set_payload_len(invalid_unmodified_len);
511                assert_eq!(0, header.short_len.value());
512            }
513
514            // modified, valid payload len (non zero)
515            for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] {
516                let mut header = header.clone();
517                header.ptype = ptype;
518                header.set_payload_len(valid_modified_len);
519                assert_eq!(valid_modified_len, header.short_len.value() as usize);
520            }
521
522            // modified, unknown len
523            for ptype in [MacsecPType::Modified, MacsecPType::Encrypted, MacsecPType::EncryptedUnmodified] {
524                let mut header = header.clone();
525                header.ptype = ptype;
526                header.set_payload_len(invalid_modified_len);
527                assert_eq!(0, header.short_len.value());
528            }
529        }
530    }
531}