smoltcp/wire/
arp.rs

1use byteorder::{ByteOrder, NetworkEndian};
2use core::fmt;
3
4use super::{Error, Result};
5use super::{EthernetAddress, Ipv4Address, Ipv4AddressExt};
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_bytes(packet.source_protocol_addr()),
285                target_hardware_addr: EthernetAddress::from_bytes(packet.target_hardware_addr()),
286                target_protocol_addr: Ipv4Address::from_bytes(packet.target_protocol_addr()),
287            }),
288            _ => Err(Error),
289        }
290    }
291
292    /// Return the length of a packet that will be emitted from this high-level representation.
293    pub const fn buffer_len(&self) -> usize {
294        match *self {
295            Repr::EthernetIpv4 { .. } => field::TPA(6, 4).end,
296        }
297    }
298
299    /// Emit a high-level representation into an Address Resolution Protocol packet.
300    pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>) {
301        match *self {
302            Repr::EthernetIpv4 {
303                operation,
304                source_hardware_addr,
305                source_protocol_addr,
306                target_hardware_addr,
307                target_protocol_addr,
308            } => {
309                packet.set_hardware_type(Hardware::Ethernet);
310                packet.set_protocol_type(Protocol::Ipv4);
311                packet.set_hardware_len(6);
312                packet.set_protocol_len(4);
313                packet.set_operation(operation);
314                packet.set_source_hardware_addr(source_hardware_addr.as_bytes());
315                packet.set_source_protocol_addr(&source_protocol_addr.octets());
316                packet.set_target_hardware_addr(target_hardware_addr.as_bytes());
317                packet.set_target_protocol_addr(&target_protocol_addr.octets());
318            }
319        }
320    }
321}
322
323impl<T: AsRef<[u8]>> fmt::Display for Packet<T> {
324    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
325        match Repr::parse(self) {
326            Ok(repr) => write!(f, "{repr}"),
327            _ => {
328                write!(f, "ARP (unrecognized)")?;
329                write!(
330                    f,
331                    " htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}",
332                    self.hardware_type(),
333                    self.protocol_type(),
334                    self.hardware_len(),
335                    self.protocol_len(),
336                    self.operation()
337                )?;
338                write!(
339                    f,
340                    " sha={:?} spa={:?} tha={:?} tpa={:?}",
341                    self.source_hardware_addr(),
342                    self.source_protocol_addr(),
343                    self.target_hardware_addr(),
344                    self.target_protocol_addr()
345                )?;
346                Ok(())
347            }
348        }
349    }
350}
351
352impl fmt::Display for Repr {
353    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
354        match *self {
355            Repr::EthernetIpv4 {
356                operation,
357                source_hardware_addr,
358                source_protocol_addr,
359                target_hardware_addr,
360                target_protocol_addr,
361            } => {
362                write!(
363                    f,
364                    "ARP type=Ethernet+IPv4 src={source_hardware_addr}/{source_protocol_addr} tgt={target_hardware_addr}/{target_protocol_addr} op={operation:?}"
365                )
366            }
367        }
368    }
369}
370
371use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
372
373impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
374    fn pretty_print(
375        buffer: &dyn AsRef<[u8]>,
376        f: &mut fmt::Formatter,
377        indent: &mut PrettyIndent,
378    ) -> fmt::Result {
379        match Packet::new_checked(buffer) {
380            Err(err) => write!(f, "{indent}({err})"),
381            Ok(packet) => write!(f, "{indent}{packet}"),
382        }
383    }
384}
385
386#[cfg(test)]
387mod test {
388    use super::*;
389
390    static PACKET_BYTES: [u8; 28] = [
391        0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x21,
392        0x22, 0x23, 0x24, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x41, 0x42, 0x43, 0x44,
393    ];
394
395    #[test]
396    fn test_deconstruct() {
397        let packet = Packet::new_unchecked(&PACKET_BYTES[..]);
398        assert_eq!(packet.hardware_type(), Hardware::Ethernet);
399        assert_eq!(packet.protocol_type(), Protocol::Ipv4);
400        assert_eq!(packet.hardware_len(), 6);
401        assert_eq!(packet.protocol_len(), 4);
402        assert_eq!(packet.operation(), Operation::Request);
403        assert_eq!(
404            packet.source_hardware_addr(),
405            &[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]
406        );
407        assert_eq!(packet.source_protocol_addr(), &[0x21, 0x22, 0x23, 0x24]);
408        assert_eq!(
409            packet.target_hardware_addr(),
410            &[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]
411        );
412        assert_eq!(packet.target_protocol_addr(), &[0x41, 0x42, 0x43, 0x44]);
413    }
414
415    #[test]
416    fn test_construct() {
417        let mut bytes = vec![0xa5; 28];
418        let mut packet = Packet::new_unchecked(&mut bytes);
419        packet.set_hardware_type(Hardware::Ethernet);
420        packet.set_protocol_type(Protocol::Ipv4);
421        packet.set_hardware_len(6);
422        packet.set_protocol_len(4);
423        packet.set_operation(Operation::Request);
424        packet.set_source_hardware_addr(&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]);
425        packet.set_source_protocol_addr(&[0x21, 0x22, 0x23, 0x24]);
426        packet.set_target_hardware_addr(&[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]);
427        packet.set_target_protocol_addr(&[0x41, 0x42, 0x43, 0x44]);
428        assert_eq!(&*packet.into_inner(), &PACKET_BYTES[..]);
429    }
430
431    fn packet_repr() -> Repr {
432        Repr::EthernetIpv4 {
433            operation: Operation::Request,
434            source_hardware_addr: EthernetAddress::from_bytes(&[
435                0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
436            ]),
437            source_protocol_addr: Ipv4Address::from_bytes(&[0x21, 0x22, 0x23, 0x24]),
438            target_hardware_addr: EthernetAddress::from_bytes(&[
439                0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
440            ]),
441            target_protocol_addr: Ipv4Address::from_bytes(&[0x41, 0x42, 0x43, 0x44]),
442        }
443    }
444
445    #[test]
446    fn test_parse() {
447        let packet = Packet::new_unchecked(&PACKET_BYTES[..]);
448        let repr = Repr::parse(&packet).unwrap();
449        assert_eq!(repr, packet_repr());
450    }
451
452    #[test]
453    fn test_emit() {
454        let mut bytes = vec![0xa5; 28];
455        let mut packet = Packet::new_unchecked(&mut bytes);
456        packet_repr().emit(&mut packet);
457        assert_eq!(&*packet.into_inner(), &PACKET_BYTES[..]);
458    }
459}