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
689    use assert_matches::assert_matches;
690    use net_types::ethernet::Mac;
691    use netstack3_base::socket::SocketIpAddr;
692    use netstack3_base::testutil::{
693        FakeBindingsCtx, FakeCoreCtx, FakeDeviceId, FakeInstant, FakeLinkDeviceId, FakeNetworkSpec,
694        FakeTimerId, FakeTxMetadata, FakeWeakDeviceId, WithFakeFrameContext, assert_empty,
695    };
696    use netstack3_base::{CtxPair, InstantContext as _, IntoCoreTimerCtx, TimerHandler};
697    use netstack3_ip::nud::testutil::{
698        assert_dynamic_neighbor_state, assert_dynamic_neighbor_with_addr, assert_neighbor_unknown,
699    };
700    use netstack3_ip::nud::{
701        DelegateNudContext, DynamicNeighborState, NudCounters, NudIcmpContext, Reachable, Stale,
702        UseDelegateNudContext,
703    };
704    use packet::{Buf, ParseBuffer};
705    use packet_formats::arp::{ArpHardwareType, ArpNetworkType, peek_arp_types};
706    use packet_formats::ipv4::Ipv4FragmentType;
707    use test_case::test_case;
708
709    use super::*;
710    use crate::internal::ethernet::EthernetLinkDevice;
711
712    const TEST_LOCAL_IPV4: Ipv4Addr = Ipv4Addr::new([1, 2, 3, 4]);
713    const TEST_LOCAL_IPV4_2: Ipv4Addr = Ipv4Addr::new([4, 5, 6, 7]);
714    const TEST_REMOTE_IPV4: Ipv4Addr = Ipv4Addr::new([5, 6, 7, 8]);
715    const TEST_ANOTHER_REMOTE_IPV4: Ipv4Addr = Ipv4Addr::new([9, 10, 11, 12]);
716    const TEST_LOCAL_MAC: Mac = Mac::new([0, 1, 2, 3, 4, 5]);
717    const TEST_REMOTE_MAC: Mac = Mac::new([6, 7, 8, 9, 10, 11]);
718    const TEST_INVALID_MAC: Mac = Mac::new([0, 0, 0, 0, 0, 0]);
719
720    /// A fake `ArpContext` that stores frames, address resolution events, and
721    /// address resolution failure events.
722    struct FakeArpCtx {
723        proto_addrs: Vec<Ipv4Addr>,
724        hw_addr: UnicastAddr<Mac>,
725        arp_state: ArpState<EthernetLinkDevice, FakeBindingsCtxImpl>,
726        inner: FakeArpInnerCtx,
727        config: FakeArpConfigCtx,
728        counters: ArpCounters,
729        nud_counters: NudCounters<Ipv4>,
730        // Stores received ARP packets that were dispatched to the IP Layer.
731        // Holds a tuple of (sender_addr, target_addr, is_arp_probe).
732        dispatched_arp_packets: Vec<(Ipv4Addr, Ipv4Addr, bool)>,
733    }
734
735    /// A fake `ArpSenderContext` that sends IP packets.
736    struct FakeArpInnerCtx;
737
738    /// A fake `ArpConfigContext`.
739    struct FakeArpConfigCtx;
740
741    impl FakeArpCtx {
742        fn new(bindings_ctx: &mut FakeBindingsCtxImpl) -> FakeArpCtx {
743            FakeArpCtx {
744                proto_addrs: vec![TEST_LOCAL_IPV4, TEST_LOCAL_IPV4_2],
745                hw_addr: UnicastAddr::new(TEST_LOCAL_MAC).unwrap(),
746                arp_state: ArpState::new::<_, IntoCoreTimerCtx>(
747                    bindings_ctx,
748                    FakeWeakDeviceId(FakeLinkDeviceId),
749                ),
750                inner: FakeArpInnerCtx,
751                config: FakeArpConfigCtx,
752                counters: Default::default(),
753                nud_counters: Default::default(),
754                dispatched_arp_packets: Default::default(),
755            }
756        }
757    }
758
759    type FakeBindingsCtxImpl = FakeBindingsCtx<
760        ArpTimerId<EthernetLinkDevice, FakeWeakDeviceId<FakeLinkDeviceId>>,
761        nud::Event<Mac, FakeLinkDeviceId, Ipv4, FakeInstant>,
762        (),
763        (),
764    >;
765
766    type FakeCoreCtxImpl = FakeCoreCtx<
767        FakeArpCtx,
768        ArpFrameMetadata<EthernetLinkDevice, FakeLinkDeviceId>,
769        FakeDeviceId,
770    >;
771
772    fn new_context() -> CtxPair<FakeCoreCtxImpl, FakeBindingsCtxImpl> {
773        CtxPair::with_default_bindings_ctx(|bindings_ctx| {
774            FakeCoreCtxImpl::with_state(FakeArpCtx::new(bindings_ctx))
775        })
776    }
777
778    enum ArpNetworkSpec {}
779    impl FakeNetworkSpec for ArpNetworkSpec {
780        type Context = CtxPair<FakeCoreCtxImpl, FakeBindingsCtxImpl>;
781        type TimerId = ArpTimerId<EthernetLinkDevice, FakeWeakDeviceId<FakeLinkDeviceId>>;
782        type SendMeta = ArpFrameMetadata<EthernetLinkDevice, FakeLinkDeviceId>;
783        type RecvMeta = ArpFrameMetadata<EthernetLinkDevice, FakeLinkDeviceId>;
784
785        fn handle_frame(
786            ctx: &mut Self::Context,
787            ArpFrameMetadata { device_id, .. }: Self::RecvMeta,
788            data: Buf<Vec<u8>>,
789        ) {
790            let CtxPair { core_ctx, bindings_ctx } = ctx;
791            handle_packet(
792                core_ctx,
793                bindings_ctx,
794                device_id,
795                FrameDestination::Individual { local: true },
796                data,
797            )
798        }
799        fn handle_timer(ctx: &mut Self::Context, dispatch: Self::TimerId, timer: FakeTimerId) {
800            let CtxPair { core_ctx, bindings_ctx } = ctx;
801            TimerHandler::handle_timer(core_ctx, bindings_ctx, dispatch, timer)
802        }
803
804        fn process_queues(_ctx: &mut Self::Context) -> bool {
805            false
806        }
807
808        fn fake_frames(ctx: &mut Self::Context) -> &mut impl WithFakeFrameContext<Self::SendMeta> {
809            &mut ctx.core_ctx
810        }
811    }
812
813    impl DeviceIdContext<EthernetLinkDevice> for FakeCoreCtxImpl {
814        type DeviceId = FakeLinkDeviceId;
815        type WeakDeviceId = FakeWeakDeviceId<FakeLinkDeviceId>;
816    }
817
818    impl DeviceIdContext<EthernetLinkDevice> for FakeArpInnerCtx {
819        type DeviceId = FakeLinkDeviceId;
820        type WeakDeviceId = FakeWeakDeviceId<FakeLinkDeviceId>;
821    }
822
823    impl ArpContext<EthernetLinkDevice, FakeBindingsCtxImpl> for FakeCoreCtxImpl {
824        type ConfigCtx<'a> = FakeArpConfigCtx;
825
826        type ArpSenderCtx<'a> = FakeArpInnerCtx;
827
828        fn with_arp_state_mut_and_sender_ctx<
829            O,
830            F: FnOnce(
831                &mut ArpState<EthernetLinkDevice, FakeBindingsCtxImpl>,
832                &mut Self::ArpSenderCtx<'_>,
833            ) -> O,
834        >(
835            &mut self,
836            FakeLinkDeviceId: &FakeLinkDeviceId,
837            cb: F,
838        ) -> O {
839            let FakeArpCtx { arp_state, inner, .. } = &mut self.state;
840            cb(arp_state, inner)
841        }
842
843        fn with_arp_state<O, F: FnOnce(&ArpState<EthernetLinkDevice, FakeBindingsCtxImpl>) -> O>(
844            &mut self,
845            FakeLinkDeviceId: &FakeLinkDeviceId,
846            cb: F,
847        ) -> O {
848            cb(&self.state.arp_state)
849        }
850
851        fn get_protocol_addr(&mut self, _device_id: &FakeLinkDeviceId) -> Option<Ipv4Addr> {
852            self.state.proto_addrs.first().copied()
853        }
854
855        fn get_hardware_addr(
856            &mut self,
857            _bindings_ctx: &mut FakeBindingsCtxImpl,
858            _device_id: &FakeLinkDeviceId,
859        ) -> UnicastAddr<Mac> {
860            self.state.hw_addr
861        }
862
863        fn with_arp_state_mut<
864            O,
865            F: FnOnce(
866                &mut ArpState<EthernetLinkDevice, FakeBindingsCtxImpl>,
867                &mut Self::ConfigCtx<'_>,
868            ) -> O,
869        >(
870            &mut self,
871            FakeLinkDeviceId: &FakeLinkDeviceId,
872            cb: F,
873        ) -> O {
874            let FakeArpCtx { arp_state, config, .. } = &mut self.state;
875            cb(arp_state, config)
876        }
877    }
878
879    impl ArpIpLayerContext<EthernetLinkDevice, FakeBindingsCtxImpl> for FakeCoreCtxImpl {
880        fn on_arp_packet(
881            &mut self,
882            _bindings_ctx: &mut FakeBindingsCtxImpl,
883            _device_id: &FakeLinkDeviceId,
884            sender_addr: Ipv4Addr,
885            target_addr: Ipv4Addr,
886            is_arp_probe: bool,
887        ) -> bool {
888            self.state.dispatched_arp_packets.push((sender_addr, target_addr, is_arp_probe));
889
890            self.state.proto_addrs.iter().any(|&a| a == target_addr)
891        }
892    }
893
894    impl UseDelegateNudContext for FakeArpCtx {}
895    impl DelegateNudContext<Ipv4> for FakeArpCtx {
896        type Delegate<T> = ArpNudCtx<T>;
897    }
898
899    impl NudIcmpContext<Ipv4, EthernetLinkDevice, FakeBindingsCtxImpl> for FakeCoreCtxImpl {
900        fn send_icmp_dest_unreachable(
901            &mut self,
902            _bindings_ctx: &mut FakeBindingsCtxImpl,
903            _frame: Buf<Vec<u8>>,
904            _device_id: Option<&Self::DeviceId>,
905            _original_src: SocketIpAddr<Ipv4Addr>,
906            _original_dst: SocketIpAddr<Ipv4Addr>,
907            _metadata: (usize, Ipv4FragmentType),
908        ) {
909            panic!("send_icmp_dest_unreachable should not be called");
910        }
911    }
912
913    impl ArpConfigContext for FakeArpConfigCtx {
914        fn with_nud_user_config<O, F: FnOnce(&NudUserConfig) -> O>(&mut self, cb: F) -> O {
915            cb(&NudUserConfig::default())
916        }
917    }
918    impl ArpConfigContext for FakeArpInnerCtx {
919        fn with_nud_user_config<O, F: FnOnce(&NudUserConfig) -> O>(&mut self, cb: F) -> O {
920            cb(&NudUserConfig::default())
921        }
922    }
923
924    impl ArpSenderContext<EthernetLinkDevice, FakeBindingsCtxImpl> for FakeArpInnerCtx {
925        fn send_ip_packet_to_neighbor_link_addr<S>(
926            &mut self,
927            _bindings_ctx: &mut FakeBindingsCtxImpl,
928            _dst_link_address: Mac,
929            _body: S,
930            _tx_meta: FakeTxMetadata,
931        ) -> Result<(), SendFrameError<S>> {
932            Ok(())
933        }
934    }
935
936    impl CounterContext<ArpCounters> for FakeArpCtx {
937        fn counters(&self) -> &ArpCounters {
938            &self.counters
939        }
940    }
941
942    impl CounterContext<NudCounters<Ipv4>> for FakeArpCtx {
943        fn counters(&self) -> &NudCounters<Ipv4> {
944            &self.nud_counters
945        }
946    }
947
948    fn send_arp_packet(
949        core_ctx: &mut FakeCoreCtxImpl,
950        bindings_ctx: &mut FakeBindingsCtxImpl,
951        op: ArpOp,
952        sender_ipv4: Ipv4Addr,
953        target_ipv4: Ipv4Addr,
954        sender_mac: Mac,
955        target_mac: Mac,
956        frame_dst: FrameDestination,
957    ) {
958        let buf = ArpPacketBuilder::new(op, sender_mac, sender_ipv4, target_mac, target_ipv4)
959            .into_serializer()
960            .serialize_vec_outer()
961            .unwrap();
962        let (hw, proto) = peek_arp_types(buf.as_ref()).unwrap();
963        assert_eq!(hw, ArpHardwareType::Ethernet);
964        assert_eq!(proto, ArpNetworkType::Ipv4);
965
966        handle_packet::<_, _, _, _>(core_ctx, bindings_ctx, FakeLinkDeviceId, frame_dst, buf);
967    }
968
969    // Validate that buf is an ARP packet with the specific op, local_ipv4,
970    // remote_ipv4, local_mac and remote_mac.
971    fn validate_arp_packet(
972        mut buf: &[u8],
973        op: ArpOp,
974        local_ipv4: Ipv4Addr,
975        remote_ipv4: Ipv4Addr,
976        local_mac: Mac,
977        remote_mac: Mac,
978    ) {
979        let packet = buf.parse::<ArpPacket<_, Mac, Ipv4Addr>>().unwrap();
980        assert_eq!(packet.sender_hardware_address(), local_mac);
981        assert_eq!(packet.target_hardware_address(), remote_mac);
982        assert_eq!(packet.sender_protocol_address(), local_ipv4);
983        assert_eq!(packet.target_protocol_address(), remote_ipv4);
984        assert_eq!(packet.operation(), op);
985    }
986
987    // Validate that we've sent `total_frames` frames in total, and that the
988    // most recent one was sent to `dst` with the given ARP packet contents.
989    fn validate_last_arp_packet(
990        core_ctx: &FakeCoreCtxImpl,
991        total_frames: usize,
992        dst: Mac,
993        op: ArpOp,
994        local_ipv4: Ipv4Addr,
995        remote_ipv4: Ipv4Addr,
996        local_mac: Mac,
997        remote_mac: Mac,
998    ) {
999        assert_eq!(core_ctx.frames().len(), total_frames);
1000        let (meta, frame) = &core_ctx.frames()[total_frames - 1];
1001        assert_eq!(meta.dst_addr, dst);
1002        validate_arp_packet(frame, op, local_ipv4, remote_ipv4, local_mac, remote_mac);
1003    }
1004
1005    #[test]
1006    fn test_receive_gratuitous_arp_request() {
1007        // Test that, when we receive a gratuitous ARP request, we cache the
1008        // sender's address information, and we do not send a response.
1009
1010        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context();
1011        send_arp_packet(
1012            &mut core_ctx,
1013            &mut bindings_ctx,
1014            ArpOp::Request,
1015            TEST_REMOTE_IPV4,
1016            TEST_REMOTE_IPV4,
1017            TEST_REMOTE_MAC,
1018            TEST_INVALID_MAC,
1019            FrameDestination::Individual { local: false },
1020        );
1021
1022        // We should have cached the sender's address information.
1023        assert_dynamic_neighbor_with_addr(
1024            &mut core_ctx,
1025            FakeLinkDeviceId,
1026            SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1027            TEST_REMOTE_MAC,
1028        );
1029        // Gratuitous ARPs should not prompt a response.
1030        assert_empty(core_ctx.frames().iter());
1031    }
1032
1033    #[test]
1034    fn test_receive_gratuitous_arp_response() {
1035        // Test that, when we receive a gratuitous ARP response, we cache the
1036        // sender's address information, and we do not send a response.
1037
1038        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context();
1039        send_arp_packet(
1040            &mut core_ctx,
1041            &mut bindings_ctx,
1042            ArpOp::Response,
1043            TEST_REMOTE_IPV4,
1044            TEST_REMOTE_IPV4,
1045            TEST_REMOTE_MAC,
1046            TEST_REMOTE_MAC,
1047            FrameDestination::Individual { local: false },
1048        );
1049
1050        // We should have cached the sender's address information.
1051        assert_dynamic_neighbor_with_addr(
1052            &mut core_ctx,
1053            FakeLinkDeviceId,
1054            SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1055            TEST_REMOTE_MAC,
1056        );
1057        // Gratuitous ARPs should not send a response.
1058        assert_empty(core_ctx.frames().iter());
1059    }
1060
1061    #[test]
1062    fn test_receive_gratuitous_arp_response_existing_request() {
1063        // Test that, if we have an outstanding request retry timer and receive
1064        // a gratuitous ARP for the same host, we cancel the timer and notify
1065        // the device layer.
1066
1067        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context();
1068
1069        // Trigger link resolution.
1070        assert_neighbor_unknown(
1071            &mut core_ctx,
1072            FakeLinkDeviceId,
1073            SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1074        );
1075        assert_eq!(
1076            NudHandler::send_ip_packet_to_neighbor(
1077                &mut core_ctx,
1078                &mut bindings_ctx,
1079                &FakeLinkDeviceId,
1080                SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1081                Buf::new([1], ..),
1082                FakeTxMetadata::default(),
1083            ),
1084            Ok(())
1085        );
1086
1087        send_arp_packet(
1088            &mut core_ctx,
1089            &mut bindings_ctx,
1090            ArpOp::Response,
1091            TEST_REMOTE_IPV4,
1092            TEST_REMOTE_IPV4,
1093            TEST_REMOTE_MAC,
1094            TEST_REMOTE_MAC,
1095            FrameDestination::Individual { local: false },
1096        );
1097
1098        // The response should now be in our cache.
1099        assert_dynamic_neighbor_with_addr(
1100            &mut core_ctx,
1101            FakeLinkDeviceId,
1102            SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1103            TEST_REMOTE_MAC,
1104        );
1105
1106        // Gratuitous ARPs should not send a response (the 1 frame is for the
1107        // original request).
1108        assert_eq!(core_ctx.frames().len(), 1);
1109    }
1110
1111    #[test_case(TEST_LOCAL_IPV4)]
1112    #[test_case(TEST_LOCAL_IPV4_2)]
1113    fn test_handle_arp_request(local_addr: Ipv4Addr) {
1114        // Test that, when we receive an ARP request, we cache the sender's
1115        // address information and send an ARP response.
1116
1117        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context();
1118
1119        send_arp_packet(
1120            &mut core_ctx,
1121            &mut bindings_ctx,
1122            ArpOp::Request,
1123            TEST_REMOTE_IPV4,
1124            local_addr,
1125            TEST_REMOTE_MAC,
1126            TEST_LOCAL_MAC,
1127            FrameDestination::Individual { local: true },
1128        );
1129
1130        // Make sure we cached the sender's address information.
1131        assert_dynamic_neighbor_with_addr(
1132            &mut core_ctx,
1133            FakeLinkDeviceId,
1134            SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1135            TEST_REMOTE_MAC,
1136        );
1137
1138        // We should have sent an ARP response.
1139        validate_last_arp_packet(
1140            &core_ctx,
1141            1,
1142            TEST_REMOTE_MAC,
1143            ArpOp::Response,
1144            local_addr,
1145            TEST_REMOTE_IPV4,
1146            TEST_LOCAL_MAC,
1147            TEST_REMOTE_MAC,
1148        );
1149    }
1150
1151    struct ArpHostConfig<'a> {
1152        name: &'a str,
1153        proto_addr: Ipv4Addr,
1154        hw_addr: Mac,
1155    }
1156
1157    #[test_case(ArpHostConfig {
1158                    name: "remote",
1159                    proto_addr: TEST_REMOTE_IPV4,
1160                    hw_addr: TEST_REMOTE_MAC
1161                },
1162                vec![]
1163    )]
1164    #[test_case(ArpHostConfig {
1165                    name: "requested_remote",
1166                    proto_addr: TEST_REMOTE_IPV4,
1167                    hw_addr: TEST_REMOTE_MAC
1168                },
1169                vec![
1170                    ArpHostConfig {
1171                        name: "non_requested_remote",
1172                        proto_addr: TEST_ANOTHER_REMOTE_IPV4,
1173                        hw_addr: TEST_REMOTE_MAC
1174                    }
1175                ]
1176    )]
1177    fn test_address_resolution(
1178        requested_remote_cfg: ArpHostConfig<'_>,
1179        other_remote_cfgs: Vec<ArpHostConfig<'_>>,
1180    ) {
1181        // Test a basic ARP resolution scenario.
1182        // We expect the following steps:
1183        // 1. When a lookup is performed and results in a cache miss, we send an
1184        //    ARP request and set a request retry timer.
1185        // 2. When the requested remote receives the request, it populates its cache with
1186        //    the local's information, and sends an ARP reply.
1187        // 3. Any non-requested remotes will neither populate their caches nor send ARP replies.
1188        // 4. When the reply is received, the timer is canceled, the table is
1189        //    updated, a new entry expiration timer is installed, and the device
1190        //    layer is notified of the resolution.
1191
1192        const LOCAL_HOST_CFG: ArpHostConfig<'_> =
1193            ArpHostConfig { name: "local", proto_addr: TEST_LOCAL_IPV4, hw_addr: TEST_LOCAL_MAC };
1194        let host_iter = other_remote_cfgs
1195            .iter()
1196            .chain(iter::once(&requested_remote_cfg))
1197            .chain(iter::once(&LOCAL_HOST_CFG));
1198
1199        let mut network = ArpNetworkSpec::new_network(
1200            {
1201                host_iter.clone().map(|cfg| {
1202                    let ArpHostConfig { name, proto_addr, hw_addr } = cfg;
1203                    let mut ctx = new_context();
1204                    let CtxPair { core_ctx, bindings_ctx: _ } = &mut ctx;
1205                    core_ctx.state.hw_addr = UnicastAddr::new(*hw_addr).unwrap();
1206                    core_ctx.state.proto_addrs = vec![*proto_addr];
1207                    (*name, ctx)
1208                })
1209            },
1210            |ctx: &str, meta: ArpFrameMetadata<_, _>| {
1211                host_iter
1212                    .clone()
1213                    .filter_map(|cfg| {
1214                        let ArpHostConfig { name, proto_addr: _, hw_addr: _ } = cfg;
1215                        if !ctx.eq(*name) { Some((*name, meta.clone(), None)) } else { None }
1216                    })
1217                    .collect::<Vec<_>>()
1218            },
1219        );
1220
1221        let ArpHostConfig {
1222            name: local_name,
1223            proto_addr: local_proto_addr,
1224            hw_addr: local_hw_addr,
1225        } = LOCAL_HOST_CFG;
1226
1227        let ArpHostConfig {
1228            name: requested_remote_name,
1229            proto_addr: requested_remote_proto_addr,
1230            hw_addr: requested_remote_hw_addr,
1231        } = requested_remote_cfg;
1232
1233        // Trigger link resolution.
1234        network.with_context(local_name, |CtxPair { core_ctx, bindings_ctx }| {
1235            assert_neighbor_unknown(
1236                core_ctx,
1237                FakeLinkDeviceId,
1238                SpecifiedAddr::new(requested_remote_proto_addr).unwrap(),
1239            );
1240            assert_eq!(
1241                NudHandler::send_ip_packet_to_neighbor(
1242                    core_ctx,
1243                    bindings_ctx,
1244                    &FakeLinkDeviceId,
1245                    SpecifiedAddr::new(requested_remote_proto_addr).unwrap(),
1246                    Buf::new([1], ..),
1247                    FakeTxMetadata::default(),
1248                ),
1249                Ok(())
1250            );
1251
1252            // We should have sent an ARP request.
1253            validate_last_arp_packet(
1254                core_ctx,
1255                1,
1256                Mac::BROADCAST,
1257                ArpOp::Request,
1258                local_proto_addr,
1259                requested_remote_proto_addr,
1260                local_hw_addr,
1261                Mac::UNSPECIFIED,
1262            );
1263        });
1264        // Step once to deliver the ARP request to the remotes.
1265        let res = network.step();
1266        assert_eq!(res.timers_fired, 0);
1267
1268        // Our faked broadcast network should deliver frames to every host other
1269        // than the sender itself. These should include all non-participating remotes
1270        // and either the local or the participating remote, depending on who is
1271        // sending the packet.
1272        let expected_frames_sent_bcast = other_remote_cfgs.len() + 1;
1273        assert_eq!(res.frames_sent, expected_frames_sent_bcast);
1274
1275        // The requested remote should have populated its ARP cache with the local's
1276        // information.
1277        network.with_context(requested_remote_name, |CtxPair { core_ctx, bindings_ctx: _ }| {
1278            assert_dynamic_neighbor_with_addr(
1279                core_ctx,
1280                FakeLinkDeviceId,
1281                SpecifiedAddr::new(local_proto_addr).unwrap(),
1282                LOCAL_HOST_CFG.hw_addr,
1283            );
1284
1285            // The requested remote should have sent an ARP response.
1286            validate_last_arp_packet(
1287                core_ctx,
1288                1,
1289                local_hw_addr,
1290                ArpOp::Response,
1291                requested_remote_proto_addr,
1292                local_proto_addr,
1293                requested_remote_hw_addr,
1294                local_hw_addr,
1295            );
1296        });
1297
1298        // Step once to deliver the ARP response to the local.
1299        let res = network.step();
1300        assert_eq!(res.timers_fired, 0);
1301        assert_eq!(res.frames_sent, expected_frames_sent_bcast);
1302
1303        // The local should have populated its cache with the remote's
1304        // information.
1305        network.with_context(local_name, |CtxPair { core_ctx, bindings_ctx: _ }| {
1306            assert_dynamic_neighbor_with_addr(
1307                core_ctx,
1308                FakeLinkDeviceId,
1309                SpecifiedAddr::new(requested_remote_proto_addr).unwrap(),
1310                requested_remote_hw_addr,
1311            );
1312        });
1313
1314        other_remote_cfgs.iter().for_each(
1315            |ArpHostConfig { name: unrequested_remote_name, proto_addr: _, hw_addr: _ }| {
1316                // The non-requested_remote should not have populated its ARP cache.
1317                network.with_context(
1318                    *unrequested_remote_name,
1319                    |CtxPair { core_ctx, bindings_ctx: _ }| {
1320                        // The non-requested_remote should not have sent an ARP response.
1321                        assert_empty(core_ctx.frames().iter());
1322
1323                        assert_neighbor_unknown(
1324                            core_ctx,
1325                            FakeLinkDeviceId,
1326                            SpecifiedAddr::new(local_proto_addr).unwrap(),
1327                        );
1328                    },
1329                )
1330            },
1331        );
1332    }
1333
1334    #[test_case(FrameDestination::Individual { local: true }, true; "unicast to us is solicited")]
1335    #[test_case(
1336        FrameDestination::Individual { local: false },
1337        false;
1338        "unicast to other addr is unsolicited"
1339    )]
1340    #[test_case(FrameDestination::Multicast, false; "multicast reply is unsolicited")]
1341    #[test_case(FrameDestination::Broadcast, false; "broadcast reply is unsolicited")]
1342    fn only_unicast_reply_treated_as_solicited(
1343        frame_dst: FrameDestination,
1344        expect_solicited: bool,
1345    ) {
1346        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context();
1347
1348        // Trigger link resolution.
1349        assert_neighbor_unknown(
1350            &mut core_ctx,
1351            FakeLinkDeviceId,
1352            SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1353        );
1354        assert_eq!(
1355            NudHandler::send_ip_packet_to_neighbor(
1356                &mut core_ctx,
1357                &mut bindings_ctx,
1358                &FakeLinkDeviceId,
1359                SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1360                Buf::new([1], ..),
1361                FakeTxMetadata::default(),
1362            ),
1363            Ok(())
1364        );
1365
1366        // Now send a confirmation with the specified frame destination.
1367        send_arp_packet(
1368            &mut core_ctx,
1369            &mut bindings_ctx,
1370            ArpOp::Response,
1371            TEST_REMOTE_IPV4,
1372            TEST_LOCAL_IPV4,
1373            TEST_REMOTE_MAC,
1374            TEST_LOCAL_MAC,
1375            frame_dst,
1376        );
1377
1378        // If the confirmation was interpreted as solicited, the entry should be
1379        // marked as REACHABLE; otherwise, it should have transitioned to STALE.
1380        let expected_state = if expect_solicited {
1381            DynamicNeighborState::Reachable(Reachable {
1382                link_address: TEST_REMOTE_MAC,
1383                last_confirmed_at: bindings_ctx.now(),
1384            })
1385        } else {
1386            DynamicNeighborState::Stale(Stale { link_address: TEST_REMOTE_MAC })
1387        };
1388        assert_dynamic_neighbor_state(
1389            &mut core_ctx,
1390            FakeLinkDeviceId,
1391            SpecifiedAddr::new(TEST_REMOTE_IPV4).unwrap(),
1392            expected_state,
1393        );
1394    }
1395
1396    // Test that we ignore ARP packets that have our hardware address as the
1397    // sender hardware address.
1398    #[test]
1399    fn test_drop_echoed_arp_packet() {
1400        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context();
1401
1402        // Receive an ARP packet that matches an ARP probe (as specified in
1403        // RFC 5227 section 2.1.1) that has been echoed back to ourselves.
1404        send_arp_packet(
1405            &mut core_ctx,
1406            &mut bindings_ctx,
1407            ArpOp::Request,
1408            Ipv4::UNSPECIFIED_ADDRESS, /* sender_ipv4 */
1409            TEST_LOCAL_IPV4,           /* target_ipv4 */
1410            TEST_LOCAL_MAC,            /* sender_mac */
1411            Mac::UNSPECIFIED,          /* target_mac */
1412            FrameDestination::Broadcast,
1413        );
1414
1415        // We should not have cached the sender's address information.
1416        assert_neighbor_unknown(
1417            &mut core_ctx,
1418            FakeLinkDeviceId,
1419            SpecifiedAddr::new(TEST_LOCAL_IPV4).unwrap(),
1420        );
1421
1422        // We should not have sent an ARP response.
1423        assert_eq!(core_ctx.frames().len(), 0);
1424    }
1425
1426    // Test sending an ARP probe as specified in Address Conflict Detection.
1427    #[test]
1428    fn test_send_arp_probe() {
1429        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context();
1430
1431        const LOCAL_IP: Ipv4Addr = Ipv4::UNSPECIFIED_ADDRESS;
1432        send_arp_request(
1433            &mut core_ctx,
1434            &mut bindings_ctx,
1435            &FakeLinkDeviceId,
1436            LOCAL_IP,
1437            TEST_REMOTE_IPV4,
1438            None,
1439        );
1440
1441        // We should have sent 1 ARP Request.
1442        let (meta, frame) = assert_matches!(core_ctx.frames(), [frame] => frame);
1443        // The request should have been broadcast, with the destination MAC
1444        // left unspecified.
1445        assert_eq!(meta.dst_addr, Mac::BROADCAST);
1446        validate_arp_packet(
1447            frame,
1448            ArpOp::Request,
1449            LOCAL_IP,
1450            TEST_REMOTE_IPV4,
1451            TEST_LOCAL_MAC,
1452            Mac::UNSPECIFIED,
1453        );
1454    }
1455
1456    // Test receiving an ARP packet and dispatching it to the IP Layer.
1457    #[test_case(ArpOp::Request, TEST_REMOTE_IPV4, TEST_LOCAL_IPV4, true; "dispatch_request")]
1458    #[test_case(ArpOp::Response, TEST_REMOTE_IPV4, TEST_LOCAL_IPV4, true; "dispatch_reply")]
1459    #[test_case(ArpOp::Request, Ipv4::UNSPECIFIED_ADDRESS, TEST_LOCAL_IPV4, true; "dispatch_probe")]
1460    #[test_case(ArpOp::Other(99), TEST_REMOTE_IPV4, TEST_LOCAL_IPV4, false; "ignore_other")]
1461    fn test_receive_arp_packet(
1462        op: ArpOp,
1463        sender_addr: Ipv4Addr,
1464        target_addr: Ipv4Addr,
1465        expect_dispatch: bool,
1466    ) {
1467        let CtxPair { mut core_ctx, mut bindings_ctx } = new_context();
1468
1469        send_arp_packet(
1470            &mut core_ctx,
1471            &mut bindings_ctx,
1472            op,
1473            sender_addr,
1474            target_addr,
1475            TEST_REMOTE_MAC,
1476            TEST_LOCAL_MAC,
1477            FrameDestination::Broadcast,
1478        );
1479
1480        let is_arp_probe = sender_addr == Ipv4::UNSPECIFIED_ADDRESS && op == ArpOp::Request;
1481
1482        if expect_dispatch {
1483            assert_eq!(
1484                &core_ctx.state.dispatched_arp_packets[..],
1485                [(sender_addr, target_addr, is_arp_probe)]
1486            );
1487        } else {
1488            assert_eq!(&core_ctx.state.dispatched_arp_packets[..], []);
1489        }
1490    }
1491}