netstack3_device/
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//! The Address Resolution Protocol (ARP).
6
7use alloc::fmt::Debug;
8use core::time::Duration;
9
10use log::{debug, trace, warn};
11use net_types::ip::{Ip, Ipv4, Ipv4Addr};
12use net_types::{SpecifiedAddr, UnicastAddr, Witness as _};
13use netstack3_base::{
14    CoreTimerContext, Counter, CounterContext, DeviceIdContext, EventContext, FrameDestination,
15    InstantBindingsTypes, LinkDevice, SendFrameContext, SendFrameError, TimerContext,
16    TxMetadataBindingsTypes, WeakDeviceIdentifier,
17};
18use netstack3_ip::nud::{
19    self, ConfirmationFlags, DynamicNeighborUpdateSource, LinkResolutionContext, NudBindingsTypes,
20    NudConfigContext, NudContext, NudHandler, NudSenderContext, NudState, NudTimerId,
21    NudUserConfig,
22};
23use packet::{BufferMut, InnerPacketBuilder, Serializer};
24use packet_formats::arp::{ArpOp, ArpPacket, ArpPacketBuilder, HType};
25use packet_formats::utils::NonZeroDuration;
26use ref_cast::RefCast;
27
28/// A link device whose addressing scheme is supported by ARP.
29///
30/// `ArpDevice` is implemented for all `L: LinkDevice where L::Address: HType`.
31pub trait ArpDevice: LinkDevice<Address: HType> {}
32
33impl<L: LinkDevice<Address: HType>> ArpDevice for L {}
34
35/// The identifier for timer events in the ARP layer.
36pub(crate) type ArpTimerId<L, D> = NudTimerId<Ipv4, L, D>;
37
38/// The metadata associated with an ARP frame.
39#[cfg_attr(test, derive(Debug, PartialEq, Clone))]
40pub struct ArpFrameMetadata<D: ArpDevice, DeviceId> {
41    /// The ID of the ARP device.
42    pub(super) device_id: DeviceId,
43    /// The destination hardware address.
44    pub(super) dst_addr: D::Address,
45}
46
47/// Counters for the ARP layer.
48#[derive(Default)]
49pub struct ArpCounters {
50    /// Count of ARP packets received from the link layer.
51    pub rx_packets: Counter,
52    /// Count of received ARP packets that were dropped due to being unparsable.
53    pub rx_malformed_packets: Counter,
54    /// Count of received ARP packets that were dropped due to being echoed.
55    /// E.g. an ARP packet sent by us that was reflected back by the network.
56    pub rx_echoed_packets: Counter,
57    /// Count of ARP request packets received.
58    pub rx_requests: Counter,
59    /// Count of ARP response packets received.
60    pub rx_responses: Counter,
61    /// Count of non-gratuitous ARP packets received and dropped because the
62    /// destination address is non-local.
63    pub rx_dropped_non_local_target: Counter,
64    /// Count of ARP request packets sent.
65    pub tx_requests: Counter,
66    /// Count of ARP request packets not sent because the source address was
67    /// unknown or unassigned.
68    pub tx_requests_dropped_no_local_addr: Counter,
69    /// Count of ARP response packets sent.
70    pub tx_responses: Counter,
71}
72
73/// An execution context for the ARP protocol that allows sending IP packets to
74/// specific neighbors.
75pub trait ArpSenderContext<D: ArpDevice, BC: ArpBindingsContext<D, Self::DeviceId>>:
76    ArpConfigContext + DeviceIdContext<D>
77{
78    /// Send an IP packet to the neighbor with address `dst_link_address`.
79    fn send_ip_packet_to_neighbor_link_addr<S>(
80        &mut self,
81        bindings_ctx: &mut BC,
82        dst_link_address: D::Address,
83        body: S,
84        meta: BC::TxMetadata,
85    ) -> Result<(), SendFrameError<S>>
86    where
87        S: Serializer,
88        S::Buffer: BufferMut;
89}
90
91// NOTE(joshlf): The `ArpDevice` parameter may seem unnecessary. We only ever
92// use the associated `HType` type, so why not just take that directly? By the
93// same token, why have it as a parameter on `ArpState`, `ArpTimerId`, and
94// `ArpFrameMetadata`? The answer is that, if we did, there would be no way to
95// distinguish between different link device protocols that all happened to use
96// the same hardware addressing scheme.
97//
98// Consider that the way that we implement context traits is via blanket impls.
99// Even though each module's code _feels_ isolated from the rest of the system,
100// in reality, all context impls end up on the same context type. In particular,
101// all impls are of the form `impl<C: SomeContextTrait> SomeOtherContextTrait
102// for C`. The `C` is the same throughout the whole stack.
103//
104// Thus, for two different link device protocols with the same `HType` and
105// `PType`, if we used an `HType` parameter rather than an `ArpDevice`
106// parameter, the `ArpContext` impls would conflict (in fact, the
107// `StateContext`, `TimerContext`, and `FrameContext` impls would all conflict
108// for similar reasons).
109
110/// The execution context for the ARP protocol provided by bindings.
111pub trait ArpBindingsContext<D: ArpDevice, DeviceId>:
112    TimerContext
113    + LinkResolutionContext<D>
114    + EventContext<nud::Event<D::Address, DeviceId, Ipv4, <Self as InstantBindingsTypes>::Instant>>
115    + TxMetadataBindingsTypes
116{
117}
118
119impl<
120    DeviceId,
121    D: ArpDevice,
122    BC: TimerContext
123        + LinkResolutionContext<D>
124        + EventContext<
125            nud::Event<D::Address, DeviceId, Ipv4, <Self as InstantBindingsTypes>::Instant>,
126        > + TxMetadataBindingsTypes,
127> ArpBindingsContext<D, DeviceId> for BC
128{
129}
130
131/// An execution context for the ARP protocol.
132pub trait ArpContext<D: ArpDevice, BC: ArpBindingsContext<D, Self::DeviceId>>:
133    DeviceIdContext<D>
134    + SendFrameContext<BC, ArpFrameMetadata<D, Self::DeviceId>>
135    + CounterContext<ArpCounters>
136{
137    /// The inner configuration context.
138    type ConfigCtx<'a>: ArpConfigContext;
139    /// The inner sender context.
140    type ArpSenderCtx<'a>: ArpSenderContext<D, BC, DeviceId = Self::DeviceId>;
141
142    /// Calls the function with a mutable reference to ARP state and the
143    /// core sender context.
144    fn with_arp_state_mut_and_sender_ctx<
145        O,
146        F: FnOnce(&mut ArpState<D, BC>, &mut Self::ArpSenderCtx<'_>) -> O,
147    >(
148        &mut self,
149        device_id: &Self::DeviceId,
150        cb: F,
151    ) -> O;
152
153    /// Get a protocol address of this interface.
154    ///
155    /// If `device_id` does not have any addresses associated with it, return
156    /// `None`.
157    ///
158    /// NOTE: If the interface has multiple addresses, an arbitrary one will be
159    /// returned.
160    fn get_protocol_addr(&mut self, device_id: &Self::DeviceId) -> Option<Ipv4Addr>;
161
162    /// Get the hardware address of this interface.
163    fn get_hardware_addr(
164        &mut self,
165        bindings_ctx: &mut BC,
166        device_id: &Self::DeviceId,
167    ) -> UnicastAddr<D::Address>;
168
169    /// Calls the function with a mutable reference to ARP state and the ARP
170    /// configuration context.
171    fn with_arp_state_mut<O, F: FnOnce(&mut ArpState<D, BC>, &mut Self::ConfigCtx<'_>) -> O>(
172        &mut self,
173        device_id: &Self::DeviceId,
174        cb: F,
175    ) -> O;
176
177    /// Calls the function with an immutable reference to ARP state.
178    fn with_arp_state<O, F: FnOnce(&ArpState<D, BC>) -> O>(
179        &mut self,
180        device_id: &Self::DeviceId,
181        cb: F,
182    ) -> O;
183}
184
185/// An execution context for ARP providing functionality from the IP layer.
186pub trait ArpIpLayerContext<D: ArpDevice, BC>: DeviceIdContext<D> {
187    /// Dispatches a received ARP Request or Reply to the IP layer.
188    ///
189    /// The IP layer may use this packet to update internal state, such as
190    /// facilitating Address Conflict Detection (RFC 5227).
191    ///
192    /// Returns whether the `target_addr` is assigned on the device. This is
193    /// used by ARP to send responses to the packet (if applicable).
194    fn on_arp_packet(
195        &mut self,
196        bindings_ctx: &mut BC,
197        device: &Self::DeviceId,
198        sender_addr: Ipv4Addr,
199        target_addr: Ipv4Addr,
200        is_arp_probe: bool,
201    ) -> bool;
202}
203
204/// An execution context for the ARP protocol that allows accessing
205/// configuration parameters.
206pub trait ArpConfigContext {
207    /// The retransmit timeout for ARP frames.
208    ///
209    /// Provided implementation always return the default RFC period.
210    fn retransmit_timeout(&mut self) -> NonZeroDuration {
211        NonZeroDuration::new(DEFAULT_ARP_REQUEST_PERIOD).unwrap()
212    }
213
214    /// Calls the callback with an immutable reference to NUD configurations.
215    fn with_nud_user_config<O, F: FnOnce(&NudUserConfig) -> O>(&mut self, cb: F) -> O;
216}
217
218/// Provides a [`NudContext`] IPv4 implementation for a core context that
219/// implements [`ArpContext`].
220#[derive(RefCast)]
221#[repr(transparent)]
222pub struct ArpNudCtx<CC>(CC);
223
224impl<D, CC> DeviceIdContext<D> for ArpNudCtx<CC>
225where
226    D: ArpDevice,
227    CC: DeviceIdContext<D>,
228{
229    type DeviceId = CC::DeviceId;
230    type WeakDeviceId = CC::WeakDeviceId;
231}
232
233impl<D, CC, BC> NudContext<Ipv4, D, BC> for ArpNudCtx<CC>
234where
235    D: ArpDevice,
236    BC: ArpBindingsContext<D, CC::DeviceId>,
237    CC: ArpContext<D, BC>,
238{
239    type ConfigCtx<'a> = ArpNudCtx<CC::ConfigCtx<'a>>;
240    type SenderCtx<'a> = ArpNudCtx<CC::ArpSenderCtx<'a>>;
241
242    fn with_nud_state_mut_and_sender_ctx<
243        O,
244        F: FnOnce(&mut NudState<Ipv4, D, BC>, &mut Self::SenderCtx<'_>) -> O,
245    >(
246        &mut self,
247        device_id: &Self::DeviceId,
248        cb: F,
249    ) -> O {
250        let Self(core_ctx) = self;
251        core_ctx.with_arp_state_mut_and_sender_ctx(device_id, |ArpState { nud }, core_ctx| {
252            cb(nud, ArpNudCtx::ref_cast_mut(core_ctx))
253        })
254    }
255
256    fn with_nud_state_mut<
257        O,
258        F: FnOnce(&mut NudState<Ipv4, D, BC>, &mut Self::ConfigCtx<'_>) -> O,
259    >(
260        &mut self,
261        device_id: &Self::DeviceId,
262        cb: F,
263    ) -> O {
264        let Self(core_ctx) = self;
265        core_ctx.with_arp_state_mut(device_id, |ArpState { nud }, core_ctx| {
266            cb(nud, ArpNudCtx::ref_cast_mut(core_ctx))
267        })
268    }
269
270    fn with_nud_state<O, F: FnOnce(&NudState<Ipv4, D, BC>) -> O>(
271        &mut self,
272        device_id: &Self::DeviceId,
273        cb: F,
274    ) -> O {
275        let Self(core_ctx) = self;
276        core_ctx.with_arp_state(device_id, |ArpState { nud }| cb(nud))
277    }
278
279    fn send_neighbor_solicitation(
280        &mut self,
281        bindings_ctx: &mut BC,
282        device_id: &Self::DeviceId,
283        lookup_addr: SpecifiedAddr<<Ipv4 as net_types::ip::Ip>::Addr>,
284        remote_link_addr: Option<<D as LinkDevice>::Address>,
285    ) {
286        let Self(core_ctx) = self;
287
288        if let Some(sender_addr) = core_ctx.get_protocol_addr(device_id) {
289            send_arp_request(
290                core_ctx,
291                bindings_ctx,
292                device_id,
293                sender_addr,
294                *lookup_addr,
295                remote_link_addr,
296            );
297        } else {
298            // RFC 826 does not specify what to do if we don't have a local address,
299            // but there is no reasonable way to send an ARP request without one (as
300            // the receiver will cache our local address on receiving the packet).
301            // So, if this is the case, we do not send an ARP request.
302            core_ctx.counters().tx_requests_dropped_no_local_addr.increment();
303            debug!("Not sending ARP request, since we don't know our local protocol address");
304        }
305    }
306}
307
308impl<CC: ArpConfigContext> NudConfigContext<Ipv4> for ArpNudCtx<CC> {
309    fn retransmit_timeout(&mut self) -> NonZeroDuration {
310        let Self(core_ctx) = self;
311        core_ctx.retransmit_timeout()
312    }
313
314    fn with_nud_user_config<O, F: FnOnce(&NudUserConfig) -> O>(&mut self, cb: F) -> O {
315        let Self(core_ctx) = self;
316        core_ctx.with_nud_user_config(cb)
317    }
318}
319
320impl<D: ArpDevice, BC: ArpBindingsContext<D, CC::DeviceId>, CC: ArpSenderContext<D, BC>>
321    NudSenderContext<Ipv4, D, BC> for ArpNudCtx<CC>
322{
323    fn send_ip_packet_to_neighbor_link_addr<S>(
324        &mut self,
325        bindings_ctx: &mut BC,
326        dst_mac: D::Address,
327        body: S,
328        meta: BC::TxMetadata,
329    ) -> Result<(), SendFrameError<S>>
330    where
331        S: Serializer,
332        S::Buffer: BufferMut,
333    {
334        let Self(core_ctx) = self;
335        core_ctx.send_ip_packet_to_neighbor_link_addr(bindings_ctx, dst_mac, body, meta)
336    }
337}
338
339pub(crate) trait ArpPacketHandler<D: ArpDevice, BC>: DeviceIdContext<D> {
340    fn handle_packet<B: BufferMut + Debug>(
341        &mut self,
342        bindings_ctx: &mut BC,
343        device_id: Self::DeviceId,
344        frame_dst: FrameDestination,
345        buffer: B,
346    );
347}
348
349impl<
350    D: ArpDevice,
351    BC: ArpBindingsContext<D, CC::DeviceId>,
352    CC: ArpContext<D, BC> + ArpIpLayerContext<D, BC> + NudHandler<Ipv4, D, BC>,
353> ArpPacketHandler<D, BC> for CC
354{
355    /// Handles an inbound ARP packet.
356    fn handle_packet<B: BufferMut + Debug>(
357        &mut self,
358        bindings_ctx: &mut BC,
359        device_id: Self::DeviceId,
360        frame_dst: FrameDestination,
361        buffer: B,
362    ) {
363        handle_packet(self, bindings_ctx, device_id, frame_dst, buffer)
364    }
365}
366
367fn handle_packet<
368    D: ArpDevice,
369    BC: ArpBindingsContext<D, CC::DeviceId>,
370    B: BufferMut + Debug,
371    CC: ArpContext<D, BC> + ArpIpLayerContext<D, BC> + NudHandler<Ipv4, D, BC>,
372>(
373    core_ctx: &mut CC,
374    bindings_ctx: &mut BC,
375    device_id: CC::DeviceId,
376    frame_dst: FrameDestination,
377    mut buffer: B,
378) {
379    core_ctx.counters().rx_packets.increment();
380    let packet = match buffer.parse::<ArpPacket<_, D::Address, Ipv4Addr>>() {
381        Ok(packet) => packet,
382        Err(err) => {
383            // If parse failed, it's because either the packet was malformed, or
384            // it was for an unexpected hardware or network protocol. In either
385            // case, we just drop the packet and move on. RFC 826's "Packet
386            // Reception" section says of packet processing algorithm, "Negative
387            // conditionals indicate an end of processing and a discarding of
388            // the packet."
389            debug!("discarding malformed ARP packet: {}", err);
390            core_ctx.counters().rx_malformed_packets.increment();
391            return;
392        }
393    };
394
395    #[derive(Debug)]
396    enum ValidArpOp {
397        Request,
398        Response,
399    }
400
401    let op = match packet.operation() {
402        ArpOp::Request => {
403            core_ctx.counters().rx_requests.increment();
404            ValidArpOp::Request
405        }
406        ArpOp::Response => {
407            core_ctx.counters().rx_responses.increment();
408            ValidArpOp::Response
409        }
410        ArpOp::Other(o) => {
411            core_ctx.counters().rx_malformed_packets.increment();
412            debug!("dropping arp packet with op = {:?}", o);
413            return;
414        }
415    };
416
417    // If the sender's hardware address is *our* hardware address, this is
418    // an echoed ARP packet (e.g. the network reflected back an ARP packet that
419    // we sent). Here we deviate from the behavior specified in RFC 826 (which
420    // makes no comment on handling echoed ARP packets), and drop the packet.
421    // There's no benefit to tracking our own ARP packets in the ARP table, and
422    // some RFCs built on top of ARP (i.e. RFC 5227 - IPv4 Address Conflict
423    // Detection) explicitly call out that echoed ARP packets should be ignored.
424    let sender_hw_addr = packet.sender_hardware_address();
425    if sender_hw_addr == *core_ctx.get_hardware_addr(bindings_ctx, &device_id) {
426        core_ctx.counters().rx_echoed_packets.increment();
427        debug!("dropping an echoed ARP packet: {op:?}");
428        return;
429    }
430
431    let sender_addr = packet.sender_protocol_address();
432    let target_addr = packet.target_protocol_address();
433
434    // As per RFC 5227, section 1.1:
435    // the term 'ARP Probe' is used to refer to an ARP Request packet, broadcast
436    // on the local link, with an all-zero 'sender IP address'.
437    let is_arp_probe = sender_addr == Ipv4::UNSPECIFIED_ADDRESS
438        && packet.operation() == ArpOp::Request
439        && frame_dst == FrameDestination::Broadcast;
440
441    // As Per RFC 5227, section 2.1.1 dispatch any received ARP packet
442    // (Request *or* Reply) to the DAD engine.
443    let targets_interface =
444        core_ctx.on_arp_packet(bindings_ctx, &device_id, sender_addr, target_addr, is_arp_probe);
445
446    enum PacketKind {
447        Gratuitous,
448        AddressedToMe,
449    }
450
451    // The following logic is equivalent to the "Packet Reception" section of
452    // RFC 826.
453    //
454    // We statically know that the hardware type and protocol type are correct,
455    // so we do not need to have additional code to check that. The remainder of
456    // the algorithm is:
457    //
458    // Merge_flag := false
459    // If the pair <protocol type, sender protocol address> is
460    //     already in my translation table, update the sender
461    //     hardware address field of the entry with the new
462    //     information in the packet and set Merge_flag to true.
463    // ?Am I the target protocol address?
464    // Yes:
465    //   If Merge_flag is false, add the triplet <protocol type,
466    //       sender protocol address, sender hardware address> to
467    //       the translation table.
468    //   ?Is the opcode ares_op$REQUEST?  (NOW look at the opcode!!)
469    //   Yes:
470    //     Swap hardware and protocol fields, putting the local
471    //         hardware and protocol addresses in the sender fields.
472    //     Set the ar$op field to ares_op$REPLY
473    //     Send the packet to the (new) target hardware address on
474    //         the same hardware on which the request was received.
475    //
476    // This can be summed up as follows:
477    //
478    // +----------+---------------+---------------+-----------------------------+
479    // | opcode   | Am I the TPA? | SPA in table? | action                      |
480    // +----------+---------------+---------------+-----------------------------+
481    // | REQUEST  | yes           | yes           | Update table, Send response |
482    // | REQUEST  | yes           | no            | Update table, Send response |
483    // | REQUEST  | no            | yes           | Update table                |
484    // | REQUEST  | no            | no            | NOP                         |
485    // | RESPONSE | yes           | yes           | Update table                |
486    // | RESPONSE | yes           | no            | Update table                |
487    // | RESPONSE | no            | yes           | Update table                |
488    // | RESPONSE | no            | no            | NOP                         |
489    // +----------+---------------+---------------+-----------------------------+
490
491    let garp = sender_addr == target_addr;
492    let (source, kind) = match (garp, targets_interface) {
493        (true, false) => {
494            // Treat all GARP messages as neighbor probes as GARPs are not
495            // responses for previously sent requests, even if the packet
496            // operation is a response OP code.
497            //
498            // Per RFC 5944 section 4.6,
499            //
500            //   A Gratuitous ARP [45] is an ARP packet sent by a node in order
501            //   to spontaneously cause other nodes to update an entry in their
502            //   ARP cache. A gratuitous ARP MAY use either an ARP Request or an
503            //   ARP Reply packet. In either case, the ARP Sender Protocol
504            //   Address and ARP Target Protocol Address are both set to the IP
505            //   address of the cache entry to be updated, and the ARP Sender
506            //   Hardware Address is set to the link-layer address to which this
507            //   cache entry should be updated. When using an ARP Reply packet,
508            //   the Target Hardware Address is also set to the link-layer
509            //   address to which this cache entry should be updated (this field
510            //   is not used in an ARP Request packet).
511            //
512            //   In either case, for a gratuitous ARP, the ARP packet MUST be
513            //   transmitted as a local broadcast packet on the local link. As
514            //   specified in [16], any node receiving any ARP packet (Request
515            //   or Reply) MUST update its local ARP cache with the Sender
516            //   Protocol and Hardware Addresses in the ARP packet, if the
517            //   receiving node has an entry for that IP address already in its
518            //   ARP cache. This requirement in the ARP protocol applies even
519            //   for ARP Request packets, and for ARP Reply packets that do not
520            //   match any ARP Request transmitted by the receiving node [16].
521            (
522                DynamicNeighborUpdateSource::Probe { link_address: sender_hw_addr },
523                PacketKind::Gratuitous,
524            )
525        }
526        (false, true) => {
527            // Consider ARP replies as solicited if they were unicast directly to us, and
528            // unsolicited otherwise.
529            let solicited = match frame_dst {
530                FrameDestination::Individual { local } => local,
531                FrameDestination::Broadcast | FrameDestination::Multicast => false,
532            };
533            let source = match op {
534                ValidArpOp::Request => {
535                    DynamicNeighborUpdateSource::Probe { link_address: sender_hw_addr }
536                }
537                ValidArpOp::Response => {
538                    DynamicNeighborUpdateSource::Confirmation {
539                        link_address: Some(sender_hw_addr),
540                        flags: ConfirmationFlags {
541                            solicited_flag: solicited,
542                            // ARP does not have the concept of an override flag in a neighbor
543                            // confirmation. In order to ensure we don't fail to update a cached
544                            // link address when a neighbor informs us of the update, always
545                            // consider ARP replies as overrides.
546                            override_flag: true,
547                        },
548                    }
549                }
550            };
551            (source, PacketKind::AddressedToMe)
552        }
553        (false, false) => {
554            core_ctx.counters().rx_dropped_non_local_target.increment();
555            trace!(
556                "non-gratuitous ARP packet not targetting us; sender = {}, target={}",
557                sender_addr, target_addr
558            );
559            return;
560        }
561        (true, true) => {
562            warn!(
563                "got gratuitous ARP packet with our address {target_addr} on device {device_id:?}, \
564                dropping...",
565            );
566            return;
567        }
568    };
569
570    if let Some(addr) = SpecifiedAddr::new(sender_addr) {
571        NudHandler::<Ipv4, D, _>::handle_neighbor_update(
572            core_ctx,
573            bindings_ctx,
574            &device_id,
575            addr,
576            source,
577        )
578    };
579
580    match kind {
581        PacketKind::Gratuitous => return,
582        PacketKind::AddressedToMe => match source {
583            DynamicNeighborUpdateSource::Probe { .. } => {
584                let self_hw_addr = core_ctx.get_hardware_addr(bindings_ctx, &device_id);
585
586                core_ctx.counters().tx_responses.increment();
587                debug!("sending ARP response for {target_addr} to {sender_addr}");
588
589                SendFrameContext::send_frame(
590                    core_ctx,
591                    bindings_ctx,
592                    ArpFrameMetadata { device_id, dst_addr: sender_hw_addr },
593                    ArpPacketBuilder::new(
594                        ArpOp::Response,
595                        self_hw_addr.get(),
596                        target_addr,
597                        sender_hw_addr,
598                        sender_addr,
599                    )
600                    .into_serializer_with(buffer),
601                )
602                .unwrap_or_else(|serializer| {
603                    warn!(
604                        "failed to send ARP response for {target_addr} to {sender_addr}: \
605                        {serializer:?}"
606                    )
607                });
608            }
609            DynamicNeighborUpdateSource::Confirmation { .. } => {}
610        },
611    }
612}
613
614// Use the same default retransmit timeout that is defined for NDP in
615// [RFC 4861 section 10], to align behavior between IPv4 and IPv6 and simplify
616// testing.
617//
618// TODO(https://fxbug.dev/42075782): allow this default to be overridden.
619//
620// [RFC 4861 section 10]: https://tools.ietf.org/html/rfc4861#section-10
621const DEFAULT_ARP_REQUEST_PERIOD: Duration = nud::IPV6_RETRANS_TIMER_DEFAULT.get();
622
623/// Sends an Arp Request for the provided lookup_addr.
624///
625/// If remote_link_addr is provided, it will be the destination of the request.
626/// If unset, the request will be broadcast.
627pub fn send_arp_request<
628    D: ArpDevice,
629    BC: ArpBindingsContext<D, CC::DeviceId>,
630    CC: ArpContext<D, BC> + CounterContext<ArpCounters>,
631>(
632    core_ctx: &mut CC,
633    bindings_ctx: &mut BC,
634    device_id: &CC::DeviceId,
635    sender_addr: Ipv4Addr,
636    lookup_addr: Ipv4Addr,
637    remote_link_addr: Option<D::Address>,
638) {
639    let self_hw_addr = core_ctx.get_hardware_addr(bindings_ctx, device_id);
640    let dst_addr = remote_link_addr.unwrap_or(D::Address::BROADCAST);
641    core_ctx.counters().tx_requests.increment();
642    debug!("sending ARP request for {lookup_addr} to {dst_addr:?}");
643    SendFrameContext::send_frame(
644        core_ctx,
645        bindings_ctx,
646        ArpFrameMetadata { device_id: device_id.clone(), dst_addr },
647        ArpPacketBuilder::new(
648            ArpOp::Request,
649            self_hw_addr.get(),
650            sender_addr,
651            // This field is relatively meaningless, since RFC 826 does not
652            // specify the behavior. However, RFC 5227 section 2.1.1. specifies
653            // that the target hardware address SHOULD be set to all 0s when
654            // sending an Address Conflict Detection probe.
655            //
656            // To accommodate this, we use the `remote_link_addr` if provided,
657            // or otherwise the unspecified address. Notably this makes the
658            // ARP target hardware address field differ from the destination
659            // address in the Frame Metadata.
660            remote_link_addr.unwrap_or(D::Address::UNSPECIFIED),
661            lookup_addr,
662        )
663        .into_serializer(),
664    )
665    .unwrap_or_else(|serializer| {
666        warn!("failed to send ARP request for {lookup_addr} to {dst_addr:?}: {serializer:?}")
667    });
668}
669
670/// The state associated with an instance of the Address Resolution Protocol
671/// (ARP).
672///
673/// Each device will contain an `ArpState` object for each of the network
674/// protocols that it supports.
675pub struct ArpState<D: ArpDevice, BT: NudBindingsTypes<D>> {
676    pub(crate) nud: NudState<Ipv4, D, BT>,
677}
678
679impl<D: ArpDevice, BC: NudBindingsTypes<D> + TimerContext> ArpState<D, BC> {
680    /// Creates a new `ArpState` for `device_id`.
681    pub fn new<
682        DeviceId: WeakDeviceIdentifier,
683        CC: CoreTimerContext<ArpTimerId<D, DeviceId>, BC>,
684    >(
685        bindings_ctx: &mut BC,
686        device_id: DeviceId,
687    ) -> Self {
688        ArpState { nud: NudState::new::<_, CC>(bindings_ctx, device_id) }
689    }
690}
691
692#[cfg(test)]
693mod tests {
694    use alloc::vec;
695    use alloc::vec::Vec;
696    use core::iter;
697    use net_types::ip::Ip;
698
699    use assert_matches::assert_matches;
700    use net_types::ethernet::Mac;
701    use netstack3_base::socket::SocketIpAddr;
702    use netstack3_base::testutil::{
703        FakeBindingsCtx, FakeCoreCtx, FakeDeviceId, FakeInstant, FakeLinkDeviceId, FakeNetworkSpec,
704        FakeTimerId, FakeTxMetadata, FakeWeakDeviceId, WithFakeFrameContext, assert_empty,
705    };
706    use netstack3_base::{CtxPair, InstantContext as _, IntoCoreTimerCtx, TimerHandler};
707    use netstack3_ip::nud::testutil::{
708        assert_dynamic_neighbor_state, assert_dynamic_neighbor_with_addr, assert_neighbor_unknown,
709    };
710    use netstack3_ip::nud::{
711        DelegateNudContext, DynamicNeighborState, NudCounters, NudIcmpContext, Reachable, Stale,
712        UseDelegateNudContext,
713    };
714    use packet::{Buf, ParseBuffer};
715    use packet_formats::arp::{ArpHardwareType, ArpNetworkType, peek_arp_types};
716    use packet_formats::ipv4::Ipv4FragmentType;
717    use test_case::test_case;
718
719    use super::*;
720    use crate::internal::ethernet::EthernetLinkDevice;
721
722    const TEST_LOCAL_IPV4: Ipv4Addr = Ipv4Addr::new([1, 2, 3, 4]);
723    const TEST_LOCAL_IPV4_2: Ipv4Addr = Ipv4Addr::new([4, 5, 6, 7]);
724    const TEST_REMOTE_IPV4: Ipv4Addr = Ipv4Addr::new([5, 6, 7, 8]);
725    const TEST_ANOTHER_REMOTE_IPV4: Ipv4Addr = Ipv4Addr::new([9, 10, 11, 12]);
726    const TEST_LOCAL_MAC: Mac = Mac::new([0, 1, 2, 3, 4, 5]);
727    const TEST_REMOTE_MAC: Mac = Mac::new([6, 7, 8, 9, 10, 11]);
728    const TEST_INVALID_MAC: Mac = Mac::new([0, 0, 0, 0, 0, 0]);
729
730    /// A fake `ArpContext` that stores frames, address resolution events, and
731    /// address resolution failure events.
732    struct FakeArpCtx {
733        proto_addrs: Vec<Ipv4Addr>,
734        hw_addr: UnicastAddr<Mac>,
735        arp_state: ArpState<EthernetLinkDevice, FakeBindingsCtxImpl>,
736        inner: FakeArpInnerCtx,
737        config: FakeArpConfigCtx,
738        counters: ArpCounters,
739        nud_counters: NudCounters<Ipv4>,
740        // Stores received ARP packets that were dispatched to the IP Layer.
741        // Holds a tuple of (sender_addr, target_addr, is_arp_probe).
742        dispatched_arp_packets: Vec<(Ipv4Addr, Ipv4Addr, bool)>,
743    }
744
745    /// A fake `ArpSenderContext` that sends IP packets.
746    struct FakeArpInnerCtx;
747
748    /// A fake `ArpConfigContext`.
749    struct FakeArpConfigCtx;
750
751    impl FakeArpCtx {
752        fn new(bindings_ctx: &mut FakeBindingsCtxImpl) -> FakeArpCtx {
753            FakeArpCtx {
754                proto_addrs: vec![TEST_LOCAL_IPV4, TEST_LOCAL_IPV4_2],
755                hw_addr: UnicastAddr::new(TEST_LOCAL_MAC).unwrap(),
756                arp_state: ArpState::new::<_, IntoCoreTimerCtx>(
757                    bindings_ctx,
758                    FakeWeakDeviceId(FakeLinkDeviceId),
759                ),
760                inner: FakeArpInnerCtx,
761                config: FakeArpConfigCtx,
762                counters: Default::default(),
763                nud_counters: Default::default(),
764                dispatched_arp_packets: Default::default(),
765            }
766        }
767    }
768
769    type FakeBindingsCtxImpl = FakeBindingsCtx<
770        ArpTimerId<EthernetLinkDevice, FakeWeakDeviceId<FakeLinkDeviceId>>,
771        nud::Event<Mac, FakeLinkDeviceId, Ipv4, FakeInstant>,
772        (),
773        (),
774    >;
775
776    type FakeCoreCtxImpl = FakeCoreCtx<
777        FakeArpCtx,
778        ArpFrameMetadata<EthernetLinkDevice, FakeLinkDeviceId>,
779        FakeDeviceId,
780    >;
781
782    fn new_context() -> CtxPair<FakeCoreCtxImpl, FakeBindingsCtxImpl> {
783        CtxPair::with_default_bindings_ctx(|bindings_ctx| {
784            FakeCoreCtxImpl::with_state(FakeArpCtx::new(bindings_ctx))
785        })
786    }
787
788    enum ArpNetworkSpec {}
789    impl FakeNetworkSpec for ArpNetworkSpec {
790        type Context = CtxPair<FakeCoreCtxImpl, FakeBindingsCtxImpl>;
791        type TimerId = ArpTimerId<EthernetLinkDevice, FakeWeakDeviceId<FakeLinkDeviceId>>;
792        type SendMeta = ArpFrameMetadata<EthernetLinkDevice, FakeLinkDeviceId>;
793        type RecvMeta = ArpFrameMetadata<EthernetLinkDevice, FakeLinkDeviceId>;
794
795        fn handle_frame(
796            ctx: &mut Self::Context,
797            ArpFrameMetadata { device_id, .. }: Self::RecvMeta,
798            data: Buf<Vec<u8>>,
799        ) {
800            let CtxPair { core_ctx, bindings_ctx } = ctx;
801            handle_packet(
802                core_ctx,
803                bindings_ctx,
804                device_id,
805                FrameDestination::Individual { local: true },
806                data,
807            )
808        }
809        fn handle_timer(ctx: &mut Self::Context, dispatch: Self::TimerId, timer: FakeTimerId) {
810            let CtxPair { core_ctx, bindings_ctx } = ctx;
811            TimerHandler::handle_timer(core_ctx, bindings_ctx, dispatch, timer)
812        }
813
814        fn process_queues(_ctx: &mut Self::Context) -> bool {
815            false
816        }
817
818        fn fake_frames(ctx: &mut Self::Context) -> &mut impl WithFakeFrameContext<Self::SendMeta> {
819            &mut ctx.core_ctx
820        }
821    }
822
823    impl DeviceIdContext<EthernetLinkDevice> for FakeCoreCtxImpl {
824        type DeviceId = FakeLinkDeviceId;
825        type WeakDeviceId = FakeWeakDeviceId<FakeLinkDeviceId>;
826    }
827
828    impl DeviceIdContext<EthernetLinkDevice> for FakeArpInnerCtx {
829        type DeviceId = FakeLinkDeviceId;
830        type WeakDeviceId = FakeWeakDeviceId<FakeLinkDeviceId>;
831    }
832
833    impl ArpContext<EthernetLinkDevice, FakeBindingsCtxImpl> for FakeCoreCtxImpl {
834        type ConfigCtx<'a> = FakeArpConfigCtx;
835
836        type ArpSenderCtx<'a> = FakeArpInnerCtx;
837
838        fn with_arp_state_mut_and_sender_ctx<
839            O,
840            F: FnOnce(
841                &mut ArpState<EthernetLinkDevice, FakeBindingsCtxImpl>,
842                &mut Self::ArpSenderCtx<'_>,
843            ) -> O,
844        >(
845            &mut self,
846            FakeLinkDeviceId: &FakeLinkDeviceId,
847            cb: F,
848        ) -> O {
849            let FakeArpCtx { arp_state, inner, .. } = &mut self.state;
850            cb(arp_state, inner)
851        }
852
853        fn with_arp_state<O, F: FnOnce(&ArpState<EthernetLinkDevice, FakeBindingsCtxImpl>) -> O>(
854            &mut self,
855            FakeLinkDeviceId: &FakeLinkDeviceId,
856            cb: F,
857        ) -> O {
858            cb(&self.state.arp_state)
859        }
860
861        fn get_protocol_addr(&mut self, _device_id: &FakeLinkDeviceId) -> Option<Ipv4Addr> {
862            self.state.proto_addrs.first().copied()
863        }
864
865        fn get_hardware_addr(
866            &mut self,
867            _bindings_ctx: &mut FakeBindingsCtxImpl,
868            _device_id: &FakeLinkDeviceId,
869        ) -> UnicastAddr<Mac> {
870            self.state.hw_addr
871        }
872
873        fn with_arp_state_mut<
874            O,
875            F: FnOnce(
876                &mut ArpState<EthernetLinkDevice, FakeBindingsCtxImpl>,
877                &mut Self::ConfigCtx<'_>,
878            ) -> O,
879        >(
880            &mut self,
881            FakeLinkDeviceId: &FakeLinkDeviceId,
882            cb: F,
883        ) -> O {
884            let FakeArpCtx { arp_state, config, .. } = &mut self.state;
885            cb(arp_state, config)
886        }
887    }
888
889    impl ArpIpLayerContext<EthernetLinkDevice, FakeBindingsCtxImpl> for FakeCoreCtxImpl {
890        fn on_arp_packet(
891            &mut self,
892            _bindings_ctx: &mut FakeBindingsCtxImpl,
893            _device_id: &FakeLinkDeviceId,
894            sender_addr: Ipv4Addr,
895            target_addr: Ipv4Addr,
896            is_arp_probe: bool,
897        ) -> bool {
898            self.state.dispatched_arp_packets.push((sender_addr, target_addr, is_arp_probe));
899
900            self.state.proto_addrs.iter().any(|&a| a == target_addr)
901        }
902    }
903
904    impl UseDelegateNudContext for FakeArpCtx {}
905    impl DelegateNudContext<Ipv4> for FakeArpCtx {
906        type Delegate<T> = ArpNudCtx<T>;
907    }
908
909    impl NudIcmpContext<Ipv4, EthernetLinkDevice, FakeBindingsCtxImpl> for FakeCoreCtxImpl {
910        fn send_icmp_dest_unreachable(
911            &mut self,
912            _bindings_ctx: &mut FakeBindingsCtxImpl,
913            _frame: Buf<Vec<u8>>,
914            _device_id: Option<&Self::DeviceId>,
915            _original_src: SocketIpAddr<Ipv4Addr>,
916            _original_dst: SocketIpAddr<Ipv4Addr>,
917            _metadata: (usize, Ipv4FragmentType),
918        ) {
919            panic!("send_icmp_dest_unreachable should not be called");
920        }
921    }
922
923    impl ArpConfigContext for FakeArpConfigCtx {
924        fn with_nud_user_config<O, F: FnOnce(&NudUserConfig) -> O>(&mut self, cb: F) -> O {
925            cb(&NudUserConfig::default())
926        }
927    }
928    impl ArpConfigContext for FakeArpInnerCtx {
929        fn with_nud_user_config<O, F: FnOnce(&NudUserConfig) -> O>(&mut self, cb: F) -> O {
930            cb(&NudUserConfig::default())
931        }
932    }
933
934    impl ArpSenderContext<EthernetLinkDevice, FakeBindingsCtxImpl> for FakeArpInnerCtx {
935        fn send_ip_packet_to_neighbor_link_addr<S>(
936            &mut self,
937            _bindings_ctx: &mut FakeBindingsCtxImpl,
938            _dst_link_address: Mac,
939            _body: S,
940            _tx_meta: FakeTxMetadata,
941        ) -> Result<(), SendFrameError<S>> {
942            Ok(())
943        }
944    }
945
946    impl CounterContext<ArpCounters> for FakeArpCtx {
947        fn counters(&self) -> &ArpCounters {
948            &self.counters
949        }
950    }
951
952    impl CounterContext<NudCounters<Ipv4>> for FakeArpCtx {
953        fn counters(&self) -> &NudCounters<Ipv4> {
954            &self.nud_counters
955        }
956    }
957
958    fn send_arp_packet(
959        core_ctx: &mut FakeCoreCtxImpl,
960        bindings_ctx: &mut FakeBindingsCtxImpl,
961        op: ArpOp,
962        sender_ipv4: Ipv4Addr,
963        target_ipv4: Ipv4Addr,
964        sender_mac: Mac,
965        target_mac: Mac,
966        frame_dst: FrameDestination,
967    ) {
968        let buf = ArpPacketBuilder::new(op, sender_mac, sender_ipv4, target_mac, target_ipv4)
969            .into_serializer()
970            .serialize_vec_outer()
971            .unwrap();
972        let (hw, proto) = peek_arp_types(buf.as_ref()).unwrap();
973        assert_eq!(hw, ArpHardwareType::Ethernet);
974        assert_eq!(proto, ArpNetworkType::Ipv4);
975
976        handle_packet::<_, _, _, _>(core_ctx, bindings_ctx, FakeLinkDeviceId, frame_dst, buf);
977    }
978
979    // Validate that buf is an ARP packet with the specific op, local_ipv4,
980    // remote_ipv4, local_mac and remote_mac.
981    fn validate_arp_packet(
982        mut buf: &[u8],
983        op: ArpOp,
984        local_ipv4: Ipv4Addr,
985        remote_ipv4: Ipv4Addr,
986        local_mac: Mac,
987        remote_mac: Mac,
988    ) {
989        let packet = buf.parse::<ArpPacket<_, Mac, Ipv4Addr>>().unwrap();
990        assert_eq!(packet.sender_hardware_address(), local_mac);
991        assert_eq!(packet.target_hardware_address(), remote_mac);
992        assert_eq!(packet.sender_protocol_address(), local_ipv4);
993        assert_eq!(packet.target_protocol_address(), remote_ipv4);
994        assert_eq!(packet.operation(), op);
995    }
996
997    // Validate that we've sent `total_frames` frames in total, and that the
998    // most recent one was sent to `dst` with the given ARP packet contents.
999    fn validate_last_arp_packet(
1000        core_ctx: &FakeCoreCtxImpl,
1001        total_frames: usize,
1002        dst: Mac,
1003        op: ArpOp,
1004        local_ipv4: Ipv4Addr,
1005        remote_ipv4: Ipv4Addr,
1006        local_mac: Mac,
1007        remote_mac: Mac,
1008    ) {
1009        assert_eq!(core_ctx.frames().len(), total_frames);
1010        let (meta, frame) = &core_ctx.frames()[total_frames - 1];
1011        assert_eq!(meta.dst_addr, dst);
1012        validate_arp_packet(frame, op, local_ipv4, remote_ipv4, local_mac, remote_mac);
1013    }
1014
1015    #[test]
1016    fn test_receive_gratuitous_arp_request() {
1017        // Test that, when we receive a gratuitous ARP request, we cache the
1018        // sender's address information, and we do not send a response.
1019
1020        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context();
1021        send_arp_packet(
1022            &mut core_ctx,
1023            &mut bindings_ctx,
1024            ArpOp::Request,
1025            TEST_REMOTE_IPV4,
1026            TEST_REMOTE_IPV4,
1027            TEST_REMOTE_MAC,
1028            TEST_INVALID_MAC,
1029            FrameDestination::Individual { local: false },
1030        );
1031
1032        // We should have cached the sender's address information.
1033        assert_dynamic_neighbor_with_addr(
1034            &mut core_ctx,
1035            FakeLinkDeviceId,
1036            SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1037            TEST_REMOTE_MAC,
1038        );
1039        // Gratuitous ARPs should not prompt a response.
1040        assert_empty(core_ctx.frames().iter());
1041    }
1042
1043    #[test]
1044    fn test_receive_gratuitous_arp_response() {
1045        // Test that, when we receive a gratuitous ARP response, we cache the
1046        // sender's address information, and we do not send a response.
1047
1048        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context();
1049        send_arp_packet(
1050            &mut core_ctx,
1051            &mut bindings_ctx,
1052            ArpOp::Response,
1053            TEST_REMOTE_IPV4,
1054            TEST_REMOTE_IPV4,
1055            TEST_REMOTE_MAC,
1056            TEST_REMOTE_MAC,
1057            FrameDestination::Individual { local: false },
1058        );
1059
1060        // We should have cached the sender's address information.
1061        assert_dynamic_neighbor_with_addr(
1062            &mut core_ctx,
1063            FakeLinkDeviceId,
1064            SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1065            TEST_REMOTE_MAC,
1066        );
1067        // Gratuitous ARPs should not send a response.
1068        assert_empty(core_ctx.frames().iter());
1069    }
1070
1071    #[test]
1072    fn test_receive_gratuitous_arp_response_existing_request() {
1073        // Test that, if we have an outstanding request retry timer and receive
1074        // a gratuitous ARP for the same host, we cancel the timer and notify
1075        // the device layer.
1076
1077        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context();
1078
1079        // Trigger link resolution.
1080        assert_neighbor_unknown(
1081            &mut core_ctx,
1082            FakeLinkDeviceId,
1083            SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1084        );
1085        assert_eq!(
1086            NudHandler::send_ip_packet_to_neighbor(
1087                &mut core_ctx,
1088                &mut bindings_ctx,
1089                &FakeLinkDeviceId,
1090                SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1091                Buf::new([1], ..),
1092                FakeTxMetadata::default(),
1093            ),
1094            Ok(())
1095        );
1096
1097        send_arp_packet(
1098            &mut core_ctx,
1099            &mut bindings_ctx,
1100            ArpOp::Response,
1101            TEST_REMOTE_IPV4,
1102            TEST_REMOTE_IPV4,
1103            TEST_REMOTE_MAC,
1104            TEST_REMOTE_MAC,
1105            FrameDestination::Individual { local: false },
1106        );
1107
1108        // The response should now be in our cache.
1109        assert_dynamic_neighbor_with_addr(
1110            &mut core_ctx,
1111            FakeLinkDeviceId,
1112            SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1113            TEST_REMOTE_MAC,
1114        );
1115
1116        // Gratuitous ARPs should not send a response (the 1 frame is for the
1117        // original request).
1118        assert_eq!(core_ctx.frames().len(), 1);
1119    }
1120
1121    #[test_case(TEST_LOCAL_IPV4)]
1122    #[test_case(TEST_LOCAL_IPV4_2)]
1123    fn test_handle_arp_request(local_addr: Ipv4Addr) {
1124        // Test that, when we receive an ARP request, we cache the sender's
1125        // address information and send an ARP response.
1126
1127        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context();
1128
1129        send_arp_packet(
1130            &mut core_ctx,
1131            &mut bindings_ctx,
1132            ArpOp::Request,
1133            TEST_REMOTE_IPV4,
1134            local_addr,
1135            TEST_REMOTE_MAC,
1136            TEST_LOCAL_MAC,
1137            FrameDestination::Individual { local: true },
1138        );
1139
1140        // Make sure we cached the sender's address information.
1141        assert_dynamic_neighbor_with_addr(
1142            &mut core_ctx,
1143            FakeLinkDeviceId,
1144            SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1145            TEST_REMOTE_MAC,
1146        );
1147
1148        // We should have sent an ARP response.
1149        validate_last_arp_packet(
1150            &core_ctx,
1151            1,
1152            TEST_REMOTE_MAC,
1153            ArpOp::Response,
1154            local_addr,
1155            TEST_REMOTE_IPV4,
1156            TEST_LOCAL_MAC,
1157            TEST_REMOTE_MAC,
1158        );
1159    }
1160
1161    struct ArpHostConfig<'a> {
1162        name: &'a str,
1163        proto_addr: Ipv4Addr,
1164        hw_addr: Mac,
1165    }
1166
1167    #[test_case(ArpHostConfig {
1168                    name: "remote",
1169                    proto_addr: TEST_REMOTE_IPV4,
1170                    hw_addr: TEST_REMOTE_MAC
1171                },
1172                vec![]
1173    )]
1174    #[test_case(ArpHostConfig {
1175                    name: "requested_remote",
1176                    proto_addr: TEST_REMOTE_IPV4,
1177                    hw_addr: TEST_REMOTE_MAC
1178                },
1179                vec![
1180                    ArpHostConfig {
1181                        name: "non_requested_remote",
1182                        proto_addr: TEST_ANOTHER_REMOTE_IPV4,
1183                        hw_addr: TEST_REMOTE_MAC
1184                    }
1185                ]
1186    )]
1187    fn test_address_resolution(
1188        requested_remote_cfg: ArpHostConfig<'_>,
1189        other_remote_cfgs: Vec<ArpHostConfig<'_>>,
1190    ) {
1191        // Test a basic ARP resolution scenario.
1192        // We expect the following steps:
1193        // 1. When a lookup is performed and results in a cache miss, we send an
1194        //    ARP request and set a request retry timer.
1195        // 2. When the requested remote receives the request, it populates its cache with
1196        //    the local's information, and sends an ARP reply.
1197        // 3. Any non-requested remotes will neither populate their caches nor send ARP replies.
1198        // 4. When the reply is received, the timer is canceled, the table is
1199        //    updated, a new entry expiration timer is installed, and the device
1200        //    layer is notified of the resolution.
1201
1202        const LOCAL_HOST_CFG: ArpHostConfig<'_> =
1203            ArpHostConfig { name: "local", proto_addr: TEST_LOCAL_IPV4, hw_addr: TEST_LOCAL_MAC };
1204        let host_iter = other_remote_cfgs
1205            .iter()
1206            .chain(iter::once(&requested_remote_cfg))
1207            .chain(iter::once(&LOCAL_HOST_CFG));
1208
1209        let mut network = ArpNetworkSpec::new_network(
1210            {
1211                host_iter.clone().map(|cfg| {
1212                    let ArpHostConfig { name, proto_addr, hw_addr } = cfg;
1213                    let mut ctx = new_context();
1214                    let CtxPair { core_ctx, bindings_ctx: _ } = &mut ctx;
1215                    core_ctx.state.hw_addr = UnicastAddr::new(*hw_addr).unwrap();
1216                    core_ctx.state.proto_addrs = vec![*proto_addr];
1217                    (*name, ctx)
1218                })
1219            },
1220            |ctx: &str, meta: ArpFrameMetadata<_, _>| {
1221                host_iter
1222                    .clone()
1223                    .filter_map(|cfg| {
1224                        let ArpHostConfig { name, proto_addr: _, hw_addr: _ } = cfg;
1225                        if !ctx.eq(*name) { Some((*name, meta.clone(), None)) } else { None }
1226                    })
1227                    .collect::<Vec<_>>()
1228            },
1229        );
1230
1231        let ArpHostConfig {
1232            name: local_name,
1233            proto_addr: local_proto_addr,
1234            hw_addr: local_hw_addr,
1235        } = LOCAL_HOST_CFG;
1236
1237        let ArpHostConfig {
1238            name: requested_remote_name,
1239            proto_addr: requested_remote_proto_addr,
1240            hw_addr: requested_remote_hw_addr,
1241        } = requested_remote_cfg;
1242
1243        // Trigger link resolution.
1244        network.with_context(local_name, |CtxPair { core_ctx, bindings_ctx }| {
1245            assert_neighbor_unknown(
1246                core_ctx,
1247                FakeLinkDeviceId,
1248                SpecifiedAddr::new(requested_remote_proto_addr).unwrap(),
1249            );
1250            assert_eq!(
1251                NudHandler::send_ip_packet_to_neighbor(
1252                    core_ctx,
1253                    bindings_ctx,
1254                    &FakeLinkDeviceId,
1255                    SpecifiedAddr::new(requested_remote_proto_addr).unwrap(),
1256                    Buf::new([1], ..),
1257                    FakeTxMetadata::default(),
1258                ),
1259                Ok(())
1260            );
1261
1262            // We should have sent an ARP request.
1263            validate_last_arp_packet(
1264                core_ctx,
1265                1,
1266                Mac::BROADCAST,
1267                ArpOp::Request,
1268                local_proto_addr,
1269                requested_remote_proto_addr,
1270                local_hw_addr,
1271                Mac::UNSPECIFIED,
1272            );
1273        });
1274        // Step once to deliver the ARP request to the remotes.
1275        let res = network.step();
1276        assert_eq!(res.timers_fired, 0);
1277
1278        // Our faked broadcast network should deliver frames to every host other
1279        // than the sender itself. These should include all non-participating remotes
1280        // and either the local or the participating remote, depending on who is
1281        // sending the packet.
1282        let expected_frames_sent_bcast = other_remote_cfgs.len() + 1;
1283        assert_eq!(res.frames_sent, expected_frames_sent_bcast);
1284
1285        // The requested remote should have populated its ARP cache with the local's
1286        // information.
1287        network.with_context(requested_remote_name, |CtxPair { core_ctx, bindings_ctx: _ }| {
1288            assert_dynamic_neighbor_with_addr(
1289                core_ctx,
1290                FakeLinkDeviceId,
1291                SpecifiedAddr::new(local_proto_addr).unwrap(),
1292                LOCAL_HOST_CFG.hw_addr,
1293            );
1294
1295            // The requested remote should have sent an ARP response.
1296            validate_last_arp_packet(
1297                core_ctx,
1298                1,
1299                local_hw_addr,
1300                ArpOp::Response,
1301                requested_remote_proto_addr,
1302                local_proto_addr,
1303                requested_remote_hw_addr,
1304                local_hw_addr,
1305            );
1306        });
1307
1308        // Step once to deliver the ARP response to the local.
1309        let res = network.step();
1310        assert_eq!(res.timers_fired, 0);
1311        assert_eq!(res.frames_sent, expected_frames_sent_bcast);
1312
1313        // The local should have populated its cache with the remote's
1314        // information.
1315        network.with_context(local_name, |CtxPair { core_ctx, bindings_ctx: _ }| {
1316            assert_dynamic_neighbor_with_addr(
1317                core_ctx,
1318                FakeLinkDeviceId,
1319                SpecifiedAddr::new(requested_remote_proto_addr).unwrap(),
1320                requested_remote_hw_addr,
1321            );
1322        });
1323
1324        other_remote_cfgs.iter().for_each(
1325            |ArpHostConfig { name: unrequested_remote_name, proto_addr: _, hw_addr: _ }| {
1326                // The non-requested_remote should not have populated its ARP cache.
1327                network.with_context(
1328                    *unrequested_remote_name,
1329                    |CtxPair { core_ctx, bindings_ctx: _ }| {
1330                        // The non-requested_remote should not have sent an ARP response.
1331                        assert_empty(core_ctx.frames().iter());
1332
1333                        assert_neighbor_unknown(
1334                            core_ctx,
1335                            FakeLinkDeviceId,
1336                            SpecifiedAddr::new(local_proto_addr).unwrap(),
1337                        );
1338                    },
1339                )
1340            },
1341        );
1342    }
1343
1344    #[test_case(FrameDestination::Individual { local: true }, true; "unicast to us is solicited")]
1345    #[test_case(
1346        FrameDestination::Individual { local: false },
1347        false;
1348        "unicast to other addr is unsolicited"
1349    )]
1350    #[test_case(FrameDestination::Multicast, false; "multicast reply is unsolicited")]
1351    #[test_case(FrameDestination::Broadcast, false; "broadcast reply is unsolicited")]
1352    fn only_unicast_reply_treated_as_solicited(
1353        frame_dst: FrameDestination,
1354        expect_solicited: bool,
1355    ) {
1356        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context();
1357
1358        // Trigger link resolution.
1359        assert_neighbor_unknown(
1360            &mut core_ctx,
1361            FakeLinkDeviceId,
1362            SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1363        );
1364        assert_eq!(
1365            NudHandler::send_ip_packet_to_neighbor(
1366                &mut core_ctx,
1367                &mut bindings_ctx,
1368                &FakeLinkDeviceId,
1369                SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1370                Buf::new([1], ..),
1371                FakeTxMetadata::default(),
1372            ),
1373            Ok(())
1374        );
1375
1376        // Now send a confirmation with the specified frame destination.
1377        send_arp_packet(
1378            &mut core_ctx,
1379            &mut bindings_ctx,
1380            ArpOp::Response,
1381            TEST_REMOTE_IPV4,
1382            TEST_LOCAL_IPV4,
1383            TEST_REMOTE_MAC,
1384            TEST_LOCAL_MAC,
1385            frame_dst,
1386        );
1387
1388        // If the confirmation was interpreted as solicited, the entry should be
1389        // marked as REACHABLE; otherwise, it should have transitioned to STALE.
1390        let expected_state = if expect_solicited {
1391            DynamicNeighborState::Reachable(Reachable {
1392                link_address: TEST_REMOTE_MAC,
1393                last_confirmed_at: bindings_ctx.now(),
1394            })
1395        } else {
1396            DynamicNeighborState::Stale(Stale { link_address: TEST_REMOTE_MAC })
1397        };
1398        assert_dynamic_neighbor_state(
1399            &mut core_ctx,
1400            FakeLinkDeviceId,
1401            SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1402            expected_state,
1403        );
1404    }
1405
1406    // Test that we ignore ARP packets that have our hardware address as the
1407    // sender hardware address.
1408    #[test]
1409    fn test_drop_echoed_arp_packet() {
1410        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context();
1411
1412        // Receive an ARP packet that matches an ARP probe (as specified in
1413        // RFC 5227 section 2.1.1) that has been echoed back to ourselves.
1414        send_arp_packet(
1415            &mut core_ctx,
1416            &mut bindings_ctx,
1417            ArpOp::Request,
1418            Ipv4::UNSPECIFIED_ADDRESS, /* sender_ipv4 */
1419            TEST_LOCAL_IPV4,           /* target_ipv4 */
1420            TEST_LOCAL_MAC,            /* sender_mac */
1421            Mac::UNSPECIFIED,          /* target_mac */
1422            FrameDestination::Broadcast,
1423        );
1424
1425        // We should not have cached the sender's address information.
1426        assert_neighbor_unknown(
1427            &mut core_ctx,
1428            FakeLinkDeviceId,
1429            SpecifiedAddr::new(TEST_LOCAL_IPV4).unwrap(),
1430        );
1431
1432        // We should not have sent an ARP response.
1433        assert_eq!(core_ctx.frames().len(), 0);
1434    }
1435
1436    // Test sending an ARP probe as specified in Address Conflict Detection.
1437    #[test]
1438    fn test_send_arp_probe() {
1439        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context();
1440
1441        const LOCAL_IP: Ipv4Addr = Ipv4::UNSPECIFIED_ADDRESS;
1442        send_arp_request(
1443            &mut core_ctx,
1444            &mut bindings_ctx,
1445            &FakeLinkDeviceId,
1446            LOCAL_IP,
1447            TEST_REMOTE_IPV4,
1448            None,
1449        );
1450
1451        // We should have sent 1 ARP Request.
1452        let (meta, frame) = assert_matches!(core_ctx.frames(), [frame] => frame);
1453        // The request should have been broadcast, with the destination MAC
1454        // left unspecified.
1455        assert_eq!(meta.dst_addr, Mac::BROADCAST);
1456        validate_arp_packet(
1457            frame,
1458            ArpOp::Request,
1459            LOCAL_IP,
1460            TEST_REMOTE_IPV4,
1461            TEST_LOCAL_MAC,
1462            Mac::UNSPECIFIED,
1463        );
1464    }
1465
1466    // Test receiving an ARP packet and dispatching it to the IP Layer.
1467    #[test_case(ArpOp::Request, TEST_REMOTE_IPV4, TEST_LOCAL_IPV4, true; "dispatch_request")]
1468    #[test_case(ArpOp::Response, TEST_REMOTE_IPV4, TEST_LOCAL_IPV4, true; "dispatch_reply")]
1469    #[test_case(ArpOp::Request, Ipv4::UNSPECIFIED_ADDRESS, TEST_LOCAL_IPV4, true; "dispatch_probe")]
1470    #[test_case(ArpOp::Other(99), TEST_REMOTE_IPV4, TEST_LOCAL_IPV4, false; "ignore_other")]
1471    fn test_receive_arp_packet(
1472        op: ArpOp,
1473        sender_addr: Ipv4Addr,
1474        target_addr: Ipv4Addr,
1475        expect_dispatch: bool,
1476    ) {
1477        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context();
1478
1479        send_arp_packet(
1480            &mut core_ctx,
1481            &mut bindings_ctx,
1482            op,
1483            sender_addr,
1484            target_addr,
1485            TEST_REMOTE_MAC,
1486            TEST_LOCAL_MAC,
1487            FrameDestination::Broadcast,
1488        );
1489
1490        let is_arp_probe = sender_addr == Ipv4::UNSPECIFIED_ADDRESS && op == ArpOp::Request;
1491
1492        if expect_dispatch {
1493            assert_eq!(
1494                &core_ctx.state.dispatched_arp_packets[..],
1495                [(sender_addr, target_addr, is_arp_probe)]
1496            );
1497        } else {
1498            assert_eq!(&core_ctx.state.dispatched_arp_packets[..], []);
1499        }
1500    }
1501}