smoltcp/wire/
ethernet.rs

1use byteorder::{ByteOrder, NetworkEndian};
2use core::fmt;
3
4use super::{Error, Result};
5
6enum_with_unknown! {
7    /// Ethernet protocol type.
8    pub enum EtherType(u16) {
9        Ipv4 = 0x0800,
10        Arp  = 0x0806,
11        Ipv6 = 0x86DD
12    }
13}
14
15impl fmt::Display for EtherType {
16    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
17        match *self {
18            EtherType::Ipv4 => write!(f, "IPv4"),
19            EtherType::Ipv6 => write!(f, "IPv6"),
20            EtherType::Arp => write!(f, "ARP"),
21            EtherType::Unknown(id) => write!(f, "0x{id:04x}"),
22        }
23    }
24}
25
26/// A six-octet Ethernet II address.
27#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
28pub struct Address(pub [u8; 6]);
29
30impl Address {
31    /// The broadcast address.
32    pub const BROADCAST: Address = Address([0xff; 6]);
33
34    /// Construct an Ethernet address from a sequence of octets, in big-endian.
35    ///
36    /// # Panics
37    /// The function panics if `data` is not six octets long.
38    pub fn from_bytes(data: &[u8]) -> Address {
39        let mut bytes = [0; 6];
40        bytes.copy_from_slice(data);
41        Address(bytes)
42    }
43
44    /// Return an Ethernet address as a sequence of octets, in big-endian.
45    pub const fn as_bytes(&self) -> &[u8] {
46        &self.0
47    }
48
49    /// Query whether the address is an unicast address.
50    pub fn is_unicast(&self) -> bool {
51        !(self.is_broadcast() || self.is_multicast())
52    }
53
54    /// Query whether this address is the broadcast address.
55    pub fn is_broadcast(&self) -> bool {
56        *self == Self::BROADCAST
57    }
58
59    /// Query whether the "multicast" bit in the OUI is set.
60    pub const fn is_multicast(&self) -> bool {
61        self.0[0] & 0x01 != 0
62    }
63
64    /// Query whether the "locally administered" bit in the OUI is set.
65    pub const fn is_local(&self) -> bool {
66        self.0[0] & 0x02 != 0
67    }
68}
69
70impl fmt::Display for Address {
71    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72        let bytes = self.0;
73        write!(
74            f,
75            "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
76            bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]
77        )
78    }
79}
80
81#[cfg(feature = "defmt")]
82impl defmt::Format for Address {
83    fn format(&self, fmt: defmt::Formatter) {
84        let bytes = self.0;
85        defmt::write!(
86            fmt,
87            "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
88            bytes[0],
89            bytes[1],
90            bytes[2],
91            bytes[3],
92            bytes[4],
93            bytes[5]
94        )
95    }
96}
97
98/// A read/write wrapper around an Ethernet II frame buffer.
99#[derive(Debug, Clone)]
100#[cfg_attr(feature = "defmt", derive(defmt::Format))]
101pub struct Frame<T: AsRef<[u8]>> {
102    buffer: T,
103}
104
105mod field {
106    use crate::wire::field::*;
107
108    pub const DESTINATION: Field = 0..6;
109    pub const SOURCE: Field = 6..12;
110    pub const ETHERTYPE: Field = 12..14;
111    pub const PAYLOAD: Rest = 14..;
112}
113
114/// The Ethernet header length
115pub const HEADER_LEN: usize = field::PAYLOAD.start;
116
117impl<T: AsRef<[u8]>> Frame<T> {
118    /// Imbue a raw octet buffer with Ethernet frame structure.
119    pub const fn new_unchecked(buffer: T) -> Frame<T> {
120        Frame { buffer }
121    }
122
123    /// Shorthand for a combination of [new_unchecked] and [check_len].
124    ///
125    /// [new_unchecked]: #method.new_unchecked
126    /// [check_len]: #method.check_len
127    pub fn new_checked(buffer: T) -> Result<Frame<T>> {
128        let packet = Self::new_unchecked(buffer);
129        packet.check_len()?;
130        Ok(packet)
131    }
132
133    /// Ensure that no accessor method will panic if called.
134    /// Returns `Err(Error)` if the buffer is too short.
135    pub fn check_len(&self) -> Result<()> {
136        let len = self.buffer.as_ref().len();
137        if len < HEADER_LEN {
138            Err(Error)
139        } else {
140            Ok(())
141        }
142    }
143
144    /// Consumes the frame, returning the underlying buffer.
145    pub fn into_inner(self) -> T {
146        self.buffer
147    }
148
149    /// Return the length of a frame header.
150    pub const fn header_len() -> usize {
151        HEADER_LEN
152    }
153
154    /// Return the length of a buffer required to hold a packet with the payload
155    /// of a given length.
156    pub const fn buffer_len(payload_len: usize) -> usize {
157        HEADER_LEN + payload_len
158    }
159
160    /// Return the destination address field.
161    #[inline]
162    pub fn dst_addr(&self) -> Address {
163        let data = self.buffer.as_ref();
164        Address::from_bytes(&data[field::DESTINATION])
165    }
166
167    /// Return the source address field.
168    #[inline]
169    pub fn src_addr(&self) -> Address {
170        let data = self.buffer.as_ref();
171        Address::from_bytes(&data[field::SOURCE])
172    }
173
174    /// Return the EtherType field, without checking for 802.1Q.
175    #[inline]
176    pub fn ethertype(&self) -> EtherType {
177        let data = self.buffer.as_ref();
178        let raw = NetworkEndian::read_u16(&data[field::ETHERTYPE]);
179        EtherType::from(raw)
180    }
181}
182
183impl<'a, T: AsRef<[u8]> + ?Sized> Frame<&'a T> {
184    /// Return a pointer to the payload, without checking for 802.1Q.
185    #[inline]
186    pub fn payload(&self) -> &'a [u8] {
187        let data = self.buffer.as_ref();
188        &data[field::PAYLOAD]
189    }
190}
191
192impl<T: AsRef<[u8]> + AsMut<[u8]>> Frame<T> {
193    /// Set the destination address field.
194    #[inline]
195    pub fn set_dst_addr(&mut self, value: Address) {
196        let data = self.buffer.as_mut();
197        data[field::DESTINATION].copy_from_slice(value.as_bytes())
198    }
199
200    /// Set the source address field.
201    #[inline]
202    pub fn set_src_addr(&mut self, value: Address) {
203        let data = self.buffer.as_mut();
204        data[field::SOURCE].copy_from_slice(value.as_bytes())
205    }
206
207    /// Set the EtherType field.
208    #[inline]
209    pub fn set_ethertype(&mut self, value: EtherType) {
210        let data = self.buffer.as_mut();
211        NetworkEndian::write_u16(&mut data[field::ETHERTYPE], value.into())
212    }
213
214    /// Return a mutable pointer to the payload.
215    #[inline]
216    pub fn payload_mut(&mut self) -> &mut [u8] {
217        let data = self.buffer.as_mut();
218        &mut data[field::PAYLOAD]
219    }
220}
221
222impl<T: AsRef<[u8]>> AsRef<[u8]> for Frame<T> {
223    fn as_ref(&self) -> &[u8] {
224        self.buffer.as_ref()
225    }
226}
227
228impl<T: AsRef<[u8]>> fmt::Display for Frame<T> {
229    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
230        write!(
231            f,
232            "EthernetII src={} dst={} type={}",
233            self.src_addr(),
234            self.dst_addr(),
235            self.ethertype()
236        )
237    }
238}
239
240use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
241
242impl<T: AsRef<[u8]>> PrettyPrint for Frame<T> {
243    fn pretty_print(
244        buffer: &dyn AsRef<[u8]>,
245        f: &mut fmt::Formatter,
246        indent: &mut PrettyIndent,
247    ) -> fmt::Result {
248        let frame = match Frame::new_checked(buffer) {
249            Err(err) => return write!(f, "{indent}({err})"),
250            Ok(frame) => frame,
251        };
252        write!(f, "{indent}{frame}")?;
253
254        match frame.ethertype() {
255            #[cfg(feature = "proto-ipv4")]
256            EtherType::Arp => {
257                indent.increase(f)?;
258                super::ArpPacket::<&[u8]>::pretty_print(&frame.payload(), f, indent)
259            }
260            #[cfg(feature = "proto-ipv4")]
261            EtherType::Ipv4 => {
262                indent.increase(f)?;
263                super::Ipv4Packet::<&[u8]>::pretty_print(&frame.payload(), f, indent)
264            }
265            #[cfg(feature = "proto-ipv6")]
266            EtherType::Ipv6 => {
267                indent.increase(f)?;
268                super::Ipv6Packet::<&[u8]>::pretty_print(&frame.payload(), f, indent)
269            }
270            _ => Ok(()),
271        }
272    }
273}
274
275/// A high-level representation of an Internet Protocol version 4 packet header.
276#[derive(Debug, PartialEq, Eq, Clone, Copy)]
277#[cfg_attr(feature = "defmt", derive(defmt::Format))]
278pub struct Repr {
279    pub src_addr: Address,
280    pub dst_addr: Address,
281    pub ethertype: EtherType,
282}
283
284impl Repr {
285    /// Parse an Ethernet II frame and return a high-level representation.
286    pub fn parse<T: AsRef<[u8]> + ?Sized>(frame: &Frame<&T>) -> Result<Repr> {
287        frame.check_len()?;
288        Ok(Repr {
289            src_addr: frame.src_addr(),
290            dst_addr: frame.dst_addr(),
291            ethertype: frame.ethertype(),
292        })
293    }
294
295    /// Return the length of a header that will be emitted from this high-level representation.
296    pub const fn buffer_len(&self) -> usize {
297        HEADER_LEN
298    }
299
300    /// Emit a high-level representation into an Ethernet II frame.
301    pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, frame: &mut Frame<T>) {
302        assert!(frame.buffer.as_ref().len() >= self.buffer_len());
303        frame.set_src_addr(self.src_addr);
304        frame.set_dst_addr(self.dst_addr);
305        frame.set_ethertype(self.ethertype);
306    }
307}
308
309#[cfg(test)]
310mod test {
311    // Tests that are valid with any combination of
312    // "proto-*" features.
313    use super::*;
314
315    #[test]
316    fn test_broadcast() {
317        assert!(Address::BROADCAST.is_broadcast());
318        assert!(!Address::BROADCAST.is_unicast());
319        assert!(Address::BROADCAST.is_multicast());
320        assert!(Address::BROADCAST.is_local());
321    }
322}
323
324#[cfg(test)]
325#[cfg(feature = "proto-ipv4")]
326mod test_ipv4 {
327    // Tests that are valid only with "proto-ipv4"
328    use super::*;
329
330    static FRAME_BYTES: [u8; 64] = [
331        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x08, 0x00, 0xaa,
332        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
333        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
334        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
335        0x00, 0x00, 0x00, 0xff,
336    ];
337
338    static PAYLOAD_BYTES: [u8; 50] = [
339        0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
340        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
341        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
342        0x00, 0x00, 0x00, 0x00, 0xff,
343    ];
344
345    #[test]
346    fn test_deconstruct() {
347        let frame = Frame::new_unchecked(&FRAME_BYTES[..]);
348        assert_eq!(
349            frame.dst_addr(),
350            Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06])
351        );
352        assert_eq!(
353            frame.src_addr(),
354            Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16])
355        );
356        assert_eq!(frame.ethertype(), EtherType::Ipv4);
357        assert_eq!(frame.payload(), &PAYLOAD_BYTES[..]);
358    }
359
360    #[test]
361    fn test_construct() {
362        let mut bytes = vec![0xa5; 64];
363        let mut frame = Frame::new_unchecked(&mut bytes);
364        frame.set_dst_addr(Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
365        frame.set_src_addr(Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]));
366        frame.set_ethertype(EtherType::Ipv4);
367        frame.payload_mut().copy_from_slice(&PAYLOAD_BYTES[..]);
368        assert_eq!(&frame.into_inner()[..], &FRAME_BYTES[..]);
369    }
370}
371
372#[cfg(test)]
373#[cfg(feature = "proto-ipv6")]
374mod test_ipv6 {
375    // Tests that are valid only with "proto-ipv6"
376    use super::*;
377
378    static FRAME_BYTES: [u8; 54] = [
379        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x86, 0xdd, 0x60,
380        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
381        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
382        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
383    ];
384
385    static PAYLOAD_BYTES: [u8; 40] = [
386        0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
387        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
388        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
389    ];
390
391    #[test]
392    fn test_deconstruct() {
393        let frame = Frame::new_unchecked(&FRAME_BYTES[..]);
394        assert_eq!(
395            frame.dst_addr(),
396            Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06])
397        );
398        assert_eq!(
399            frame.src_addr(),
400            Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16])
401        );
402        assert_eq!(frame.ethertype(), EtherType::Ipv6);
403        assert_eq!(frame.payload(), &PAYLOAD_BYTES[..]);
404    }
405
406    #[test]
407    fn test_construct() {
408        let mut bytes = vec![0xa5; 54];
409        let mut frame = Frame::new_unchecked(&mut bytes);
410        frame.set_dst_addr(Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
411        frame.set_src_addr(Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]));
412        frame.set_ethertype(EtherType::Ipv6);
413        assert_eq!(PAYLOAD_BYTES.len(), frame.payload_mut().len());
414        frame.payload_mut().copy_from_slice(&PAYLOAD_BYTES[..]);
415        assert_eq!(&frame.into_inner()[..], &FRAME_BYTES[..]);
416    }
417}