Skip to main content

smoltcp/wire/
arp.rs

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