Skip to main content

netlink/
neighbors.rs

1// Copyright 2026 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//! A module for managing neighbor information by receiving RTM_*NEIGH Netlink
6//! messages and maintaining neighbor table state from Netstack.
7
8use std::collections::{BTreeMap, HashMap, HashSet};
9use std::net::IpAddr;
10use std::num::NonZeroU64;
11
12use crate::Errno;
13use crate::client::{ClientTable, InternalClient};
14use crate::logging::{log_debug, log_warn};
15use crate::messaging::Sender;
16use crate::multicast_groups::ModernGroup;
17use crate::netlink_packet::UNSPECIFIED_SEQUENCE_NUMBER;
18use crate::protocol_family::ProtocolFamily;
19use crate::protocol_family::route::NetlinkRoute;
20use crate::util::respond_to_completer;
21use derivative::Derivative;
22use futures::StreamExt as _;
23use futures::channel::oneshot;
24use linux_uapi::rtnetlink_groups_RTNLGRP_NEIGH;
25use net_types::ip::IpVersion;
26use netlink_packet_core::{
27    NLM_F_APPEND, NLM_F_CREATE, NLM_F_EXCL, NLM_F_MULTIPART, NLM_F_REPLACE, NetlinkMessage,
28};
29use netlink_packet_route::neighbour::{
30    NeighbourAddress, NeighbourAttribute, NeighbourFlags, NeighbourHeader, NeighbourMessage,
31    NeighbourState,
32};
33use netlink_packet_route::route::RouteType;
34use netlink_packet_route::{AddressFamily, RouteNetlinkMessage};
35use thiserror::Error;
36
37use fidl_fuchsia_net as fnet;
38use fidl_fuchsia_net_ext as fnet_ext;
39use fidl_fuchsia_net_interfaces_ext as fnet_interfaces_ext;
40use fidl_fuchsia_net_neighbor as fnet_neighbor;
41use fidl_fuchsia_net_neighbor_ext as fnet_neighbor_ext;
42
43/// NetlinkNeighborMessage conversion related errors.
44#[derive(Debug, PartialEq)]
45pub(crate) enum NetlinkNeighborMessageConversionError {
46    /// Interface id could not be downcasted to fit into the expected u32.
47    InvalidInterfaceId(u64),
48}
49
50/// A wrapper type for the netlink_packet_route `NeighbourMessage` to enable conversions
51/// from [`fnet_neighbor_ext::Entry`].
52#[derive(Clone, Debug, Eq, PartialEq)]
53pub(crate) struct NetlinkNeighborMessage(pub(crate) NeighbourMessage);
54
55impl NetlinkNeighborMessage {
56    pub(crate) fn optionally_from(
57        neighbor: fnet_neighbor_ext::Entry,
58    ) -> Option<NetlinkNeighborMessage> {
59        match neighbor.try_into() {
60            Ok(message) => Some(message),
61            Err(NetlinkNeighborMessageConversionError::InvalidInterfaceId(id)) => {
62                log_warn!("Invalid interface id found in neighbor table entry: {}", id);
63                None
64            }
65        }
66    }
67
68    /// Wrap the inner [`NeighbourMessage`] in an [`RtnlMessage::NewNeighbour`].
69    pub(crate) fn into_rtnl_new_neighbor(
70        self,
71        sequence_number: u32,
72        is_dump: bool,
73    ) -> NetlinkMessage<RouteNetlinkMessage> {
74        let NetlinkNeighborMessage(message) = self;
75        let mut msg: NetlinkMessage<RouteNetlinkMessage> =
76            RouteNetlinkMessage::NewNeighbour(message).into();
77        msg.header.sequence_number = sequence_number;
78        if is_dump {
79            msg.header.flags |= NLM_F_MULTIPART;
80        }
81        msg.finalize();
82        msg
83    }
84
85    /// Wrap the inner [`NeighbourMessage`] in an [`RtnlMessage::DelNeighbour`].
86    pub(crate) fn into_rtnl_del_neighbor(self) -> NetlinkMessage<RouteNetlinkMessage> {
87        let NetlinkNeighborMessage(message) = self;
88        let mut msg: NetlinkMessage<RouteNetlinkMessage> =
89            RouteNetlinkMessage::DelNeighbour(message).into();
90        msg.finalize();
91        msg
92    }
93}
94
95impl TryFrom<fnet_neighbor_ext::Entry> for NetlinkNeighborMessage {
96    type Error = NetlinkNeighborMessageConversionError;
97
98    fn try_from(
99        neighbor: fnet_neighbor_ext::Entry,
100    ) -> Result<NetlinkNeighborMessage, NetlinkNeighborMessageConversionError> {
101        let mut header = NeighbourHeader::default();
102        let fnet_ext::IpAddress(addr) = neighbor.neighbor.into();
103        header.family = match addr {
104            IpAddr::V4(_) => AddressFamily::Inet,
105            IpAddr::V6(_) => AddressFamily::Inet6,
106        };
107        header.ifindex = neighbor.interface.get().try_into().map_err(|_| {
108            NetlinkNeighborMessageConversionError::InvalidInterfaceId(neighbor.interface.get())
109        })?;
110        header.state = match neighbor.state {
111            fnet_neighbor::EntryState::Delay => NeighbourState::Delay,
112            fnet_neighbor::EntryState::Incomplete => NeighbourState::Incomplete,
113            fnet_neighbor::EntryState::Probe => NeighbourState::Probe,
114            fnet_neighbor::EntryState::Reachable => NeighbourState::Reachable,
115            fnet_neighbor::EntryState::Stale => NeighbourState::Stale,
116            fnet_neighbor::EntryState::Static => NeighbourState::Permanent,
117            fnet_neighbor::EntryState::Unreachable => NeighbourState::Failed,
118        };
119        // Unlike Linux, Netstack3 only keeps unicast addresses in its neighbor
120        // tables so there's no need to derive this from the address and/or
121        // interface properties.
122        header.kind = RouteType::Unicast;
123
124        let mut attributes = vec![];
125        attributes.push(NeighbourAttribute::Destination(match addr {
126            IpAddr::V4(addr) => addr.into(),
127            IpAddr::V6(addr) => addr.into(),
128        }));
129        if let Some(mac) = neighbor.mac {
130            attributes.push(NeighbourAttribute::LinkLocalAddress(mac.octets.into()));
131        }
132        // TODO(https://fxbug.dev/488135156): Include the `CacheInfo` attribute
133        // with the last update time set.
134
135        let mut msg = NeighbourMessage::default();
136        msg.header = header;
137        msg.attributes = attributes;
138        Ok(NetlinkNeighborMessage(msg))
139    }
140}
141
142fn neighbor_fidl_ip(
143    family: AddressFamily,
144    address: Option<&NeighbourAddress>,
145) -> Result<fnet::IpAddress, RequestError> {
146    match family {
147        AddressFamily::Inet => match address {
148            Some(NeighbourAddress::Inet(addr)) => Ok(fnet_ext::IpAddress(IpAddr::V4(*addr)).into()),
149            Some(_) => Err(RequestError::AddressFamilyMismatch(family)),
150            None => Err(RequestError::MissingIpAddress),
151        },
152        AddressFamily::Inet6 => match address {
153            Some(NeighbourAddress::Inet6(addr)) => {
154                Ok(fnet_ext::IpAddress(IpAddr::V6(*addr)).into())
155            }
156            Some(_) => Err(RequestError::AddressFamilyMismatch(family)),
157            None => Err(RequestError::MissingIpAddress),
158        },
159        _ => Err(RequestError::InvalidAddressFamily(family)),
160    }
161}
162
163/// Arguments for an RTM_GETNEIGH [`Request`].
164#[derive(Copy, Clone, Debug, PartialEq, Eq)]
165pub(crate) enum GetNeighborArgs {
166    Dump { ip_version: Option<IpVersion>, interface: Option<NonZeroU64> },
167    Get { ip: fnet::IpAddress, interface: NonZeroU64 },
168}
169
170impl GetNeighborArgs {
171    // Attempts to convert a netlink_packet_route `NeighbourMessage` into
172    // `GetNeighborArgs`.
173    pub(crate) fn try_from_rtnl_neighbor(
174        message: &NeighbourMessage,
175        is_dump: bool,
176    ) -> Result<Self, RequestError> {
177        if is_dump {
178            Self::dump_request_from_rtnl_neighbor(message)
179                .inspect_err(|e| log_debug!("{e} in dump neighbors request"))
180        } else {
181            Self::get_request_from_rtnl_neighbor(message)
182                .inspect_err(|e| log_debug!("{e} in get neighbors request"))
183        }
184    }
185
186    fn dump_request_from_rtnl_neighbor(message: &NeighbourMessage) -> Result<Self, RequestError> {
187        let NeighbourHeader { family, flags, .. } = &message.header;
188        if flags.contains(NeighbourFlags::Proxy) {
189            // Netstack3 does not support ARP/NDP proxying.
190            // TODO(https://fxbug.dev/42111873): Support ARP/NDP proxying.
191            log_warn!("unsupported Proxy flag in dump neighbors request");
192            return Err(RequestError::UnsupportedFlags(*flags));
193        }
194        // TODO(https://fxbug.dev/500013355): Support strict validation of dump
195        // requests.
196        let ip_version = match family {
197            AddressFamily::Unspec => None,
198            AddressFamily::Inet => Some(IpVersion::V4),
199            AddressFamily::Inet6 => Some(IpVersion::V6),
200            family => {
201                return Err(RequestError::InvalidAddressFamily(*family));
202            }
203        };
204        // Note that the interface index is pulled from the attribute here,
205        // whereas it's pulled from the header for get requests. This is
206        // intentional, in order to maintain consistency with Linux's behavior.
207        let interface = message
208            .attributes
209            .iter()
210            .find_map(|attr| match attr {
211                NeighbourAttribute::IfIndex(ifindex) => Some(u64::from(*ifindex).try_into()),
212                _ => None,
213            })
214            .transpose()
215            // 0 is treated as a lack of filter.
216            .unwrap_or(None);
217        Ok(GetNeighborArgs::Dump { ip_version, interface })
218    }
219
220    fn get_request_from_rtnl_neighbor(message: &NeighbourMessage) -> Result<Self, RequestError> {
221        let NeighbourHeader { ifindex, family, state, flags, kind } = &message.header;
222        if *state != NeighbourState::None {
223            return Err(RequestError::InvalidState {
224                actual: *state,
225                expected: NeighbourState::None,
226            });
227        }
228        if *kind != RouteType::Unspec {
229            return Err(RequestError::InvalidKind(*kind));
230        }
231        if flags.intersects(!NeighbourFlags::Proxy) {
232            return Err(RequestError::InvalidFlags(*flags));
233        }
234        if flags.contains(NeighbourFlags::Proxy) {
235            // Netstack3 does not support ARP/NDP proxying.
236            // TODO(https://fxbug.dev/42111873): Support ARP/NDP proxying.
237            log_warn!("unsupported Proxy flag in get neighbor request");
238            return Err(RequestError::UnsupportedFlags(*flags));
239        }
240
241        let (address, unsupported) = message.attributes.iter().fold(
242            (None, false),
243            |(address_acc, unsupported_acc), attr| {
244                match attr {
245                    NeighbourAttribute::Destination(addr) => {
246                        // Note: In the event the Destination attribute is
247                        // provided multiple times, keep the first.
248                        (address_acc.or(Some(addr)), unsupported_acc)
249                    }
250                    _ => {
251                        if !unsupported_acc {
252                            // Only log for the first invalid attribute to avoid spamming.
253                            log_warn!(
254                                "unsupported request attribute: {attr:?} in get neighbor\
255                                request; only `DST` is supported"
256                            );
257                        }
258                        (address_acc, true)
259                    }
260                }
261            },
262        );
263        if unsupported {
264            return Err(RequestError::InvalidAttribute);
265        }
266        let ip = neighbor_fidl_ip(*family, address)?;
267        // Note that the interface index is pulled from the header here, whereas
268        // it's pulled from the attribute for dump requests. This is
269        // intentional, in order to maintain consistency with Linux's behavior.
270        let interface =
271            u64::from(*ifindex).try_into().map_err(|_| RequestError::MissingInterface)?;
272        Ok(GetNeighborArgs::Get { ip, interface })
273    }
274}
275
276/// Arguments for an RTM_NEWNEIGH [`Request`].
277#[derive(Copy, Clone, Debug, PartialEq, Eq)]
278pub(crate) enum NewNeighborArgs {
279    CreateStatic { ip: fnet::IpAddress, interface: NonZeroU64, mac: fnet::MacAddress },
280    ProbeExisting { ip: fnet::IpAddress, interface: NonZeroU64 },
281}
282
283impl NewNeighborArgs {
284    // Attempts to convert a netlink_packet_route `NeighbourMessage` into
285    // `NewNeighborArgs`.
286    pub(crate) fn try_from_rtnl_neighbor(
287        message: &NeighbourMessage,
288        netlink_flags: u16,
289    ) -> Result<Self, RequestError> {
290        Self::try_from_rtnl_neighbor_internal(message, netlink_flags)
291            .inspect_err(|e| log_debug!("{e} in new neighbor request"))
292    }
293
294    fn try_from_rtnl_neighbor_internal(
295        message: &NeighbourMessage,
296        netlink_flags: u16,
297    ) -> Result<Self, RequestError> {
298        let NeighbourHeader { ifindex, family, flags, state, .. } = &message.header;
299        if flags.contains(NeighbourFlags::Proxy) {
300            // Netstack3 does not support ARP/NDP proxying.
301            // TODO(https://fxbug.dev/42111873): Support ARP/NDP proxying.
302            log_warn!("unsupported Proxy flag in new neighbor request");
303            return Err(RequestError::UnsupportedFlags(*flags));
304        }
305
306        // Read common attributes required for identifying the neighbor.
307
308        let (ip_addr, ll_addr) =
309            message.attributes.iter().fold((None, None), |acc @ (ip, ll), attr| match attr {
310                // Note: In the event an attribute is provided multiple times,
311                // keep the first value.
312                NeighbourAttribute::Destination(addr) => (ip.or(Some(addr)), ll),
313                NeighbourAttribute::LinkLocalAddress(addr) => (ip, ll.or(Some(addr))),
314                _ => acc,
315            });
316        let ip = neighbor_fidl_ip(*family, ip_addr)?;
317        let interface =
318            u64::from(*ifindex).try_into().map_err(|_| RequestError::MissingInterface)?;
319
320        // Determine the specific operation and check operation-specific
321        // attributes.
322
323        let new_neighbor_flags = NLM_F_CREATE | NLM_F_REPLACE | NLM_F_EXCL | NLM_F_APPEND;
324        let set_flags = netlink_flags & new_neighbor_flags;
325        if set_flags == NLM_F_REPLACE {
326            // If the caller only specified `NLM_F_REPLACE`, the only case
327            // Netstack3 supports is triggering an immediate neighbor probe.
328            if *state != NeighbourState::Probe {
329                return Err(RequestError::InvalidState {
330                    actual: *state,
331                    expected: NeighbourState::Probe,
332                });
333            }
334            // Setting the link address while triggering a neighbor probe is
335            // unsupported.
336            if ll_addr.is_some() {
337                return Err(RequestError::InvalidAttribute);
338            }
339            Ok(NewNeighborArgs::ProbeExisting { interface, ip })
340        } else if set_flags == (NLM_F_CREATE | NLM_F_REPLACE) {
341            // If the caller specified `NLM_F_CREATE`, Netstack3 only supports
342            // addition of static neighbors.
343            if *state != NeighbourState::Permanent {
344                return Err(RequestError::InvalidState {
345                    actual: *state,
346                    expected: NeighbourState::Permanent,
347                });
348            }
349            let mac = ll_addr.ok_or(RequestError::MissingMacAddress).and_then(|mac| {
350                mac.clone()
351                    .try_into()
352                    .map_err(|_| RequestError::InvalidMacAddress)
353                    .map(|octets| fnet::MacAddress { octets })
354            })?;
355            Ok(NewNeighborArgs::CreateStatic { interface, ip, mac })
356        } else {
357            Err(RequestError::UnsupportedOperation)
358        }
359    }
360}
361
362/// Arguments for an RTM_DELNEIGH [`Request`].
363#[derive(Copy, Clone, Debug, PartialEq, Eq)]
364pub(crate) struct DelNeighborArgs {
365    pub(crate) ip: fnet::IpAddress,
366    pub(crate) interface: NonZeroU64,
367}
368
369impl DelNeighborArgs {
370    // Attempts to convert a netlink_packet_route `NeighbourMessage` into
371    // `DelNeighborArgs`.
372    pub(crate) fn try_from_rtnl_neighbor(message: &NeighbourMessage) -> Result<Self, RequestError> {
373        Self::try_from_rtnl_neighbor_internal(message)
374            .inspect_err(|e| log_debug!("{e} in del neighbor request"))
375    }
376
377    fn try_from_rtnl_neighbor_internal(message: &NeighbourMessage) -> Result<Self, RequestError> {
378        let NeighbourHeader { ifindex, family, flags, .. } = &message.header;
379        if flags.contains(NeighbourFlags::Proxy) {
380            // Netstack3 does not support ARP/NDP proxying.
381            // TODO(https://fxbug.dev/42111873): Support ARP/NDP proxying.
382            log_warn!("unsupported Proxy flag in del neighbor request");
383            return Err(RequestError::UnsupportedFlags(*flags));
384        }
385
386        let address = message.attributes.iter().find_map(|attr| match attr {
387            NeighbourAttribute::Destination(addr) => Some(addr),
388            _ => None,
389        });
390        let ip = neighbor_fidl_ip(*family, address)?;
391        let interface =
392            u64::from(*ifindex).try_into().map_err(|_| RequestError::MissingInterface)?;
393        Ok(Self { interface, ip })
394    }
395}
396
397/// [`Request`] arguments associated with neighbors.
398#[derive(Copy, Clone, Debug, PartialEq, Eq)]
399pub(crate) enum NeighborRequestArgs {
400    /// RTM_GETNEIGH
401    Get(GetNeighborArgs),
402    /// RTM_NEWNEIGH
403    New(NewNeighborArgs),
404    /// RTM_DELNEIGH
405    Del(DelNeighborArgs),
406}
407
408/// An error encountered while handling a [`Request`].
409#[derive(Copy, Clone, Debug, PartialEq, Eq, Error)]
410pub(crate) enum RequestError {
411    /// Invalid state in neighbor header.
412    #[error("invalid state; expected={expected:?}, actual={actual:?}")]
413    InvalidState { actual: NeighbourState, expected: NeighbourState },
414    /// Invalid kind in neighbor header.
415    #[error("invalid kind: {0:?}")]
416    InvalidKind(RouteType),
417    /// Invalid flags in neighbor header.
418    #[error("invalid flags: {0:?}")]
419    InvalidFlags(NeighbourFlags),
420    /// Unsupported flags.
421    #[error("unsupported flags: {0:?}")]
422    UnsupportedFlags(NeighbourFlags),
423    /// Invalid address family.
424    #[error("invalid address family: {0:?}")]
425    InvalidAddressFamily(AddressFamily),
426    /// Address family in request header doesn't match family of address.
427    // In practice this should never be encountered:
428    // `NeighbourAddress::parse_with_param` parses the address based on the
429    // address family from the header, and a discrepancy between the expected
430    // and actual address length results in a parsing failure.
431    #[error("address family mismatch; expected={0:?}")]
432    AddressFamilyMismatch(AddressFamily),
433    /// Request doesn't specify required neighbor IP address.
434    #[error("missing required `DST` attribute")]
435    MissingIpAddress,
436    /// Request doesn't specify required neighbor MAC address.
437    #[error("missing required `LLADDR` attribute")]
438    MissingMacAddress,
439    /// Request doesn't specify required interface.
440    #[error("missing required interface")]
441    MissingInterface,
442    /// Request specifies invalid attribute(s).
443    #[error("invalid request attribute")]
444    InvalidAttribute,
445    /// No such neighbor.
446    #[error("no such neighbor")]
447    NeighborNotFound,
448    /// Interface not found.
449    #[error("no such interface")]
450    InterfaceNotFound,
451    /// Invalid neighbor IP address.
452    #[error("invalid neighbor IP address")]
453    InvalidIpAddress,
454    /// Invalid neighbor MAC address.
455    #[error("invalid neighbor MAC address")]
456    InvalidMacAddress,
457    /// Interface not supported.
458    #[error("interface not supported")]
459    InterfaceUnsupported,
460    /// Neighbor link address unknown.
461    #[error("link address unknown")]
462    LinkAddressUnknown,
463    /// Operation not supported.
464    #[error("unsupported operation")]
465    UnsupportedOperation,
466    /// Table is full.
467    #[error("table full")]
468    TableFull,
469}
470
471impl From<RequestError> for Errno {
472    fn from(value: RequestError) -> Self {
473        match value {
474            RequestError::InvalidState { .. } => Errno::EINVAL,
475            RequestError::InvalidKind(_) => Errno::EINVAL,
476            RequestError::InvalidFlags(_) => Errno::EINVAL,
477            RequestError::UnsupportedFlags(_) => Errno::ENOTSUP,
478            RequestError::InvalidAddressFamily(_) => Errno::EAFNOSUPPORT,
479            RequestError::AddressFamilyMismatch(_) => Errno::EINVAL,
480            RequestError::MissingIpAddress => Errno::EINVAL,
481            RequestError::MissingMacAddress => Errno::EINVAL,
482            RequestError::MissingInterface => Errno::EINVAL,
483            RequestError::InvalidAttribute => Errno::EINVAL,
484            RequestError::NeighborNotFound => Errno::ENOENT,
485            RequestError::InterfaceNotFound => Errno::ENODEV,
486            RequestError::InvalidIpAddress => Errno::EINVAL,
487            RequestError::InvalidMacAddress => Errno::EINVAL,
488            RequestError::InterfaceUnsupported => Errno::ENOTSUP,
489            RequestError::LinkAddressUnknown => Errno::EINVAL,
490            RequestError::UnsupportedOperation => Errno::ENOTSUP,
491            RequestError::TableFull => Errno::ENOBUFS,
492        }
493    }
494}
495
496impl From<fnet_neighbor::ControllerError> for RequestError {
497    fn from(value: fnet_neighbor::ControllerError) -> Self {
498        use fnet_neighbor::ControllerError;
499        match value {
500            ControllerError::InterfaceNotFound => RequestError::InterfaceNotFound,
501            ControllerError::InterfaceNotSupported => RequestError::InterfaceUnsupported,
502            ControllerError::InvalidIpAddress => RequestError::InvalidIpAddress,
503            ControllerError::MacAddressNotUnicast => RequestError::InvalidMacAddress,
504            ControllerError::NeighborNotFound => RequestError::NeighborNotFound,
505            ControllerError::LinkAddressUnknown => RequestError::LinkAddressUnknown,
506            ControllerError::TooManyEntries => RequestError::TableFull,
507            ControllerError::__SourceBreaking { unknown_ordinal: e } => {
508                panic!("encountered unknown controller error: {e:?}")
509            }
510        }
511    }
512}
513
514/// Trait abstracting the ability to check if an interface exists.
515pub(crate) trait LookupIfInterfaceExists {
516    /// Returns whether an interface exists at the provided index.
517    fn exists(&self, interface: NonZeroU64) -> bool;
518}
519
520type InterfaceMap = BTreeMap<
521    u64,
522    fnet_interfaces_ext::PropertiesAndState<
523        crate::interfaces::InterfaceState,
524        fnet_interfaces_ext::AllInterest,
525    >,
526>;
527
528impl LookupIfInterfaceExists for InterfaceMap {
529    fn exists(&self, interface: NonZeroU64) -> bool {
530        self.contains_key(&interface.get())
531    }
532}
533
534/// A request associated with neighbors.
535#[derive(Derivative)]
536#[derivative(Debug(bound = ""))]
537pub(crate) struct Request<S: Sender<<NetlinkRoute as ProtocolFamily>::Response>> {
538    /// The resource and operation-specific argument(s) for this request.
539    pub args: NeighborRequestArgs,
540    /// The request's sequence number.
541    ///
542    /// This value will be copied verbatim into any message sent as a result of
543    /// this request.
544    pub sequence_number: u32,
545    /// The client that made the request.
546    pub client: InternalClient<NetlinkRoute, S>,
547    /// A completer that will have the result of the request sent over.
548    pub completer: oneshot::Sender<Result<(), RequestError>>,
549}
550
551/// A subset of `NeighborRequestArgs`, containing only `Request` types that can be pending.
552#[derive(Clone, Debug, PartialEq, Eq)]
553pub(crate) enum PendingNeighborRequestArgs {
554    /// RTM_NEWNEIGH
555    New(NewNeighborArgs),
556    /// RTM_DELNEIGH
557    Del(DelNeighborArgs),
558}
559
560#[derive(Derivative)]
561#[derivative(Debug(bound = ""))]
562pub(crate) struct PendingNeighborRequest<S: Sender<<NetlinkRoute as ProtocolFamily>::Response>> {
563    request_args: PendingNeighborRequestArgs,
564    client: InternalClient<NetlinkRoute, S>,
565    completer: oneshot::Sender<Result<(), RequestError>>,
566}
567
568/// Errors related to handling neighbor events from Netstack.
569#[derive(Debug, Error, PartialEq)]
570pub(crate) enum HandleWatchEventError {
571    /// An event indicated a neighbor was removed that was not previously known.
572    #[error("Netstack reported removal of an unknown neighbor: {0:?}")]
573    UnknownNeighborRemoved(fnet_neighbor_ext::Entry),
574    /// An event indicated a neighbor was changed that was not previously known.
575    #[error("Netstack reported change of an unknown neighbor: {0:?}")]
576    UnknownNeighborChanged(fnet_neighbor_ext::Entry),
577    /// An event indicated a neighbor was added that conflicts with a known
578    /// neighbor.
579    #[error(
580        "Netstack reported addition of a neighbor that already exists: \
581        existing={existing:?}, new={new:?}"
582    )]
583    ConflictingNeighborAdded { existing: fnet_neighbor_ext::Entry, new: fnet_neighbor_ext::Entry },
584    /// An `Existing` or `Idle` event was received after collecting the initial
585    /// neighbors from the event stream.
586    #[error("Netstack reported unexpected event: {0:?}")]
587    UnexpectedEventReceived(fnet_neighbor_ext::Event),
588}
589
590#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
591struct NeighborKey {
592    interface: NonZeroU64,
593    neighbor: fnet::IpAddress,
594}
595
596impl From<&fnet_neighbor_ext::Entry> for NeighborKey {
597    fn from(
598        fnet_neighbor_ext::Entry { interface, neighbor, .. }: &fnet_neighbor_ext::Entry,
599    ) -> NeighborKey {
600        NeighborKey { interface: *interface, neighbor: *neighbor }
601    }
602}
603
604/// Handles asynchronous work related to RTM_*NEIGH messages.
605///
606/// Can respond to RTM_*NEIGH message requests.
607pub(crate) struct NeighborsWorker {
608    neighbor_table: HashMap<NeighborKey, fnet_neighbor_ext::Entry>,
609    neighbors_controller: fnet_neighbor::ControllerProxy,
610}
611
612impl NeighborsWorker {
613    /// Create the Netlink Neighbors Worker.
614    ///
615    /// Panics if the existing neighbors cannot be retrieved from
616    /// `neighbors_view` or if the response contains conflicting neighbors.
617    pub(crate) async fn create(
618        neighbors_view: &fnet_neighbor::ViewProxy,
619        neighbors_controller: fnet_neighbor::ControllerProxy,
620    ) -> (
621        Self,
622        impl futures::Stream<
623            Item = Result<fnet_neighbor_ext::Event, fnet_neighbor_ext::EntryIteratorError>,
624        > + Unpin
625        + 'static,
626    ) {
627        let mut neighbor_event_stream = Box::pin(
628            fnet_neighbor_ext::event_stream_from_view(neighbors_view)
629                .expect("connecting to fuchsia.net.neighbors.View FIDL should succeed"),
630        );
631        let existing_neighbors: HashSet<fnet_neighbor_ext::Entry> =
632            fnet_neighbor_ext::collect_neighbors_until_idle(neighbor_event_stream.by_ref())
633                .await
634                .expect("determining existing neighbors should succeed");
635        let existing_count = existing_neighbors.len();
636        let neighbor_table = existing_neighbors
637            .into_iter()
638            .map(|e| (NeighborKey::from(&e), e))
639            .collect::<HashMap<_, _>>();
640        assert_eq!(
641            neighbor_table.len(),
642            existing_count,
643            "conflicting existing entry in neighbor table"
644        );
645        (Self { neighbor_table, neighbors_controller }, neighbor_event_stream)
646    }
647
648    pub(crate) fn handle_neighbor_watcher_event<
649        S: Sender<<NetlinkRoute as ProtocolFamily>::Response>,
650    >(
651        &mut self,
652        event: fnet_neighbor_ext::Event,
653        clients: &ClientTable<NetlinkRoute, S>,
654    ) -> Result<(), HandleWatchEventError> {
655        let message_for_group = match event {
656            fnet_neighbor_ext::Event::Removed(entry) => {
657                match self.neighbor_table.remove(&(&entry).into()) {
658                    Some(_) => Ok(NetlinkNeighborMessage::optionally_from(entry)
659                        .map(NetlinkNeighborMessage::into_rtnl_del_neighbor)),
660                    None => Err(HandleWatchEventError::UnknownNeighborRemoved(entry)),
661                }
662            }
663            fnet_neighbor_ext::Event::Added(entry) => {
664                match self.neighbor_table.insert((&entry).into(), entry.clone()) {
665                    Some(existing) => Err(HandleWatchEventError::ConflictingNeighborAdded {
666                        existing,
667                        new: entry,
668                    }),
669                    None => Ok(NetlinkNeighborMessage::optionally_from(entry)
670                        .map(|n| n.into_rtnl_new_neighbor(UNSPECIFIED_SEQUENCE_NUMBER, false))),
671                }
672            }
673            fnet_neighbor_ext::Event::Changed(entry) => {
674                match self.neighbor_table.insert((&entry).into(), entry.clone()) {
675                    Some(_) => Ok(NetlinkNeighborMessage::optionally_from(entry)
676                        .map(|n| n.into_rtnl_new_neighbor(UNSPECIFIED_SEQUENCE_NUMBER, false))),
677                    None => Err(HandleWatchEventError::UnknownNeighborChanged(entry)),
678                }
679            }
680            e @ fnet_neighbor_ext::Event::Existing(_) | e @ fnet_neighbor_ext::Event::Idle => {
681                Err(HandleWatchEventError::UnexpectedEventReceived(e))
682            }
683        }?;
684        if let Some(message) = message_for_group {
685            clients.send_message_to_group(message, ModernGroup(rtnetlink_groups_RTNLGRP_NEIGH));
686        }
687        Ok(())
688    }
689
690    pub(crate) async fn handle_request<S: Sender<<NetlinkRoute as ProtocolFamily>::Response>>(
691        &mut self,
692        Request { args, mut client, sequence_number, completer }: Request<S>,
693        interface_lookup: &impl LookupIfInterfaceExists,
694    ) -> Option<PendingNeighborRequest<S>> {
695        enum HandleResult {
696            Done(Result<(), RequestError>),
697            Pending(PendingNeighborRequestArgs),
698        }
699        let result = match args {
700            NeighborRequestArgs::Get(args) => match args {
701                GetNeighborArgs::Dump { ip_version, interface } => {
702                    self.neighbor_table
703                        .values()
704                        .filter(|n| {
705                            ip_version.map_or(true, |ip_version| match n.neighbor {
706                                fnet::IpAddress::Ipv4(_) => ip_version == IpVersion::V4,
707                                fnet::IpAddress::Ipv6(_) => ip_version == IpVersion::V6,
708                            })
709                        })
710                        .filter(|n| interface.map_or(true, |i| n.interface == i))
711                        .filter_map(|e| NetlinkNeighborMessage::optionally_from(e.clone()))
712                        .for_each(|m| {
713                            client.send_unicast(m.into_rtnl_new_neighbor(sequence_number, true));
714                        });
715                    HandleResult::Done(Ok(()))
716                }
717                GetNeighborArgs::Get { ip, interface } => {
718                    let neighbor = self
719                        .neighbor_table
720                        .get(&NeighborKey { interface, neighbor: ip })
721                        .map(|e| NetlinkNeighborMessage::optionally_from(e.clone()))
722                        .flatten();
723                    match neighbor {
724                        Some(msg) => {
725                            client.send_unicast(msg.into_rtnl_new_neighbor(sequence_number, false));
726                            HandleResult::Done(Ok(()))
727                        }
728                        None => {
729                            let err = if interface_lookup.exists(interface) {
730                                RequestError::NeighborNotFound
731                            } else {
732                                RequestError::InterfaceNotFound
733                            };
734                            HandleResult::Done(Err(err))
735                        }
736                    }
737                }
738            },
739            NeighborRequestArgs::New(args) => match args {
740                args @ NewNeighborArgs::CreateStatic { ip, interface, mac } => {
741                    let response = self
742                        .neighbors_controller
743                        .add_entry(interface.get(), &ip, &mac)
744                        .await
745                        .expect("sent neighbor controller request");
746                    match response {
747                        Ok(_) => HandleResult::Pending(PendingNeighborRequestArgs::New(args)),
748                        Err(e) => HandleResult::Done(Err(e.into())),
749                    }
750                }
751                args @ NewNeighborArgs::ProbeExisting { ip, interface } => {
752                    let response = self
753                        .neighbors_controller
754                        .probe_entry(interface.get(), &ip)
755                        .await
756                        .expect("sent neighbor controller request");
757                    match response {
758                        Ok(_) => HandleResult::Pending(PendingNeighborRequestArgs::New(args)),
759                        Err(e) => HandleResult::Done(Err(e.into())),
760                    }
761                }
762            },
763            NeighborRequestArgs::Del(args @ DelNeighborArgs { interface, ip }) => {
764                let response = self
765                    .neighbors_controller
766                    .remove_entry(interface.get(), &ip)
767                    .await
768                    .expect("sent neighbor controller request");
769                match response {
770                    Ok(_) => HandleResult::Pending(PendingNeighborRequestArgs::Del(args)),
771                    Err(e) => HandleResult::Done(Err(e.into())),
772                }
773            }
774        };
775
776        match result {
777            HandleResult::Done(result) => {
778                log_debug!("handled request {args:?} from {client} with result = {result:?}");
779                respond_to_completer(client, completer, result, args);
780                None
781            }
782            HandleResult::Pending(request_args) => {
783                log_debug!("pending request {args:?} from {client}");
784                Some(PendingNeighborRequest { request_args, client, completer })
785            }
786        }
787    }
788
789    /// Checks whether a `PendingRequest` can be marked completed given the
790    /// current state of the worker. If so, notifies the request's completer and
791    /// returns `None`. If not, returns the `PendingRequest` as `Some`.
792    ///
793    /// TODO(https://fxbug.dev/488124265): Use synchronization primitives to
794    /// more robustly match requests to their corresponding watch events.
795    pub(crate) fn handle_pending_request<S: Sender<<NetlinkRoute as ProtocolFamily>::Response>>(
796        &self,
797        pending_neighbor_request: PendingNeighborRequest<S>,
798    ) -> Option<PendingNeighborRequest<S>> {
799        let PendingNeighborRequest { request_args, client: _, completer: _ } =
800            &pending_neighbor_request;
801
802        let done = match request_args {
803            PendingNeighborRequestArgs::New(NewNeighborArgs::ProbeExisting { ip, interface }) => {
804                // Assuming the `ProbeEntry` call succeeds, this is guaranteed
805                // to complete eventually, despite the fact that Netstack does
806                // not generate a `Changed` event if the neighbor is already in
807                // the expected state.
808                //
809                // If the neighbor was not in the expected state when Netstack
810                // processed the request, then a `Changed` event is generated,
811                // and since this method is called after each event that Netlink
812                // processes, this condition must eventually be true.
813                //
814                // If the neighbor *was* in the expected state when Netstack
815                // processed the request, then no `Changed` event is generated,
816                // but it's necessarily true that the immediately preceding
817                // `Added` or `Changed` event for the neighbor must contain the
818                // expected state.
819                //
820                // Here there are two cases to consider: Netlink has either
821                // already processed that event, or not.
822                //
823                // In the former case, the fact that there cannot have been
824                // intervening events means that this check will succeed on the
825                // call to this method that immediately follows the Controller
826                // request in the event loop.
827                //
828                // In the latter case, the event will be processed in a later
829                // iteration of the event loop, at which point this condition
830                // will become true.
831                self.neighbor_table
832                    .get(&NeighborKey { interface: *interface, neighbor: *ip })
833                    .map_or(false, |entry| entry.state == fnet_neighbor::EntryState::Probe)
834            }
835            PendingNeighborRequestArgs::New(NewNeighborArgs::CreateStatic {
836                ip,
837                interface,
838                mac,
839            }) => {
840                // It's also true here that Netstack does not generate an event
841                // if the neighbor is already in the expected state, but this is
842                // nevertheless guaranteed to complete eventually by the same
843                // logic as above.
844                self.neighbor_table
845                    .get(&NeighborKey { interface: *interface, neighbor: *ip })
846                    .map_or(false, |entry| {
847                        entry.mac.is_some_and(|m| m == *mac)
848                            && entry.state == fnet_neighbor::EntryState::Static
849                    })
850            }
851            PendingNeighborRequestArgs::Del(DelNeighborArgs { ip, interface }) => !self
852                .neighbor_table
853                .contains_key(&NeighborKey { interface: *interface, neighbor: *ip }),
854        };
855
856        if done {
857            log_debug!("completed pending request; req = {pending_neighbor_request:?}");
858            let PendingNeighborRequest { request_args, client, completer } =
859                pending_neighbor_request;
860
861            respond_to_completer(client, completer, Ok(()), request_args);
862            None
863        } else {
864            // Put the pending request back so that it can be handled later.
865            log_debug!("pending request not done yet; req = {pending_neighbor_request:?}");
866            Some(pending_neighbor_request)
867        }
868    }
869}
870
871#[cfg(test)]
872mod tests {
873    use crate::client::ClientTable;
874    use crate::client::testutil::{CLIENT_ID_1, CLIENT_ID_2, new_fake_client};
875    use crate::interfaces::testutil::FakeInterfacesHandler;
876    use crate::messaging::testutil::{FakeSender, SentMessage};
877    use crate::route_eventloop::{
878        EventLoopComponent, EventLoopInputs, EventLoopSpec, EventLoopState, IncludedWorkers,
879        Optional, Required, UnifiedRequest,
880    };
881
882    use super::*;
883
884    use assert_matches::assert_matches;
885    use fidl_fuchsia_net as fnet;
886    use fidl_fuchsia_net_interfaces as fnet_interfaces;
887    use fidl_fuchsia_net_neighbor::ViewRequest;
888    use fidl_fuchsia_net_neighbor_ext::testutil::EventSpec;
889    use fidl_fuchsia_net_root as fnet_root;
890    use futures::channel::mpsc;
891    use futures::{FutureExt as _, SinkExt as _, TryStreamExt as _, pin_mut};
892    use maplit::hashset;
893    use net_declare::{fidl_ip, std_ip_v4, std_ip_v6};
894    use netlink_packet_core::NetlinkPayload;
895    use netlink_packet_route::neighbour::{NeighbourAddress, NeighbourFlags};
896    use std::collections::HashSet;
897    use test_case::test_case;
898
899    fn valid_neighbor_entry() -> fnet_neighbor_ext::Entry {
900        fnet_neighbor_ext::Entry {
901            interface: NonZeroU64::new(1).unwrap(),
902            neighbor: fidl_ip!("192.168.0.1"),
903            state: fnet_neighbor::EntryState::Reachable,
904            mac: Some(fnet::MacAddress { octets: [0, 1, 2, 3, 4, 5] }),
905            updated_at: 123456,
906        }
907    }
908
909    #[test]
910    fn netlink_neighbor_message_from_entry_invalid_iface_id() {
911        let entry = fnet_neighbor_ext::Entry {
912            interface: NonZeroU64::new(u64::MAX).unwrap(),
913            ..valid_neighbor_entry()
914        };
915
916        assert_eq!(
917            NetlinkNeighborMessage::try_from(entry),
918            Err(NetlinkNeighborMessageConversionError::InvalidInterfaceId(u64::MAX))
919        );
920    }
921
922    #[test]
923    fn netlink_neighbor_message_from_entry_valid_iface_id() {
924        assert_matches!(
925            NetlinkNeighborMessage::try_from(fnet_neighbor_ext::Entry {
926                interface: NonZeroU64::new(1).unwrap(),
927                ..valid_neighbor_entry()
928            }),
929            Ok(NetlinkNeighborMessage(NeighbourMessage {
930                header: NeighbourHeader { ifindex: 1, .. },
931                ..
932            }))
933        );
934    }
935
936    #[test_case(fnet_neighbor::EntryState::Delay, NeighbourState::Delay; "delay")]
937    #[test_case(fnet_neighbor::EntryState::Incomplete, NeighbourState::Incomplete; "incomplete")]
938    #[test_case(fnet_neighbor::EntryState::Probe, NeighbourState::Probe; "probe")]
939    #[test_case(fnet_neighbor::EntryState::Reachable, NeighbourState::Reachable; "reachable")]
940    #[test_case(fnet_neighbor::EntryState::Stale, NeighbourState::Stale; "stale")]
941    #[test_case(fnet_neighbor::EntryState::Static, NeighbourState::Permanent; "permanent")]
942    #[test_case(fnet_neighbor::EntryState::Unreachable, NeighbourState::Failed; "failed")]
943    fn netlink_neighbor_message_from_entry_state_converted(
944        fidl_state: fnet_neighbor::EntryState,
945        expected: NeighbourState,
946    ) {
947        assert_matches!(
948            NetlinkNeighborMessage::try_from(fnet_neighbor_ext::Entry {
949                state: fidl_state,
950                ..valid_neighbor_entry()
951            }),
952            Ok(NetlinkNeighborMessage(NeighbourMessage {
953                header: NeighbourHeader { state, .. },
954                ..
955            })) if state == expected
956        );
957    }
958
959    #[test]
960    fn netlink_neighbor_message_from_entry_ipv4() {
961        let fidl_entry = fnet_neighbor_ext::Entry {
962            neighbor: fidl_ip!("192.168.0.1"),
963            ..valid_neighbor_entry()
964        };
965        let NetlinkNeighborMessage(message) =
966            fidl_entry.try_into().expect("should be able to convert valid neighbor entry");
967
968        assert_eq!(message.header.family, AddressFamily::Inet);
969        let expected_address: NeighbourAddress = std_ip_v4!("192.168.0.1").into();
970        assert_matches!(
971            &message.attributes[..],
972            [
973                NeighbourAttribute::Destination(address),
974                NeighbourAttribute::LinkLocalAddress(_)
975            ] if *address == expected_address
976        );
977    }
978
979    #[test]
980    fn netlink_neighbor_message_from_entry_ipv6() {
981        let fidl_entry =
982            fnet_neighbor_ext::Entry { neighbor: fidl_ip!("fe80::1"), ..valid_neighbor_entry() };
983        let NetlinkNeighborMessage(message) =
984            fidl_entry.try_into().expect("should be able to convert valid neighbor entry");
985
986        assert_eq!(message.header.family, AddressFamily::Inet6);
987        let expected_address: NeighbourAddress = std_ip_v6!("fe80::1").into();
988        assert_matches!(
989            &message.attributes[..],
990            [
991                NeighbourAttribute::Destination(address),
992                NeighbourAttribute::LinkLocalAddress(_)
993            ] if *address == expected_address
994        );
995    }
996
997    #[test]
998    fn netlink_neighbor_message_from_entry_address_link_local_present() {
999        let fidl_entry = fnet_neighbor_ext::Entry {
1000            mac: Some(fnet::MacAddress { octets: [0, 1, 2, 3, 4, 5] }),
1001            ..valid_neighbor_entry()
1002        };
1003        let NetlinkNeighborMessage(message) =
1004            fidl_entry.try_into().expect("should be able to convert valid neighbor entry");
1005
1006        assert_matches!(
1007            &message.attributes[..],
1008            [
1009                NeighbourAttribute::Destination(_),
1010                NeighbourAttribute::LinkLocalAddress(addr)
1011            ] if addr == &[0, 1, 2, 3, 4, 5]
1012        );
1013    }
1014
1015    #[test]
1016    fn netlink_neighbor_message_from_entry_address_link_local_absent() {
1017        let fidl_entry = fnet_neighbor_ext::Entry { mac: None, ..valid_neighbor_entry() };
1018        let NetlinkNeighborMessage(message) =
1019            fidl_entry.try_into().expect("should be able to convert valid neighbor entry");
1020
1021        assert_matches!(&message.attributes[..], [NeighbourAttribute::Destination(_)]);
1022    }
1023
1024    #[test]
1025    fn netlink_neighbor_message_optionally_from_failure() {
1026        assert_eq!(
1027            NetlinkNeighborMessage::optionally_from(fnet_neighbor_ext::Entry {
1028                interface: NonZeroU64::new(u64::MAX).unwrap(),
1029                ..valid_neighbor_entry()
1030            }),
1031            None
1032        );
1033    }
1034
1035    #[test]
1036    fn netlink_neighbor_message_optionally_from_success() {
1037        let fidl_entry = fnet_neighbor_ext::Entry {
1038            interface: NonZeroU64::new(1).unwrap(),
1039            neighbor: fidl_ip!("192.168.0.1"),
1040            state: fnet_neighbor::EntryState::Reachable,
1041            mac: None,
1042            updated_at: 123456,
1043        };
1044
1045        let mut expected_message = NeighbourMessage::default();
1046        expected_message.header = NeighbourHeader {
1047            ifindex: 1,
1048            family: AddressFamily::Inet,
1049            state: NeighbourState::Reachable,
1050            flags: NeighbourFlags::empty(),
1051            kind: RouteType::Unicast,
1052        };
1053        expected_message.attributes =
1054            vec![NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into())];
1055
1056        assert_eq!(
1057            NetlinkNeighborMessage::optionally_from(fidl_entry),
1058            Some(NetlinkNeighborMessage(expected_message))
1059        );
1060    }
1061
1062    #[test]
1063    fn netlink_neighbor_message_into_rtnl_new_neighbor() {
1064        let message: NetlinkNeighborMessage = valid_neighbor_entry()
1065            .try_into()
1066            .expect("should be able to convert valid neighbor entry");
1067        let NetlinkNeighborMessage(payload) = &message;
1068
1069        let expected_payload =
1070            NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewNeighbour(payload.clone()));
1071
1072        let result = message.clone().into_rtnl_new_neighbor(1, true);
1073        assert_eq!(result.payload, expected_payload);
1074        assert_eq!(result.header.sequence_number, 1);
1075        assert_eq!(result.header.flags & NLM_F_MULTIPART, NLM_F_MULTIPART);
1076
1077        let result = message.into_rtnl_new_neighbor(1, false);
1078        assert_eq!(result.payload, expected_payload);
1079        assert_ne!(result.header.flags & NLM_F_MULTIPART, NLM_F_MULTIPART);
1080    }
1081
1082    #[test]
1083    fn neighbor_keyed_by_interface_and_ip() {
1084        let entry = fnet_neighbor_ext::Entry {
1085            interface: NonZeroU64::new(1).unwrap(),
1086            neighbor: fidl_ip!("192.168.0.1"),
1087            mac: None,
1088            state: fnet_neighbor::EntryState::Reachable,
1089            updated_at: 123456,
1090        };
1091
1092        let same_iface_and_ip = fnet_neighbor_ext::Entry {
1093            mac: Some(fnet::MacAddress { octets: [0, 1, 2, 3, 4, 5] }),
1094            state: fnet_neighbor::EntryState::Stale,
1095            updated_at: 654321,
1096            ..entry
1097        };
1098        assert_eq!(NeighborKey::from(&entry), NeighborKey::from(&same_iface_and_ip));
1099
1100        let different_iface =
1101            fnet_neighbor_ext::Entry { interface: NonZeroU64::new(2).unwrap(), ..entry };
1102        assert_ne!(NeighborKey::from(&entry), NeighborKey::from(&different_iface));
1103
1104        let different_ip = fnet_neighbor_ext::Entry { neighbor: fidl_ip!("192.168.0.2"), ..entry };
1105        assert_ne!(NeighborKey::from(&entry), NeighborKey::from(&different_ip));
1106
1107        let different_iface_and_ip = fnet_neighbor_ext::Entry {
1108            interface: NonZeroU64::new(2).unwrap(),
1109            neighbor: fidl_ip!("192.168.0.2"),
1110            ..entry
1111        };
1112        assert_ne!(NeighborKey::from(&entry), NeighborKey::from(&different_iface_and_ip));
1113    }
1114
1115    #[fuchsia::test]
1116    #[should_panic(expected = "determining existing neighbors should succeed")]
1117    async fn neighbors_worker_create_panics_on_view_protocol_error() {
1118        let (controller, _controller_server_end) =
1119            fidl::endpoints::create_proxy::<fnet_neighbor::ControllerMarker>();
1120        let (view, view_server_end) = fidl::endpoints::create_proxy::<fnet_neighbor::ViewMarker>();
1121        // Close the channel without responding.
1122        drop(view_server_end);
1123
1124        let (_worker, _remaining) = NeighborsWorker::create(&view, controller).await;
1125    }
1126
1127    #[fuchsia::test]
1128    #[should_panic(expected = "determining existing neighbors should succeed")]
1129    async fn neighbors_worker_create_panics_on_event_stream_error() {
1130        let (controller, _controller_server_end) =
1131            fidl::endpoints::create_proxy::<fnet_neighbor::ControllerMarker>();
1132        let (view, view_server_end) = fidl::endpoints::create_proxy::<fnet_neighbor::ViewMarker>();
1133        let mut view_request_stream = view_server_end.into_stream();
1134
1135        let entry_iter_fut = view_request_stream
1136            .next()
1137            .then(|req| {
1138                match req
1139                    .expect("View request_stream unexpectedly ended")
1140                    .expect("failed to receive `OpenEntryIterator` request")
1141                {
1142                    ViewRequest::OpenEntryIterator { it, .. } => {
1143                        // Close the channel without responding.
1144                        drop(it);
1145                        futures::future::ready(())
1146                    }
1147                }
1148            })
1149            .fuse();
1150
1151        let worker_fut = NeighborsWorker::create(&view, controller);
1152
1153        let ((), (_worker, _remaining)) = futures::join!(entry_iter_fut, worker_fut);
1154    }
1155
1156    #[fuchsia::test]
1157    #[should_panic(expected = "conflicting existing entry")]
1158    async fn neighbors_worker_create_panics_on_conflicting_entry() {
1159        let events: Vec<_> = [
1160            // Create two neighbors with the same `NeighborKey` but differing
1161            // fields; truly duplicate entries are ignored.
1162            fnet_neighbor_ext::Entry {
1163                state: fnet_neighbor::EntryState::Reachable,
1164                ..valid_neighbor_entry()
1165            },
1166            fnet_neighbor_ext::Entry {
1167                state: fnet_neighbor::EntryState::Stale,
1168                ..valid_neighbor_entry()
1169            },
1170        ]
1171        .into_iter()
1172        .map(Into::into)
1173        .map(fnet_neighbor::EntryIteratorItem::Existing)
1174        .chain(std::iter::once(fnet_neighbor::EntryIteratorItem::Idle(fnet_neighbor::IdleEvent)))
1175        .collect();
1176        let batches = vec![events];
1177        let (view, server_fut) =
1178            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(batches));
1179
1180        let (controller, _controller_server_end) =
1181            fidl::endpoints::create_proxy::<fnet_neighbor::ControllerMarker>();
1182        let worker_fut = NeighborsWorker::create(&view, controller);
1183
1184        let ((), (_worker, _remaining)) = futures::join!(server_fut, worker_fut);
1185    }
1186
1187    #[fuchsia::test]
1188    async fn neighbors_worker_create_success() {
1189        use fnet_neighbor_ext::testutil::EventSpec::*;
1190        let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
1191            Existing(1),
1192            Existing(2),
1193            Existing(3),
1194            Idle,
1195            Added(4),
1196        ]);
1197        let (view, server_fut) =
1198            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(vec![
1199                events.clone(),
1200            ]));
1201
1202        let (controller, _controller_server_end) =
1203            fidl::endpoints::create_proxy::<fnet_neighbor::ControllerMarker>();
1204        let worker_fut = NeighborsWorker::create(&view, controller);
1205
1206        let ((), (worker, event_stream)) = futures::join!(server_fut, worker_fut);
1207
1208        let remaining_events: Vec<_> = event_stream.collect().await;
1209        assert_matches!(
1210            &remaining_events[..],
1211            [
1212                Ok(fnet_neighbor_ext::Event::Added(_)),
1213                Err(fnet_neighbor_ext::EntryIteratorError::Fidl(
1214                    fidl::Error::ClientChannelClosed { .. }
1215                ))
1216            ]
1217        );
1218
1219        for event in events {
1220            match event {
1221                fnet_neighbor::EntryIteratorItem::Existing(fidl_entry) => {
1222                    let entry: fnet_neighbor_ext::Entry = fidl_entry.try_into().unwrap();
1223                    assert_eq!(worker.neighbor_table.get(&(&entry).into()), Some(&entry));
1224                }
1225                _ => {}
1226            }
1227        }
1228    }
1229
1230    #[test_case(
1231        EventSpec::Added(2),
1232        |e| matches!(e, HandleWatchEventError::ConflictingNeighborAdded { .. });
1233        "conflicting added"
1234    )]
1235    #[test_case(
1236        EventSpec::Removed(4),
1237        |e| matches!(e, HandleWatchEventError::UnknownNeighborRemoved(_));
1238        "unknown removed"
1239    )]
1240    #[test_case(
1241        EventSpec::Changed(4),
1242        |e| matches!(e, HandleWatchEventError::UnknownNeighborChanged(_));
1243        "unknown changed"
1244    )]
1245    #[test_case(
1246        EventSpec::Existing(4),
1247        |e| matches!(e, HandleWatchEventError::UnexpectedEventReceived(_));
1248        "existing after initial collection"
1249    )]
1250    #[test_case(
1251        EventSpec::Idle,
1252        |e| matches!(e, HandleWatchEventError::UnexpectedEventReceived(_));
1253        "idle after initial collection"
1254    )]
1255    #[fuchsia::test]
1256    async fn neighbors_worker_handle_watch_event_failure(
1257        spec: EventSpec,
1258        error_matcher: fn(&HandleWatchEventError) -> bool,
1259    ) {
1260        use fnet_neighbor_ext::testutil::EventSpec::*;
1261        let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
1262            Existing(1),
1263            Existing(2),
1264            Existing(3),
1265            Idle,
1266            spec,
1267        ]);
1268        let (view, server_fut) =
1269            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(vec![
1270                events.clone(),
1271            ]));
1272
1273        let (controller, _controller_server_end) =
1274            fidl::endpoints::create_proxy::<fnet_neighbor::ControllerMarker>();
1275        let worker_fut = NeighborsWorker::create(&view, controller);
1276
1277        let ((), (mut worker, event_stream)) = futures::join!(server_fut, worker_fut);
1278
1279        let client_table = ClientTable::<NetlinkRoute, FakeSender<_>>::default();
1280
1281        let remaining_events: Vec<_> = event_stream.collect().await;
1282        assert_eq!(remaining_events.len(), 2);
1283        match &remaining_events[0] {
1284            Ok(event) => {
1285                assert_matches!(
1286                    worker.handle_neighbor_watcher_event(event.clone(), &client_table),
1287                    Err(error) if error_matcher(&error)
1288                );
1289            }
1290            _ => panic!("expected bad event in stream"),
1291        }
1292        match &remaining_events[1] {
1293            Err(fnet_neighbor_ext::EntryIteratorError::Fidl(
1294                fidl::Error::ClientChannelClosed { .. },
1295            )) => {}
1296            _ => panic!("expected PEER_CLOSED error at end of stream"),
1297        }
1298    }
1299
1300    #[fuchsia::test]
1301    async fn neighbors_worker_handle_added_event() {
1302        use fnet_neighbor_ext::testutil::EventSpec::*;
1303        let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
1304            Existing(1),
1305            Existing(2),
1306            Existing(3),
1307            Idle,
1308            Added(4),
1309        ]);
1310        let (view, server_fut) =
1311            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(vec![
1312                events.clone(),
1313            ]));
1314
1315        let (controller, _controller_server_end) =
1316            fidl::endpoints::create_proxy::<fnet_neighbor::ControllerMarker>();
1317        let worker_fut = NeighborsWorker::create(&view, controller);
1318
1319        let ((), (mut worker, event_stream)) = futures::join!(server_fut, worker_fut);
1320
1321        let client_table = ClientTable::<NetlinkRoute, FakeSender<_>>::default();
1322
1323        let remaining_events: Vec<_> = event_stream.collect().await;
1324        assert_eq!(remaining_events.len(), 2);
1325        match &remaining_events[0] {
1326            Ok(e @ fnet_neighbor_ext::Event::Added(entry)) => {
1327                let key = NeighborKey::from(entry);
1328                assert_eq!(worker.neighbor_table.get(&key), None);
1329                assert_matches!(
1330                    worker.handle_neighbor_watcher_event(e.clone(), &client_table),
1331                    Ok(_)
1332                );
1333                assert_eq!(worker.neighbor_table.get(&key), Some(entry));
1334            }
1335            _ => panic!("expected Added event in stream"),
1336        }
1337        match &remaining_events[1] {
1338            Err(fnet_neighbor_ext::EntryIteratorError::Fidl(
1339                fidl::Error::ClientChannelClosed { .. },
1340            )) => {}
1341            _ => panic!("expected PEER_CLOSED error at end of stream"),
1342        }
1343    }
1344
1345    #[fuchsia::test]
1346    async fn neighbors_worker_handle_removed_event() {
1347        use fnet_neighbor_ext::testutil::EventSpec::*;
1348        let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
1349            Existing(1),
1350            Existing(2),
1351            Existing(3),
1352            Idle,
1353            Removed(2),
1354        ]);
1355        let (view, server_fut) =
1356            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(vec![
1357                events.clone(),
1358            ]));
1359
1360        let (controller, _controller_server_end) =
1361            fidl::endpoints::create_proxy::<fnet_neighbor::ControllerMarker>();
1362        let worker_fut = NeighborsWorker::create(&view, controller);
1363
1364        let ((), (mut worker, event_stream)) = futures::join!(server_fut, worker_fut);
1365
1366        let client_table = ClientTable::<NetlinkRoute, FakeSender<_>>::default();
1367
1368        let remaining_events: Vec<_> = event_stream.collect().await;
1369        assert_eq!(remaining_events.len(), 2);
1370        match &remaining_events[0] {
1371            Ok(e @ fnet_neighbor_ext::Event::Removed(entry)) => {
1372                let key = NeighborKey::from(entry);
1373                assert_eq!(worker.neighbor_table.get(&key), Some(entry));
1374                assert_matches!(
1375                    worker.handle_neighbor_watcher_event(e.clone(), &client_table),
1376                    Ok(_)
1377                );
1378                assert_eq!(worker.neighbor_table.get(&key), None);
1379            }
1380            _ => panic!("expected Removed event in stream"),
1381        }
1382        match &remaining_events[1] {
1383            Err(fnet_neighbor_ext::EntryIteratorError::Fidl(
1384                fidl::Error::ClientChannelClosed { .. },
1385            )) => {}
1386            _ => panic!("expected PEER_CLOSED error at end of stream"),
1387        }
1388    }
1389
1390    #[fuchsia::test]
1391    async fn neighbors_worker_handle_changed_event() {
1392        use fnet_neighbor_ext::testutil::EventSpec::*;
1393        let mut events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
1394            Existing(1),
1395            Existing(2),
1396            Existing(3),
1397            Idle,
1398            Changed(2),
1399        ]);
1400        match &mut events[1] {
1401            fnet_neighbor::EntryIteratorItem::Existing(entry) => {
1402                entry.updated_at = Some(1234);
1403            }
1404            _ => panic!("expected Existing event in stream"),
1405        }
1406        match &mut events[4] {
1407            fnet_neighbor::EntryIteratorItem::Changed(entry) => {
1408                entry.updated_at = Some(5678);
1409            }
1410            _ => panic!("expected Changed event in stream"),
1411        }
1412
1413        let (view, server_fut) =
1414            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(vec![
1415                events.clone(),
1416            ]));
1417
1418        let (controller, _controller_server_end) =
1419            fidl::endpoints::create_proxy::<fnet_neighbor::ControllerMarker>();
1420        let worker_fut = NeighborsWorker::create(&view, controller);
1421
1422        let ((), (mut worker, event_stream)) = futures::join!(server_fut, worker_fut);
1423
1424        let client_table = ClientTable::<NetlinkRoute, FakeSender<_>>::default();
1425
1426        let remaining_events: Vec<_> = event_stream.collect().await;
1427        assert_eq!(remaining_events.len(), 2);
1428        match &remaining_events[0] {
1429            Ok(e @ fnet_neighbor_ext::Event::Changed(entry)) => {
1430                let key = NeighborKey::from(entry);
1431                assert_matches!(
1432                    worker.neighbor_table.get(&key),
1433                    Some(fnet_neighbor_ext::Entry { updated_at: 1234, .. })
1434                );
1435                assert_matches!(
1436                    worker.handle_neighbor_watcher_event(e.clone(), &client_table),
1437                    Ok(_)
1438                );
1439                assert_matches!(
1440                    worker.neighbor_table.get(&key),
1441                    Some(fnet_neighbor_ext::Entry { updated_at: 5678, .. })
1442                );
1443            }
1444            _ => panic!("expected Changed event in stream"),
1445        }
1446        match &remaining_events[1] {
1447            Err(fnet_neighbor_ext::EntryIteratorError::Fidl(
1448                fidl::Error::ClientChannelClosed { .. },
1449            )) => {}
1450            _ => panic!("expected PEER_CLOSED error at end of stream"),
1451        }
1452    }
1453
1454    impl LookupIfInterfaceExists for HashSet<u64> {
1455        fn exists(&self, idx: NonZeroU64) -> bool {
1456            self.contains(&idx.get())
1457        }
1458    }
1459
1460    #[test_case(HashSet::new(), RequestError::InterfaceNotFound; "interface does not exist")]
1461    #[test_case(
1462        hashset!{1}, RequestError::NeighborNotFound;
1463        "interface exists"
1464    )]
1465    #[fuchsia::test]
1466    async fn neighbors_worker_handle_get_neighbor_not_found(
1467        interface_lookup: HashSet<u64>,
1468        expected_error: RequestError,
1469    ) {
1470        let (mut sender_sink, client, _async_work_drain_task) =
1471            new_fake_client(CLIENT_ID_1, vec![]);
1472        let (completer, completer_rcv) = oneshot::channel();
1473        let request = Request {
1474            args: NeighborRequestArgs::Get(GetNeighborArgs::Get {
1475                ip: fidl_ip!("192.168.0.1"),
1476                interface: NonZeroU64::new(1).unwrap(),
1477            }),
1478            sequence_number: 1,
1479            client,
1480            completer,
1481        };
1482
1483        let events: Vec<_> = [
1484            fnet_neighbor_ext::Entry {
1485                interface: NonZeroU64::new(2).unwrap(),
1486                neighbor: fidl_ip!("192.168.0.1"),
1487                ..valid_neighbor_entry()
1488            },
1489            fnet_neighbor_ext::Entry {
1490                interface: NonZeroU64::new(1).unwrap(),
1491                neighbor: fidl_ip!("fe80::2"),
1492                ..valid_neighbor_entry()
1493            },
1494        ]
1495        .into_iter()
1496        .map(Into::into)
1497        .map(fnet_neighbor::EntryIteratorItem::Existing)
1498        .chain(std::iter::once(fnet_neighbor::EntryIteratorItem::Idle(fnet_neighbor::IdleEvent)))
1499        .collect();
1500
1501        let batches = vec![events];
1502        let (view, server_fut) =
1503            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(batches));
1504
1505        let (controller, _controller_server_end) =
1506            fidl::endpoints::create_proxy::<fnet_neighbor::ControllerMarker>();
1507        let worker_fut = NeighborsWorker::create(&view, controller);
1508        let ((), (mut worker, _event_stream)) = futures::join!(server_fut, worker_fut);
1509
1510        assert_matches!(
1511            worker.handle_request(request, &interface_lookup).await,
1512            None // No pending work expected.
1513        );
1514
1515        let result = completer_rcv.await.expect("completer channel should not be closed");
1516        assert_matches!(result, Err(e) if e == expected_error);
1517        assert_eq!(&sender_sink.take_messages()[..], &[]);
1518    }
1519
1520    #[test_case(
1521        GetNeighborArgs::Dump{ ip_version: None, interface: None },
1522        &[1, 2, 3, 4];
1523        "dump all"
1524    )]
1525    #[test_case(
1526        GetNeighborArgs::Dump{ ip_version: Some(IpVersion::V4), interface: None },
1527        &[1, 3];
1528        "dump ipv4 only"
1529    )]
1530    #[test_case(
1531        GetNeighborArgs::Dump{ ip_version: Some(IpVersion::V6), interface: None },
1532        &[2, 4];
1533        "dump ipv6 only"
1534    )]
1535    #[test_case(
1536        GetNeighborArgs::Dump{
1537            ip_version: Some(IpVersion::V6),
1538            interface: Some(NonZeroU64::new(4).unwrap())
1539        },
1540        &[4];
1541        "dump interface 4 ipv6"
1542    )]
1543    #[test_case(
1544        GetNeighborArgs::Dump{
1545            ip_version: Some(IpVersion::V4),
1546            interface: Some(NonZeroU64::new(4).unwrap())
1547        },
1548        &[];
1549        "dump interface 4 ipv4"
1550    )]
1551    #[test_case(
1552        GetNeighborArgs::Get{ ip: fidl_ip!("192.168.0.1"), interface: NonZeroU64::new(1).unwrap() },
1553        &[1];
1554        "get ipv4"
1555    )]
1556    #[test_case(
1557        GetNeighborArgs::Get{ ip: fidl_ip!("fe80::2"), interface: NonZeroU64::new(2).unwrap() },
1558        &[2];
1559        "get ipv6"
1560    )]
1561    #[fuchsia::test]
1562    async fn neighbors_worker_handle_get_request(
1563        get_args: GetNeighborArgs,
1564        expected_ifindexes: &[u32],
1565    ) {
1566        let (mut sender_sink, client, _async_work_drain_task) =
1567            new_fake_client(CLIENT_ID_1, vec![]);
1568        let (completer, completer_rcv) = oneshot::channel();
1569        let request = Request {
1570            args: NeighborRequestArgs::Get(get_args),
1571            sequence_number: 1,
1572            client,
1573            completer,
1574        };
1575
1576        let events: Vec<_> = [
1577            fnet_neighbor_ext::Entry {
1578                interface: NonZeroU64::new(1).unwrap(),
1579                neighbor: fidl_ip!("192.168.0.1"),
1580                ..valid_neighbor_entry()
1581            },
1582            fnet_neighbor_ext::Entry {
1583                interface: NonZeroU64::new(2).unwrap(),
1584                neighbor: fidl_ip!("fe80::2"),
1585                ..valid_neighbor_entry()
1586            },
1587            fnet_neighbor_ext::Entry {
1588                interface: NonZeroU64::new(3).unwrap(),
1589                neighbor: fidl_ip!("192.168.0.3"),
1590                ..valid_neighbor_entry()
1591            },
1592            fnet_neighbor_ext::Entry {
1593                interface: NonZeroU64::new(4).unwrap(),
1594                neighbor: fidl_ip!("fe80::4"),
1595                ..valid_neighbor_entry()
1596            },
1597        ]
1598        .into_iter()
1599        .map(Into::into)
1600        .map(fnet_neighbor::EntryIteratorItem::Existing)
1601        .chain(std::iter::once(fnet_neighbor::EntryIteratorItem::Idle(fnet_neighbor::IdleEvent)))
1602        .collect();
1603
1604        let batches = vec![events];
1605        let (view, server_fut) =
1606            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(batches));
1607
1608        let (controller, _controller_server_end) =
1609            fidl::endpoints::create_proxy::<fnet_neighbor::ControllerMarker>();
1610        let worker_fut = NeighborsWorker::create(&view, controller);
1611        let ((), (mut worker, _event_stream)) = futures::join!(server_fut, worker_fut);
1612
1613        assert_matches!(
1614            worker.handle_request(request, &BTreeMap::new()).await,
1615            None // No pending work expected.
1616        );
1617
1618        completer_rcv
1619            .await
1620            .expect("completer channel should not be closed")
1621            .expect("request handling result should be OK");
1622
1623        let mut ifindexes_seen = Vec::new();
1624        for sent_message in sender_sink.take_messages() {
1625            match sent_message.message.payload {
1626                NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewNeighbour(
1627                    NeighbourMessage { header: NeighbourHeader { ifindex, .. }, .. },
1628                )) => {
1629                    ifindexes_seen.push(ifindex);
1630                }
1631                _ => panic!("unexpected message sent"),
1632            }
1633        }
1634        ifindexes_seen.sort();
1635        assert_eq!(&ifindexes_seen[..], expected_ifindexes);
1636    }
1637
1638    #[fuchsia::test]
1639    async fn neighbors_worker_handle_del_request_controller_error() {
1640        let (_sender_sink, client, _async_work_drain_task) = new_fake_client(CLIENT_ID_1, vec![]);
1641        let (completer, completer_rcv) = oneshot::channel();
1642        let request = Request {
1643            args: NeighborRequestArgs::Del(DelNeighborArgs {
1644                ip: fidl_ip!("192.168.0.1"),
1645                interface: NonZeroU64::new(1).unwrap(),
1646            }),
1647            sequence_number: 1,
1648            client,
1649            completer,
1650        };
1651
1652        let events = {
1653            use fnet_neighbor_ext::testutil::EventSpec::*;
1654            fnet_neighbor_ext::testutil::generate_events_from_spec(&[Idle])
1655        };
1656        let (view, server_fut) =
1657            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(vec![events]));
1658        let (controller, mut controller_request_stream) =
1659            fidl::endpoints::create_proxy_and_stream::<fnet_neighbor::ControllerMarker>();
1660        let worker_fut = NeighborsWorker::create(&view, controller);
1661        let ((), (mut worker, _event_stream)) = futures::join!(server_fut, worker_fut);
1662
1663        let interfaces = BTreeMap::new();
1664        let handle_request_fut = worker.handle_request(request, &interfaces);
1665        let controller_fut = async {
1666            match controller_request_stream
1667                .next()
1668                .await
1669                .expect("controller stream should not be closed")
1670                .expect("failed to receive controller request")
1671            {
1672                fnet_neighbor::ControllerRequest::RemoveEntry {
1673                    interface: _,
1674                    neighbor: _,
1675                    responder,
1676                } => {
1677                    responder
1678                        .send(Err(fnet_neighbor::ControllerError::InterfaceNotFound))
1679                        .expect("failed to send error response");
1680                }
1681                _ => panic!("unexpected controller request"),
1682            }
1683        };
1684
1685        let (handle_result, ()) = futures::join!(handle_request_fut, controller_fut);
1686        assert_matches!(handle_result, None);
1687
1688        let result = completer_rcv.await.expect("completer channel should not be closed");
1689        assert_matches!(result, Err(RequestError::InterfaceNotFound));
1690    }
1691
1692    #[fuchsia::test]
1693    async fn neighbors_worker_handle_del_request_success() {
1694        let (_sender_sink, client, _async_work_drain_task) = new_fake_client(CLIENT_ID_1, vec![]);
1695        let (completer, _completer_rcv) = oneshot::channel();
1696        let args =
1697            DelNeighborArgs { ip: fidl_ip!("192.168.0.1"), interface: NonZeroU64::new(1).unwrap() };
1698        let request =
1699            Request { args: NeighborRequestArgs::Del(args), sequence_number: 1, client, completer };
1700
1701        let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
1702            fnet_neighbor_ext::testutil::EventSpec::Idle,
1703        ]);
1704        let batches = vec![events];
1705        let (view, server_fut) =
1706            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(batches));
1707        let (controller, mut controller_request_stream) =
1708            fidl::endpoints::create_proxy_and_stream::<fnet_neighbor::ControllerMarker>();
1709        let worker_fut = NeighborsWorker::create(&view, controller);
1710        let ((), (mut worker, _event_stream)) = futures::join!(server_fut, worker_fut);
1711
1712        let interfaces = BTreeMap::new();
1713        let handle_request_fut = worker.handle_request(request, &interfaces);
1714        let controller_fut = async {
1715            match controller_request_stream
1716                .next()
1717                .await
1718                .expect("controller stream should not be closed")
1719                .expect("failed to receive controller request")
1720            {
1721                fnet_neighbor::ControllerRequest::RemoveEntry {
1722                    interface: _,
1723                    neighbor: _,
1724                    responder,
1725                } => {
1726                    responder.send(Ok(())).expect("failed to send success response");
1727                }
1728                _ => panic!("unexpected controller request"),
1729            }
1730        };
1731
1732        let (handle_result, ()) = futures::join!(handle_request_fut, controller_fut);
1733        let pending = handle_result.expect("expected pending request");
1734        assert_eq!(pending.request_args, PendingNeighborRequestArgs::Del(args));
1735    }
1736
1737    #[fuchsia::test]
1738    async fn neighbors_worker_handle_pending_del_request() {
1739        let (_sender_sink, client, _async_work_drain_task) = new_fake_client(CLIENT_ID_1, vec![]);
1740        let (completer, mut completer_rcv) = oneshot::channel();
1741        let args =
1742            DelNeighborArgs { ip: fidl_ip!("192.168.0.1"), interface: NonZeroU64::new(1).unwrap() };
1743        let pending = PendingNeighborRequest {
1744            request_args: PendingNeighborRequestArgs::Del(args),
1745            client,
1746            completer,
1747        };
1748
1749        let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
1750            fnet_neighbor_ext::testutil::EventSpec::Idle,
1751        ]);
1752        let batches = vec![events];
1753        let (view, server_fut) =
1754            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(batches));
1755        let (controller, _controller_server_end) =
1756            fidl::endpoints::create_proxy::<fnet_neighbor::ControllerMarker>();
1757        let worker_fut = NeighborsWorker::create(&view, controller);
1758        let ((), (mut worker, _event_stream)) = futures::join!(server_fut, worker_fut);
1759
1760        let key = NeighborKey { interface: args.interface, neighbor: args.ip };
1761        let _ = worker.neighbor_table.insert(
1762            key,
1763            fnet_neighbor_ext::Entry {
1764                interface: args.interface,
1765                neighbor: args.ip,
1766                ..valid_neighbor_entry()
1767            },
1768        );
1769
1770        // Still present, should remain pending.
1771        let pending = worker.handle_pending_request(pending).expect("expected pending");
1772        assert_matches!(completer_rcv.try_recv(), Ok(None)); // Completer still blocked.
1773
1774        // Remove from table, should complete.
1775        let _ = worker.neighbor_table.remove(&key);
1776        assert_matches!(worker.handle_pending_request(pending), None);
1777
1778        let result = completer_rcv.try_recv().expect("completer channel should not be closed");
1779        assert_matches!(result, Some(Ok(())));
1780    }
1781
1782    #[fuchsia::test]
1783    async fn neighbors_worker_handle_create_static_request_controller_error() {
1784        let (_sender_sink, client, _async_work_drain_task) = new_fake_client(CLIENT_ID_1, vec![]);
1785        let (completer, completer_rcv) = oneshot::channel();
1786        let args = NewNeighborArgs::CreateStatic {
1787            ip: fidl_ip!("192.168.0.1"),
1788            interface: NonZeroU64::new(1).unwrap(),
1789            mac: fnet::MacAddress { octets: [0, 1, 2, 3, 4, 5] },
1790        };
1791        let request =
1792            Request { args: NeighborRequestArgs::New(args), sequence_number: 1, client, completer };
1793
1794        let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
1795            fnet_neighbor_ext::testutil::EventSpec::Idle,
1796        ]);
1797        let batches = vec![events];
1798        let (view, server_fut) =
1799            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(batches));
1800        let (controller, mut controller_request_stream) =
1801            fidl::endpoints::create_proxy_and_stream::<fnet_neighbor::ControllerMarker>();
1802        let worker_fut = NeighborsWorker::create(&view, controller);
1803        let ((), (mut worker, _event_stream)) = futures::join!(server_fut, worker_fut);
1804
1805        let interfaces = BTreeMap::new();
1806        let handle_request_fut = worker.handle_request(request, &interfaces);
1807        let controller_fut = async {
1808            match controller_request_stream
1809                .next()
1810                .await
1811                .expect("controller stream should not be closed")
1812                .expect("failed to receive controller request")
1813            {
1814                fnet_neighbor::ControllerRequest::AddEntry {
1815                    interface: _,
1816                    neighbor: _,
1817                    mac: _,
1818                    responder,
1819                } => {
1820                    responder
1821                        .send(Err(fnet_neighbor::ControllerError::InterfaceNotFound))
1822                        .expect("failed to send error response");
1823                }
1824                _ => panic!("unexpected controller request"),
1825            }
1826        };
1827
1828        let (handle_result, ()) = futures::join!(handle_request_fut, controller_fut);
1829        assert_matches!(handle_result, None);
1830
1831        let result = completer_rcv.await.expect("completer channel should not be closed");
1832        assert_matches!(result, Err(RequestError::InterfaceNotFound));
1833    }
1834
1835    #[fuchsia::test]
1836    async fn neighbors_worker_handle_create_static_request_success() {
1837        let (_sender_sink, client, _async_work_drain_task) = new_fake_client(CLIENT_ID_1, vec![]);
1838        let (completer, _completer_rcv) = oneshot::channel();
1839        let args = NewNeighborArgs::CreateStatic {
1840            ip: fidl_ip!("192.168.0.1"),
1841            interface: NonZeroU64::new(1).unwrap(),
1842            mac: fnet::MacAddress { octets: [0, 1, 2, 3, 4, 5] },
1843        };
1844        let request =
1845            Request { args: NeighborRequestArgs::New(args), sequence_number: 1, client, completer };
1846
1847        let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
1848            fnet_neighbor_ext::testutil::EventSpec::Idle,
1849        ]);
1850        let (view, server_fut) =
1851            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(vec![events]));
1852        let (controller, mut controller_request_stream) =
1853            fidl::endpoints::create_proxy_and_stream::<fnet_neighbor::ControllerMarker>();
1854        let worker_fut = NeighborsWorker::create(&view, controller);
1855        let ((), (mut worker, _event_stream)) = futures::join!(server_fut, worker_fut);
1856
1857        let interfaces = BTreeMap::new();
1858        let handle_request_fut = worker.handle_request(request, &interfaces);
1859        let controller_fut = async {
1860            match controller_request_stream
1861                .next()
1862                .await
1863                .expect("controller stream should not be closed")
1864                .expect("failed to receive controller request")
1865            {
1866                fnet_neighbor::ControllerRequest::AddEntry {
1867                    interface: _,
1868                    neighbor: _,
1869                    mac: _,
1870                    responder,
1871                } => {
1872                    responder.send(Ok(())).expect("failed to send success response");
1873                }
1874                _ => panic!("unexpected controller request"),
1875            }
1876        };
1877
1878        let (handle_result, ()) = futures::join!(handle_request_fut, controller_fut);
1879        let pending = handle_result.expect("expected pending request");
1880        assert_eq!(pending.request_args, PendingNeighborRequestArgs::New(args));
1881    }
1882
1883    #[fuchsia::test]
1884    async fn neighbors_worker_handle_probe_request_controller_error() {
1885        let (_sender_sink, client, _async_work_drain_task) = new_fake_client(CLIENT_ID_1, vec![]);
1886        let (completer, completer_rcv) = oneshot::channel();
1887        let args = NewNeighborArgs::ProbeExisting {
1888            ip: fidl_ip!("192.168.0.1"),
1889            interface: NonZeroU64::new(1).unwrap(),
1890        };
1891        let request =
1892            Request { args: NeighborRequestArgs::New(args), sequence_number: 1, client, completer };
1893
1894        let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
1895            fnet_neighbor_ext::testutil::EventSpec::Idle,
1896        ]);
1897        let (view, server_fut) =
1898            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(vec![events]));
1899        let (controller, mut controller_request_stream) =
1900            fidl::endpoints::create_proxy_and_stream::<fnet_neighbor::ControllerMarker>();
1901        let worker_fut = NeighborsWorker::create(&view, controller);
1902        let ((), (mut worker, _event_stream)) = futures::join!(server_fut, worker_fut);
1903
1904        let interfaces = BTreeMap::new();
1905        let handle_request_fut = worker.handle_request(request, &interfaces);
1906        let controller_fut = async {
1907            match controller_request_stream
1908                .next()
1909                .await
1910                .expect("controller stream should not be closed")
1911                .expect("failed to receive controller request")
1912            {
1913                fnet_neighbor::ControllerRequest::ProbeEntry {
1914                    interface: _,
1915                    neighbor: _,
1916                    responder,
1917                } => {
1918                    responder
1919                        .send(Err(fnet_neighbor::ControllerError::InterfaceNotFound))
1920                        .expect("failed to send error response");
1921                }
1922                _ => panic!("unexpected controller request"),
1923            }
1924        };
1925
1926        let (handle_result, ()) = futures::join!(handle_request_fut, controller_fut);
1927        assert_matches!(handle_result, None);
1928
1929        let result = completer_rcv.await.expect("completer channel should not be closed");
1930        assert_matches!(result, Err(RequestError::InterfaceNotFound));
1931    }
1932
1933    #[fuchsia::test]
1934    async fn neighbors_worker_handle_probe_request_success() {
1935        let (_sender_sink, client, _async_work_drain_task) = new_fake_client(CLIENT_ID_1, vec![]);
1936        let (completer, _completer_rcv) = oneshot::channel();
1937        let args = NewNeighborArgs::ProbeExisting {
1938            ip: fidl_ip!("192.168.0.1"),
1939            interface: NonZeroU64::new(1).unwrap(),
1940        };
1941        let request =
1942            Request { args: NeighborRequestArgs::New(args), sequence_number: 1, client, completer };
1943
1944        let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
1945            fnet_neighbor_ext::testutil::EventSpec::Idle,
1946        ]);
1947        let (view, server_fut) =
1948            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(vec![events]));
1949        let (controller, mut controller_request_stream) =
1950            fidl::endpoints::create_proxy_and_stream::<fnet_neighbor::ControllerMarker>();
1951        let worker_fut = NeighborsWorker::create(&view, controller);
1952        let ((), (mut worker, _event_stream)) = futures::join!(server_fut, worker_fut);
1953
1954        let interfaces = BTreeMap::new();
1955        let handle_request_fut = worker.handle_request(request, &interfaces);
1956        let controller_fut = async {
1957            match controller_request_stream
1958                .next()
1959                .await
1960                .expect("controller stream should not be closed")
1961                .expect("failed to receive controller request")
1962            {
1963                fnet_neighbor::ControllerRequest::ProbeEntry {
1964                    interface: _,
1965                    neighbor: _,
1966                    responder,
1967                } => {
1968                    responder.send(Ok(())).expect("failed to send success response");
1969                }
1970                _ => panic!("unexpected controller request"),
1971            }
1972        };
1973
1974        let (handle_result, ()) = futures::join!(handle_request_fut, controller_fut);
1975        let pending = handle_result.expect("expected pending request");
1976        assert_eq!(pending.request_args, PendingNeighborRequestArgs::New(args));
1977    }
1978
1979    #[fuchsia::test]
1980    async fn neighbors_worker_handle_pending_create_static_request() {
1981        let (_sender_sink, client, _async_work_drain_task) = new_fake_client(CLIENT_ID_1, vec![]);
1982        let (completer, mut completer_rcv) = oneshot::channel();
1983        let neighbor = valid_neighbor_entry();
1984        let args = NewNeighborArgs::CreateStatic {
1985            ip: neighbor.neighbor,
1986            interface: neighbor.interface,
1987            mac: neighbor.mac.unwrap(),
1988        };
1989        let pending = PendingNeighborRequest {
1990            request_args: PendingNeighborRequestArgs::New(args),
1991            client,
1992            completer,
1993        };
1994
1995        let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
1996            fnet_neighbor_ext::testutil::EventSpec::Idle,
1997        ]);
1998        let (view, server_fut) =
1999            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(vec![events]));
2000        let (controller, _controller_server_end) =
2001            fidl::endpoints::create_proxy::<fnet_neighbor::ControllerMarker>();
2002        let worker_fut = NeighborsWorker::create(&view, controller);
2003        let ((), (mut worker, _event_stream)) = futures::join!(server_fut, worker_fut);
2004
2005        let key = NeighborKey { interface: neighbor.interface, neighbor: neighbor.neighbor };
2006
2007        // Not present, should remain pending.
2008        let pending = worker.handle_pending_request(pending).expect("expected pending");
2009        assert_matches!(completer_rcv.try_recv(), Ok(None));
2010
2011        // Insert with wrong MAC, should remain pending.
2012        let _ = worker.neighbor_table.insert(
2013            key,
2014            fnet_neighbor_ext::Entry {
2015                mac: Some(fnet::MacAddress { octets: [0, 0, 0, 0, 0, 0] }),
2016                state: fnet_neighbor::EntryState::Static,
2017                ..neighbor
2018            },
2019        );
2020        let pending = worker.handle_pending_request(pending).expect("expected pending");
2021        assert_matches!(completer_rcv.try_recv(), Ok(None));
2022
2023        // Insert with wrong state, should remain pending.
2024        let _ = worker.neighbor_table.insert(
2025            key,
2026            fnet_neighbor_ext::Entry {
2027                mac: Some(fnet::MacAddress { octets: [0, 1, 2, 3, 4, 5] }),
2028                state: fnet_neighbor::EntryState::Reachable,
2029                ..neighbor
2030            },
2031        );
2032        let pending = worker.handle_pending_request(pending).expect("expected pending");
2033        assert_matches!(completer_rcv.try_recv(), Ok(None));
2034
2035        // Insert correct entry, should complete.
2036        let _ = worker.neighbor_table.insert(
2037            key,
2038            fnet_neighbor_ext::Entry {
2039                mac: Some(fnet::MacAddress { octets: [0, 1, 2, 3, 4, 5] }),
2040                state: fnet_neighbor::EntryState::Static,
2041                ..neighbor
2042            },
2043        );
2044        assert_matches!(worker.handle_pending_request(pending), None);
2045
2046        let result = completer_rcv.try_recv().expect("completer channel should not be closed");
2047        assert_matches!(result, Some(Ok(())));
2048    }
2049
2050    #[fuchsia::test]
2051    async fn neighbors_worker_handle_pending_probe_request() {
2052        let (_sender_sink, client, _async_work_drain_task) = new_fake_client(CLIENT_ID_1, vec![]);
2053        let (completer, mut completer_rcv) = oneshot::channel();
2054        let neighbor = valid_neighbor_entry();
2055        let args =
2056            NewNeighborArgs::ProbeExisting { ip: neighbor.neighbor, interface: neighbor.interface };
2057        let pending = PendingNeighborRequest {
2058            request_args: PendingNeighborRequestArgs::New(args),
2059            client,
2060            completer,
2061        };
2062
2063        let events = fnet_neighbor_ext::testutil::generate_events_from_spec(&[
2064            fnet_neighbor_ext::testutil::EventSpec::Idle,
2065        ]);
2066        let (view, server_fut) =
2067            fnet_neighbor_ext::testutil::create_fake_view(futures::stream::iter(vec![events]));
2068        let (controller, _controller_server_end) =
2069            fidl::endpoints::create_proxy::<fnet_neighbor::ControllerMarker>();
2070        let worker_fut = NeighborsWorker::create(&view, controller);
2071        let ((), (mut worker, _event_stream)) = futures::join!(server_fut, worker_fut);
2072
2073        let key = NeighborKey { interface: neighbor.interface, neighbor: neighbor.neighbor };
2074
2075        // Not present, should remain pending.
2076        let pending = worker.handle_pending_request(pending).expect("expected pending");
2077        assert_matches!(completer_rcv.try_recv(), Ok(None));
2078
2079        // Insert with wrong state, should remain pending.
2080        let _ = worker.neighbor_table.insert(
2081            key,
2082            fnet_neighbor_ext::Entry { state: fnet_neighbor::EntryState::Reachable, ..neighbor },
2083        );
2084        let pending = worker.handle_pending_request(pending).expect("expected pending");
2085        assert_matches!(completer_rcv.try_recv(), Ok(None));
2086
2087        // Insert correct entry, should complete.
2088        let _ = worker.neighbor_table.insert(
2089            key,
2090            fnet_neighbor_ext::Entry { state: fnet_neighbor::EntryState::Probe, ..neighbor },
2091        );
2092        assert_matches!(worker.handle_pending_request(pending), None);
2093
2094        let result = completer_rcv.try_recv().expect("completer channel should not be closed");
2095        assert_matches!(result, Some(Ok(())));
2096    }
2097
2098    #[test_case(
2099        false,
2100        NeighbourHeader {
2101            ifindex: 1,
2102            family: AddressFamily::Inet,
2103            ..Default::default()
2104        },
2105        &[
2106            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2107        ] => Ok(GetNeighborArgs::Get {
2108            ip: fidl_ip!("192.168.0.1"),
2109            interface: NonZeroU64::new(1).unwrap(),
2110        });
2111        "get ipv4 success"
2112    )]
2113    #[test_case(
2114        false,
2115        NeighbourHeader {
2116            ifindex: 1,
2117            family: AddressFamily::Inet6,
2118            ..Default::default()
2119        },
2120        &[
2121            NeighbourAttribute::Destination(std_ip_v6!("fe80::1").into()),
2122        ] => Ok(GetNeighborArgs::Get {
2123            ip: fidl_ip!("fe80::1"),
2124            interface: NonZeroU64::new(1).unwrap(),
2125        });
2126        "get ipv6 success"
2127    )]
2128    #[test_case(
2129        false,
2130        NeighbourHeader {
2131            ifindex: 1,
2132            family: AddressFamily::Inet,
2133            state: NeighbourState::Reachable,
2134            ..Default::default()
2135        },
2136        &[
2137            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2138        ] => Err(RequestError::InvalidState {
2139            actual: NeighbourState::Reachable, expected: NeighbourState::None
2140        });
2141        "get invalid request state"
2142    )]
2143    #[test_case(
2144        false,
2145        NeighbourHeader {
2146            ifindex: 1,
2147            family: AddressFamily::Inet,
2148            kind: RouteType::Broadcast,
2149            ..Default::default()
2150        },
2151        &[
2152            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2153        ] => Err(RequestError::InvalidKind(RouteType::Broadcast));
2154        "get invalid request kind"
2155    )]
2156    #[test_case(
2157        false,
2158        NeighbourHeader {
2159            ifindex: 1,
2160            family: AddressFamily::Inet,
2161            flags: NeighbourFlags::Router,
2162            ..Default::default()
2163        },
2164        &[
2165            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2166        ] => Err(RequestError::InvalidFlags(NeighbourFlags::Router));
2167        "get invalid request flag"
2168    )]
2169    #[test_case(
2170        false,
2171        NeighbourHeader {
2172            ifindex: 1,
2173            family: AddressFamily::Inet,
2174            flags: NeighbourFlags::Proxy,
2175            ..Default::default()
2176        },
2177        &[
2178            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2179        ] => Err(RequestError::UnsupportedFlags(NeighbourFlags::Proxy));
2180        "get unsupported request flag"
2181    )]
2182    #[test_case(
2183        false,
2184        NeighbourHeader {
2185            ifindex: 1,
2186            family: AddressFamily::Inet6,
2187            ..Default::default()
2188        },
2189        &[
2190            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2191        ] => Err(RequestError::AddressFamilyMismatch(AddressFamily::Inet6));
2192        "get address family mismatch"
2193    )]
2194    #[test_case(
2195        false,
2196        NeighbourHeader {
2197            family: AddressFamily::Inet,
2198            ..Default::default()
2199        },
2200        &[
2201            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2202        ] => Err(RequestError::MissingInterface);
2203        "get interface unspecified"
2204    )]
2205    #[test_case(
2206        false,
2207        NeighbourHeader {
2208            ifindex: 1,
2209            family: AddressFamily::Inet,
2210            ..Default::default()
2211        },
2212        &[] => Err(RequestError::MissingIpAddress);
2213        "get destination unspecified"
2214    )]
2215    #[test_case(
2216        false,
2217        NeighbourHeader {
2218            ifindex: 1,
2219            family: AddressFamily::Inet,
2220            ..Default::default()
2221        },
2222        &[
2223            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2224            NeighbourAttribute::LinkLocalAddress(vec![0, 1, 2, 3, 4, 5]),
2225        ] => Err(RequestError::InvalidAttribute);
2226        "get invalid attribute"
2227    )]
2228    #[test_case(
2229        true,
2230        NeighbourHeader::default(),
2231        &[] => Ok(GetNeighborArgs::Dump {
2232            ip_version: None,
2233            interface: None,
2234        });
2235        "dump all"
2236    )]
2237    #[test_case(
2238        true,
2239        NeighbourHeader {
2240            family: AddressFamily::Inet,
2241            ..Default::default()
2242        },
2243        &[] => Ok(GetNeighborArgs::Dump {
2244            ip_version: Some(IpVersion::V4),
2245            interface: None,
2246        });
2247        "dump ipv4 only"
2248    )]
2249    #[test_case(
2250        true,
2251        NeighbourHeader {
2252            family: AddressFamily::Inet6,
2253            ..Default::default()
2254        },
2255        &[] => Ok(GetNeighborArgs::Dump {
2256            ip_version: Some(IpVersion::V6),
2257            interface: None,
2258        });
2259        "dump ipv6 only"
2260    )]
2261    #[test_case(
2262        true,
2263        NeighbourHeader::default(),
2264        &[
2265            NeighbourAttribute::IfIndex(1),
2266        ] => Ok(GetNeighborArgs::Dump {
2267            ip_version: None,
2268            interface: Some(NonZeroU64::new(1).unwrap()),
2269        });
2270        "dump interface 1"
2271    )]
2272    #[test_case(
2273        true,
2274        NeighbourHeader::default(),
2275        &[
2276            NeighbourAttribute::IfIndex(0),
2277        ] => Ok(GetNeighborArgs::Dump {
2278            ip_version: None,
2279            interface: None,
2280        });
2281        "dump interface 0 treated as all interfaces"
2282    )]
2283    #[test_case(
2284        true,
2285        NeighbourHeader {
2286            family: AddressFamily::Local,
2287            ..Default::default()
2288        },
2289        &[] => Err(RequestError::InvalidAddressFamily(AddressFamily::Local));
2290        "dump invalid address family"
2291    )]
2292    #[test_case(
2293        true,
2294        NeighbourHeader::default(),
2295        &[
2296            NeighbourAttribute::LinkLocalAddress(vec![0, 1, 2, 3, 4, 5]),
2297        ] => Ok(GetNeighborArgs::Dump {
2298            ip_version: None,
2299            interface: None,
2300        });
2301        "dump unsupported attribute ignored (non-strict)"
2302    )]
2303    #[test_case(
2304        true,
2305        NeighbourHeader {
2306            flags: NeighbourFlags::Proxy,
2307            ..Default::default()
2308        },
2309        &[] => Err(RequestError::UnsupportedFlags(NeighbourFlags::Proxy));
2310        "dump unsupported request flag"
2311    )]
2312    #[fuchsia::test]
2313    fn get_neighbor_args_try_from_rtnl_neighbor(
2314        is_dump: bool,
2315        header: NeighbourHeader,
2316        attrs: &[NeighbourAttribute],
2317    ) -> Result<GetNeighborArgs, RequestError> {
2318        let mut message = NeighbourMessage::default();
2319        message.header = header;
2320        message.attributes = attrs.to_vec();
2321        GetNeighborArgs::try_from_rtnl_neighbor(&message, is_dump)
2322    }
2323
2324    #[test_case(
2325        NeighbourHeader {
2326            ifindex: 1,
2327            family: AddressFamily::Inet,
2328            ..Default::default()
2329        },
2330        &[
2331            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2332        ] => Ok(DelNeighborArgs {
2333            ip: fidl_ip!("192.168.0.1"),
2334            interface: NonZeroU64::new(1).unwrap(),
2335        });
2336        "del ipv4 success"
2337    )]
2338    #[test_case(
2339        NeighbourHeader {
2340            ifindex: 1,
2341            family: AddressFamily::Inet6,
2342            ..Default::default()
2343        },
2344        &[
2345            NeighbourAttribute::Destination(std_ip_v6!("fe80::1").into()),
2346        ] => Ok(DelNeighborArgs {
2347            ip: fidl_ip!("fe80::1"),
2348            interface: NonZeroU64::new(1).unwrap(),
2349        });
2350        "del ipv6 success"
2351    )]
2352    #[test_case(
2353        NeighbourHeader {
2354            ifindex: 1,
2355            family: AddressFamily::Inet,
2356            flags: NeighbourFlags::Proxy,
2357            ..Default::default()
2358        },
2359        &[
2360            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2361        ] => Err(RequestError::UnsupportedFlags(NeighbourFlags::Proxy));
2362        "del unsupported request flag"
2363    )]
2364    #[test_case(
2365        NeighbourHeader {
2366            family: AddressFamily::Inet,
2367            ..Default::default()
2368        },
2369        &[
2370            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2371        ] => Err(RequestError::MissingInterface);
2372        "del interface unspecified"
2373    )]
2374    #[test_case(
2375        NeighbourHeader {
2376            ifindex: 1,
2377            family: AddressFamily::Inet,
2378            ..Default::default()
2379        },
2380        &[] => Err(RequestError::MissingIpAddress);
2381        "del destination unspecified"
2382    )]
2383    #[fuchsia::test]
2384    fn del_neighbor_args_try_from_rtnl_neighbor(
2385        header: NeighbourHeader,
2386        attrs: &[NeighbourAttribute],
2387    ) -> Result<DelNeighborArgs, RequestError> {
2388        let mut message = NeighbourMessage::default();
2389        message.header = header;
2390        message.attributes = attrs.to_vec();
2391        DelNeighborArgs::try_from_rtnl_neighbor(&message)
2392    }
2393
2394    #[test_case(
2395        NLM_F_CREATE | NLM_F_REPLACE,
2396        NeighbourHeader {
2397            ifindex: 1,
2398            family: AddressFamily::Inet,
2399            state: NeighbourState::Permanent,
2400            ..Default::default()
2401        },
2402        &[
2403            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2404            NeighbourAttribute::LinkLocalAddress(vec![0, 1, 2, 3, 4, 5]),
2405        ] => Ok(NewNeighborArgs::CreateStatic {
2406            ip: fidl_ip!("192.168.0.1"),
2407            interface: NonZeroU64::new(1).unwrap(),
2408            mac: fnet::MacAddress { octets: [0, 1, 2, 3, 4, 5] },
2409        });
2410        "create static ipv4 success"
2411    )]
2412    #[test_case(
2413        NLM_F_CREATE | NLM_F_REPLACE,
2414        NeighbourHeader {
2415            ifindex: 1,
2416            family: AddressFamily::Inet6,
2417            state: NeighbourState::Permanent,
2418            ..Default::default()
2419        },
2420        &[
2421            NeighbourAttribute::Destination(std_ip_v6!("fe80::1").into()),
2422            NeighbourAttribute::LinkLocalAddress(vec![0, 1, 2, 3, 4, 5]),
2423        ] => Ok(NewNeighborArgs::CreateStatic {
2424            ip: fidl_ip!("fe80::1"),
2425            interface: NonZeroU64::new(1).unwrap(),
2426            mac: fnet::MacAddress { octets: [0, 1, 2, 3, 4, 5] },
2427        });
2428        "create static ipv6 success"
2429    )]
2430    #[test_case(
2431        NLM_F_REPLACE,
2432        NeighbourHeader {
2433            ifindex: 1,
2434            family: AddressFamily::Inet,
2435            state: NeighbourState::Probe,
2436            ..Default::default()
2437        },
2438        &[
2439            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2440        ] => Ok(NewNeighborArgs::ProbeExisting {
2441            ip: fidl_ip!("192.168.0.1"),
2442            interface: NonZeroU64::new(1).unwrap(),
2443        });
2444        "probe existing ipv4 success"
2445    )]
2446    #[test_case(
2447        NLM_F_REPLACE,
2448        NeighbourHeader {
2449            ifindex: 1,
2450            family: AddressFamily::Inet6,
2451            state: NeighbourState::Probe,
2452            ..Default::default()
2453        },
2454        &[
2455            NeighbourAttribute::Destination(std_ip_v6!("fe80::1").into()),
2456        ] => Ok(NewNeighborArgs::ProbeExisting {
2457            ip: fidl_ip!("fe80::1"),
2458            interface: NonZeroU64::new(1).unwrap(),
2459        });
2460        "probe existing ipv6 success"
2461    )]
2462    #[test_case(
2463        NLM_F_REPLACE,
2464        NeighbourHeader {
2465            ifindex: 1,
2466            family: AddressFamily::Inet,
2467            flags: NeighbourFlags::Proxy,
2468            ..Default::default()
2469        },
2470        &[
2471            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2472        ] => Err(RequestError::UnsupportedFlags(NeighbourFlags::Proxy));
2473        "unsupported proxy flag"
2474    )]
2475    #[test_case(
2476        NLM_F_CREATE | NLM_F_REPLACE,
2477        NeighbourHeader {
2478            ifindex: 1,
2479            family: AddressFamily::Inet,
2480            state: NeighbourState::Reachable,
2481            ..Default::default()
2482        },
2483        &[
2484            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2485            NeighbourAttribute::LinkLocalAddress(vec![0, 1, 2, 3, 4, 5]),
2486        ] => Err(RequestError::InvalidState {
2487            actual: NeighbourState::Reachable,
2488            expected: NeighbourState::Permanent,
2489        });
2490        "create invalid state"
2491    )]
2492    #[test_case(
2493        NLM_F_REPLACE,
2494        NeighbourHeader {
2495            ifindex: 1,
2496            family: AddressFamily::Inet,
2497            state: NeighbourState::Permanent,
2498            ..Default::default()
2499        },
2500        &[
2501            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2502        ] => Err(RequestError::InvalidState {
2503            actual: NeighbourState::Permanent,
2504            expected: NeighbourState::Probe,
2505        });
2506        "probe invalid state"
2507    )]
2508    #[test_case(
2509        NLM_F_CREATE | NLM_F_REPLACE,
2510        NeighbourHeader {
2511            ifindex: 1,
2512            family: AddressFamily::Inet,
2513            state: NeighbourState::Permanent,
2514            ..Default::default()
2515        },
2516        &[
2517            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2518        ] => Err(RequestError::MissingMacAddress);
2519        "create missing mac address"
2520    )]
2521    #[test_case(
2522        NLM_F_EXCL,
2523        NeighbourHeader {
2524            ifindex: 1,
2525            family: AddressFamily::Inet,
2526            ..Default::default()
2527        },
2528        &[
2529            NeighbourAttribute::Destination(std_ip_v4!("192.168.0.1").into()),
2530        ] => Err(RequestError::UnsupportedOperation);
2531        "unsupported operation flags"
2532    )]
2533    #[fuchsia::test]
2534    fn new_neighbor_args_try_from_rtnl_neighbor(
2535        netlink_flags: u16,
2536        header: NeighbourHeader,
2537        attrs: &[NeighbourAttribute],
2538    ) -> Result<NewNeighborArgs, RequestError> {
2539        let mut message = NeighbourMessage::default();
2540        message.header = header;
2541        message.attributes = attrs.to_vec();
2542        NewNeighborArgs::try_from_rtnl_neighbor(&message, netlink_flags)
2543    }
2544
2545    #[test_case(
2546        RequestError::InvalidState {
2547            actual: NeighbourState::Reachable, expected:NeighbourState::None
2548        } => Errno::EINVAL;
2549        "invalid state"
2550    )]
2551    #[test_case(RequestError::InvalidKind(RouteType::Broadcast) => Errno::EINVAL; "invalid kind")]
2552    #[test_case(
2553        RequestError::InvalidFlags(NeighbourFlags::Router) => Errno::EINVAL;
2554        "invalid flags"
2555    )]
2556    #[test_case(
2557        RequestError::UnsupportedFlags(NeighbourFlags::Proxy) => Errno::ENOTSUP;
2558        "unsupported flags"
2559    )]
2560    #[test_case(
2561        RequestError::InvalidAddressFamily(AddressFamily::Local) => Errno::EAFNOSUPPORT;
2562        "invalid address family"
2563    )]
2564    #[test_case(
2565        RequestError::AddressFamilyMismatch(AddressFamily::Inet6) => Errno::EINVAL;
2566        "address family mismatch"
2567    )]
2568    #[test_case(RequestError::MissingInterface => Errno::EINVAL; "interface unspecified")]
2569    #[test_case(RequestError::MissingIpAddress => Errno::EINVAL; "destination unspecified")]
2570    #[test_case(RequestError::InvalidAttribute => Errno::EINVAL; "invalid attribute")]
2571    #[test_case(RequestError::NeighborNotFound => Errno::ENOENT; "neighbor not found")]
2572    #[test_case(RequestError::InterfaceNotFound => Errno::ENODEV; "interface not found")]
2573    #[test_case(RequestError::InvalidIpAddress => Errno::EINVAL; "invalid IP address")]
2574    #[test_case(RequestError::InvalidMacAddress => Errno::EINVAL; "invalid MAC address")]
2575    #[test_case(RequestError::InterfaceUnsupported => Errno::ENOTSUP; "unsupported interface")]
2576    #[fuchsia::test]
2577    fn request_error_into_errno(error: RequestError) -> Errno {
2578        error.into()
2579    }
2580
2581    enum NeighborsAndInterfaces {}
2582    impl EventLoopSpec for NeighborsAndInterfaces {
2583        type NeighborWorker = Required;
2584
2585        type InterfacesProxy = Required;
2586        type InterfacesStateProxy = Required;
2587        type InterfacesHandler = Required;
2588        type RouteClients = Required;
2589        type InterfacesWorker = Required;
2590
2591        type V4RoutesState = Optional;
2592        type V6RoutesState = Optional;
2593        type V4RoutesSetProvider = Optional;
2594        type V6RoutesSetProvider = Optional;
2595        type V4RouteTableProvider = Optional;
2596        type V6RouteTableProvider = Optional;
2597
2598        type RoutesV4Worker = Optional;
2599        type RoutesV6Worker = Optional;
2600        type RuleV4Worker = Optional;
2601        type RuleV6Worker = Optional;
2602        type NduseroptWorker = Optional;
2603    }
2604
2605    const TEST_SEQUENCE_NUMBER: u32 = 1234;
2606
2607    struct EventLoopSetup {
2608        event_loop: EventLoopState<
2609            FakeInterfacesHandler,
2610            FakeSender<RouteNetlinkMessage>,
2611            NeighborsAndInterfaces,
2612        >,
2613        request_sink: mpsc::Sender<UnifiedRequest<FakeSender<RouteNetlinkMessage>>>,
2614        neighbors_controller_request_stream: fnet_neighbor::ControllerRequestStream,
2615        neighbor_event_sink: mpsc::UnboundedSender<Vec<fnet_neighbor::EntryIteratorItem>>,
2616        interface_event_sink: mpsc::UnboundedSender<fnet_interfaces::Event>,
2617    }
2618
2619    async fn build_event_loop(
2620        scope: &fuchsia_async::Scope,
2621        neighbor_events: &[EventSpec],
2622    ) -> EventLoopSetup {
2623        let included_workers = IncludedWorkers {
2624            routes_v4: EventLoopComponent::Absent(Optional),
2625            routes_v6: EventLoopComponent::Absent(Optional),
2626            interfaces: EventLoopComponent::Present(()),
2627            rules_v4: EventLoopComponent::Absent(Optional),
2628            rules_v6: EventLoopComponent::Absent(Optional),
2629            neighbors: EventLoopComponent::Present(()),
2630            nduseropt: EventLoopComponent::Absent(Optional),
2631        };
2632        let (request_sink, request_stream) = mpsc::channel(1);
2633
2634        // Configure fake neighbor watch events.
2635
2636        let (neighbors_controller, neighbors_controller_request_stream) =
2637            fidl::endpoints::create_proxy_and_stream::<fnet_neighbor::ControllerMarker>();
2638
2639        let (neighbors_view, neighbor_event_sink) = {
2640            let events = fnet_neighbor_ext::testutil::generate_events_from_spec(neighbor_events);
2641            let (event_sender, event_receiver) = mpsc::unbounded();
2642            event_sender.unbounded_send(events).expect("failed to send events");
2643            let (neighbors_view, neighbors_fut) =
2644                fnet_neighbor_ext::testutil::create_fake_view(event_receiver);
2645            let _join_handle = scope.spawn(neighbors_fut);
2646            (neighbors_view, event_sender)
2647        };
2648
2649        // Configure fake interface watch events.
2650
2651        let (interfaces_handler, _interfaces_handler_sink) =
2652            crate::interfaces::testutil::FakeInterfacesHandler::new();
2653        let (interfaces_proxy, _interfaces) =
2654            fidl::endpoints::create_proxy::<fnet_root::InterfacesMarker>();
2655        let (interfaces_state_proxy, interfaces_state) =
2656            fidl::endpoints::create_proxy::<fnet_interfaces::StateMarker>();
2657        let route_clients = ClientTable::default();
2658
2659        let interface_event_sink = {
2660            let if_stream = interfaces_state.into_stream();
2661            let if_watcher_stream = if_stream
2662                .and_then(|req| match req {
2663                    fnet_interfaces::StateRequest::GetWatcher {
2664                        options: _,
2665                        watcher,
2666                        control_handle: _,
2667                    } => futures::future::ready(Ok(watcher.into_stream())),
2668                })
2669                .try_flatten()
2670                .map(|res| res.expect("watcher stream error"));
2671            let (event_sender, event_receiver) = mpsc::unbounded();
2672            event_sender
2673                .unbounded_send(fnet_interfaces::Event::Idle(fnet_interfaces::Empty))
2674                .expect("failed to send event");
2675            let interfaces_fut =
2676                if_watcher_stream.zip(event_receiver).for_each(|(req, update)| async move {
2677                    match req {
2678                        fnet_interfaces::WatcherRequest::Watch { responder } => {
2679                            responder.send(&update).expect("send watch response")
2680                        }
2681                    }
2682                });
2683            let _join_handle = scope.spawn(interfaces_fut);
2684            event_sender
2685        };
2686
2687        // Set up the event loop.
2688
2689        let (_async_work_sink, async_work_receiver) = mpsc::unbounded();
2690        let base_inputs: EventLoopInputs<
2691            FakeInterfacesHandler,
2692            FakeSender<RouteNetlinkMessage>,
2693            NeighborsAndInterfaces,
2694        > = EventLoopInputs {
2695            neighbors_view: EventLoopComponent::Present(neighbors_view),
2696            neighbors_controller: EventLoopComponent::Present(neighbors_controller),
2697
2698            route_clients: EventLoopComponent::Present(route_clients),
2699            interfaces_handler: EventLoopComponent::Present(interfaces_handler),
2700            interfaces_proxy: EventLoopComponent::Present(interfaces_proxy),
2701            interfaces_state_proxy: EventLoopComponent::Present(interfaces_state_proxy),
2702
2703            async_work_receiver,
2704
2705            v4_routes_state: EventLoopComponent::Absent(Optional),
2706            v6_routes_state: EventLoopComponent::Absent(Optional),
2707            v4_main_route_table: EventLoopComponent::Absent(Optional),
2708            v6_main_route_table: EventLoopComponent::Absent(Optional),
2709            v4_route_table_provider: EventLoopComponent::Absent(Optional),
2710            v6_route_table_provider: EventLoopComponent::Absent(Optional),
2711            v4_rule_table: EventLoopComponent::Absent(Optional),
2712            v6_rule_table: EventLoopComponent::Absent(Optional),
2713            ndp_option_watcher_provider: EventLoopComponent::Absent(Optional),
2714
2715            unified_request_stream: request_stream,
2716        };
2717
2718        let event_loop = base_inputs.initialize(included_workers).await;
2719        EventLoopSetup {
2720            event_loop,
2721            request_sink,
2722            neighbors_controller_request_stream,
2723            neighbor_event_sink,
2724            interface_event_sink,
2725        }
2726    }
2727
2728    #[fuchsia::test]
2729    async fn event_loop_with_watch_events_and_get_request() {
2730        let scope = fuchsia_async::Scope::new();
2731        use fnet_neighbor_ext::testutil::EventSpec::*;
2732        let EventLoopSetup {
2733            mut event_loop,
2734            mut request_sink,
2735            neighbors_controller_request_stream: _,
2736            neighbor_event_sink,
2737            interface_event_sink,
2738        } = build_event_loop(&scope, &[Existing(1), Existing(2), Existing(3), Idle, Added(4)])
2739            .await;
2740
2741        // Wait for `Added` event to be processed.
2742        event_loop.run_one_step_in_tests().await;
2743
2744        // Send a dump request and check the response.
2745
2746        let (mut response_sink, neighbor_client, async_work_drain_task) =
2747            crate::client::testutil::new_fake_client::<NetlinkRoute>(
2748                crate::client::testutil::CLIENT_ID_1,
2749                [],
2750            );
2751        let _join_handle = scope.spawn(async_work_drain_task);
2752
2753        let (completer, waiter) = oneshot::channel();
2754        let get_request: UnifiedRequest<FakeSender<RouteNetlinkMessage>> =
2755            UnifiedRequest::NeighborsRequest(Request {
2756                args: NeighborRequestArgs::Get(GetNeighborArgs::Dump {
2757                    ip_version: None,
2758                    interface: None,
2759                }),
2760                sequence_number: TEST_SEQUENCE_NUMBER,
2761                client: neighbor_client.clone(),
2762                completer,
2763            });
2764        request_sink.send(get_request).await.unwrap();
2765
2766        // Wait for client request to be processed.
2767        event_loop.run_one_step_in_tests().await;
2768        assert_matches!(waiter.await.unwrap(), Ok(()));
2769
2770        let responses = response_sink.take_messages();
2771        assert_eq!(responses.len(), 4); // 3 existing + 1 added.
2772        for response in responses {
2773            assert_matches!(
2774                response.message.payload,
2775                NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewNeighbour(_))
2776            );
2777        }
2778
2779        neighbor_event_sink.close_channel();
2780        interface_event_sink.close_channel();
2781        drop(neighbor_client);
2782        scope.join().await;
2783    }
2784
2785    #[fuchsia::test]
2786    async fn event_loop_with_watch_events_and_delete_request() {
2787        let scope = fuchsia_async::Scope::new();
2788        let neighbor_events = {
2789            use fnet_neighbor_ext::testutil::EventSpec::*;
2790            vec![Existing(1), Existing(2), Idle]
2791        };
2792        let EventLoopSetup {
2793            mut event_loop,
2794            mut request_sink,
2795            neighbors_controller_request_stream,
2796            neighbor_event_sink,
2797            interface_event_sink,
2798        } = build_event_loop(&scope, &neighbor_events).await;
2799
2800        let fnet_neighbor::EntryIteratorItem::Existing(to_delete) =
2801            fnet_neighbor_ext::testutil::generate_event_from_spec(&neighbor_events[0])
2802        else {
2803            panic!("unexpected event")
2804        };
2805        let to_delete: fnet_neighbor_ext::Entry =
2806            to_delete.try_into().expect("extension conversion failed");
2807
2808        let (mut response_sink, neighbor_client, async_work_drain_task) =
2809            crate::client::testutil::new_fake_client::<NetlinkRoute>(
2810                crate::client::testutil::CLIENT_ID_1,
2811                [],
2812            );
2813        let _join_handle = scope.spawn(async_work_drain_task);
2814
2815        // Send an RTM_DELNEIGH request for an existing neighbor.
2816
2817        let (completer, waiter) = oneshot::channel();
2818        let waiter = waiter.fuse();
2819        pin_mut!(waiter);
2820
2821        let del_request: UnifiedRequest<FakeSender<RouteNetlinkMessage>> =
2822            UnifiedRequest::NeighborsRequest(Request {
2823                args: NeighborRequestArgs::Del(DelNeighborArgs {
2824                    ip: to_delete.neighbor,
2825                    interface: to_delete.interface,
2826                }),
2827                sequence_number: TEST_SEQUENCE_NUMBER,
2828                client: neighbor_client.clone(),
2829                completer,
2830            });
2831        request_sink.send(del_request).await.expect("failed to send delete request");
2832
2833        // Handle the expected Controller.RemoveEntry request and verify that
2834        // the RTM_DELNEIGH request is still pending.
2835
2836        let controller_req_fut = neighbors_controller_request_stream
2837            .into_future()
2838            .then(async move |(req, _rest)| {
2839                match req
2840                    .expect("Controller request stream unexpectedly ended")
2841                    .expect("failed to receive `Controller` request")
2842                {
2843                    fnet_neighbor::ControllerRequest::RemoveEntry {
2844                        interface,
2845                        neighbor,
2846                        responder,
2847                        ..
2848                    } => {
2849                        assert_eq!(interface, to_delete.interface.get());
2850                        assert_eq!(neighbor, to_delete.neighbor);
2851                        responder.send(Ok(())).expect("failed to respond to RemoveEntry");
2852                    }
2853                    _ => panic!("unexpected controller request"),
2854                }
2855            })
2856            .fuse();
2857        let _join_handle = scope.spawn(controller_req_fut);
2858        event_loop.run_one_step_in_tests().await;
2859
2860        assert_matches!(waiter.as_mut().now_or_never(), None);
2861        assert_eq!(response_sink.take_messages().len(), 0);
2862
2863        // Send an unrelated neighbor watch event and verify that the request is
2864        // still pending.
2865
2866        {
2867            use fnet_neighbor_ext::testutil::EventSpec::*;
2868            neighbor_event_sink
2869                .unbounded_send(fnet_neighbor_ext::testutil::generate_events_from_spec(&[Added(3)]))
2870                .expect("failed to send event");
2871        }
2872
2873        event_loop.run_one_step_in_tests().await;
2874        assert_matches!(waiter.as_mut().now_or_never(), None);
2875        assert_eq!(response_sink.take_messages().len(), 0);
2876
2877        // Send a neighbor watch event indicating successful removal and verify
2878        // that the request is completed.
2879
2880        {
2881            use fnet_neighbor_ext::testutil::EventSpec::*;
2882            neighbor_event_sink
2883                .unbounded_send(fnet_neighbor_ext::testutil::generate_events_from_spec(&[Removed(
2884                    1,
2885                )]))
2886                .expect("failed to send event");
2887        }
2888
2889        event_loop.run_one_step_in_tests().await;
2890        assert_matches!(waiter.await.expect("waiter channel should not be closed"), Ok(()));
2891        // The event loop & worker aren't responsible for the final response to
2892        // the client (either none, or ACK if requested), so there's nothing
2893        // more to check here.
2894
2895        neighbor_event_sink.close_channel();
2896        interface_event_sink.close_channel();
2897        drop(neighbor_client);
2898        scope.join().await;
2899    }
2900
2901    #[fuchsia::test]
2902    async fn event_loop_with_watch_events_create_static_neighbor() {
2903        let scope = fuchsia_async::Scope::new();
2904        let neighbor_events = vec![fnet_neighbor_ext::testutil::EventSpec::Idle];
2905        let EventLoopSetup {
2906            mut event_loop,
2907            mut request_sink,
2908            neighbors_controller_request_stream,
2909            neighbor_event_sink,
2910            interface_event_sink,
2911        } = build_event_loop(&scope, &neighbor_events).await;
2912
2913        let (mut response_sink, neighbor_client, async_work_drain_task) =
2914            crate::client::testutil::new_fake_client::<NetlinkRoute>(
2915                crate::client::testutil::CLIENT_ID_1,
2916                [],
2917            );
2918        let _join_handle = scope.spawn(async_work_drain_task);
2919
2920        // Create expected entry but without sending neighbor event.
2921
2922        let (event, entry) = {
2923            use fnet_neighbor_ext::testutil::EventSpec::*;
2924            let mut added = fnet_neighbor_ext::testutil::generate_event_from_spec(&Added(1));
2925            let fnet_neighbor::EntryIteratorItem::Added(to_add) = &mut added else {
2926                panic!("unexpected event")
2927            };
2928            to_add.state = Some(fnet_neighbor::EntryState::Static);
2929            let entry: fnet_neighbor_ext::Entry =
2930                to_add.clone().try_into().expect("extension conversion failed");
2931            (added, entry)
2932        };
2933
2934        // Send an RTM_NEWNEIGH request for an existing neighbor.
2935
2936        let (completer, waiter) = oneshot::channel();
2937        let waiter = waiter.fuse();
2938        pin_mut!(waiter);
2939
2940        let create_request: UnifiedRequest<FakeSender<RouteNetlinkMessage>> =
2941            UnifiedRequest::NeighborsRequest(Request {
2942                args: NeighborRequestArgs::New(NewNeighborArgs::CreateStatic {
2943                    ip: entry.neighbor,
2944                    interface: entry.interface,
2945                    mac: entry.mac.unwrap(),
2946                }),
2947                sequence_number: TEST_SEQUENCE_NUMBER,
2948                client: neighbor_client.clone(),
2949                completer,
2950            });
2951        request_sink.send(create_request).await.expect("failed to send create request");
2952
2953        // Handle the expected Controller.AddEntry request and verify that the
2954        // RTM_NEWNEIGH request is still pending.
2955
2956        let controller_req_fut = neighbors_controller_request_stream
2957            .into_future()
2958            .then(async move |(req, _rest)| {
2959                match req
2960                    .expect("Controller request stream unexpectedly ended")
2961                    .expect("failed to receive `Controller` request")
2962                {
2963                    fnet_neighbor::ControllerRequest::AddEntry {
2964                        interface,
2965                        neighbor,
2966                        mac,
2967                        responder,
2968                    } => {
2969                        assert_eq!(interface, entry.interface.get());
2970                        assert_eq!(neighbor, entry.neighbor);
2971                        assert_eq!(mac, entry.mac.unwrap());
2972                        responder.send(Ok(())).expect("failed to respond to AddEntry");
2973                    }
2974                    _ => panic!("unexpected controller request"),
2975                }
2976            })
2977            .fuse();
2978        let _join_handle = scope.spawn(controller_req_fut);
2979
2980        event_loop.run_one_step_in_tests().await;
2981        assert_matches!(waiter.as_mut().now_or_never(), None);
2982        assert_eq!(response_sink.take_messages().len(), 0);
2983
2984        // Send an unrelated neighbor watch event and verify that the request is
2985        // still pending.
2986
2987        {
2988            use fnet_neighbor_ext::testutil::EventSpec::*;
2989            neighbor_event_sink
2990                .unbounded_send(fnet_neighbor_ext::testutil::generate_events_from_spec(&[Added(3)]))
2991                .expect("failed to send event");
2992        }
2993
2994        event_loop.run_one_step_in_tests().await;
2995        assert_matches!(waiter.as_mut().now_or_never(), None);
2996        assert_eq!(response_sink.take_messages().len(), 0);
2997
2998        // Send a neighbor watch event indicating successful creation and verify
2999        // that the request is completed.
3000
3001        neighbor_event_sink.unbounded_send(vec![event]).expect("failed to send event");
3002
3003        event_loop.run_one_step_in_tests().await;
3004        assert_matches!(waiter.await.expect("waiter channel should not be closed"), Ok(()));
3005        // The event loop & worker aren't responsible for the final response to
3006        // the client (either none, or ACK if requested), so there's nothing
3007        // more to check here.
3008
3009        neighbor_event_sink.close_channel();
3010        interface_event_sink.close_channel();
3011        drop(neighbor_client);
3012        scope.join().await;
3013    }
3014
3015    #[fuchsia::test]
3016    async fn event_loop_with_watch_events_create_already_existing_succeeds_without_event() {
3017        let scope = fuchsia_async::Scope::new();
3018        let neighbor_events = vec![fnet_neighbor_ext::testutil::EventSpec::Idle];
3019        let EventLoopSetup {
3020            mut event_loop,
3021            mut request_sink,
3022            neighbors_controller_request_stream,
3023            neighbor_event_sink,
3024            interface_event_sink,
3025        } = build_event_loop(&scope, &neighbor_events).await;
3026
3027        let (mut response_sink, neighbor_client, async_work_drain_task) =
3028            crate::client::testutil::new_fake_client::<NetlinkRoute>(
3029                crate::client::testutil::CLIENT_ID_1,
3030                [],
3031            );
3032        let _join_handle = scope.spawn(async_work_drain_task);
3033
3034        // Report existing static entry.
3035
3036        let (event, entry) = {
3037            use fnet_neighbor_ext::testutil::EventSpec::*;
3038            let mut added = fnet_neighbor_ext::testutil::generate_event_from_spec(&Added(1));
3039            let fnet_neighbor::EntryIteratorItem::Added(to_add) = &mut added else {
3040                panic!("unexpected event")
3041            };
3042            to_add.state = Some(fnet_neighbor::EntryState::Static);
3043            let entry: fnet_neighbor_ext::Entry =
3044                to_add.clone().try_into().expect("extension conversion failed");
3045            (added, entry)
3046        };
3047        neighbor_event_sink.unbounded_send(vec![event]).expect("failed to send event");
3048        event_loop.run_one_step_in_tests().await;
3049
3050        // Send an RTM_NEWNEIGH request for an existing neighbor.
3051
3052        let (completer, waiter) = oneshot::channel();
3053        let waiter = waiter.fuse();
3054        pin_mut!(waiter);
3055
3056        let create_request: UnifiedRequest<FakeSender<RouteNetlinkMessage>> =
3057            UnifiedRequest::NeighborsRequest(Request {
3058                args: NeighborRequestArgs::New(NewNeighborArgs::CreateStatic {
3059                    ip: entry.neighbor,
3060                    interface: entry.interface,
3061                    mac: entry.mac.unwrap(),
3062                }),
3063                sequence_number: TEST_SEQUENCE_NUMBER,
3064                client: neighbor_client.clone(),
3065                completer,
3066            });
3067        request_sink.send(create_request).await.expect("failed to send create request");
3068
3069        // Handle the expected Controller.AddEntry request and verify that the
3070        // RTM_NEWNEIGH request is not pending.
3071
3072        let controller_req_fut = neighbors_controller_request_stream
3073            .into_future()
3074            .then(async move |(req, _rest)| {
3075                match req
3076                    .expect("Controller request stream unexpectedly ended")
3077                    .expect("failed to receive `Controller` request")
3078                {
3079                    fnet_neighbor::ControllerRequest::AddEntry {
3080                        interface,
3081                        neighbor,
3082                        mac,
3083                        responder,
3084                    } => {
3085                        assert_eq!(interface, entry.interface.get());
3086                        assert_eq!(neighbor, entry.neighbor);
3087                        assert_eq!(mac, entry.mac.unwrap());
3088                        responder.send(Ok(())).expect("failed to respond to AddEntry");
3089                    }
3090                    _ => panic!("unexpected controller request"),
3091                }
3092            })
3093            .fuse();
3094        let _join_handle = scope.spawn(controller_req_fut);
3095
3096        event_loop.run_one_step_in_tests().await;
3097        assert_matches!(waiter.await.expect("waiter channel should not be closed"), Ok(()));
3098        assert_eq!(response_sink.take_messages().len(), 0);
3099        // The event loop & worker aren't responsible for the final response to
3100        // the client (either none, or ACK if requested), so there's nothing
3101        // more to check here.
3102
3103        neighbor_event_sink.close_channel();
3104        interface_event_sink.close_channel();
3105        drop(neighbor_client);
3106        scope.join().await;
3107    }
3108
3109    #[fuchsia::test]
3110    async fn event_loop_with_watch_events_probe_neighbor() {
3111        let scope = fuchsia_async::Scope::new();
3112        let neighbor_events = vec![fnet_neighbor_ext::testutil::EventSpec::Idle];
3113        let EventLoopSetup {
3114            mut event_loop,
3115            mut request_sink,
3116            neighbors_controller_request_stream,
3117            neighbor_event_sink,
3118            interface_event_sink,
3119        } = build_event_loop(&scope, &neighbor_events).await;
3120
3121        let (mut response_sink, neighbor_client, async_work_drain_task) =
3122            crate::client::testutil::new_fake_client::<NetlinkRoute>(
3123                crate::client::testutil::CLIENT_ID_1,
3124                [],
3125            );
3126        let _join_handle = scope.spawn(async_work_drain_task);
3127
3128        // Report existing entry not in `Probe` state.
3129
3130        let (event, entry) = {
3131            use fnet_neighbor_ext::testutil::EventSpec::*;
3132            let mut added = fnet_neighbor_ext::testutil::generate_event_from_spec(&Added(1));
3133            let fnet_neighbor::EntryIteratorItem::Added(to_add) = &mut added else {
3134                panic!("unexpected event")
3135            };
3136            to_add.state = Some(fnet_neighbor::EntryState::Reachable);
3137            let entry: fnet_neighbor_ext::Entry =
3138                to_add.clone().try_into().expect("extension conversion failed");
3139            (added, entry)
3140        };
3141        neighbor_event_sink.unbounded_send(vec![event]).expect("failed to send event");
3142        event_loop.run_one_step_in_tests().await;
3143
3144        // Send an RTM_NEWNEIGH request to probe the existing neighbor.
3145
3146        let (completer, waiter) = oneshot::channel();
3147        let waiter = waiter.fuse();
3148        pin_mut!(waiter);
3149
3150        let probe_request: UnifiedRequest<FakeSender<RouteNetlinkMessage>> =
3151            UnifiedRequest::NeighborsRequest(Request {
3152                args: NeighborRequestArgs::New(NewNeighborArgs::ProbeExisting {
3153                    ip: entry.neighbor,
3154                    interface: entry.interface,
3155                }),
3156                sequence_number: TEST_SEQUENCE_NUMBER,
3157                client: neighbor_client.clone(),
3158                completer,
3159            });
3160        request_sink.send(probe_request).await.expect("failed to send create request");
3161
3162        // Handle the expected Controller.ProbeEntry request and verify that the
3163        // RTM_NEWNEIGH request is still pending.
3164
3165        let controller_req_fut = neighbors_controller_request_stream
3166            .into_future()
3167            .then(async move |(req, _rest)| {
3168                match req
3169                    .expect("Controller request stream unexpectedly ended")
3170                    .expect("failed to receive `Controller` request")
3171                {
3172                    fnet_neighbor::ControllerRequest::ProbeEntry {
3173                        interface,
3174                        neighbor,
3175                        responder,
3176                    } => {
3177                        assert_eq!(interface, entry.interface.get());
3178                        assert_eq!(neighbor, entry.neighbor);
3179                        responder.send(Ok(())).expect("failed to respond to ProbeEntry");
3180                    }
3181                    _ => panic!("unexpected controller request"),
3182                }
3183            })
3184            .fuse();
3185        let _join_handle = scope.spawn(controller_req_fut);
3186
3187        event_loop.run_one_step_in_tests().await;
3188        assert_matches!(waiter.as_mut().now_or_never(), None);
3189        assert_eq!(response_sink.take_messages().len(), 0);
3190
3191        // Send an unrelated neighbor watch event and verify that the request is
3192        // still pending.
3193
3194        {
3195            use fnet_neighbor_ext::testutil::EventSpec::*;
3196            neighbor_event_sink
3197                .unbounded_send(fnet_neighbor_ext::testutil::generate_events_from_spec(&[Added(3)]))
3198                .expect("failed to send event");
3199        }
3200
3201        event_loop.run_one_step_in_tests().await;
3202        assert_matches!(waiter.as_mut().now_or_never(), None);
3203        assert_eq!(response_sink.take_messages().len(), 0);
3204
3205        // Send a neighbor watch event indicating successful transition to
3206        // `Probe` and verify that the request is completed.
3207
3208        {
3209            let mut changed = entry.clone();
3210            changed.state = fnet_neighbor::EntryState::Probe;
3211            let changed_event = fnet_neighbor::EntryIteratorItem::Changed(changed.into());
3212            neighbor_event_sink.unbounded_send(vec![changed_event]).expect("failed to send event");
3213        }
3214
3215        event_loop.run_one_step_in_tests().await;
3216        assert_matches!(waiter.await.expect("waiter channel should not be closed"), Ok(()));
3217        // The event loop & worker aren't responsible for the final response to
3218        // the client (either none, or ACK if requested), so there's nothing
3219        // more to check here.
3220
3221        neighbor_event_sink.close_channel();
3222        interface_event_sink.close_channel();
3223        drop(neighbor_client);
3224        scope.join().await;
3225    }
3226
3227    #[fuchsia::test]
3228    async fn event_loop_with_watch_events_probe_already_probing_succeeds_without_event() {
3229        let scope = fuchsia_async::Scope::new();
3230        let neighbor_events = vec![fnet_neighbor_ext::testutil::EventSpec::Idle];
3231        let EventLoopSetup {
3232            mut event_loop,
3233            mut request_sink,
3234            neighbors_controller_request_stream,
3235            neighbor_event_sink,
3236            interface_event_sink,
3237        } = build_event_loop(&scope, &neighbor_events).await;
3238
3239        let (mut response_sink, neighbor_client, async_work_drain_task) =
3240            crate::client::testutil::new_fake_client::<NetlinkRoute>(
3241                crate::client::testutil::CLIENT_ID_1,
3242                [],
3243            );
3244        let _join_handle = scope.spawn(async_work_drain_task);
3245
3246        // Report existing `Probe` entry.
3247
3248        let (event, entry) = {
3249            use fnet_neighbor_ext::testutil::EventSpec::*;
3250            let mut added = fnet_neighbor_ext::testutil::generate_event_from_spec(&Added(1));
3251            let fnet_neighbor::EntryIteratorItem::Added(to_add) = &mut added else {
3252                panic!("unexpected event")
3253            };
3254            to_add.state = Some(fnet_neighbor::EntryState::Probe);
3255            let entry: fnet_neighbor_ext::Entry =
3256                to_add.clone().try_into().expect("extension conversion failed");
3257            (added, entry)
3258        };
3259        neighbor_event_sink.unbounded_send(vec![event]).expect("failed to send event");
3260        event_loop.run_one_step_in_tests().await;
3261
3262        // Send an RTM_NEWNEIGH request for the neighbor in `Probe` state.
3263
3264        let (completer, waiter) = oneshot::channel();
3265        let waiter = waiter.fuse();
3266        pin_mut!(waiter);
3267
3268        let probe_request: UnifiedRequest<FakeSender<RouteNetlinkMessage>> =
3269            UnifiedRequest::NeighborsRequest(Request {
3270                args: NeighborRequestArgs::New(NewNeighborArgs::ProbeExisting {
3271                    ip: entry.neighbor,
3272                    interface: entry.interface,
3273                }),
3274                sequence_number: TEST_SEQUENCE_NUMBER,
3275                client: neighbor_client.clone(),
3276                completer,
3277            });
3278        request_sink.send(probe_request).await.expect("failed to send create request");
3279
3280        // Handle the expected Controller.ProbeEntry request and verify that the
3281        // RTM_NEWNEIGH request is not pending.
3282
3283        let controller_req_fut = neighbors_controller_request_stream
3284            .into_future()
3285            .then(async move |(req, _rest)| {
3286                match req
3287                    .expect("Controller request stream unexpectedly ended")
3288                    .expect("failed to receive `Controller` request")
3289                {
3290                    fnet_neighbor::ControllerRequest::ProbeEntry {
3291                        interface,
3292                        neighbor,
3293                        responder,
3294                    } => {
3295                        assert_eq!(interface, entry.interface.get());
3296                        assert_eq!(neighbor, entry.neighbor);
3297                        responder.send(Ok(())).expect("failed to respond to ProbeEntry");
3298                    }
3299                    _ => panic!("unexpected controller request"),
3300                }
3301            })
3302            .fuse();
3303        let _join_handle = scope.spawn(controller_req_fut);
3304
3305        event_loop.run_one_step_in_tests().await;
3306        assert_matches!(waiter.await.expect("waiter channel should not be closed"), Ok(()));
3307        assert_eq!(response_sink.take_messages().len(), 0);
3308        // The event loop & worker aren't responsible for the final response to
3309        // the client (either none, or ACK if requested), so there's nothing
3310        // more to check here.
3311
3312        neighbor_event_sink.close_channel();
3313        interface_event_sink.close_channel();
3314        drop(neighbor_client);
3315        scope.join().await;
3316    }
3317
3318    #[fuchsia::test]
3319    async fn neighbors_worker_sends_multicast_updates() {
3320        let (mut sender_sink_member, client_member, _async_work_drain_task_1) =
3321            new_fake_client::<NetlinkRoute>(
3322                CLIENT_ID_1,
3323                [ModernGroup(rtnetlink_groups_RTNLGRP_NEIGH)],
3324            );
3325        let (mut sender_sink_non_member, client_non_member, _async_work_drain_task_2) =
3326            new_fake_client::<NetlinkRoute>(CLIENT_ID_2, []);
3327
3328        let client_table = ClientTable::default();
3329        client_table.add_client(client_member);
3330        client_table.add_client(client_non_member);
3331
3332        let (neighbors_controller, _server_end) =
3333            fidl::endpoints::create_proxy::<fnet_neighbor::ControllerMarker>();
3334        let mut worker = NeighborsWorker { neighbor_table: HashMap::new(), neighbors_controller };
3335
3336        let entry = valid_neighbor_entry();
3337
3338        // Verify that an `Added` event results in an `RTM_NEWNEIGH` multicast
3339        // message.
3340
3341        worker
3342            .handle_neighbor_watcher_event(
3343                fnet_neighbor_ext::Event::Added(entry.clone()),
3344                &client_table,
3345            )
3346            .expect("handle added");
3347
3348        let messages = sender_sink_member.take_messages();
3349        assert_eq!(messages.len(), 1);
3350        let SentMessage { message, group } = &messages[0];
3351        assert_eq!(group, &Some(ModernGroup(rtnetlink_groups_RTNLGRP_NEIGH)));
3352        assert_matches!(
3353            &message.payload,
3354            NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewNeighbour(msg)) => {
3355                assert_eq!(msg.header.ifindex as u64, entry.interface.get());
3356            }
3357        );
3358        assert_eq!(message.header.sequence_number, UNSPECIFIED_SEQUENCE_NUMBER);
3359        assert_eq!(sender_sink_non_member.take_messages().len(), 0);
3360
3361        // Verify that a `Changed` event results in an `RTM_NEWNEIGH` multicast
3362        // message.
3363
3364        let mut new_entry = entry.clone();
3365        new_entry.state = fnet_neighbor::EntryState::Stale;
3366        worker
3367            .handle_neighbor_watcher_event(
3368                fnet_neighbor_ext::Event::Changed(new_entry.clone()),
3369                &client_table,
3370            )
3371            .expect("handle changed");
3372
3373        let messages = sender_sink_member.take_messages();
3374        assert_eq!(messages.len(), 1);
3375        let SentMessage { message, group } = &messages[0];
3376        assert_eq!(group, &Some(ModernGroup(rtnetlink_groups_RTNLGRP_NEIGH)));
3377        assert_matches!(
3378            &message.payload,
3379            NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewNeighbour(msg)) => {
3380                assert_eq!(msg.header.state, NeighbourState::Stale);
3381            }
3382        );
3383        assert_eq!(sender_sink_non_member.take_messages().len(), 0);
3384
3385        // Verify that a `Removed` event results in a `RTM_DELNEIGH` multicast
3386        // message.
3387
3388        worker
3389            .handle_neighbor_watcher_event(
3390                fnet_neighbor_ext::Event::Removed(new_entry.clone()),
3391                &client_table,
3392            )
3393            .expect("handle removed");
3394
3395        let messages = sender_sink_member.take_messages();
3396        assert_eq!(messages.len(), 1);
3397        let SentMessage { message, group } = &messages[0];
3398        assert_eq!(group, &Some(ModernGroup(rtnetlink_groups_RTNLGRP_NEIGH)));
3399        assert_matches!(
3400            &message.payload,
3401            NetlinkPayload::InnerMessage(RouteNetlinkMessage::DelNeighbour(msg)) => {
3402                assert_eq!(msg.header.ifindex as u64, entry.interface.get());
3403            }
3404        );
3405        assert_eq!(sender_sink_non_member.take_messages().len(), 0);
3406    }
3407}