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