Skip to main content

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