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