Skip to main content

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