smoltcp/wire/
arp.rs

1use byteorder::{ByteOrder, NetworkEndian};
2use core::fmt;
3
4use super::{Error, Result};
5
6pub use super::EthernetProtocol as Protocol;
7
8enum_with_unknown! {
9    /// ARP hardware type.
10    pub enum Hardware(u16) {
11        Ethernet = 1
12    }
13}
14
15enum_with_unknown! {
16    /// ARP operation type.
17    pub enum Operation(u16) {
18        Request = 1,
19        Reply = 2
20    }
21}
22
23/// A read/write wrapper around an Address Resolution Protocol packet buffer.
24#[derive(Debug, PartialEq, Eq, Clone)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub struct Packet<T: AsRef<[u8]>> {
27    buffer: T,
28}
29
30mod field {
31    #![allow(non_snake_case)]
32
33    use crate::wire::field::*;
34
35    pub const HTYPE: Field = 0..2;
36    pub const PTYPE: Field = 2..4;
37    pub const HLEN: usize = 4;
38    pub const PLEN: usize = 5;
39    pub const OPER: Field = 6..8;
40
41    #[inline]
42    pub const fn SHA(hardware_len: u8, _protocol_len: u8) -> Field {
43        let start = OPER.end;
44        start..(start + hardware_len as usize)
45    }
46
47    #[inline]
48    pub const fn SPA(hardware_len: u8, protocol_len: u8) -> Field {
49        let start = SHA(hardware_len, protocol_len).end;
50        start..(start + protocol_len as usize)
51    }
52
53    #[inline]
54    pub const fn THA(hardware_len: u8, protocol_len: u8) -> Field {
55        let start = SPA(hardware_len, protocol_len).end;
56        start..(start + hardware_len as usize)
57    }
58
59    #[inline]
60    pub const fn TPA(hardware_len: u8, protocol_len: u8) -> Field {
61        let start = THA(hardware_len, protocol_len).end;
62        start..(start + protocol_len as usize)
63    }
64}
65
66impl<T: AsRef<[u8]>> Packet<T> {
67    /// Imbue a raw octet buffer with ARP packet structure.
68    pub const fn new_unchecked(buffer: T) -> Packet<T> {
69        Packet { buffer }
70    }
71
72    /// Shorthand for a combination of [new_unchecked] and [check_len].
73    ///
74    /// [new_unchecked]: #method.new_unchecked
75    /// [check_len]: #method.check_len
76    pub fn new_checked(buffer: T) -> Result<Packet<T>> {
77        let packet = Self::new_unchecked(buffer);
78        packet.check_len()?;
79        Ok(packet)
80    }
81
82    /// Ensure that no accessor method will panic if called.
83    /// Returns `Err(Error)` if the buffer is too short.
84    ///
85    /// The result of this check is invalidated by calling [set_hardware_len] or
86    /// [set_protocol_len].
87    ///
88    /// [set_hardware_len]: #method.set_hardware_len
89    /// [set_protocol_len]: #method.set_protocol_len
90    #[allow(clippy::if_same_then_else)]
91    pub fn check_len(&self) -> Result<()> {
92        let len = self.buffer.as_ref().len();
93        if len < field::OPER.end {
94            Err(Error)
95        } else if len < field::TPA(self.hardware_len(), self.protocol_len()).end {
96            Err(Error)
97        } else {
98            Ok(())
99        }
100    }
101
102    /// Consume the packet, returning the underlying buffer.
103    pub fn into_inner(self) -> T {
104        self.buffer
105    }
106
107    /// Return the hardware type field.
108    #[inline]
109    pub fn hardware_type(&self) -> Hardware {
110        let data = self.buffer.as_ref();
111        let raw = NetworkEndian::read_u16(&data[field::HTYPE]);
112        Hardware::from(raw)
113    }
114
115    /// Return the protocol type field.
116    #[inline]
117    pub fn protocol_type(&self) -> Protocol {
118        let data = self.buffer.as_ref();
119        let raw = NetworkEndian::read_u16(&data[field::PTYPE]);
120        Protocol::from(raw)
121    }
122
123    /// Return the hardware length field.
124    #[inline]
125    pub fn hardware_len(&self) -> u8 {
126        let data = self.buffer.as_ref();
127        data[field::HLEN]
128    }
129
130    /// Return the protocol length field.
131    #[inline]
132    pub fn protocol_len(&self) -> u8 {
133        let data = self.buffer.as_ref();
134        data[field::PLEN]
135    }
136
137    /// Return the operation field.
138    #[inline]
139    pub fn operation(&self) -> Operation {
140        let data = self.buffer.as_ref();
141        let raw = NetworkEndian::read_u16(&data[field::OPER]);
142        Operation::from(raw)
143    }
144
145    /// Return the source hardware address field.
146    pub fn source_hardware_addr(&self) -> &[u8] {
147        let data = self.buffer.as_ref();
148        &data[field::SHA(self.hardware_len(), self.protocol_len())]
149    }
150
151    /// Return the source protocol address field.
152    pub fn source_protocol_addr(&self) -> &[u8] {
153        let data = self.buffer.as_ref();
154        &data[field::SPA(self.hardware_len(), self.protocol_len())]
155    }
156
157    /// Return the target hardware address field.
158    pub fn target_hardware_addr(&self) -> &[u8] {
159        let data = self.buffer.as_ref();
160        &data[field::THA(self.hardware_len(), self.protocol_len())]
161    }
162
163    /// Return the target protocol address field.
164    pub fn target_protocol_addr(&self) -> &[u8] {
165        let data = self.buffer.as_ref();
166        &data[field::TPA(self.hardware_len(), self.protocol_len())]
167    }
168}
169
170impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
171    /// Set the hardware type field.
172    #[inline]
173    pub fn set_hardware_type(&mut self, value: Hardware) {
174        let data = self.buffer.as_mut();
175        NetworkEndian::write_u16(&mut data[field::HTYPE], value.into())
176    }
177
178    /// Set the protocol type field.
179    #[inline]
180    pub fn set_protocol_type(&mut self, value: Protocol) {
181        let data = self.buffer.as_mut();
182        NetworkEndian::write_u16(&mut data[field::PTYPE], value.into())
183    }
184
185    /// Set the hardware length field.
186    #[inline]
187    pub fn set_hardware_len(&mut self, value: u8) {
188        let data = self.buffer.as_mut();
189        data[field::HLEN] = value
190    }
191
192    /// Set the protocol length field.
193    #[inline]
194    pub fn set_protocol_len(&mut self, value: u8) {
195        let data = self.buffer.as_mut();
196        data[field::PLEN] = value
197    }
198
199    /// Set the operation field.
200    #[inline]
201    pub fn set_operation(&mut self, value: Operation) {
202        let data = self.buffer.as_mut();
203        NetworkEndian::write_u16(&mut data[field::OPER], value.into())
204    }
205
206    /// Set the source hardware address field.
207    ///
208    /// # Panics
209    /// The function panics if `value` is not `self.hardware_len()` long.
210    pub fn set_source_hardware_addr(&mut self, value: &[u8]) {
211        let (hardware_len, protocol_len) = (self.hardware_len(), self.protocol_len());
212        let data = self.buffer.as_mut();
213        data[field::SHA(hardware_len, protocol_len)].copy_from_slice(value)
214    }
215
216    /// Set the source protocol address field.
217    ///
218    /// # Panics
219    /// The function panics if `value` is not `self.protocol_len()` long.
220    pub fn set_source_protocol_addr(&mut self, value: &[u8]) {
221        let (hardware_len, protocol_len) = (self.hardware_len(), self.protocol_len());
222        let data = self.buffer.as_mut();
223        data[field::SPA(hardware_len, protocol_len)].copy_from_slice(value)
224    }
225
226    /// Set the target hardware address field.
227    ///
228    /// # Panics
229    /// The function panics if `value` is not `self.hardware_len()` long.
230    pub fn set_target_hardware_addr(&mut self, value: &[u8]) {
231        let (hardware_len, protocol_len) = (self.hardware_len(), self.protocol_len());
232        let data = self.buffer.as_mut();
233        data[field::THA(hardware_len, protocol_len)].copy_from_slice(value)
234    }
235
236    /// Set the target protocol address field.
237    ///
238    /// # Panics
239    /// The function panics if `value` is not `self.protocol_len()` long.
240    pub fn set_target_protocol_addr(&mut self, value: &[u8]) {
241        let (hardware_len, protocol_len) = (self.hardware_len(), self.protocol_len());
242        let data = self.buffer.as_mut();
243        data[field::TPA(hardware_len, protocol_len)].copy_from_slice(value)
244    }
245}
246
247impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
248    fn as_ref(&self) -> &[u8] {
249        self.buffer.as_ref()
250    }
251}
252
253use crate::wire::{EthernetAddress, Ipv4Address};
254
255/// A high-level representation of an Address Resolution Protocol packet.
256#[derive(Debug, PartialEq, Eq, Clone, Copy)]
257#[cfg_attr(feature = "defmt", derive(defmt::Format))]
258#[non_exhaustive]
259pub enum Repr {
260    /// An Ethernet and IPv4 Address Resolution Protocol packet.
261    EthernetIpv4 {
262        operation: Operation,
263        source_hardware_addr: EthernetAddress,
264        source_protocol_addr: Ipv4Address,
265        target_hardware_addr: EthernetAddress,
266        target_protocol_addr: Ipv4Address,
267    },
268}
269
270impl Repr {
271    /// Parse an Address Resolution Protocol packet and return a high-level representation,
272    /// or return `Err(Error)` if the packet is not recognized.
273    pub fn parse<T: AsRef<[u8]>>(packet: &Packet<T>) -> Result<Repr> {
274        match (
275            packet.hardware_type(),
276            packet.protocol_type(),
277            packet.hardware_len(),
278            packet.protocol_len(),
279        ) {
280            (Hardware::Ethernet, Protocol::Ipv4, 6, 4) => Ok(Repr::EthernetIpv4 {
281                operation: packet.operation(),
282                source_hardware_addr: EthernetAddress::from_bytes(packet.source_hardware_addr()),
283                source_protocol_addr: Ipv4Address::from_bytes(packet.source_protocol_addr()),
284                target_hardware_addr: EthernetAddress::from_bytes(packet.target_hardware_addr()),
285                target_protocol_addr: Ipv4Address::from_bytes(packet.target_protocol_addr()),
286            }),
287            _ => Err(Error),
288        }
289    }
290
291    /// Return the length of a packet that will be emitted from this high-level representation.
292    pub const fn buffer_len(&self) -> usize {
293        match *self {
294            Repr::EthernetIpv4 { .. } => field::TPA(6, 4).end,
295        }
296    }
297
298    /// Emit a high-level representation into an Address Resolution Protocol packet.
299    pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>) {
300        match *self {
301            Repr::EthernetIpv4 {
302                operation,
303                source_hardware_addr,
304                source_protocol_addr,
305                target_hardware_addr,
306                target_protocol_addr,
307            } => {
308                packet.set_hardware_type(Hardware::Ethernet);
309                packet.set_protocol_type(Protocol::Ipv4);
310                packet.set_hardware_len(6);
311                packet.set_protocol_len(4);
312                packet.set_operation(operation);
313                packet.set_source_hardware_addr(source_hardware_addr.as_bytes());
314                packet.set_source_protocol_addr(source_protocol_addr.as_bytes());
315                packet.set_target_hardware_addr(target_hardware_addr.as_bytes());
316                packet.set_target_protocol_addr(target_protocol_addr.as_bytes());
317            }
318        }
319    }
320}
321
322impl<T: AsRef<[u8]>> fmt::Display for Packet<T> {
323    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
324        match Repr::parse(self) {
325            Ok(repr) => write!(f, "{repr}"),
326            _ => {
327                write!(f, "ARP (unrecognized)")?;
328                write!(
329                    f,
330                    " htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}",
331                    self.hardware_type(),
332                    self.protocol_type(),
333                    self.hardware_len(),
334                    self.protocol_len(),
335                    self.operation()
336                )?;
337                write!(
338                    f,
339                    " sha={:?} spa={:?} tha={:?} tpa={:?}",
340                    self.source_hardware_addr(),
341                    self.source_protocol_addr(),
342                    self.target_hardware_addr(),
343                    self.target_protocol_addr()
344                )?;
345                Ok(())
346            }
347        }
348    }
349}
350
351impl fmt::Display for Repr {
352    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
353        match *self {
354            Repr::EthernetIpv4 {
355                operation,
356                source_hardware_addr,
357                source_protocol_addr,
358                target_hardware_addr,
359                target_protocol_addr,
360            } => {
361                write!(
362                    f,
363                    "ARP type=Ethernet+IPv4 src={source_hardware_addr}/{source_protocol_addr} tgt={target_hardware_addr}/{target_protocol_addr} op={operation:?}"
364                )
365            }
366        }
367    }
368}
369
370use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
371
372impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
373    fn pretty_print(
374        buffer: &dyn AsRef<[u8]>,
375        f: &mut fmt::Formatter,
376        indent: &mut PrettyIndent,
377    ) -> fmt::Result {
378        match Packet::new_checked(buffer) {
379            Err(err) => write!(f, "{indent}({err})"),
380            Ok(packet) => write!(f, "{indent}{packet}"),
381        }
382    }
383}
384
385#[cfg(test)]
386mod test {
387    use super::*;
388
389    static PACKET_BYTES: [u8; 28] = [
390        0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x21,
391        0x22, 0x23, 0x24, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x41, 0x42, 0x43, 0x44,
392    ];
393
394    #[test]
395    fn test_deconstruct() {
396        let packet = Packet::new_unchecked(&PACKET_BYTES[..]);
397        assert_eq!(packet.hardware_type(), Hardware::Ethernet);
398        assert_eq!(packet.protocol_type(), Protocol::Ipv4);
399        assert_eq!(packet.hardware_len(), 6);
400        assert_eq!(packet.protocol_len(), 4);
401        assert_eq!(packet.operation(), Operation::Request);
402        assert_eq!(
403            packet.source_hardware_addr(),
404            &[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]
405        );
406        assert_eq!(packet.source_protocol_addr(), &[0x21, 0x22, 0x23, 0x24]);
407        assert_eq!(
408            packet.target_hardware_addr(),
409            &[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]
410        );
411        assert_eq!(packet.target_protocol_addr(), &[0x41, 0x42, 0x43, 0x44]);
412    }
413
414    #[test]
415    fn test_construct() {
416        let mut bytes = vec![0xa5; 28];
417        let mut packet = Packet::new_unchecked(&mut bytes);
418        packet.set_hardware_type(Hardware::Ethernet);
419        packet.set_protocol_type(Protocol::Ipv4);
420        packet.set_hardware_len(6);
421        packet.set_protocol_len(4);
422        packet.set_operation(Operation::Request);
423        packet.set_source_hardware_addr(&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]);
424        packet.set_source_protocol_addr(&[0x21, 0x22, 0x23, 0x24]);
425        packet.set_target_hardware_addr(&[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]);
426        packet.set_target_protocol_addr(&[0x41, 0x42, 0x43, 0x44]);
427        assert_eq!(&*packet.into_inner(), &PACKET_BYTES[..]);
428    }
429
430    fn packet_repr() -> Repr {
431        Repr::EthernetIpv4 {
432            operation: Operation::Request,
433            source_hardware_addr: EthernetAddress::from_bytes(&[
434                0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
435            ]),
436            source_protocol_addr: Ipv4Address::from_bytes(&[0x21, 0x22, 0x23, 0x24]),
437            target_hardware_addr: EthernetAddress::from_bytes(&[
438                0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
439            ]),
440            target_protocol_addr: Ipv4Address::from_bytes(&[0x41, 0x42, 0x43, 0x44]),
441        }
442    }
443
444    #[test]
445    fn test_parse() {
446        let packet = Packet::new_unchecked(&PACKET_BYTES[..]);
447        let repr = Repr::parse(&packet).unwrap();
448        assert_eq!(repr, packet_repr());
449    }
450
451    #[test]
452    fn test_emit() {
453        let mut bytes = vec![0xa5; 28];
454        let mut packet = Packet::new_unchecked(&mut bytes);
455        packet_repr().emit(&mut packet);
456        assert_eq!(&*packet.into_inner(), &PACKET_BYTES[..]);
457    }
458}