vmm/dumbo/pdu/
ethernet.rs

1// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Contains support for parsing and writing Ethernet frames. Does not currently offer support for
5//! 802.1Q tags.
6
7use std::fmt::Debug;
8
9use super::Incomplete;
10use super::bytes::{InnerBytes, NetworkBytes, NetworkBytesMut};
11use crate::dumbo::MacAddr;
12
13const DST_MAC_OFFSET: usize = 0;
14const SRC_MAC_OFFSET: usize = 6;
15const ETHERTYPE_OFFSET: usize = 12;
16
17// We don't support 802.1Q tags.
18// TODO: support 802.1Q tags?! If so, don't forget to change the speculative_test_* functions
19// for ARP and IPv4.
20/// Payload offset in an ethernet frame
21pub const PAYLOAD_OFFSET: usize = 14;
22
23/// Ethertype value for ARP frames.
24pub const ETHERTYPE_ARP: u16 = 0x0806;
25/// Ethertype value for IPv4 packets.
26pub const ETHERTYPE_IPV4: u16 = 0x0800;
27
28/// Describes the errors which may occur when handling Ethernet frames.
29#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)]
30pub enum EthernetError {
31    /// The specified byte sequence is shorter than the Ethernet header length.
32    SliceTooShort,
33}
34
35/// Interprets the inner bytes as an Ethernet frame.
36#[derive(Debug)]
37pub struct EthernetFrame<'a, T: 'a> {
38    bytes: InnerBytes<'a, T>,
39}
40
41#[allow(clippy::len_without_is_empty)]
42impl<T: NetworkBytes + Debug> EthernetFrame<'_, T> {
43    /// Interprets `bytes` as an Ethernet frame without any validity checks.
44    ///
45    /// # Panics
46    ///
47    ///  This method does not panic, but further method calls on the resulting object may panic if
48    /// `bytes` contains invalid input.
49    #[inline]
50    pub fn from_bytes_unchecked(bytes: T) -> Self {
51        EthernetFrame {
52            bytes: InnerBytes::new(bytes),
53        }
54    }
55
56    /// Checks whether the specified byte sequence can be interpreted as an Ethernet frame.
57    #[inline]
58    pub fn from_bytes(bytes: T) -> Result<Self, EthernetError> {
59        if bytes.len() < PAYLOAD_OFFSET {
60            return Err(EthernetError::SliceTooShort);
61        }
62
63        Ok(EthernetFrame::from_bytes_unchecked(bytes))
64    }
65
66    /// Returns the destination MAC address.
67    #[inline]
68    pub fn dst_mac(&self) -> MacAddr {
69        MacAddr::from_bytes_unchecked(&self.bytes[DST_MAC_OFFSET..SRC_MAC_OFFSET])
70    }
71
72    /// Returns the source MAC address.
73    #[inline]
74    pub fn src_mac(&self) -> MacAddr {
75        MacAddr::from_bytes_unchecked(&self.bytes[SRC_MAC_OFFSET..ETHERTYPE_OFFSET])
76    }
77
78    /// Returns the ethertype of the frame.
79    #[inline]
80    pub fn ethertype(&self) -> u16 {
81        self.bytes.ntohs_unchecked(ETHERTYPE_OFFSET)
82    }
83
84    /// Returns the offset of the payload within the frame.
85    #[inline]
86    pub fn payload_offset(&self) -> usize {
87        PAYLOAD_OFFSET
88    }
89
90    /// Returns the payload of the frame as an `[&u8]` slice.
91    #[inline]
92    pub fn payload(&self) -> &[u8] {
93        self.bytes.split_at(self.payload_offset()).1
94    }
95
96    /// Returns the length of the frame.
97    #[inline]
98    pub fn len(&self) -> usize {
99        self.bytes.len()
100    }
101}
102
103impl<T: NetworkBytesMut + Debug> EthernetFrame<'_, T> {
104    /// Attempts to write an Ethernet frame using the given header fields to `buf`.
105    fn new_with_header(
106        buf: T,
107        dst_mac: MacAddr,
108        src_mac: MacAddr,
109        ethertype: u16,
110    ) -> Result<Self, EthernetError> {
111        if buf.len() < PAYLOAD_OFFSET {
112            return Err(EthernetError::SliceTooShort);
113        }
114
115        let mut frame = EthernetFrame::from_bytes_unchecked(buf);
116
117        frame
118            .set_dst_mac(dst_mac)
119            .set_src_mac(src_mac)
120            .set_ethertype(ethertype);
121
122        Ok(frame)
123    }
124
125    /// Attempts to write an incomplete Ethernet frame (whose length is currently unknown) to `buf`,
126    /// using the specified header fields.
127    #[inline]
128    pub fn write_incomplete(
129        buf: T,
130        dst_mac: MacAddr,
131        src_mac: MacAddr,
132        ethertype: u16,
133    ) -> Result<Incomplete<Self>, EthernetError> {
134        Ok(Incomplete::new(Self::new_with_header(
135            buf, dst_mac, src_mac, ethertype,
136        )?))
137    }
138
139    /// Sets the destination MAC address.
140    #[inline]
141    pub fn set_dst_mac(&mut self, addr: MacAddr) -> &mut Self {
142        self.bytes[DST_MAC_OFFSET..SRC_MAC_OFFSET].copy_from_slice(addr.get_bytes());
143        self
144    }
145
146    /// Sets the source MAC address.
147    #[inline]
148    pub fn set_src_mac(&mut self, addr: MacAddr) -> &mut Self {
149        self.bytes[SRC_MAC_OFFSET..ETHERTYPE_OFFSET].copy_from_slice(addr.get_bytes());
150        self
151    }
152
153    /// Sets the ethertype of the frame.
154    #[inline]
155    pub fn set_ethertype(&mut self, value: u16) -> &mut Self {
156        self.bytes.htons_unchecked(ETHERTYPE_OFFSET, value);
157        self
158    }
159
160    /// Returns the payload of the frame as a `&mut [u8]` slice.
161    #[inline]
162    pub fn payload_mut(&mut self) -> &mut [u8] {
163        // We need this let to avoid confusing the borrow checker.
164        let offset = self.payload_offset();
165        self.bytes.split_at_mut(offset).1
166    }
167}
168
169impl<'a, T: NetworkBytes + Debug> Incomplete<EthernetFrame<'a, T>> {
170    /// Completes the inner frame by shrinking it to its actual length.
171    ///
172    /// # Panics
173    ///
174    /// This method panics if `len` is greater than the length of the inner byte sequence.
175    #[inline]
176    pub fn with_payload_len_unchecked(mut self, payload_len: usize) -> EthernetFrame<'a, T> {
177        let payload_offset = self.inner.payload_offset();
178        self.inner
179            .bytes
180            .shrink_unchecked(payload_offset + payload_len);
181        self.inner
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use std::str::FromStr;
188
189    use super::*;
190
191    #[test]
192    fn test_ethernet_frame() {
193        let mut a = [0u8; 10000];
194        let mut bad_array = [0u8; 1];
195
196        let dst_mac = MacAddr::from_str("01:23:45:67:89:ab").unwrap();
197        let src_mac = MacAddr::from_str("cd:ef:01:23:45:67").unwrap();
198        let ethertype = 1289;
199
200        assert_eq!(
201            EthernetFrame::from_bytes(bad_array.as_ref()).unwrap_err(),
202            EthernetError::SliceTooShort
203        );
204        assert_eq!(
205            EthernetFrame::new_with_header(bad_array.as_mut(), dst_mac, src_mac, ethertype)
206                .unwrap_err(),
207            EthernetError::SliceTooShort
208        );
209
210        {
211            let mut f1 =
212                EthernetFrame::new_with_header(a.as_mut(), dst_mac, src_mac, ethertype).unwrap();
213
214            assert_eq!(f1.dst_mac(), dst_mac);
215            assert_eq!(f1.src_mac(), src_mac);
216            assert_eq!(f1.ethertype(), ethertype);
217            f1.payload_mut()[1] = 132;
218        }
219
220        {
221            let f2 = EthernetFrame::from_bytes(a.as_ref()).unwrap();
222
223            assert_eq!(f2.dst_mac(), dst_mac);
224            assert_eq!(f2.src_mac(), src_mac);
225            assert_eq!(f2.ethertype(), ethertype);
226            assert_eq!(f2.payload()[1], 132);
227            assert_eq!(f2.len(), f2.bytes.len());
228        }
229
230        {
231            let f3 =
232                EthernetFrame::write_incomplete(a.as_mut(), dst_mac, src_mac, ethertype).unwrap();
233            let f3_complete = f3.with_payload_len_unchecked(123);
234            assert_eq!(f3_complete.len(), f3_complete.payload_offset() + 123);
235        }
236    }
237}
238
239#[cfg(kani)]
240#[allow(dead_code)] // Avoid warning when using stubs.
241mod kani_proofs {
242    use super::*;
243    use crate::utils::net::mac::MAC_ADDR_LEN;
244
245    // See the Virtual I/O Device (VIRTIO) specification, Sec. 5.1.6.2.
246    // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.pdf
247    pub const MAX_FRAME_SIZE: usize = 1514;
248
249    const MAC_ADDR_LEN_USIZE: usize = MAC_ADDR_LEN as usize;
250
251    impl<'a, T: NetworkBytesMut + Debug> EthernetFrame<'a, T> {
252        fn is_valid(&self) -> bool {
253            self.len() >= PAYLOAD_OFFSET
254        }
255    }
256
257    // We consider the MMDS Network Stack spec for all postconditions in the harnesses.
258    // See https://github.com/firecracker-microvm/firecracker/blob/main/docs/mmds/mmds-design.md#mmds-network-stack
259
260    #[kani::proof]
261    fn verify_from_bytes_unchecked() {
262        // Create non-deterministic stream of bytes up to MAX_FRAME_SIZE
263        let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
264        let slice_length = bytes.len();
265
266        // Verify from_bytes_unchecked
267        let ethernet = EthernetFrame::from_bytes_unchecked(bytes.as_mut());
268
269        // Check for post-conditions
270        assert_eq!(ethernet.len(), slice_length);
271        assert!(
272            !(ethernet.is_valid()) || (ethernet.payload().len() == slice_length - PAYLOAD_OFFSET)
273        );
274    }
275
276    #[kani::proof]
277    fn verify_from_bytes() {
278        // Create non-deterministic stream of bytes up to MAX_FRAME_SIZE
279        let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
280        let slice_length = bytes.len();
281
282        // Verify from_bytes
283        let ethernet = EthernetFrame::from_bytes(bytes.as_mut());
284
285        // Check for post-conditions
286        if slice_length >= PAYLOAD_OFFSET {
287            let ethernet = ethernet.unwrap();
288            assert!(ethernet.is_valid());
289            assert_eq!(ethernet.len(), slice_length);
290            assert_eq!(ethernet.payload().len(), slice_length - PAYLOAD_OFFSET);
291        } else {
292            ethernet.unwrap_err();
293        }
294    }
295
296    #[kani::proof]
297    fn verify_dst_mac() {
298        // Create non-deterministic stream of bytes up to MAX_FRAME_SIZE
299        let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
300
301        // Create valid non-deterministic ethernet
302        let ethernet = EthernetFrame::from_bytes(bytes.as_mut());
303        kani::assume(ethernet.is_ok());
304        let mut ethernet = ethernet.unwrap();
305
306        // Verify set_dst_mac
307        let mac_bytes: [u8; MAC_ADDR_LEN as usize] = kani::any();
308        let dst_mac = MacAddr::from(mac_bytes);
309        ethernet.set_dst_mac(dst_mac);
310
311        // Verify dst_mac
312        let dst_addr = EthernetFrame::dst_mac(&ethernet);
313
314        // Check for post-conditions
315
316        // MAC addresses should always have 48 bits
317        assert_eq!(dst_addr.get_bytes().len(), MAC_ADDR_LEN as usize);
318
319        // Check duality between set_dst_mac and dst_mac operations
320        let i: usize = kani::any();
321        kani::assume(i < mac_bytes.len());
322        assert_eq!(mac_bytes[i], dst_addr.get_bytes()[i]);
323    }
324
325    #[kani::proof]
326    fn verify_src_mac() {
327        // Create non-deterministic stream of bytes up to MAX_FRAME_SIZE
328        let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
329
330        // Create valid non-deterministic ethernet
331        let ethernet = EthernetFrame::from_bytes(bytes.as_mut());
332        kani::assume(ethernet.is_ok());
333        let mut ethernet = ethernet.unwrap();
334
335        // Verify set_src_mac
336        let mac_bytes: [u8; MAC_ADDR_LEN as usize] = kani::any();
337        let src_mac = MacAddr::from(mac_bytes);
338        ethernet.set_src_mac(src_mac);
339
340        // Verify src_mac
341        let src_addr = EthernetFrame::src_mac(&ethernet);
342
343        // Check for post-conditions
344
345        // MAC addresses should always have 48 bits
346        assert_eq!(src_addr.get_bytes().len(), MAC_ADDR_LEN as usize);
347
348        // Check duality between set_src_mac and src_mac operations
349        let i: usize = kani::any();
350        kani::assume(i < mac_bytes.len());
351        assert_eq!(mac_bytes[i], src_addr.get_bytes()[i]);
352    }
353
354    #[kani::proof]
355    fn verify_src_mac_isolation() {
356        // Create non-deterministic stream of bytes up to MAX_FRAME_SIZE
357        let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
358
359        // Create valid non-deterministic ethernet
360        let ethernet = EthernetFrame::from_bytes(bytes.as_mut());
361        kani::assume(ethernet.is_ok());
362        let mut ethernet = ethernet.unwrap();
363
364        // Verify set_src_mac
365        let mac_bytes: [u8; MAC_ADDR_LEN as usize] = kani::any();
366        let src_mac = MacAddr::from(mac_bytes);
367        ethernet.set_src_mac(src_mac);
368
369        let payload_offset = ethernet.payload_offset();
370
371        if kani::any() {
372            let dst_mac_bytes: [u8; MAC_ADDR_LEN as usize] = kani::any();
373            let dst_mac = MacAddr::from(dst_mac_bytes);
374            ethernet.set_dst_mac(dst_mac);
375        }
376        if kani::any() {
377            let ethertype_in: u16 = kani::any();
378            ethernet.set_ethertype(ethertype_in);
379        }
380
381        // Payload info doesn't change
382        assert_eq!(ethernet.payload_offset(), payload_offset);
383
384        // Verify src_mac
385        let src_addr = EthernetFrame::src_mac(&ethernet);
386
387        // Check for post-conditions
388
389        // MAC addresses should always have 48 bits
390        assert_eq!(src_addr.get_bytes().len(), MAC_ADDR_LEN as usize);
391
392        // Check duality between set_src_mac and src_mac operations
393        let i: usize = kani::any();
394        kani::assume(i < mac_bytes.len());
395        assert_eq!(mac_bytes[i], src_addr.get_bytes()[i]);
396    }
397
398    #[kani::proof]
399    fn verify_ethertype() {
400        // Create non-deterministic stream of bytes up to MAX_FRAME_SIZE
401        let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
402
403        // Create valid non-deterministic ethernet
404        let ethernet = EthernetFrame::from_bytes(bytes.as_mut());
405        kani::assume(ethernet.is_ok());
406        let mut ethernet = ethernet.unwrap();
407
408        // Verify set_ethertype
409        let ethertype_in: u16 = kani::any();
410        ethernet.set_ethertype(ethertype_in);
411
412        // Verify ethertype
413        let ethertype_out = ethernet.ethertype();
414
415        // Check for post-conditions
416
417        // Check duality between set_ethertype and ethertype operations
418        assert_eq!(ethertype_in, ethertype_out);
419    }
420
421    #[kani::proof]
422    #[kani::unwind(1515)]
423    fn verify_payload() {
424        // Create non-deterministic stream of bytes up to MAX_FRAME_SIZE
425        let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
426
427        // Create valid non-deterministic ethernet
428        let ethernet = EthernetFrame::from_bytes(bytes.as_mut());
429        kani::assume(ethernet.is_ok());
430        let ethernet = ethernet.unwrap();
431
432        // Verify payload_offset
433        let payload_offset = ethernet.payload_offset();
434
435        // Verify payload()
436        let payload = ethernet.payload();
437
438        // Verify payload_mut
439        let payload_mut = ethernet.payload();
440
441        // Check for post-conditions
442
443        // Check payload_offset value
444        assert_eq!(payload_offset, PAYLOAD_OFFSET);
445
446        // Check equivalence
447        assert_eq!(payload, payload_mut);
448    }
449
450    #[kani::proof]
451    fn verify_new_with_header() {
452        // Create non-deterministic stream of bytes up to MAX_FRAME_SIZE
453        let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
454        let bytes_length = bytes.len();
455
456        // Create valid non-deterministic dst_mac
457        let dst_mac_bytes: [u8; MAC_ADDR_LEN as usize] =
458            kani::Arbitrary::any_array::<MAC_ADDR_LEN_USIZE>();
459        let dst_mac = MacAddr::from(dst_mac_bytes);
460
461        // Create valid non-deterministic src_mac
462        let src_mac_bytes: [u8; MAC_ADDR_LEN as usize] =
463            kani::Arbitrary::any_array::<MAC_ADDR_LEN_USIZE>();
464        let src_mac = MacAddr::from(src_mac_bytes);
465
466        // Create valid non-deterministic ethertype
467        let ethertype: u16 = kani::any();
468
469        // Verify new_with_header
470        let frame =
471            EthernetFrame::new_with_header(bytes.as_mut(), dst_mac, src_mac, ethertype).unwrap();
472
473        // Check for post-conditions
474        assert_eq!(frame.dst_mac(), dst_mac);
475        assert_eq!(frame.src_mac(), src_mac);
476        assert_eq!(frame.ethertype(), ethertype);
477        assert_eq!(frame.len(), bytes_length);
478        assert!(frame.is_valid() && (frame.payload().len() == bytes_length - PAYLOAD_OFFSET));
479    }
480
481    #[kani::proof]
482    fn verify_write_incomplete() {
483        // Create non-deterministic stream of bytes up to MAX_FRAME_SIZE
484        let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
485
486        // Create valid non-deterministic dst_mac
487        let dst_mac_bytes: [u8; MAC_ADDR_LEN as usize] =
488            kani::Arbitrary::any_array::<MAC_ADDR_LEN_USIZE>();
489        let dst_mac = MacAddr::from(dst_mac_bytes);
490
491        // Create valid non-deterministic src_mac
492        let src_mac_bytes: [u8; MAC_ADDR_LEN as usize] =
493            kani::Arbitrary::any_array::<MAC_ADDR_LEN_USIZE>();
494        let src_mac = MacAddr::from(src_mac_bytes);
495
496        // Create valid non-deterministic ethertype
497        let ethertype: u16 = kani::any();
498
499        // Verify write_incomplete
500        let incomplete_frame =
501            EthernetFrame::write_incomplete(bytes.as_mut(), dst_mac, src_mac, ethertype).unwrap();
502
503        // Check for post-conditions
504        assert_eq!(incomplete_frame.inner.dst_mac(), dst_mac);
505        assert_eq!(incomplete_frame.inner.src_mac(), src_mac);
506        assert_eq!(incomplete_frame.inner.ethertype(), ethertype);
507    }
508
509    #[kani::proof]
510    #[kani::solver(cadical)]
511    fn verify_with_payload_len_unchecked() {
512        // Create non-deterministic stream of bytes up to MAX_FRAME_SIZE
513        let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
514
515        // Create valid non-deterministic dst_mac
516        let dst_mac_bytes: [u8; MAC_ADDR_LEN as usize] =
517            kani::Arbitrary::any_array::<MAC_ADDR_LEN_USIZE>();
518        let dst_mac = MacAddr::from(dst_mac_bytes);
519
520        // Create valid non-deterministic src_mac
521        let src_mac_bytes: [u8; MAC_ADDR_LEN as usize] =
522            kani::Arbitrary::any_array::<MAC_ADDR_LEN_USIZE>();
523        let src_mac = MacAddr::from(src_mac_bytes);
524
525        // Create valid non-deterministic ethertype
526        let ethertype: u16 = kani::any();
527
528        // Create a non-deterministic incomplete frame
529        let incomplete_frame =
530            EthernetFrame::write_incomplete(bytes.as_mut(), dst_mac, src_mac, ethertype).unwrap();
531        let incomplete_frame_payload_offset = incomplete_frame.inner.payload_offset();
532        let incomplete_frame_len = incomplete_frame.inner.len();
533
534        // Create a non-deterministic payload_len
535        let payload_len: usize = kani::any();
536        kani::assume(payload_len <= incomplete_frame_len - incomplete_frame_payload_offset);
537
538        // Verify with_payload_len_unchecked
539        let unchecked_frame = incomplete_frame.with_payload_len_unchecked(payload_len);
540
541        // Check for post-conditions
542        assert!(unchecked_frame.is_valid());
543        assert_eq!(unchecked_frame.dst_mac(), dst_mac);
544        assert_eq!(unchecked_frame.src_mac(), src_mac);
545        assert_eq!(unchecked_frame.ethertype(), ethertype);
546    }
547}