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