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