Skip to main content

packet_formats/
ethernet.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Parsing and serialization of Ethernet frames.
6
7use net_types::ethernet::Mac;
8use net_types::ip::{Ip, IpVersion, Ipv4, Ipv6};
9use packet::{
10    BufferView, BufferViewMut, FragmentedBytesMut, NestablePacketBuilder, NoOpSerializationContext,
11    PacketBuilder, PacketConstraints, ParsablePacket, ParseMetadata, SerializationContext,
12    SerializeTarget,
13};
14use zerocopy::byteorder::network_endian::{U16, U32};
15use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, Unaligned};
16
17use crate::error::{ParseError, ParseResult};
18
19const ETHERNET_MIN_ILLEGAL_ETHERTYPE: u16 = 1501;
20const ETHERNET_MAX_ILLEGAL_ETHERTYPE: u16 = 1535;
21
22create_protocol_enum!(
23    /// An EtherType number.
24    #[allow(missing_docs)]
25    #[derive(Copy, Clone, Hash, Eq, PartialEq)]
26    pub enum EtherType: u16 {
27        Ipv4, 0x0800, "IPv4";
28        Arp, 0x0806, "ARP";
29        Ipv6, 0x86DD, "IPv6";
30        _, "EtherType {}";
31    }
32);
33
34impl EtherType {
35    /// Constructs the relevant [`EtherType`] from the given [`IpVersion`].
36    pub fn from_ip_version(ip_version: IpVersion) -> Self {
37        match ip_version {
38            IpVersion::V4 => EtherType::Ipv4,
39            IpVersion::V6 => EtherType::Ipv6,
40        }
41    }
42
43    /// Constructs the relevant [`IpVersion`] from the given [`EtherType`].
44    pub fn to_ip_version(self) -> Option<IpVersion> {
45        match self {
46            EtherType::Ipv4 => Some(IpVersion::V4),
47            EtherType::Ipv6 => Some(IpVersion::V6),
48            _ => None,
49        }
50    }
51}
52
53/// An extension trait adding IP-related functionality to `Ipv4` and `Ipv6`.
54pub trait EthernetIpExt: Ip {
55    /// The `EtherType` value for an associated IP version.
56    const ETHER_TYPE: EtherType;
57}
58
59impl EthernetIpExt for Ipv4 {
60    const ETHER_TYPE: EtherType = EtherType::Ipv4;
61}
62
63impl EthernetIpExt for Ipv6 {
64    const ETHER_TYPE: EtherType = EtherType::Ipv6;
65}
66
67#[derive(KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
68#[repr(C)]
69struct HeaderPrefix {
70    dst_mac: Mac,
71    src_mac: Mac,
72}
73
74const TPID_8021Q: u16 = 0x8100;
75const TPID_8021AD: u16 = 0x88a8;
76
77/// An Ethernet frame.
78///
79/// An `EthernetFrame` shares its underlying memory with the byte slice it was
80/// parsed from or serialized to, meaning that no copying or extra allocation is
81/// necessary.
82pub struct EthernetFrame<B> {
83    hdr_prefix: Ref<B, HeaderPrefix>,
84    tag: Option<Ref<B, U32>>,
85    ethertype: Ref<B, U16>,
86    body: B,
87}
88
89/// Whether or not an Ethernet frame's length should be checked during parsing.
90///
91/// When the `Check` variant is used, the Ethernet frame will be rejected if its
92/// total length (including header, but excluding the Frame Check Sequence (FCS)
93/// footer) is less than the required minimum of 60 bytes.
94#[derive(PartialEq)]
95pub enum EthernetFrameLengthCheck {
96    /// Check that the Ethernet frame's total length (including header, but
97    /// excluding the Frame Check Sequence (FCS) footer) satisfies the required
98    /// minimum of 60 bytes.
99    Check,
100    /// Do not check the Ethernet frame's total length. The frame will still be
101    /// rejected if a complete, valid header is not present, but the body may be
102    /// 0 bytes long.
103    NoCheck,
104}
105
106impl<B: SplitByteSlice> ParsablePacket<B, EthernetFrameLengthCheck> for EthernetFrame<B> {
107    type Error = ParseError;
108
109    fn parse_metadata(&self) -> ParseMetadata {
110        let header_len = Ref::bytes(&self.hdr_prefix).len()
111            + self.tag.as_ref().map(|tag| Ref::bytes(tag).len()).unwrap_or(0)
112            + Ref::bytes(&self.ethertype).len();
113        ParseMetadata::from_packet(header_len, self.body.len(), 0)
114    }
115
116    fn parse<BV: BufferView<B>>(
117        mut buffer: BV,
118        length_check: EthernetFrameLengthCheck,
119    ) -> ParseResult<Self> {
120        // See for details: https://en.wikipedia.org/wiki/Ethernet_frame#Frame_%E2%80%93_data_link_layer
121
122        let hdr_prefix = buffer
123            .take_obj_front::<HeaderPrefix>()
124            .ok_or_else(debug_err_fn!(ParseError::Format, "too few bytes for header"))?;
125        if length_check == EthernetFrameLengthCheck::Check && buffer.len() < 48 {
126            // The minimum frame size (not including the Frame Check Sequence
127            // (FCS) footer, which we do not handle in this code) is 60 bytes.
128            // We've already consumed 12 bytes for the header prefix, so we must
129            // have at least 48 bytes left.
130            return debug_err!(Err(ParseError::Format), "too few bytes for frame");
131        }
132
133        // The tag (either IEEE 802.1Q or 802.1ad) is an optional four-byte
134        // field. If present, it precedes the ethertype, and its first two bytes
135        // (where the ethertype bytes are normally) are called the Tag Protocol
136        // Identifier (TPID). A TPID of TPID_8021Q implies an 802.1Q tag, a TPID
137        // of TPID_8021AD implies an 802.1ad tag, and anything else implies that
138        // there is no tag - it's a normal ethertype field.
139        let ethertype_or_tpid = buffer
140            .peek_obj_front::<U16>()
141            .ok_or_else(debug_err_fn!(ParseError::Format, "too few bytes for header"))?
142            .get();
143        let (tag, ethertype, body) = match ethertype_or_tpid {
144            self::TPID_8021Q | self::TPID_8021AD => (
145                Some(
146                    buffer.take_obj_front().ok_or_else(debug_err_fn!(
147                        ParseError::Format,
148                        "too few bytes for header"
149                    ))?,
150                ),
151                buffer
152                    .take_obj_front()
153                    .ok_or_else(debug_err_fn!(ParseError::Format, "too few bytes for header"))?,
154                buffer.into_rest(),
155            ),
156            _ => (
157                None,
158                buffer
159                    .take_obj_front()
160                    .ok_or_else(debug_err_fn!(ParseError::Format, "too few bytes for header"))?,
161                buffer.into_rest(),
162            ),
163        };
164
165        let frame = EthernetFrame { hdr_prefix, tag, ethertype, body };
166        let et = frame.ethertype.get();
167        if (ETHERNET_MIN_ILLEGAL_ETHERTYPE..=ETHERNET_MAX_ILLEGAL_ETHERTYPE).contains(&et)
168            || (et < ETHERNET_MIN_ILLEGAL_ETHERTYPE && et as usize != frame.body.len())
169        {
170            // EtherType values between 1500 and 1536 are disallowed, and values
171            // of 1500 and below are used to indicate the body length.
172            return debug_err!(Err(ParseError::Format), "invalid ethertype number: {:x}", et);
173        }
174        Ok(frame)
175    }
176}
177
178impl<B: SplitByteSlice> EthernetFrame<B> {
179    /// The frame body.
180    pub fn body(&self) -> &[u8] {
181        &self.body
182    }
183
184    /// Consumes the frame and returns the body.
185    pub fn into_body(self) -> B
186    where
187        B: Copy,
188    {
189        self.body
190    }
191
192    /// The source MAC address.
193    pub fn src_mac(&self) -> Mac {
194        self.hdr_prefix.src_mac
195    }
196
197    /// The destination MAC address.
198    pub fn dst_mac(&self) -> Mac {
199        self.hdr_prefix.dst_mac
200    }
201
202    /// The IEEE 802.1Q tag, if present.
203    pub fn tag(&self) -> Option<u32> {
204        self.tag.as_ref().map(|t| t.get())
205    }
206
207    /// The EtherType.
208    ///
209    /// `ethertype` returns the `EtherType` from the Ethernet header. However,
210    /// some values of the EtherType header field are used to indicate the
211    /// length of the frame's body. In this case, `ethertype` returns `None`.
212    pub fn ethertype(&self) -> Option<EtherType> {
213        let et = self.ethertype.get();
214        if et < ETHERNET_MIN_ILLEGAL_ETHERTYPE {
215            return None;
216        }
217        // values in (1500, 1536) are illegal, and shouldn't make it through
218        // parse
219        debug_assert!(et > ETHERNET_MAX_ILLEGAL_ETHERTYPE);
220        Some(EtherType::from(et))
221    }
222
223    // The size of the frame header.
224    fn header_len(&self) -> usize {
225        Ref::bytes(&self.hdr_prefix).len()
226            + self.tag.as_ref().map(|t| Ref::bytes(t).len()).unwrap_or(0)
227            + Ref::bytes(&self.ethertype).len()
228    }
229
230    // Total frame length including header prefix, tag, EtherType, and body.
231    // This is not the same as the length as optionally encoded in the
232    // EtherType.
233    // TODO(rheacock): remove `allow(dead_code)` when this is used.
234    #[allow(dead_code)]
235    fn total_frame_len(&self) -> usize {
236        self.header_len() + self.body.len()
237    }
238
239    /// Construct a builder with the same contents as this frame.
240    pub fn builder(&self) -> EthernetFrameBuilder {
241        EthernetFrameBuilder {
242            src_mac: self.src_mac(),
243            dst_mac: self.dst_mac(),
244            ethertype: self.ethertype.get(),
245            min_body_len: ETHERNET_MIN_BODY_LEN_NO_TAG,
246        }
247    }
248}
249
250/// A builder for Ethernet frames.
251///
252/// A [`PacketBuilder`] that serializes into an Ethernet frame. The padding
253/// parameter `P` can be used to choose how the body of the frame is padded.
254#[derive(Debug, Clone)]
255pub struct EthernetFrameBuilder {
256    src_mac: Mac,
257    dst_mac: Mac,
258    ethertype: u16,
259    min_body_len: usize,
260}
261
262impl EthernetFrameBuilder {
263    /// Construct a new `EthernetFrameBuilder`.
264    ///
265    /// The provided source and destination [`Mac`] addresses and [`EtherType`]
266    /// will be placed in the Ethernet frame header. The `min_body_len`
267    /// parameter sets the minimum length of the frame's body in bytes. If,
268    /// during serialization, the inner packet builder produces a smaller body
269    /// than `min_body_len`, it will be padded with trailing zero bytes up to
270    /// `min_body_len`.
271    pub fn new(
272        src_mac: Mac,
273        dst_mac: Mac,
274        ethertype: EtherType,
275        min_body_len: usize,
276    ) -> EthernetFrameBuilder {
277        EthernetFrameBuilder { src_mac, dst_mac, ethertype: ethertype.into(), min_body_len }
278    }
279
280    /// Returns the source MAC address for the builder.
281    pub fn src_mac(&self) -> Mac {
282        self.src_mac
283    }
284
285    /// Returns the destination MAC address for the builder.
286    pub fn dst_mac(&self) -> Mac {
287        self.dst_mac
288    }
289}
290
291// NOTE(joshlf): header_len and min_body_len assume no 802.1Q or 802.1ad tag. We
292// don't support creating packets with these tags at the moment, so this is a
293// sound assumption. If we support them in the future, we will need to update
294// these to compute dynamically.
295
296/// Ethernet frame context relevant to serialization.
297pub struct EthernetEnvelope;
298
299/// A trait for Ethernet serialization contexts.
300pub trait EthernetSerializationContext: SerializationContext {
301    /// Converts an `EthernetEnvelope` into the serialization context's state.
302    fn envelope_to_state(envelope: EthernetEnvelope) -> Self::ContextState;
303}
304
305impl EthernetSerializationContext for NoOpSerializationContext {
306    fn envelope_to_state(_envelope: EthernetEnvelope) -> Self::ContextState {
307        ()
308    }
309}
310
311impl NestablePacketBuilder for EthernetFrameBuilder {
312    fn constraints(&self) -> PacketConstraints {
313        PacketConstraints::new(ETHERNET_HDR_LEN_NO_TAG, 0, self.min_body_len, usize::MAX)
314    }
315}
316
317impl<C: EthernetSerializationContext> PacketBuilder<C> for EthernetFrameBuilder {
318    fn context_state(&self) -> C::ContextState {
319        C::envelope_to_state(EthernetEnvelope)
320    }
321
322    fn serialize(
323        &self,
324        _context: &mut C,
325        target: &mut SerializeTarget<'_>,
326        body: FragmentedBytesMut<'_, '_>,
327    ) {
328        // NOTE: EtherType values of 1500 and below are used to indicate the
329        // length of the body in bytes. We don't need to validate this because
330        // the EtherType enum has no variants with values in that range.
331
332        let total_len = target.header.len() + body.len();
333        // implements BufferViewMut, giving us take_obj_xxx_zero methods
334        let mut header = &mut target.header;
335
336        header
337            .write_obj_front(&HeaderPrefix { src_mac: self.src_mac, dst_mac: self.dst_mac })
338            .expect("too few bytes for Ethernet header");
339        header
340            .write_obj_front(&U16::new(self.ethertype))
341            .expect("too few bytes for Ethernet header");
342
343        // NOTE(joshlf): This doesn't include the tag. If we ever add support
344        // for serializing tags, we will need to update this.
345        let min_frame_size = self.min_body_len + ETHERNET_HDR_LEN_NO_TAG;
346
347        // Assert this here so that if there isn't enough space for even an
348        // Ethernet header, we report that more specific error.
349        assert!(
350            total_len >= min_frame_size,
351            "total frame size of {} bytes is below minimum frame size of {}",
352            total_len,
353            min_frame_size,
354        );
355    }
356}
357
358/// The length of an Ethernet header when it has no tags.
359pub const ETHERNET_HDR_LEN_NO_TAG: usize = 14;
360
361/// The minimum length of an Ethernet frame's body when the header contains no tags.
362pub const ETHERNET_MIN_BODY_LEN_NO_TAG: usize = 46;
363
364/// Constants useful for testing.
365pub mod testutil {
366    pub use super::{ETHERNET_HDR_LEN_NO_TAG, ETHERNET_MIN_BODY_LEN_NO_TAG};
367
368    /// Ethernet frame, in bytes.
369    pub const ETHERNET_DST_MAC_BYTE_OFFSET: usize = 0;
370
371    /// The offset to the start of the source MAC address from the start of the
372    /// Ethernet frame, in bytes.
373    pub const ETHERNET_SRC_MAC_BYTE_OFFSET: usize = 6;
374}
375
376#[cfg(test)]
377mod tests {
378    use byteorder::{ByteOrder, NetworkEndian};
379    use packet::{
380        AsFragmentedByteSlice, Buf, GrowBufferMut, InnerPacketBuilder, NoOpSerializationContext,
381        ParseBuffer, Serializer,
382    };
383
384    use super::*;
385
386    const DEFAULT_DST_MAC: Mac = Mac::new([0, 1, 2, 3, 4, 5]);
387    const DEFAULT_SRC_MAC: Mac = Mac::new([6, 7, 8, 9, 10, 11]);
388    const ETHERNET_ETHERTYPE_BYTE_OFFSET: usize = 12;
389    const ETHERNET_MIN_FRAME_LEN: usize = 60;
390
391    // Return a buffer for testing parsing with values 0..60 except for the
392    // EtherType field, which is EtherType::Arp. Also return the contents
393    // of the body.
394    fn new_parse_buf() -> ([u8; ETHERNET_MIN_FRAME_LEN], [u8; ETHERNET_MIN_BODY_LEN_NO_TAG]) {
395        let mut buf = [0; ETHERNET_MIN_FRAME_LEN];
396        for (i, elem) in buf.iter_mut().enumerate() {
397            *elem = i as u8;
398        }
399        NetworkEndian::write_u16(&mut buf[ETHERNET_ETHERTYPE_BYTE_OFFSET..], EtherType::Arp.into());
400        let mut body = [0; ETHERNET_MIN_BODY_LEN_NO_TAG];
401        (&mut body).copy_from_slice(&buf[ETHERNET_HDR_LEN_NO_TAG..]);
402        (buf, body)
403    }
404
405    // Return a test buffer with values 0..46 to be used as a test payload for
406    // serialization.
407    fn new_serialize_buf() -> [u8; ETHERNET_MIN_BODY_LEN_NO_TAG] {
408        let mut buf = [0; ETHERNET_MIN_BODY_LEN_NO_TAG];
409        for (i, elem) in buf.iter_mut().enumerate() {
410            *elem = i as u8;
411        }
412        buf
413    }
414
415    #[test]
416    fn test_parse() {
417        crate::testutil::set_logger_for_test();
418        let (mut backing_buf, body) = new_parse_buf();
419        let mut buf = &mut backing_buf[..];
420        // Test parsing with a sufficiently long body.
421        let frame = buf.parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::Check).unwrap();
422        assert_eq!(frame.hdr_prefix.dst_mac, DEFAULT_DST_MAC);
423        assert_eq!(frame.hdr_prefix.src_mac, DEFAULT_SRC_MAC);
424        assert!(frame.tag.is_none());
425        assert_eq!(frame.tag(), None);
426        assert_eq!(frame.ethertype(), Some(EtherType::Arp));
427        assert_eq!(frame.body(), &body[..]);
428        // Test parsing with a too-short body but length checking disabled.
429        let mut buf = &mut backing_buf[..ETHERNET_HDR_LEN_NO_TAG];
430        let frame =
431            buf.parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::NoCheck).unwrap();
432        assert_eq!(frame.hdr_prefix.dst_mac, DEFAULT_DST_MAC);
433        assert_eq!(frame.hdr_prefix.src_mac, DEFAULT_SRC_MAC);
434        assert!(frame.tag.is_none());
435        assert_eq!(frame.tag(), None);
436        assert_eq!(frame.ethertype(), Some(EtherType::Arp));
437        assert_eq!(frame.body(), &[]);
438
439        // For both of the TPIDs that imply the existence of a tag, make sure
440        // that the tag is present and correct (and that all of the normal
441        // checks succeed).
442        for tpid in [TPID_8021Q, TPID_8021AD].iter() {
443            let (mut buf, body) = new_parse_buf();
444            let mut buf = &mut buf[..];
445
446            const TPID_OFFSET: usize = 12;
447            NetworkEndian::write_u16(&mut buf[TPID_OFFSET..], *tpid);
448            // write a valid EtherType
449            NetworkEndian::write_u16(&mut buf[TPID_OFFSET + 4..], EtherType::Arp.into());
450
451            let frame =
452                buf.parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::Check).unwrap();
453            assert_eq!(frame.hdr_prefix.dst_mac, DEFAULT_DST_MAC);
454            assert_eq!(frame.hdr_prefix.src_mac, DEFAULT_SRC_MAC);
455            assert_eq!(frame.ethertype(), Some(EtherType::Arp));
456
457            let want_tag =
458                u32::from(*tpid) << 16 | ((TPID_OFFSET as u32 + 2) << 8) | (TPID_OFFSET as u32 + 3);
459            assert_eq!(frame.tag(), Some(want_tag));
460            // Offset by 4 since new_parse_buf returns a body on the assumption
461            // that there's no tag.
462            assert_eq!(frame.body(), &body[4..]);
463        }
464    }
465
466    #[test]
467    fn test_ethertype() {
468        // EtherTypes of 1500 and below must match the body length
469        let mut buf = [0u8; 1014];
470        // an incorrect length results in error
471        NetworkEndian::write_u16(&mut buf[ETHERNET_ETHERTYPE_BYTE_OFFSET..], 1001);
472        assert!(
473            (&mut buf[..])
474                .parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::Check)
475                .is_err()
476        );
477
478        // a correct length results in success
479        NetworkEndian::write_u16(&mut buf[ETHERNET_ETHERTYPE_BYTE_OFFSET..], 1000);
480        assert_eq!(
481            (&mut buf[..])
482                .parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::Check)
483                .unwrap()
484                .ethertype(),
485            None
486        );
487
488        // an unrecognized EtherType is returned numerically
489        let mut buf = [0u8; 1014];
490        NetworkEndian::write_u16(
491            &mut buf[ETHERNET_ETHERTYPE_BYTE_OFFSET..],
492            ETHERNET_MAX_ILLEGAL_ETHERTYPE + 1,
493        );
494        assert_eq!(
495            (&mut buf[..])
496                .parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::Check)
497                .unwrap()
498                .ethertype(),
499            Some(EtherType::Other(ETHERNET_MAX_ILLEGAL_ETHERTYPE + 1))
500        );
501    }
502
503    fn new_test_ethernet_packet_builder() -> EthernetFrameBuilder {
504        EthernetFrameBuilder::new(
505            DEFAULT_SRC_MAC,
506            DEFAULT_DST_MAC,
507            EtherType::Arp,
508            ETHERNET_MIN_BODY_LEN_NO_TAG,
509        )
510    }
511
512    #[test]
513    fn test_serialize() {
514        let buf = new_test_ethernet_packet_builder()
515            .wrap_body((&new_serialize_buf()[..]).into_serializer())
516            .serialize_vec_outer(&mut NoOpSerializationContext)
517            .unwrap();
518        assert_eq!(
519            &buf.as_ref()[..ETHERNET_HDR_LEN_NO_TAG],
520            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0x08, 0x06]
521        );
522    }
523
524    #[test]
525    fn test_serialize_zeroes() {
526        // Test that EthernetFrame::serialize properly zeroes memory before
527        // serializing the header.
528        let mut buf_0 = [0; ETHERNET_MIN_FRAME_LEN];
529        let _: Buf<&mut [u8]> = new_test_ethernet_packet_builder()
530            .wrap_body(Buf::new(&mut buf_0[..], ETHERNET_HDR_LEN_NO_TAG..))
531            .serialize_vec_outer(&mut NoOpSerializationContext)
532            .unwrap()
533            .unwrap_a();
534        let mut buf_1 = [0; ETHERNET_MIN_FRAME_LEN];
535        (&mut buf_1[..ETHERNET_HDR_LEN_NO_TAG]).copy_from_slice(&[0xFF; ETHERNET_HDR_LEN_NO_TAG]);
536        let _: Buf<&mut [u8]> = new_test_ethernet_packet_builder()
537            .wrap_body(Buf::new(&mut buf_1[..], ETHERNET_HDR_LEN_NO_TAG..))
538            .serialize_vec_outer(&mut NoOpSerializationContext)
539            .unwrap()
540            .unwrap_a();
541        assert_eq!(&buf_0[..], &buf_1[..]);
542    }
543
544    #[test]
545    fn test_parse_error() {
546        // 1 byte shorter than the minimum
547        let mut buf = [0u8; ETHERNET_MIN_FRAME_LEN - 1];
548        assert!(
549            (&mut buf[..])
550                .parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::Check)
551                .is_err()
552        );
553
554        // 1 byte shorter than the minimum header length still fails even if
555        // length checking is disabled
556        let mut buf = [0u8; ETHERNET_HDR_LEN_NO_TAG - 1];
557        assert!(
558            (&mut buf[..])
559                .parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::NoCheck)
560                .is_err()
561        );
562
563        // an ethertype of 1500 should be validated as the length of the body
564        let mut buf = [0u8; ETHERNET_MIN_FRAME_LEN];
565        NetworkEndian::write_u16(
566            &mut buf[ETHERNET_ETHERTYPE_BYTE_OFFSET..],
567            ETHERNET_MIN_ILLEGAL_ETHERTYPE - 1,
568        );
569        assert!(
570            (&mut buf[..])
571                .parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::Check)
572                .is_err()
573        );
574
575        // an ethertype of 1501 is illegal because it's in the range [1501, 1535]
576        let mut buf = [0u8; ETHERNET_MIN_FRAME_LEN];
577        NetworkEndian::write_u16(
578            &mut buf[ETHERNET_ETHERTYPE_BYTE_OFFSET..],
579            ETHERNET_MIN_ILLEGAL_ETHERTYPE,
580        );
581        assert!(
582            (&mut buf[..])
583                .parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::Check)
584                .is_err()
585        );
586
587        // an ethertype of 1535 is illegal
588        let mut buf = [0u8; ETHERNET_MIN_FRAME_LEN];
589        NetworkEndian::write_u16(
590            &mut buf[ETHERNET_ETHERTYPE_BYTE_OFFSET..],
591            ETHERNET_MAX_ILLEGAL_ETHERTYPE,
592        );
593        assert!(
594            (&mut buf[..])
595                .parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::Check)
596                .is_err()
597        );
598    }
599
600    #[test]
601    #[should_panic(expected = "bytes is below minimum frame size of")]
602    fn test_serialize_panic() {
603        // create with a body which is below the minimum length
604        let mut buf = [0u8; ETHERNET_MIN_FRAME_LEN - 1];
605        let mut b = [&mut buf[..]];
606        let buf = b.as_fragmented_byte_slice();
607        let (header, body, footer) = buf.try_split_contiguous(ETHERNET_HDR_LEN_NO_TAG..).unwrap();
608        new_test_ethernet_packet_builder().serialize(
609            &mut NoOpSerializationContext,
610            &mut SerializeTarget { header, footer },
611            body,
612        );
613    }
614
615    #[test]
616    fn test_custom_min_body_len() {
617        const MIN_BODY_LEN: usize = 4;
618        const UNWRITTEN_BYTE: u8 = 0xAA;
619
620        let builder = EthernetFrameBuilder::new(
621            Mac::new([0, 1, 2, 3, 4, 5]),
622            Mac::new([6, 7, 8, 9, 10, 11]),
623            EtherType::Arp,
624            MIN_BODY_LEN,
625        );
626
627        let mut buffer = [UNWRITTEN_BYTE; ETHERNET_MIN_FRAME_LEN];
628        // TODO(https://fxbug.dev/42079821): Don't use this `#[doc(hidden)]`
629        // method, and use the public API instead.
630        GrowBufferMut::serialize(
631            &mut Buf::new(&mut buffer[..], ETHERNET_HDR_LEN_NO_TAG..ETHERNET_HDR_LEN_NO_TAG),
632            &mut NoOpSerializationContext,
633            builder,
634        );
635
636        let (header, tail) = buffer.split_at(ETHERNET_HDR_LEN_NO_TAG);
637        let (padding, unwritten) = tail.split_at(MIN_BODY_LEN);
638        assert_eq!(
639            header,
640            &[
641                6, 7, 8, 9, 10, 11, // dst_mac
642                0, 1, 2, 3, 4, 5, // src_mac
643                08, 06, // ethertype
644            ]
645        );
646        assert_eq!(padding, &[0; MIN_BODY_LEN]);
647        assert_eq!(
648            unwritten,
649            &[UNWRITTEN_BYTE; ETHERNET_MIN_FRAME_LEN - MIN_BODY_LEN - ETHERNET_HDR_LEN_NO_TAG]
650        );
651    }
652}