packet_formats/
arp.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Parsing and serialization of ARP packets.
6
7#[cfg(test)]
8use core::fmt::{self, Debug, Formatter};
9use core::hash::Hash;
10use core::mem;
11
12use net_types::ethernet::Mac;
13use net_types::ip::{IpAddress, Ipv4Addr};
14use packet::{BufferView, BufferViewMut, InnerPacketBuilder, ParsablePacket, ParseMetadata};
15use zerocopy::byteorder::network_endian::U16;
16use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, Unaligned};
17
18use crate::error::{ParseError, ParseResult};
19
20#[cfg(test)]
21pub(crate) const ARP_HDR_LEN: usize = 8;
22#[cfg(test)]
23pub(crate) const ARP_ETHERNET_IPV4_PACKET_LEN: usize = 28;
24
25create_protocol_enum!(
26    /// The type of an ARP operation.
27    #[derive(Copy, Clone, Eq, PartialEq)]
28    #[allow(missing_docs)]
29    pub enum ArpOp : u16 {
30        Request, 0x0001, "Request";
31        Response, 0x0002, "Response";
32        _, "ArpOp {}";
33    }
34);
35
36/// A trait to represent an ARP hardware type.
37pub trait HType: FromBytes + IntoBytes + Immutable + Unaligned + Copy + Clone + Hash + Eq {
38    /// The hardware type.
39    const HTYPE: ArpHardwareType;
40    /// The in-memory size of an instance of the type.
41    const HLEN: u8;
42    /// The broadcast address for this type.
43    const BROADCAST: Self;
44    /// The all-zeros address for this type.
45    const UNSPECIFIED: Self;
46}
47
48/// A trait to represent an ARP protocol type.
49pub trait PType: FromBytes + IntoBytes + Immutable + Unaligned + Copy + Clone + Hash + Eq {
50    /// The protocol type.
51    const PTYPE: ArpNetworkType;
52    /// The in-memory size of an instance of the type.
53    const PLEN: u8;
54}
55
56impl HType for Mac {
57    const HTYPE: ArpHardwareType = ArpHardwareType::Ethernet;
58    const HLEN: u8 = mem::size_of::<Mac>() as u8;
59    const BROADCAST: Mac = Mac::BROADCAST;
60    const UNSPECIFIED: Mac = Mac::UNSPECIFIED;
61}
62
63impl PType for Ipv4Addr {
64    const PTYPE: ArpNetworkType = ArpNetworkType::Ipv4;
65    const PLEN: u8 = Ipv4Addr::BYTES;
66}
67
68create_protocol_enum!(
69    /// An ARP hardware protocol.
70    #[derive(PartialEq)]
71    #[allow(missing_docs)]
72    pub enum ArpHardwareType : u16 {
73        Ethernet, 0x0001, "Ethernet";
74    }
75);
76
77create_protocol_enum!(
78    /// An ARP network protocol.
79    #[derive(PartialEq)]
80    #[allow(missing_docs)]
81    pub enum ArpNetworkType : u16 {
82        Ipv4, 0x0800, "IPv4";
83    }
84);
85
86#[derive(Default, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
87#[repr(C)]
88struct Header {
89    htype: U16, // Hardware (e.g. Ethernet)
90    ptype: U16, // Protocol (e.g. IPv4)
91    hlen: u8,   // Length (in octets) of hardware address
92    plen: u8,   // Length (in octets) of protocol address
93    oper: U16,  // Operation: 1 for Req, 2 for Reply
94}
95
96impl Header {
97    fn new<HwAddr: HType, ProtoAddr: PType>(op: ArpOp) -> Header {
98        Header {
99            htype: U16::new(<HwAddr as HType>::HTYPE.into()),
100            hlen: <HwAddr as HType>::HLEN,
101            ptype: U16::new(<ProtoAddr as PType>::PTYPE.into()),
102            plen: <ProtoAddr as PType>::PLEN,
103            oper: U16::new(op.into()),
104        }
105    }
106}
107
108/// Peek at an ARP header to see what hardware and protocol address types are
109/// used.
110///
111/// Since `ArpPacket` is statically typed with the hardware and protocol address
112/// types expected in the header and body, these types must be known ahead of
113/// time before calling `parse`. If multiple different types are valid in a
114/// given parsing context, and so the caller cannot know ahead of time which
115/// types to use, `peek_arp_types` can be used to peek at the header first to
116/// figure out which static types should be used in a subsequent call to
117/// `parse`.
118///
119/// Note that `peek_arp_types` only inspects certain fields in the header, and
120/// so `peek_arp_types` succeeding does not guarantee that a subsequent call to
121/// `parse` will also succeed.
122pub fn peek_arp_types<B: SplitByteSlice>(
123    bytes: B,
124) -> ParseResult<(ArpHardwareType, ArpNetworkType)> {
125    let (header, _) = Ref::<B, Header>::from_prefix(bytes).map_err(Into::into).map_err(
126        |_: zerocopy::SizeError<_, _>| debug_err!(ParseError::Format, "too few bytes for header"),
127    )?;
128
129    let hw = ArpHardwareType::try_from(header.htype.get()).ok().ok_or_else(debug_err_fn!(
130        ParseError::NotSupported,
131        "unrecognized hardware protocol: {:x}",
132        header.htype.get()
133    ))?;
134    let proto = ArpNetworkType::try_from(header.ptype.get()).ok().ok_or_else(debug_err_fn!(
135        ParseError::NotSupported,
136        "unrecognized network protocol: {:x}",
137        header.ptype.get()
138    ))?;
139    let hlen = match hw {
140        ArpHardwareType::Ethernet => <Mac as HType>::HLEN,
141    };
142    let plen = match proto {
143        ArpNetworkType::Ipv4 => <Ipv4Addr as PType>::PLEN,
144    };
145    if header.hlen != hlen || header.plen != plen {
146        return debug_err!(
147            Err(ParseError::Format),
148            "unexpected hardware or protocol address length for protocol {:?}",
149            proto
150        );
151    }
152    Ok((hw, proto))
153}
154
155// Body has the same memory layout (thanks to repr(C)) as an ARP body. Thus, we
156// can simply reinterpret the bytes of the ARP body as a Body and then safely
157// access its fields.
158#[derive(KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
159#[repr(C)]
160struct Body<HwAddr, ProtoAddr> {
161    sha: HwAddr,
162    spa: ProtoAddr,
163    tha: HwAddr,
164    tpa: ProtoAddr,
165}
166
167/// An ARP packet.
168///
169/// A `ArpPacket` shares its underlying memory with the byte slice it was parsed
170/// from or serialized to, meaning that no copying or extra allocation is
171/// necessary.
172pub struct ArpPacket<B, HwAddr, ProtoAddr> {
173    header: Ref<B, Header>,
174    body: Ref<B, Body<HwAddr, ProtoAddr>>,
175}
176
177impl<B: SplitByteSlice, HwAddr, ProtoAddr> ParsablePacket<B, ()> for ArpPacket<B, HwAddr, ProtoAddr>
178where
179    HwAddr: Copy + HType + FromBytes + KnownLayout + Unaligned,
180    ProtoAddr: Copy + PType + FromBytes + KnownLayout + Unaligned,
181{
182    type Error = ParseError;
183
184    fn parse_metadata(&self) -> ParseMetadata {
185        ParseMetadata::from_inner_packet(
186            Ref::bytes(&self.header).len() + Ref::bytes(&self.body).len(),
187        )
188    }
189
190    fn parse<BV: BufferView<B>>(mut buffer: BV, _args: ()) -> ParseResult<Self> {
191        let header = buffer
192            .take_obj_front::<Header>()
193            .ok_or_else(debug_err_fn!(ParseError::Format, "too few bytes for header"))?;
194        let body = buffer
195            .take_obj_front::<Body<HwAddr, ProtoAddr>>()
196            .ok_or_else(debug_err_fn!(ParseError::Format, "too few bytes for body"))?;
197        // Consume any padding bytes added by the previous layer.
198        let _: B = buffer.take_rest_front();
199
200        if header.htype.get() != <HwAddr as HType>::HTYPE.into()
201            || header.ptype.get() != <ProtoAddr as PType>::PTYPE.into()
202        {
203            return debug_err!(
204                Err(ParseError::NotExpected),
205                "unexpected hardware or network protocols"
206            );
207        }
208        if header.hlen != <HwAddr as HType>::HLEN || header.plen != <ProtoAddr as PType>::PLEN {
209            return debug_err!(
210                Err(ParseError::Format),
211                "unexpected hardware or protocol address length"
212            );
213        }
214
215        if let ArpOp::Other(x) = header.oper.get().into() {
216            return debug_err!(Err(ParseError::Format), "unrecognized op code: {:x}", x);
217        }
218
219        Ok(ArpPacket { header, body })
220    }
221}
222
223impl<B: SplitByteSlice, HwAddr, ProtoAddr> ArpPacket<B, HwAddr, ProtoAddr>
224where
225    HwAddr: Copy + HType + FromBytes + KnownLayout + Immutable + Unaligned,
226    ProtoAddr: Copy + PType + FromBytes + KnownLayout + Immutable + Unaligned,
227{
228    /// The type of ARP packet
229    pub fn operation(&self) -> ArpOp {
230        self.header.oper.get().into()
231    }
232
233    /// The hardware address of the ARP packet sender.
234    pub fn sender_hardware_address(&self) -> HwAddr {
235        self.body.sha
236    }
237
238    /// The protocol address of the ARP packet sender.
239    pub fn sender_protocol_address(&self) -> ProtoAddr {
240        self.body.spa
241    }
242
243    /// The hardware address of the ARP packet target.
244    pub fn target_hardware_address(&self) -> HwAddr {
245        self.body.tha
246    }
247
248    /// The protocol address of the ARP packet target.
249    pub fn target_protocol_address(&self) -> ProtoAddr {
250        self.body.tpa
251    }
252
253    /// Construct a builder with the same contents as this packet.
254    pub fn builder(&self) -> ArpPacketBuilder<HwAddr, ProtoAddr> {
255        ArpPacketBuilder {
256            op: self.operation(),
257            sha: self.sender_hardware_address(),
258            spa: self.sender_protocol_address(),
259            tha: self.target_hardware_address(),
260            tpa: self.target_protocol_address(),
261        }
262    }
263}
264
265/// A builder for ARP packets.
266#[derive(Debug)]
267pub struct ArpPacketBuilder<HwAddr, ProtoAddr> {
268    op: ArpOp,
269    sha: HwAddr,
270    spa: ProtoAddr,
271    tha: HwAddr,
272    tpa: ProtoAddr,
273}
274
275impl<HwAddr, ProtoAddr> ArpPacketBuilder<HwAddr, ProtoAddr> {
276    /// Construct a new `ArpPacketBuilder`.
277    pub fn new(
278        operation: ArpOp,
279        sender_hardware_addr: HwAddr,
280        sender_protocol_addr: ProtoAddr,
281        target_hardware_addr: HwAddr,
282        target_protocol_addr: ProtoAddr,
283    ) -> ArpPacketBuilder<HwAddr, ProtoAddr> {
284        ArpPacketBuilder {
285            op: operation,
286            sha: sender_hardware_addr,
287            spa: sender_protocol_addr,
288            tha: target_hardware_addr,
289            tpa: target_protocol_addr,
290        }
291    }
292}
293
294impl<HwAddr, ProtoAddr> InnerPacketBuilder for ArpPacketBuilder<HwAddr, ProtoAddr>
295where
296    HwAddr: Copy + HType + FromBytes + IntoBytes + Immutable + Unaligned,
297    ProtoAddr: Copy + PType + FromBytes + IntoBytes + Immutable + Unaligned,
298{
299    fn bytes_len(&self) -> usize {
300        mem::size_of::<Header>() + mem::size_of::<Body<HwAddr, ProtoAddr>>()
301    }
302
303    fn serialize(&self, mut buffer: &mut [u8]) {
304        // implements BufferViewMut, giving us write_obj_front method
305        let mut buffer = &mut buffer;
306        buffer
307            .write_obj_front(&Header::new::<HwAddr, ProtoAddr>(self.op))
308            .expect("too few bytes for ARP packet");
309        buffer
310            .write_obj_front(&Body { sha: self.sha, spa: self.spa, tha: self.tha, tpa: self.tpa })
311            .expect("too few bytes for ARP packet");
312    }
313}
314
315#[cfg(test)]
316impl<B, HwAddr, ProtoAddr> Debug for ArpPacket<B, HwAddr, ProtoAddr> {
317    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
318        write!(fmt, "ArpPacket")
319    }
320}
321
322#[cfg(test)]
323mod tests {
324    use packet::{PacketBuilder, ParseBuffer, Serializer};
325
326    use super::*;
327    use crate::ethernet::{EthernetFrame, EthernetFrameLengthCheck};
328    use crate::testutil::*;
329
330    const TEST_SENDER_IPV4: Ipv4Addr = Ipv4Addr::new([1, 2, 3, 4]);
331    const TEST_TARGET_IPV4: Ipv4Addr = Ipv4Addr::new([5, 6, 7, 8]);
332    const TEST_SENDER_MAC: Mac = Mac::new([0, 1, 2, 3, 4, 5]);
333    const TEST_TARGET_MAC: Mac = Mac::new([6, 7, 8, 9, 10, 11]);
334
335    #[test]
336    fn test_parse_serialize_full() {
337        use crate::testdata::arp_request::*;
338
339        let mut buf = ETHERNET_FRAME.bytes;
340        let frame = buf.parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::Check).unwrap();
341        verify_ethernet_frame(&frame, ETHERNET_FRAME);
342
343        let (hw, proto) = peek_arp_types(frame.body()).unwrap();
344        assert_eq!(hw, ArpHardwareType::Ethernet);
345        assert_eq!(proto, ArpNetworkType::Ipv4);
346
347        let mut body = frame.body();
348        let arp = body.parse::<ArpPacket<_, Mac, Ipv4Addr>>().unwrap();
349        assert_eq!(arp.operation(), ARP_OPERATION);
350        assert_eq!(frame.src_mac(), arp.sender_hardware_address());
351
352        let frame_bytes = frame
353            .builder()
354            .wrap_body(arp.builder().into_serializer())
355            .serialize_vec_outer()
356            .unwrap();
357        assert_eq!(frame_bytes.as_ref(), ETHERNET_FRAME.bytes);
358    }
359
360    fn header_to_bytes(header: Header) -> [u8; ARP_HDR_LEN] {
361        zerocopy::transmute!(header)
362    }
363
364    // Return a new Header for an Ethernet/IPv4 ARP request.
365    fn new_header() -> Header {
366        Header::new::<Mac, Ipv4Addr>(ArpOp::Request)
367    }
368
369    #[test]
370    fn test_peek() {
371        let header = new_header();
372        let (hw, proto) = peek_arp_types(&header_to_bytes(header)[..]).unwrap();
373        assert_eq!(hw, ArpHardwareType::Ethernet);
374        assert_eq!(proto, ArpNetworkType::Ipv4);
375
376        // Test that an invalid operation is not rejected; peek_arp_types does
377        // not inspect the operation.
378        let mut header = new_header();
379        header.oper = U16::new(3);
380        let (hw, proto) = peek_arp_types(&header_to_bytes(header)[..]).unwrap();
381        assert_eq!(hw, ArpHardwareType::Ethernet);
382        assert_eq!(proto, ArpNetworkType::Ipv4);
383    }
384
385    #[test]
386    fn test_parse() {
387        let mut buf = &mut [
388            0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 5, 6, 7, 8,
389        ][..];
390        (&mut buf[..ARP_HDR_LEN]).copy_from_slice(&header_to_bytes(new_header()));
391        let (hw, proto) = peek_arp_types(&buf[..]).unwrap();
392        assert_eq!(hw, ArpHardwareType::Ethernet);
393        assert_eq!(proto, ArpNetworkType::Ipv4);
394
395        let buf = &mut buf;
396        let packet = buf.parse::<ArpPacket<_, Mac, Ipv4Addr>>().unwrap();
397        assert_eq!(packet.sender_hardware_address(), TEST_SENDER_MAC);
398        assert_eq!(packet.sender_protocol_address(), TEST_SENDER_IPV4);
399        assert_eq!(packet.target_hardware_address(), TEST_TARGET_MAC);
400        assert_eq!(packet.target_protocol_address(), TEST_TARGET_IPV4);
401        assert_eq!(packet.operation(), ArpOp::Request);
402    }
403
404    #[test]
405    fn test_serialize() {
406        let mut buf = ArpPacketBuilder::new(
407            ArpOp::Request,
408            TEST_SENDER_MAC,
409            TEST_SENDER_IPV4,
410            TEST_TARGET_MAC,
411            TEST_TARGET_IPV4,
412        )
413        .into_serializer()
414        .serialize_vec_outer()
415        .unwrap();
416        assert_eq!(
417            AsRef::<[u8]>::as_ref(&buf),
418            &[0, 1, 8, 0, 6, 4, 0, 1, 0, 1, 2, 3, 4, 5, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 5, 6, 7, 8,]
419        );
420        let packet = buf.parse::<ArpPacket<_, Mac, Ipv4Addr>>().unwrap();
421        assert_eq!(packet.sender_hardware_address(), TEST_SENDER_MAC);
422        assert_eq!(packet.sender_protocol_address(), TEST_SENDER_IPV4);
423        assert_eq!(packet.target_hardware_address(), TEST_TARGET_MAC);
424        assert_eq!(packet.target_protocol_address(), TEST_TARGET_IPV4);
425        assert_eq!(packet.operation(), ArpOp::Request);
426    }
427
428    #[test]
429    fn test_peek_error() {
430        // Test that a header which is too short is rejected.
431        let buf = [0; ARP_HDR_LEN - 1];
432        assert_eq!(peek_arp_types(&buf[..]).unwrap_err(), ParseError::Format);
433
434        // Test that an unexpected hardware protocol type is rejected.
435        let mut header = new_header();
436        header.htype = U16::ZERO;
437        assert_eq!(
438            peek_arp_types(&header_to_bytes(header)[..]).unwrap_err(),
439            ParseError::NotSupported
440        );
441
442        // Test that an unexpected network protocol type is rejected.
443        let mut header = new_header();
444        header.ptype = U16::ZERO;
445        assert_eq!(
446            peek_arp_types(&header_to_bytes(header)[..]).unwrap_err(),
447            ParseError::NotSupported
448        );
449
450        // Test that an incorrect hardware address len is rejected.
451        let mut header = new_header();
452        header.hlen = 7;
453        assert_eq!(peek_arp_types(&header_to_bytes(header)[..]).unwrap_err(), ParseError::Format);
454
455        // Test that an incorrect protocol address len is rejected.
456        let mut header = new_header();
457        header.plen = 5;
458        assert_eq!(peek_arp_types(&header_to_bytes(header)[..]).unwrap_err(), ParseError::Format);
459    }
460
461    #[test]
462    fn test_parse_error() {
463        // Assert that parsing a buffer results in an error.
464        fn assert_err(mut buf: &[u8], err: ParseError) {
465            assert_eq!(buf.parse::<ArpPacket<_, Mac, Ipv4Addr>>().unwrap_err(), err);
466        }
467
468        // Assert that parsing a particular header results in an error.
469        fn assert_header_err(header: Header, err: ParseError) {
470            let mut buf = [0; ARP_ETHERNET_IPV4_PACKET_LEN];
471            *Ref::<_, Header>::from_prefix(&mut buf[..]).unwrap().0 = header;
472            assert_err(&buf[..], err);
473        }
474
475        // Test that a packet which is too short is rejected.
476        let buf = [0; ARP_ETHERNET_IPV4_PACKET_LEN - 1];
477        assert_err(&buf[..], ParseError::Format);
478
479        // Test that an unexpected hardware protocol type is rejected.
480        let mut header = new_header();
481        header.htype = U16::ZERO;
482        assert_header_err(header, ParseError::NotExpected);
483
484        // Test that an unexpected network protocol type is rejected.
485        let mut header = new_header();
486        header.ptype = U16::ZERO;
487        assert_header_err(header, ParseError::NotExpected);
488
489        // Test that an incorrect hardware address len is rejected.
490        let mut header = new_header();
491        header.hlen = 7;
492        assert_header_err(header, ParseError::Format);
493
494        // Test that an incorrect protocol address len is rejected.
495        let mut header = new_header();
496        header.plen = 5;
497        assert_header_err(header, ParseError::Format);
498
499        // Test that an invalid operation is rejected.
500        let mut header = new_header();
501        header.oper = U16::new(3);
502        assert_header_err(header, ParseError::Format);
503    }
504
505    #[test]
506    fn test_serialize_zeroes() {
507        // Test that ArpPacket::serialize properly zeroes memory before
508        // serializing the packet.
509        let mut buf_0 = [0; ARP_ETHERNET_IPV4_PACKET_LEN];
510        ArpPacketBuilder::new(
511            ArpOp::Request,
512            TEST_SENDER_MAC,
513            TEST_SENDER_IPV4,
514            TEST_TARGET_MAC,
515            TEST_TARGET_IPV4,
516        )
517        .serialize(&mut buf_0[..]);
518        let mut buf_1 = [0xFF; ARP_ETHERNET_IPV4_PACKET_LEN];
519        ArpPacketBuilder::new(
520            ArpOp::Request,
521            TEST_SENDER_MAC,
522            TEST_SENDER_IPV4,
523            TEST_TARGET_MAC,
524            TEST_TARGET_IPV4,
525        )
526        .serialize(&mut buf_1[..]);
527        assert_eq!(buf_0, buf_1);
528    }
529
530    #[test]
531    #[should_panic(expected = "too few bytes for ARP packet")]
532    fn test_serialize_panic_insufficient_packet_space() {
533        // Test that a buffer which doesn't leave enough room for the packet is
534        // rejected.
535        ArpPacketBuilder::new(
536            ArpOp::Request,
537            TEST_SENDER_MAC,
538            TEST_SENDER_IPV4,
539            TEST_TARGET_MAC,
540            TEST_TARGET_IPV4,
541        )
542        .serialize(&mut [0; ARP_ETHERNET_IPV4_PACKET_LEN - 1]);
543    }
544}