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