netlink/
interfaces.rs

1// Copyright 2023 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 RTM_LINK and RTM_ADDR information by generating
6//! RTM_LINK and RTM_ADDR Netlink messages based on events received from
7//! Netstack's interface watcher.
8
9use std::collections::BTreeMap;
10use std::fmt::Debug;
11use std::net::IpAddr;
12use std::num::{NonZeroU32, NonZeroU64};
13
14use fidl_fuchsia_net_ext::IntoExt as _;
15use fidl_fuchsia_net_interfaces_admin::{
16    self as fnet_interfaces_admin, AddressRemovalReason, InterfaceRemovedReason,
17};
18use fidl_fuchsia_net_interfaces_ext::admin::{
19    AddressStateProviderError, TerminalError, wait_for_address_added_event,
20};
21use fidl_fuchsia_net_interfaces_ext::{self as fnet_interfaces_ext, Update as _};
22use {
23    fidl_fuchsia_net as fnet, fidl_fuchsia_net_interfaces as fnet_interfaces,
24    fidl_fuchsia_net_root as fnet_root,
25};
26
27use derivative::Derivative;
28use either::Either;
29use futures::StreamExt as _;
30use futures::channel::oneshot;
31use linux_uapi::{
32    ARPHRD_6LOWPAN, ARPHRD_ETHER, ARPHRD_LOOPBACK, ARPHRD_PPP, ARPHRD_VOID,
33    net_device_flags_IFF_LOOPBACK, net_device_flags_IFF_LOWER_UP, net_device_flags_IFF_RUNNING,
34    net_device_flags_IFF_UP, rtnetlink_groups_RTNLGRP_IPV4_IFADDR,
35    rtnetlink_groups_RTNLGRP_IPV6_IFADDR, rtnetlink_groups_RTNLGRP_LINK,
36};
37use net_types::ip::{AddrSubnetEither, IpVersion, Ipv4, Ipv6};
38use netlink_packet_core::{NLM_F_MULTIPART, NetlinkMessage};
39use netlink_packet_route::address::{
40    AddressAttribute, AddressFlags, AddressHeader, AddressHeaderFlags, AddressMessage,
41};
42use netlink_packet_route::link::{
43    LinkAttribute, LinkFlags, LinkHeader, LinkLayerType, LinkMessage, State,
44};
45use netlink_packet_route::{AddressFamily, RouteNetlinkMessage};
46
47use crate::SysctlError;
48use crate::client::{ClientTable, InternalClient};
49use crate::logging::{log_debug, log_error, log_warn};
50use crate::messaging::Sender;
51use crate::multicast_groups::ModernGroup;
52use crate::netlink_packet::UNSPECIFIED_SEQUENCE_NUMBER;
53use crate::netlink_packet::errno::Errno;
54use crate::protocol_family::ProtocolFamily;
55use crate::protocol_family::route::NetlinkRoute;
56use crate::route_tables::{NetlinkRouteTableIndex, RouteTable, RouteTableMap, UnmanagedTable};
57use crate::util::respond_to_completer;
58
59/// A handler for interface events.
60pub trait InterfacesHandler: Send + Sync + 'static {
61    /// Handle a new link.
62    fn handle_new_link(&mut self, name: &str, interface_id: NonZeroU64);
63
64    /// Handle a deleted link.
65    fn handle_deleted_link(&mut self, name: &str);
66
67    /// Handle the idle event.
68    fn handle_idle_event(&mut self) {}
69}
70
71/// Represents the ways RTM_*LINK messages may specify an individual link.
72#[derive(Clone, Debug, PartialEq, Eq)]
73pub(crate) enum LinkSpecifier {
74    Index(NonZeroU32),
75    Name(String),
76}
77
78/// Arguments for an RTM_GETLINK [`Request`].
79#[derive(Clone, Debug, PartialEq, Eq)]
80pub(crate) enum GetLinkArgs {
81    /// Dump state for all the links.
82    Dump,
83    /// Get a specific link.
84    Get(LinkSpecifier),
85}
86
87/// Arguments for an RTM_SETLINK ['Request`].
88#[derive(Clone, Debug, PartialEq, Eq)]
89pub(crate) struct SetLinkArgs {
90    /// The link to update.
91    pub(crate) link: LinkSpecifier,
92    /// `Some` if the link's admin enabled state should be updated to the
93    /// provided `bool`.
94    pub(crate) enable: Option<bool>,
95}
96
97/// [`Request`] arguments associated with links.
98#[derive(Clone, Debug, PartialEq, Eq)]
99pub(crate) enum LinkRequestArgs {
100    /// RTM_GETLINK
101    Get(GetLinkArgs),
102    /// RTM_SETLINK
103    Set(SetLinkArgs),
104}
105
106/// Arguments for an RTM_GETADDR [`Request`].
107#[derive(Copy, Clone, Debug, PartialEq, Eq)]
108pub(crate) enum GetAddressArgs {
109    /// Dump state for all addresses with the optional IP version filter.
110    Dump { ip_version_filter: Option<IpVersion> },
111    // TODO(https://issues.fuchsia.dev/296616404): Support get requests w/
112    // filter.
113}
114
115/// The address and interface ID arguments for address requests.
116#[derive(Copy, Clone, Debug, PartialEq, Eq)]
117pub(crate) struct AddressAndInterfaceArgs {
118    pub address: AddrSubnetEither,
119    pub interface_id: NonZeroU32,
120}
121
122/// Arguments for an RTM_NEWADDR [`Request`].
123#[derive(Copy, Clone, Debug, PartialEq, Eq)]
124pub(crate) struct NewAddressArgs {
125    /// The address to be added and the interface to add it to.
126    pub address_and_interface_id: AddressAndInterfaceArgs,
127    /// Indicates whether or not an on-link route should be added for the
128    /// address's subnet.
129    pub add_subnet_route: bool,
130}
131
132/// Arguments for an RTM_DELADDR [`Request`].
133#[derive(Copy, Clone, Debug, PartialEq, Eq)]
134pub(crate) struct DelAddressArgs {
135    /// The address to be removed and the interface to remove it from.
136    pub address_and_interface_id: AddressAndInterfaceArgs,
137}
138
139/// [`Request`] arguments associated with addresses.
140#[derive(Copy, Clone, Debug, PartialEq, Eq)]
141pub(crate) enum AddressRequestArgs {
142    /// RTM_GETADDR
143    Get(GetAddressArgs),
144    /// RTM_NEWADDR
145    New(NewAddressArgs),
146    /// RTM_DELADDR
147    #[allow(unused)]
148    Del(DelAddressArgs),
149}
150
151/// The argument(s) for a [`Request`].
152#[derive(Clone, Debug, PartialEq, Eq)]
153pub(crate) enum RequestArgs {
154    Link(LinkRequestArgs),
155    Address(AddressRequestArgs),
156}
157
158/// An error encountered while handling a [`Request`].
159#[derive(Copy, Clone, Debug, PartialEq, Eq)]
160pub(crate) enum RequestError {
161    Unknown,
162    InvalidRequest,
163    UnrecognizedInterface,
164    AlreadyExists,
165    AddressNotFound,
166}
167
168impl RequestError {
169    pub(crate) fn into_errno(self) -> Errno {
170        match self {
171            RequestError::Unknown => {
172                log_error!("observed an unknown error, reporting `EINVAL` as the best guess");
173                Errno::EINVAL
174            }
175            RequestError::InvalidRequest => Errno::EINVAL,
176            RequestError::UnrecognizedInterface => Errno::ENODEV,
177            RequestError::AlreadyExists => Errno::EEXIST,
178            RequestError::AddressNotFound => Errno::EADDRNOTAVAIL,
179        }
180    }
181}
182
183fn map_existing_interface_terminal_error(
184    e: TerminalError<InterfaceRemovedReason>,
185    interface_id: NonZeroU64,
186) -> RequestError {
187    match e {
188        TerminalError::Fidl(e) => {
189            // If the channel was closed, then we likely tried to get a control
190            // chandle to an interface that does not exist.
191            if !e.is_closed() {
192                log_error!(
193                    "unexpected interface terminal error for interface ({:?}): {:?}",
194                    interface_id,
195                    e,
196                )
197            }
198        }
199        TerminalError::Terminal(reason) => match reason {
200            reason @ (InterfaceRemovedReason::DuplicateName
201            | InterfaceRemovedReason::PortAlreadyBound
202            | InterfaceRemovedReason::BadPort) => {
203                // These errors are only expected when the interface fails to
204                // be installed.
205                unreachable!(
206                    "unexpected interface removed reason {:?} for interface ({:?})",
207                    reason, interface_id,
208                )
209            }
210            InterfaceRemovedReason::PortClosed | InterfaceRemovedReason::User => {
211                // The interface was removed. Treat this scenario as if the
212                // interface did not exist.
213            }
214            reason => {
215                // `InterfaceRemovedReason` is a flexible FIDL enum so we
216                // cannot exhaustively match.
217                //
218                // We don't know what the reason is but we know the interface
219                // was removed so just assume that the unrecognized reason is
220                // valid and return the same error as if it was removed with
221                // `PortClosed`/`User` reasons.
222                log_error!(
223                    "unrecognized removal reason {:?} from interface {:?}",
224                    reason,
225                    interface_id
226                )
227            }
228        },
229    }
230
231    RequestError::UnrecognizedInterface
232}
233
234/// A request associated with links or addresses.
235#[derive(Derivative)]
236#[derivative(Debug(bound = ""))]
237pub(crate) struct Request<S: Sender<<NetlinkRoute as ProtocolFamily>::Response>> {
238    /// The resource and operation-specific argument(s) for this request.
239    pub args: RequestArgs,
240    /// The request's sequence number.
241    ///
242    /// This value will be copied verbatim into any message sent as a result of
243    /// this request.
244    pub sequence_number: u32,
245    /// The client that made the request.
246    pub client: InternalClient<NetlinkRoute, S>,
247    /// A completer that will have the result of the request sent over.
248    pub completer: oneshot::Sender<Result<(), RequestError>>,
249}
250
251/// Handles asynchronous work related to RTM_LINK and RTM_ADDR messages.
252///
253/// Can respond to interface watcher events and RTM_LINK and RTM_ADDR
254/// message requests.
255pub(crate) struct InterfacesWorkerState<H, S: Sender<<NetlinkRoute as ProtocolFamily>::Response>> {
256    /// A handler for interface events.
257    interfaces_handler: H,
258    /// An `InterfacesProxy` to get controlling access to interfaces.
259    interfaces_proxy: fnet_root::InterfacesProxy,
260    /// The current set of clients of NETLINK_ROUTE protocol family.
261    route_clients: ClientTable<NetlinkRoute, S>,
262    /// The table of interfaces and associated state discovered through the
263    /// interfaces watcher.
264    pub(crate) interface_properties: BTreeMap<
265        u64,
266        fnet_interfaces_ext::PropertiesAndState<InterfaceState, fnet_interfaces_ext::AllInterest>,
267    >,
268    /// Corresponds to the `/proc/sys/net/ipv6/conf/default/accept_ra_rt_table`.
269    /// It is the default sysctl value for the interfaces to be added.
270    pub(crate) default_accept_ra_rt_table: AcceptRaRtTable,
271    /// Corresponds to the `/proc/sys/net/ipv6/conf/all/accept_ra_rt_table`.
272    /// It does _nothing_ upon write, same as Linux.
273    pub(crate) all_accept_ra_rt_table: AcceptRaRtTable,
274}
275
276/// This models the `accept_ra_rt_table` sysctl.
277///
278/// The sysctl behaves as follows:
279///   - = 0: default. Put routes into RT6_TABLE_MAIN if the interface
280///     is not in a VRF, or into the VRF table if it is.
281///   - > 0: manual. Put routes into the specified table.
282///   - < 0: automatic. Add the absolute value of the sysctl to the
283///     device's ifindex, and use that table.
284#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
285pub(crate) enum AcceptRaRtTable {
286    /// Installs routes in the main table.
287    #[default]
288    Main,
289    /// Installs routes in the specified table.
290    Manual(u32),
291    /// Installs routes in table ID that is interface ID plus the diff.
292    Auto(u32),
293}
294
295impl From<i32> for AcceptRaRtTable {
296    fn from(val: i32) -> Self {
297        if val == 0 {
298            Self::Main
299        } else if val > 0 {
300            Self::Manual(val.unsigned_abs())
301        } else {
302            Self::Auto(val.unsigned_abs())
303        }
304    }
305}
306
307impl From<AcceptRaRtTable> for i32 {
308    fn from(val: AcceptRaRtTable) -> Self {
309        match val {
310            AcceptRaRtTable::Main => 0,
311            AcceptRaRtTable::Manual(val) => i32::try_from(val).expect("larger than i32::MAX"),
312            AcceptRaRtTable::Auto(val) => {
313                0i32.checked_sub_unsigned(val).expect("less than i32::MIN")
314            }
315        }
316    }
317}
318
319#[derive(Debug, Default, Clone)]
320pub(crate) struct InterfaceState {
321    // `BTreeMap` so that addresses are iterated in deterministic order
322    // (useful for tests).
323    addresses: BTreeMap<fnet::IpAddress, NetlinkAddressMessage>,
324    link_address: Option<Vec<u8>>,
325    control: Option<fnet_interfaces_ext::admin::Control>,
326    accept_ra_rt_table: AcceptRaRtTable,
327}
328
329impl InterfaceState {
330    pub(crate) fn accept_ra_rt_table(&self) -> AcceptRaRtTable {
331        self.accept_ra_rt_table
332    }
333
334    pub(crate) async fn set_accept_ra_rt_table(
335        &mut self,
336        interface_id: NonZeroU64,
337        new_accept_ra_rt_table: AcceptRaRtTable,
338        interfaces_proxy: &fnet_root::InterfacesProxy,
339        route_table_maps: Option<(&mut RouteTableMap<Ipv4>, &mut RouteTableMap<Ipv6>)>,
340    ) -> Result<(), SysctlError> {
341        let old_accept_ra_rt_table = self.accept_ra_rt_table;
342        if old_accept_ra_rt_table == new_accept_ra_rt_table {
343            return Ok(());
344        }
345
346        enum InsertOrRemove {
347            Insert,
348            Remove,
349        }
350
351        let (delta, insert_or_remove) = match (old_accept_ra_rt_table, new_accept_ra_rt_table) {
352            (AcceptRaRtTable::Main, AcceptRaRtTable::Auto(delta)) => {
353                (delta, InsertOrRemove::Insert)
354            }
355            (AcceptRaRtTable::Auto(delta), AcceptRaRtTable::Main) => {
356                (delta, InsertOrRemove::Remove)
357            }
358            (from, to) => {
359                log::error!("unsupported transition from {from:?} to {to:?}");
360                return Err(SysctlError::Unsupported);
361            }
362        };
363
364        let netlink_id = match u32::try_from(interface_id.get()) {
365            Ok(i) => {
366                NetlinkRouteTableIndex::new(i.checked_add(delta).ok_or(SysctlError::Unsupported)?)
367            }
368            Err(std::num::TryFromIntError { .. }) => {
369                log::error!(
370                    "not using local route table for interface \
371                {interface_id:?} because it is not representable in u32"
372                );
373                return Err(SysctlError::Unsupported);
374            }
375        };
376
377        self.accept_ra_rt_table = new_accept_ra_rt_table;
378
379        let Some((v4_route_table_map, v6_route_table_map)) = route_table_maps else {
380            return Ok(());
381        };
382        let control = self.control(interfaces_proxy, interface_id);
383        match insert_or_remove {
384            InsertOrRemove::Insert => {
385                let result = futures::future::try_join(
386                    UnmanagedTable::<Ipv4>::interface_local(
387                        control,
388                        &v4_route_table_map.route_table_provider(),
389                    ),
390                    UnmanagedTable::<Ipv6>::interface_local(
391                        control,
392                        &v6_route_table_map.route_table_provider(),
393                    ),
394                )
395                .await;
396                match result {
397                    Ok((local_table_v4, local_table_v6)) => {
398                        log::info!(
399                            "local table mapping for {interface_id}: \
400                            {netlink_id:?} -> ({:?}, {:?})",
401                            local_table_v4.fidl_table_id,
402                            local_table_v6.fidl_table_id,
403                        );
404                        v4_route_table_map
405                            .insert(netlink_id, RouteTable::Unmanaged(local_table_v4));
406                        v6_route_table_map
407                            .insert(netlink_id, RouteTable::Unmanaged(local_table_v6));
408                    }
409                    Err(err) => {
410                        log::info!("failed to get a local table for {interface_id}: {err:?}");
411                    }
412                }
413            }
414            InsertOrRemove::Remove => {
415                let _: Option<_> = v4_route_table_map.remove(netlink_id);
416                let _: Option<_> = v6_route_table_map.remove(netlink_id);
417            }
418        }
419        Ok(())
420    }
421
422    pub(crate) fn control(
423        &mut self,
424        interfaces_proxy: &fnet_root::InterfacesProxy,
425        interface_id: NonZeroU64,
426    ) -> &fnet_interfaces_ext::admin::Control {
427        self.control.get_or_insert_with(|| {
428            let (control, server_end) = fnet_interfaces_ext::admin::Control::create_endpoints()
429                .expect("create Control endpoints");
430            interfaces_proxy
431                .get_admin(interface_id.get(), server_end)
432                .expect("send get admin request");
433            control
434        })
435    }
436}
437
438async fn set_link_address(
439    interfaces_proxy: &fnet_root::InterfacesProxy,
440    id: NonZeroU64,
441    link_address: &mut Option<Vec<u8>>,
442) {
443    match interfaces_proxy
444        .get_mac(id.get())
445        .await
446        .expect("netstack should never close its end of `fuchsia.net.root/Interfaces`")
447    {
448        Ok(None) => {
449            // The request succeeded but the interface has no address.
450            log_debug!("no MAC address for interface ({id:?})")
451        }
452        Ok(Some(mac)) => {
453            let fnet::MacAddress { octets } = *mac;
454            assert_eq!(link_address.replace(octets.to_vec()), None)
455        }
456        Err(fnet_root::InterfacesGetMacError::NotFound) => {
457            // We only get here if the interface has been removed after we
458            // learned about it through the interfaces watcher. Do nothing as
459            // a removed event should come for this interface shortly.
460            log_warn!("failed to get MAC address for interface ({id:?}) with not found error")
461        }
462    }
463}
464
465#[derive(Clone, Copy, Debug)]
466enum PendingRequestKind {
467    AddAddress(AddressAndInterfaceArgs),
468    DelAddress(AddressAndInterfaceArgs),
469    DisableInterface(NonZeroU64),
470    // TODO(https://issues.fuchsia.dev/290372180): Support Pending
471    // "EnableInterface" requests once link_state is available via a FIDL API
472}
473
474#[derive(Derivative)]
475#[derivative(Debug(bound = ""))]
476pub(crate) struct PendingRequest<S: Sender<<NetlinkRoute as ProtocolFamily>::Response>> {
477    kind: PendingRequestKind,
478    client: InternalClient<NetlinkRoute, S>,
479    completer: oneshot::Sender<Result<(), RequestError>>,
480}
481
482impl<H: InterfacesHandler, S: Sender<<NetlinkRoute as ProtocolFamily>::Response>>
483    InterfacesWorkerState<H, S>
484{
485    /// Create the Netlink Interfaces Worker.
486    ///
487    /// # Panics
488    ///
489    /// Panics if an unexpected error is encountered on one of the FIDL
490    /// connections with the netstack.
491    pub(crate) async fn create(
492        mut interfaces_handler: H,
493        route_clients: ClientTable<NetlinkRoute, S>,
494        interfaces_proxy: fnet_root::InterfacesProxy,
495        interfaces_state_proxy: fnet_interfaces::StateProxy,
496    ) -> (
497        Self,
498        impl futures::Stream<
499            Item = Result<
500                fnet_interfaces_ext::EventWithInterest<fnet_interfaces_ext::AllInterest>,
501                fidl::Error,
502            >,
503        > + 'static,
504    ) {
505        let mut if_event_stream = Box::pin(
506            fnet_interfaces_ext::event_stream_from_state(
507                &interfaces_state_proxy,
508                fnet_interfaces_ext::WatchOptions {
509                    included_addresses: fnet_interfaces_ext::IncludedAddresses::All,
510                    ..Default::default()
511                },
512            )
513            .expect("connecting to fuchsia.net.interfaces.State FIDL should succeed"),
514        );
515
516        let mut interface_properties = fnet_interfaces_ext::existing(
517            if_event_stream.by_ref(),
518            BTreeMap::<u64, fnet_interfaces_ext::PropertiesAndState<InterfaceState, _>>::new(),
519        )
520        .await
521        .expect("determining already installed interfaces should succeed");
522
523        for fnet_interfaces_ext::PropertiesAndState { properties, state } in
524            interface_properties.values_mut()
525        {
526            set_link_address(&interfaces_proxy, properties.id, &mut state.link_address).await;
527
528            if let Some(interface_addresses) =
529                addresses_optionally_from_interface_properties(properties)
530            {
531                state.addresses = interface_addresses;
532            }
533
534            interfaces_handler.handle_new_link(&properties.name, properties.id);
535        }
536
537        interfaces_handler.handle_idle_event();
538
539        (
540            InterfacesWorkerState {
541                interfaces_handler,
542                interfaces_proxy,
543                route_clients,
544                interface_properties,
545                default_accept_ra_rt_table: Default::default(),
546                all_accept_ra_rt_table: Default::default(),
547            },
548            if_event_stream,
549        )
550    }
551
552    /// Handles events observed from the interface watcher by updating the
553    /// table of discovered interfaces.
554    ///
555    /// # Panics
556    ///
557    /// Panics if an unexpected Interface Watcher Event is published by the
558    /// Netstack.
559    pub(crate) async fn handle_interface_watcher_event(
560        &mut self,
561        event: fnet_interfaces_ext::EventWithInterest<fnet_interfaces_ext::AllInterest>,
562        route_table_maps: Option<(&mut RouteTableMap<Ipv4>, &mut RouteTableMap<Ipv6>)>,
563    ) {
564        let update = self
565            .interface_properties
566            .update(event)
567            .expect("Netstack interface event resulted in an invalid update");
568
569        match update {
570            fnet_interfaces_ext::UpdateResult::Added { properties, state } => {
571                set_link_address(&self.interfaces_proxy, properties.id, &mut state.link_address)
572                    .await;
573
574                let interface_id = properties.id;
575
576                // The newly added device should have the default sysctl.
577                let initial_value = self.default_accept_ra_rt_table;
578                state
579                    .set_accept_ra_rt_table(
580                        interface_id,
581                        initial_value,
582                        &self.interfaces_proxy,
583                        route_table_maps,
584                    )
585                    .await
586                    .unwrap_or_else(|_err| {
587                        log::error!("failed to update the accept_ra_rt_table for {interface_id:?}")
588                    });
589
590                if let Some(message) =
591                    NetlinkLinkMessage::optionally_from(properties, &state.link_address)
592                {
593                    self.route_clients.send_message_to_group(
594                        message.into_rtnl_new_link(UNSPECIFIED_SEQUENCE_NUMBER, false),
595                        ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
596                    )
597                }
598
599                // Send address messages after the link message for newly added links
600                // so that netlink clients are aware of the interface before sending
601                // address messages for an interface.
602                if let Some(updated_addresses) =
603                    addresses_optionally_from_interface_properties(properties)
604                {
605                    update_addresses(&mut state.addresses, updated_addresses, &self.route_clients);
606                }
607
608                self.interfaces_handler.handle_new_link(&properties.name, properties.id);
609
610                log_debug!("processed add/existing event for id {}", properties.id);
611            }
612            fnet_interfaces_ext::UpdateResult::Changed {
613                previous:
614                    fnet_interfaces::Properties {
615                        online,
616                        addresses,
617                        id: _,
618                        name: _,
619                        has_default_ipv4_route: _,
620                        has_default_ipv6_route: _,
621                        port_class: _,
622                        ..
623                    },
624                current:
625                    current @ fnet_interfaces_ext::Properties {
626                        id,
627                        addresses: _,
628                        name: _,
629                        port_class: _,
630                        online: _,
631                        has_default_ipv4_route: _,
632                        has_default_ipv6_route: _,
633                        port_identity_koid: _,
634                    },
635                state:
636                    InterfaceState {
637                        addresses: interface_addresses,
638                        link_address,
639                        control: _,
640                        accept_ra_rt_table: _,
641                    },
642            } => {
643                if online.is_some() {
644                    if let Some(message) =
645                        NetlinkLinkMessage::optionally_from(current, link_address)
646                    {
647                        self.route_clients.send_message_to_group(
648                            message.into_rtnl_new_link(UNSPECIFIED_SEQUENCE_NUMBER, false),
649                            ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
650                        )
651                    }
652
653                    log_debug!("processed interface link change event for id {}", id);
654                };
655
656                // The `is_some` check is not strictly necessary because
657                // `update_addresses` will calculate the delta before sending
658                // updates but is useful as an optimization when addresses don't
659                // change (<avoid allocations and message comparisons that will net
660                // no updates).
661                if addresses.is_some() {
662                    if let Some(updated_addresses) =
663                        addresses_optionally_from_interface_properties(current)
664                    {
665                        update_addresses(
666                            interface_addresses,
667                            updated_addresses,
668                            &self.route_clients,
669                        );
670                    }
671
672                    log_debug!("processed interface address change event for id {}", id);
673                }
674            }
675            fnet_interfaces_ext::UpdateResult::Removed(
676                fnet_interfaces_ext::PropertiesAndState {
677                    properties,
678                    state:
679                        InterfaceState {
680                            mut addresses,
681                            link_address,
682                            control: _,
683                            accept_ra_rt_table: _,
684                        },
685                },
686            ) => {
687                update_addresses(&mut addresses, BTreeMap::new(), &self.route_clients);
688
689                // Send link messages after the address message for removed links
690                // so that netlink clients are aware of the interface throughout the
691                // address messages.
692                if let Some(message) =
693                    NetlinkLinkMessage::optionally_from(&properties, &link_address)
694                {
695                    self.route_clients.send_message_to_group(
696                        message.into_rtnl_del_link(UNSPECIFIED_SEQUENCE_NUMBER),
697                        ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
698                    )
699                }
700
701                self.interfaces_handler.handle_deleted_link(&properties.name);
702
703                log_debug!("processed interface remove event for id {}", properties.id);
704            }
705            fnet_interfaces_ext::UpdateResult::Existing { properties, state: _ } => {
706                panic!("Netstack reported the addition of an existing interface: {properties:?}");
707            }
708            fnet_interfaces_ext::UpdateResult::NoChange => {}
709        }
710    }
711
712    /// Checks whether a `PendingRequest` can be marked completed given the current state of the
713    /// worker. If so, notifies the request's completer and returns `None`. If not, returns
714    /// the `PendingRequest` as `Some`.
715    pub(crate) fn handle_pending_request(
716        &self,
717        pending_request: PendingRequest<S>,
718    ) -> Option<PendingRequest<S>> {
719        let PendingRequest { kind, client: _, completer: _ } = &pending_request;
720        let contains_addr = |&AddressAndInterfaceArgs { address, interface_id }| {
721            // NB: The interface must exist, because we were able to
722            // successfully add/remove an address (hence the pending
723            // request). The Netstack will send a 'changed' event to
724            // reflect the address add/remove before sending a `removed`
725            // event for the interface.
726            let fnet_interfaces_ext::PropertiesAndState {
727                properties: _,
728                state:
729                    InterfaceState { addresses, link_address: _, control: _, accept_ra_rt_table: _ },
730            } = self
731                .interface_properties
732                .get(&interface_id.get().into())
733                .expect("interfaces with pending address change should exist");
734            let fnet::Subnet { addr, prefix_len: _ } = address.clone().into_ext();
735            addresses.contains_key(&addr)
736        };
737
738        let done = match kind {
739            PendingRequestKind::AddAddress(address_and_interface_args) => {
740                contains_addr(address_and_interface_args)
741            }
742            PendingRequestKind::DelAddress(address_and_interface_args) => {
743                !contains_addr(address_and_interface_args)
744            }
745            PendingRequestKind::DisableInterface(interface_id) => {
746                // NB: The interface must exist, because we were able to
747                // successfully disabled it (hence the pending request).
748                // The Netstack will send a 'changed' event to reflect
749                // the disable, before sending a `removed` event.
750                let fnet_interfaces_ext::PropertiesAndState { properties, state: _ } =
751                    self.interface_properties.get(&interface_id.get()).unwrap_or_else(|| {
752                        panic!("interface {interface_id} with pending disable should exist")
753                    });
754                // Note here we check "is the interface offline" which
755                // is a combination of, "is the underlying link state
756                // down" and "is the interface admin disabled". This
757                // means we cannot know with certainty whether the link
758                // is enabled or disabled. we take our best guess here.
759                // TODO(https://issues.fuchsia.dev/290372180): Make this
760                // check exact once link status is available via a FIDL
761                // API.
762                !properties.online
763            }
764        };
765
766        if done {
767            log_debug!("completed pending request; req = {pending_request:?}");
768
769            let PendingRequest { kind, client, completer } = pending_request;
770
771            respond_to_completer(client, completer, Ok(()), kind);
772            None
773        } else {
774            // Put the pending request back so that it can be handled later.
775            log_debug!("pending request not done yet; req = {pending_request:?}");
776            Some(pending_request)
777        }
778    }
779
780    /// Returns an admistrative control for the interface.
781    ///
782    /// Returns `None` if the interface is not known by the `EventLoop`.
783    fn get_interface_control(
784        &mut self,
785        interface_id: NonZeroU64,
786    ) -> Option<&fnet_interfaces_ext::admin::Control> {
787        let interface = self.interface_properties.get_mut(&interface_id.get())?;
788
789        Some(interface.state.control(&self.interfaces_proxy, interface_id))
790    }
791
792    // Get the associated `PropertiesAndState` for the given `LinkSpecifier`
793    fn get_link(
794        &self,
795        specifier: LinkSpecifier,
796    ) -> Option<
797        &fnet_interfaces_ext::PropertiesAndState<InterfaceState, fnet_interfaces_ext::AllInterest>,
798    > {
799        match specifier {
800            LinkSpecifier::Index(id) => self.interface_properties.get(&id.get().into()),
801            LinkSpecifier::Name(name) => self.interface_properties.values().find(
802                |fnet_interfaces_ext::PropertiesAndState { properties, state: _ }| {
803                    properties.name == name
804                },
805            ),
806        }
807    }
808
809    /// Handles a "RTM_GETLINK" request.
810    ///
811    /// The resulting "RTM_NEWLINK" messages will be sent directly to the
812    /// provided 'client'.
813    fn handle_get_link_request(
814        &self,
815        args: GetLinkArgs,
816        sequence_number: u32,
817        client: &mut InternalClient<NetlinkRoute, S>,
818    ) -> Result<(), RequestError> {
819        let (is_dump, interfaces_iter) = match args {
820            GetLinkArgs::Dump => {
821                let ifaces = self.interface_properties.values();
822                (true, Either::Left(ifaces))
823            }
824            GetLinkArgs::Get(specifier) => {
825                let iface = self.get_link(specifier).ok_or(RequestError::UnrecognizedInterface)?;
826                (false, Either::Right(std::iter::once(iface)))
827            }
828        };
829
830        interfaces_iter
831            .filter_map(
832                |fnet_interfaces_ext::PropertiesAndState {
833                     properties,
834                     state:
835                         InterfaceState {
836                             addresses: _,
837                             link_address,
838                             control: _,
839                             accept_ra_rt_table: _,
840                         },
841                 }| {
842                    NetlinkLinkMessage::optionally_from(&properties, &link_address)
843                },
844            )
845            .for_each(|message| {
846                client.send_unicast(message.into_rtnl_new_link(sequence_number, is_dump))
847            });
848        Ok(())
849    }
850
851    /// Handles a "RTM_SETLINK" request.
852    async fn handle_set_link_request(
853        &mut self,
854        args: SetLinkArgs,
855    ) -> Result<Option<PendingRequestKind>, RequestError> {
856        let SetLinkArgs { link, enable } = args;
857        let id = self.get_link(link).ok_or(RequestError::UnrecognizedInterface)?.properties.id;
858
859        // NB: Only check if their is a change after verifying the provided
860        // interface is valid. This is for conformance with Linux which will
861        // return ENODEV for invalid devices, even if no-change was requested.
862        let Some(enable) = enable else { return Ok(None) };
863
864        let control = self.get_interface_control(id).ok_or(RequestError::UnrecognizedInterface)?;
865
866        if enable {
867            let _did_enable = control
868                .enable()
869                .await
870                .map_err(|e| {
871                    log_warn!("error enabling interface {id}: {e:?}");
872                    map_existing_interface_terminal_error(e, id)
873                })?
874                .map_err(|e: fnet_interfaces_admin::ControlEnableError| {
875                    // `ControlEnableError` is currently an empty flexible enum.
876                    // It's not possible to know what went wrong.
877                    log_error!("failed to enable interface {id} for unknown reason: {e:?}");
878                    RequestError::Unknown
879                })?;
880            // TODO(https://issues.fuchsia.dev/290372180): Synchronize this
881            // request with observed changes from the watcher, once link status
882            // is available via a FIDL API.
883            Ok(None)
884        } else {
885            let did_disable = control
886                .disable()
887                .await
888                .map_err(|e| {
889                    log_warn!("error disabling interface {id}: {e:?}");
890                    map_existing_interface_terminal_error(e, id)
891                })?
892                .map_err(|e: fnet_interfaces_admin::ControlDisableError| {
893                    // `ControlDisableError` is currently an empty flexible enum.
894                    // It's not possible to know what went wrong,
895                    log_error!("failed to disable interface {id} for unknown reason: {e:?}");
896                    RequestError::Unknown
897                })?;
898            Ok(did_disable.then_some(PendingRequestKind::DisableInterface(id)))
899        }
900    }
901
902    /// Handles a new address request.
903    ///
904    /// Returns the address and interface ID if the address was successfully
905    /// added so that the caller can make sure their local state (from the
906    /// interfaces watcher) has sent an event holding the added address.
907    async fn handle_new_address_request(
908        &mut self,
909        NewAddressArgs {
910            address_and_interface_id:
911                address_and_interface_id @ AddressAndInterfaceArgs { address, interface_id },
912            add_subnet_route,
913        }: NewAddressArgs,
914    ) -> Result<Option<AddressAndInterfaceArgs>, RequestError> {
915        let control = self
916            .get_interface_control(interface_id.into())
917            .ok_or(RequestError::UnrecognizedInterface)?;
918
919        let (asp, asp_server_end) =
920            fidl::endpoints::create_proxy::<fnet_interfaces_admin::AddressStateProviderMarker>();
921        control
922            .add_address(
923                &address.into_ext(),
924                &fnet_interfaces_admin::AddressParameters {
925                    // TODO(https://fxbug.dev/42074223): Update how we add subnet
926                    // routes for addresses.
927                    add_subnet_route: Some(add_subnet_route),
928                    ..fnet_interfaces_admin::AddressParameters::default()
929                },
930                asp_server_end,
931            )
932            .map_err(|e| {
933                log_warn!("error adding {address} to interface ({interface_id}): {e:?}");
934                map_existing_interface_terminal_error(e, interface_id.into())
935            })?;
936
937        // Detach the ASP so that the address's lifetime isn't bound to the
938        // client end of the ASP.
939        //
940        // We do this first because `assignment_state_stream` takes ownership
941        // of the ASP proxy.
942        asp.detach().unwrap_or_else(|e| {
943            // Likely failed because the address addition failed or it was
944            // immediately removed. Don't fail just yet because we need to check
945            // the assignment state & terminal error below.
946            log_warn!(
947                "error detaching ASP for {} on interface ({}): {:?}",
948                address,
949                interface_id,
950                e
951            )
952        });
953
954        match wait_for_address_added_event(&mut asp.take_event_stream()).await {
955            Ok(()) => {
956                log_debug!("{} added on interface ({})", address, interface_id);
957                Ok(Some(address_and_interface_id))
958            }
959            Err(e) => {
960                log_warn!(
961                    "error waiting for state update for {} on interface ({}): {:?}",
962                    address,
963                    interface_id,
964                    e,
965                );
966
967                Err(match e {
968                    AddressStateProviderError::AddressRemoved(reason) => match reason {
969                        AddressRemovalReason::Invalid | AddressRemovalReason::InvalidProperties => {
970                            RequestError::InvalidRequest
971                        }
972                        AddressRemovalReason::AlreadyAssigned => RequestError::AlreadyExists,
973                        reason @ (AddressRemovalReason::DadFailed
974                        | AddressRemovalReason::Forfeited
975                        | AddressRemovalReason::InterfaceRemoved
976                        | AddressRemovalReason::UserRemoved) => {
977                            // These errors are only returned when the address
978                            // is removed after it has been added. We have not
979                            // yet observed the initial state so these removal
980                            // reasons are unexpected.
981                            unreachable!(
982                                "expected netstack to send initial state before removing {} on interface ({}) with reason {:?}",
983                                address, interface_id, reason,
984                            )
985                        }
986                    },
987                    AddressStateProviderError::Fidl(e) => {
988                        if !e.is_closed() {
989                            log_error!(
990                                "unexpected ASP error when adding {} on interface ({}): {:?}",
991                                address,
992                                interface_id,
993                                e,
994                            )
995                        }
996
997                        RequestError::UnrecognizedInterface
998                    }
999                    AddressStateProviderError::ChannelClosed => {
1000                        // If the channel is closed, assume the interface was
1001                        // removed.
1002                        RequestError::UnrecognizedInterface
1003                    }
1004                })
1005            }
1006        }
1007    }
1008
1009    /// Handles a delete address request.
1010    ///
1011    /// Returns the address and interface ID if the address was successfully
1012    /// removed so that the caller can make sure their local state (from the
1013    /// interfaces watcher) has sent an event without the removed address.
1014    async fn handle_del_address_request(
1015        &mut self,
1016        DelAddressArgs {
1017            address_and_interface_id:
1018                address_and_interface_id @ AddressAndInterfaceArgs { address, interface_id },
1019        }: DelAddressArgs,
1020    ) -> Result<AddressAndInterfaceArgs, RequestError> {
1021        let control = self
1022            .get_interface_control(interface_id.into())
1023            .ok_or(RequestError::UnrecognizedInterface)?;
1024
1025        match control.remove_address(&address.into_ext()).await.map_err(|e| {
1026            log_warn!("error removing {address} from interface ({interface_id}): {e:?}");
1027            map_existing_interface_terminal_error(e, interface_id.into())
1028        })? {
1029            Ok(did_remove) => {
1030                if did_remove {
1031                    Ok(address_and_interface_id)
1032                } else {
1033                    Err(RequestError::AddressNotFound)
1034                }
1035            }
1036            Err(e) => {
1037                // `e` is a flexible FIDL enum so we cannot exhaustively match.
1038                let e: fnet_interfaces_admin::ControlRemoveAddressError = e;
1039                match e {
1040                    fnet_interfaces_admin::ControlRemoveAddressErrorUnknown!() => {
1041                        log_error!(
1042                            "unrecognized address removal error {:?} for address {} on interface ({})",
1043                            e,
1044                            address,
1045                            interface_id,
1046                        );
1047
1048                        // Assume the error was because the request was invalid.
1049                        Err(RequestError::InvalidRequest)
1050                    }
1051                }
1052            }
1053        }
1054    }
1055
1056    /// Handles a [`Request`].
1057    ///
1058    /// Returns a [`PendingRequest`] if state was updated and the caller needs
1059    /// to make sure the update has been propagated to the local state (the
1060    /// interfaces watcher has sent an event for our update).
1061    pub(crate) async fn handle_request(
1062        &mut self,
1063        Request { args, sequence_number, mut client, completer }: Request<S>,
1064    ) -> Option<PendingRequest<S>> {
1065        log_debug!("handling request {args:?} from {client}");
1066
1067        let result = match args.clone() {
1068            RequestArgs::Link(LinkRequestArgs::Get(args)) => {
1069                self.handle_get_link_request(args, sequence_number, &mut client)
1070            }
1071            RequestArgs::Link(LinkRequestArgs::Set(args)) => {
1072                match self.handle_set_link_request(args).await {
1073                    Ok(Some(kind)) => return Some(PendingRequest { kind, client, completer }),
1074                    Ok(None) => Ok(()),
1075                    Err(e) => Err(e),
1076                }
1077            }
1078            RequestArgs::Address(args) => match args {
1079                AddressRequestArgs::Get(args) => match args {
1080                    GetAddressArgs::Dump { ip_version_filter } => {
1081                        self.interface_properties
1082                            .values()
1083                            .map(|iface| iface.state.addresses.values())
1084                            .flatten()
1085                            .filter(|NetlinkAddressMessage(message)| {
1086                                ip_version_filter.map_or(true, |ip_version| {
1087                                    ip_version.eq(&match message.header.family() {
1088                                        AddressFamily::Inet => IpVersion::V4,
1089                                        AddressFamily::Inet6 => IpVersion::V6,
1090                                        family => unreachable!(
1091                                            "unexpected address family ({:?}); addr = {:?}",
1092                                            family, message,
1093                                        ),
1094                                    })
1095                                })
1096                            })
1097                            .for_each(|message| {
1098                                client.send_unicast(message.to_rtnl_new_addr(sequence_number, true))
1099                            });
1100                        Ok(())
1101                    }
1102                },
1103                AddressRequestArgs::New(args) => {
1104                    match self.handle_new_address_request(args).await {
1105                        Ok(None) => Ok(()),
1106                        Ok(Some(address_and_interface_id)) => {
1107                            return Some(PendingRequest {
1108                                kind: PendingRequestKind::AddAddress(address_and_interface_id),
1109                                client,
1110                                completer,
1111                            });
1112                        }
1113                        Err(e) => Err(e),
1114                    }
1115                }
1116                AddressRequestArgs::Del(args) => {
1117                    match self.handle_del_address_request(args).await {
1118                        Ok(address_and_interface_id) => {
1119                            return Some(PendingRequest {
1120                                kind: PendingRequestKind::DelAddress(address_and_interface_id),
1121                                client,
1122                                completer,
1123                            });
1124                        }
1125                        Err(e) => Err(e),
1126                    }
1127                }
1128            },
1129        };
1130
1131        log_debug!("handled request {args:?} from {client} with result = {result:?}");
1132        respond_to_completer(client, completer, result, args);
1133        None
1134    }
1135}
1136
1137fn update_addresses<S: Sender<<NetlinkRoute as ProtocolFamily>::Response>>(
1138    existing_addresses: &mut BTreeMap<fnet::IpAddress, NetlinkAddressMessage>,
1139    updated_addresses: BTreeMap<fnet::IpAddress, NetlinkAddressMessage>,
1140    route_clients: &ClientTable<NetlinkRoute, S>,
1141) {
1142    enum UpdateKind {
1143        New,
1144        Del,
1145    }
1146
1147    let send_update = |addr: &NetlinkAddressMessage, kind| {
1148        let NetlinkAddressMessage(inner) = addr;
1149        let group = match inner.header.family() {
1150            AddressFamily::Inet => rtnetlink_groups_RTNLGRP_IPV4_IFADDR,
1151            AddressFamily::Inet6 => rtnetlink_groups_RTNLGRP_IPV6_IFADDR,
1152            family => {
1153                unreachable!("unrecognized interface address family ({family:?}); addr = {addr:?}")
1154            }
1155        };
1156
1157        let message = match kind {
1158            UpdateKind::New => addr.to_rtnl_new_addr(UNSPECIFIED_SEQUENCE_NUMBER, false),
1159            UpdateKind::Del => addr.to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
1160        };
1161
1162        route_clients.send_message_to_group(message, ModernGroup(group));
1163    };
1164
1165    // Send a message to interested listeners only if the address is newly added
1166    // or its message has changed.
1167    for (key, message) in updated_addresses.iter() {
1168        if existing_addresses.get(key) != Some(message) {
1169            send_update(message, UpdateKind::New)
1170        }
1171    }
1172
1173    existing_addresses.retain(|addr, message| {
1174        // If the address exists in the latest update, keep it. If it was
1175        // updated, we will update this map with the updated values below.
1176        if updated_addresses.contains_key(addr) {
1177            return true;
1178        }
1179
1180        // The address is not present in the interfaces latest update so it
1181        // has been deleted.
1182        send_update(message, UpdateKind::Del);
1183
1184        false
1185    });
1186
1187    // Update our set of existing addresses with the latest set known to be
1188    // assigned to the interface.
1189    existing_addresses.extend(updated_addresses);
1190}
1191
1192/// A wrapper type for the netlink_packet_route `LinkMessage` to enable conversions
1193/// from [`fnet_interfaces_ext::Properties`]. The addresses component of this
1194/// struct will be handled separately.
1195#[derive(Clone, Debug, Eq, PartialEq)]
1196pub(crate) struct NetlinkLinkMessage(LinkMessage);
1197
1198impl NetlinkLinkMessage {
1199    fn optionally_from(
1200        properties: &fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>,
1201        link_address: &Option<Vec<u8>>,
1202    ) -> Option<Self> {
1203        match interface_properties_to_link_message(properties, link_address) {
1204            Ok(o) => Some(o),
1205            Err(NetlinkLinkMessageConversionError::InvalidInterfaceId(id)) => {
1206                log_warn!("Invalid interface id: {:?}", id);
1207                None
1208            }
1209        }
1210    }
1211
1212    pub(crate) fn into_rtnl_new_link(
1213        self,
1214        sequence_number: u32,
1215        is_dump: bool,
1216    ) -> NetlinkMessage<RouteNetlinkMessage> {
1217        let Self(message) = self;
1218        let mut msg: NetlinkMessage<RouteNetlinkMessage> =
1219            RouteNetlinkMessage::NewLink(message).into();
1220        msg.header.sequence_number = sequence_number;
1221        if is_dump {
1222            msg.header.flags |= NLM_F_MULTIPART;
1223        }
1224        msg.finalize();
1225        msg
1226    }
1227
1228    fn into_rtnl_del_link(self, sequence_number: u32) -> NetlinkMessage<RouteNetlinkMessage> {
1229        let Self(message) = self;
1230        let mut msg: NetlinkMessage<RouteNetlinkMessage> =
1231            RouteNetlinkMessage::DelLink(message).into();
1232        msg.header.sequence_number = sequence_number;
1233        msg.finalize();
1234        msg
1235    }
1236}
1237
1238// NetlinkLinkMessage conversion related errors.
1239#[derive(Debug, PartialEq)]
1240pub(crate) enum NetlinkLinkMessageConversionError {
1241    // Interface id could not be downcasted to fit into the expected u32.
1242    InvalidInterfaceId(u64),
1243}
1244
1245fn port_class_to_link_type(port_class: fnet_interfaces_ext::PortClass) -> u16 {
1246    match port_class {
1247        fnet_interfaces_ext::PortClass::Loopback => ARPHRD_LOOPBACK,
1248        fnet_interfaces_ext::PortClass::Blackhole => ARPHRD_VOID,
1249        fnet_interfaces_ext::PortClass::Ethernet
1250        | fnet_interfaces_ext::PortClass::Bridge
1251        | fnet_interfaces_ext::PortClass::WlanClient
1252        | fnet_interfaces_ext::PortClass::WlanAp => ARPHRD_ETHER,
1253        fnet_interfaces_ext::PortClass::Ppp => ARPHRD_PPP,
1254        // NB: Virtual devices on fuchsia are overloaded. This may be a
1255        // tun/tap/no-op interface. Return `ARPHRD_VOID` since we have
1256        // insufficient information to precisely classify the link_type.
1257        fnet_interfaces_ext::PortClass::Virtual => ARPHRD_VOID,
1258        fnet_interfaces_ext::PortClass::Lowpan => ARPHRD_6LOWPAN,
1259    }
1260    .try_into()
1261    .expect("potential values will fit into the u16 range")
1262}
1263
1264// Netstack only reports 'online' when the 'admin status' is 'enabled' and the 'link
1265// state' is UP. IFF_RUNNING represents only `link state` UP, so it is likely that
1266// there will be cases where a flag should be set to IFF_RUNNING but we can not make
1267// the determination with the information provided.
1268//
1269// Per https://www.kernel.org/doc/html/latest/networking/operstates.html#querying-from-userspace,
1270//
1271//   Administrative state is the result of "ip link set dev <dev> up or down" and
1272//   reflects whether the administrator wants to use the device for traffic. [...]
1273//   Operational state shows the ability of an interface to transmit this user data.
1274//
1275//   Both admin and operational state can be queried via the netlink operation
1276//   RTM_GETLINK. It is also possible to subscribe to RTNLGRP_LINK to be
1277//   notified of updates while the interface is admin up. This is important for
1278//   setting from userspace.
1279//
1280//   These values contain interface state:
1281//
1282//   ifinfomsg::if_flags & IFF_UP:
1283//       Interface is admin up
1284//
1285//   ifinfomsg::if_flags & IFF_RUNNING:
1286//       Interface is in RFC2863 operational state UP or UNKNOWN. This is for
1287//       backward compatibility, routing daemons, dhcp clients can use this flag
1288//       to determine whether they should use the interface.
1289//
1290//   ifinfomsg::if_flags & IFF_LOWER_UP:
1291//       Driver has signaled netif_carrier_on()
1292//
1293//   ...
1294const ONLINE_IF_FLAGS: u32 =
1295    net_device_flags_IFF_UP | net_device_flags_IFF_RUNNING | net_device_flags_IFF_LOWER_UP;
1296
1297// Implement conversions from `Properties` to `NetlinkLinkMessage`
1298// which is fallible iff, the interface has an id greater than u32.
1299fn interface_properties_to_link_message(
1300    fnet_interfaces_ext::Properties {
1301        id,
1302        name,
1303        port_class,
1304        online,
1305        addresses: _,
1306        has_default_ipv4_route: _,
1307        has_default_ipv6_route: _,
1308        port_identity_koid: _,
1309    }: &fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>,
1310    link_address: &Option<Vec<u8>>,
1311) -> Result<NetlinkLinkMessage, NetlinkLinkMessageConversionError> {
1312    let online = *online;
1313    let mut link_header = LinkHeader::default();
1314
1315    link_header.interface_family = AddressFamily::Unspec;
1316
1317    // We expect interface ids to safely fit in the range of u32 values.
1318    let id: u32 = match id.get().try_into() {
1319        Err(std::num::TryFromIntError { .. }) => {
1320            return Err(NetlinkLinkMessageConversionError::InvalidInterfaceId(id.clone().into()));
1321        }
1322        Ok(id) => id,
1323    };
1324    link_header.index = id;
1325
1326    let link_layer_type = port_class_to_link_type(*port_class);
1327    link_header.link_layer_type = LinkLayerType::from(link_layer_type);
1328
1329    let mut flags = 0;
1330    if online {
1331        flags |= ONLINE_IF_FLAGS;
1332    };
1333    if link_header.link_layer_type == LinkLayerType::Loopback {
1334        flags |= net_device_flags_IFF_LOOPBACK;
1335    };
1336    if *port_class == fnet_interfaces_ext::PortClass::WlanClient {
1337        // Upstream consumers expect WLAN interfaces to be "administratively up" even when
1338        // they're disconnected. Since we don't currently distinguish between device-layer
1339        // (adminitstrative) enablement and IP-layer enablement, we'll unconditionally
1340        // mark all WLAN interfaces as IFF_UP (administratively up).
1341        // TODO(b/290372180): Determine the actual device enablement status.
1342        flags |= net_device_flags_IFF_UP;
1343    }
1344
1345    // SAFETY: This and the following .unwrap() are safe as LinkFlags
1346    // can hold any valid u32.
1347    link_header.flags = LinkFlags::from_bits(flags).unwrap();
1348
1349    // As per netlink_package_route and rtnetlink documentation, this should be set to
1350    // `0xffff_ffff` and reserved for future use.
1351    link_header.change_mask = LinkFlags::from_bits(u32::MAX).unwrap();
1352
1353    // The NLA order follows the list that attributes are listed on the
1354    // rtnetlink man page.
1355    // The following fields are used in the options in the NLA, but they do
1356    // not have any corresponding values in `fnet_interfaces_ext::Properties`.
1357    //
1358    // IFLA_BROADCAST
1359    // IFLA_MTU
1360    // IFLA_LINK
1361    // IFLA_QDISC
1362    // IFLA_STATS
1363    //
1364    // There are other NLAs observed via the netlink_packet_route crate, and do
1365    // not have corresponding values in `fnet_interfaces_ext::Properties`.
1366    // This list is documented within issuetracker.google.com/283137644.
1367    let nlas = [
1368        LinkAttribute::IfName(name.clone()),
1369        LinkAttribute::Link(link_layer_type.into()),
1370        // Netstack only exposes enough state to determine between `Up` and `Down`
1371        // operating state.
1372        LinkAttribute::OperState(if online { State::Up } else { State::Down }),
1373    ]
1374    .into_iter()
1375    // If the interface has a link-address, include it in the NLAs.
1376    .chain(link_address.clone().map(LinkAttribute::Address))
1377    .collect();
1378
1379    let mut link_message = LinkMessage::default();
1380    link_message.header = link_header;
1381    link_message.attributes = nlas;
1382
1383    return Ok(NetlinkLinkMessage(link_message));
1384}
1385
1386/// A wrapper type for the netlink_packet_route `AddressMessage` to enable conversions
1387/// from [`fnet_interfaces_ext::Properties`] and implement hashing.
1388#[derive(Clone, Debug, Eq, PartialEq)]
1389pub(crate) struct NetlinkAddressMessage(AddressMessage);
1390
1391impl NetlinkAddressMessage {
1392    pub(crate) fn to_rtnl_new_addr(
1393        &self,
1394        sequence_number: u32,
1395        is_dump: bool,
1396    ) -> NetlinkMessage<RouteNetlinkMessage> {
1397        let Self(message) = self;
1398        let mut message: NetlinkMessage<RouteNetlinkMessage> =
1399            RouteNetlinkMessage::NewAddress(message.clone()).into();
1400        message.header.sequence_number = sequence_number;
1401        if is_dump {
1402            message.header.flags |= NLM_F_MULTIPART;
1403        }
1404        message.finalize();
1405        message
1406    }
1407
1408    pub(crate) fn to_rtnl_del_addr(
1409        &self,
1410        sequence_number: u32,
1411    ) -> NetlinkMessage<RouteNetlinkMessage> {
1412        let Self(message) = self;
1413        let mut message: NetlinkMessage<RouteNetlinkMessage> =
1414            RouteNetlinkMessage::DelAddress(message.clone()).into();
1415        message.header.sequence_number = sequence_number;
1416        message.finalize();
1417        message
1418    }
1419}
1420
1421// NetlinkAddressMessage conversion related errors.
1422#[derive(Debug, PartialEq)]
1423enum NetlinkAddressMessageConversionError {
1424    // Interface id could not be downcasted to fit into the expected u32.
1425    InvalidInterfaceId(u64),
1426}
1427
1428fn addresses_optionally_from_interface_properties(
1429    properties: &fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>,
1430) -> Option<BTreeMap<fnet::IpAddress, NetlinkAddressMessage>> {
1431    match interface_properties_to_address_messages(properties) {
1432        Ok(o) => Some(o),
1433        Err(NetlinkAddressMessageConversionError::InvalidInterfaceId(id)) => {
1434            log_warn!("Invalid interface id: {:?}", id);
1435            None
1436        }
1437    }
1438}
1439
1440// Implement conversions from `Properties` to `Vec<NetlinkAddressMessage>`
1441// which is fallible iff, the interface has an id greater than u32.
1442fn interface_properties_to_address_messages(
1443    fnet_interfaces_ext::Properties {
1444        id,
1445        name,
1446        addresses,
1447        port_class: _,
1448        online: _,
1449        has_default_ipv4_route: _,
1450        has_default_ipv6_route: _,
1451        port_identity_koid: _,
1452    }: &fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>,
1453) -> Result<BTreeMap<fnet::IpAddress, NetlinkAddressMessage>, NetlinkAddressMessageConversionError>
1454{
1455    // We expect interface ids to safely fit in the range of the u32 values.
1456    let id: u32 = match id.get().try_into() {
1457        Err(std::num::TryFromIntError { .. }) => {
1458            return Err(NetlinkAddressMessageConversionError::InvalidInterfaceId(
1459                id.clone().into(),
1460            ));
1461        }
1462        Ok(id) => id,
1463    };
1464
1465    let address_messages = addresses
1466        .into_iter()
1467        .map(
1468            |fnet_interfaces_ext::Address {
1469                 addr: fnet::Subnet { addr, prefix_len },
1470                 valid_until: _,
1471                 preferred_lifetime_info: _,
1472                 assignment_state,
1473             }| {
1474                let mut addr_header = AddressHeader::default();
1475
1476                let (family, addr_bytes) = match addr {
1477                    fnet::IpAddress::Ipv4(ip_addr) => {
1478                        (AddressFamily::Inet, IpAddr::V4(ip_addr.addr.into()))
1479                    }
1480                    fnet::IpAddress::Ipv6(ip_addr) => {
1481                        (AddressFamily::Inet6, IpAddr::V6(ip_addr.addr.into()))
1482                    }
1483                };
1484
1485                // The possible constants below are in the range of u8-accepted values, so they can
1486                // be safely casted to a u8.
1487                addr_header.family = family.into();
1488                addr_header.prefix_len = *prefix_len;
1489
1490                // TODO(https://issues.fuchsia.dev/284980862): Determine proper
1491                // mapping from Netstack properties to address flags.
1492                let flags = AddressHeaderFlags::Permanent
1493                    | match assignment_state {
1494                        fnet_interfaces::AddressAssignmentState::Assigned => {
1495                            AddressHeaderFlags::empty()
1496                        }
1497                        fnet_interfaces::AddressAssignmentState::Tentative
1498                        | fnet_interfaces::AddressAssignmentState::Unavailable => {
1499                            // There is no equivalent `IFA_F_` flag for
1500                            // `Unavailable` so we treat it as tentative to
1501                            // signal that the address is installed but not
1502                            // considered assigned.
1503                            AddressHeaderFlags::Tentative
1504                        }
1505                    };
1506                addr_header.flags = flags.bits();
1507                addr_header.index = id.into();
1508
1509                // The NLA order follows the list that attributes are listed on the
1510                // rtnetlink man page.
1511                // The following fields are used in the options in the NLA, but they do
1512                // not have any corresponding values in `fnet_interfaces_ext::Properties` or
1513                // `fnet_interfaces_ext::Address`.
1514                //
1515                // IFA_LOCAL
1516                // IFA_BROADCAST
1517                // IFA_ANYCAST
1518                // IFA_CACHEINFO
1519                //
1520                // IFA_MULTICAST is documented via the netlink_packet_route crate but is not
1521                // present on the rtnetlink page.
1522                let nlas = vec![
1523                    AddressAttribute::Address(addr_bytes),
1524                    AddressAttribute::Label(name.clone()),
1525                    // SAFETY: This unwrap is safe because AddressFlags overlaps with
1526                    // AddressHeaderFlags.
1527                    AddressAttribute::Flags(AddressFlags::from_bits(flags.bits().into()).unwrap()),
1528                ];
1529
1530                let mut addr_message = AddressMessage::default();
1531                addr_message.header = addr_header;
1532                addr_message.attributes = nlas;
1533                (addr.clone(), NetlinkAddressMessage(addr_message))
1534            },
1535        )
1536        .collect();
1537
1538    Ok(address_messages)
1539}
1540
1541#[cfg(test)]
1542pub(crate) mod testutil {
1543    use super::*;
1544
1545    use fuchsia_sync::Mutex;
1546    use std::convert::Infallible as Never;
1547    use std::sync::Arc;
1548
1549    use futures::TryStreamExt as _;
1550    use futures::channel::mpsc;
1551    use futures::future::Future;
1552    use futures::stream::Stream;
1553    use net_declare::{fidl_subnet, net_addr_subnet};
1554
1555    use crate::client::AsyncWorkItem;
1556    use crate::messaging::testutil::FakeSender;
1557    use crate::protocol_family::route::NetlinkRouteNotifiedGroup;
1558    use crate::route_eventloop::{EventLoopComponent, IncludedWorkers, Optional, Required};
1559
1560    pub(crate) const LO_INTERFACE_ID: u64 = 1;
1561    pub(crate) const LO_NAME: &str = "lo";
1562    pub(crate) const ETH_INTERFACE_ID: u64 = 2;
1563    pub(crate) const ETH_NAME: &str = "eth";
1564    pub(crate) const WLAN_INTERFACE_ID: u64 = 3;
1565    pub(crate) const WLAN_NAME: &str = "wlan";
1566    pub(crate) const PPP_INTERFACE_ID: u64 = 4;
1567    pub(crate) const PPP_NAME: &str = "ppp";
1568
1569    pub(crate) const BRIDGE: fnet_interfaces_ext::PortClass =
1570        fnet_interfaces_ext::PortClass::Bridge;
1571    pub(crate) const ETHERNET: fnet_interfaces_ext::PortClass =
1572        fnet_interfaces_ext::PortClass::Ethernet;
1573    pub(crate) const WLAN_CLIENT: fnet_interfaces_ext::PortClass =
1574        fnet_interfaces_ext::PortClass::WlanClient;
1575    pub(crate) const WLAN_AP: fnet_interfaces_ext::PortClass =
1576        fnet_interfaces_ext::PortClass::WlanAp;
1577    pub(crate) const PPP: fnet_interfaces_ext::PortClass = fnet_interfaces_ext::PortClass::Ppp;
1578    pub(crate) const LOOPBACK: fnet_interfaces_ext::PortClass =
1579        fnet_interfaces_ext::PortClass::Loopback;
1580    pub(crate) const TEST_V4_ADDR: fnet::Subnet = fidl_subnet!("192.0.2.1/24");
1581    pub(crate) const TEST_V6_ADDR: fnet::Subnet = fidl_subnet!("2001:db8::1/32");
1582
1583    // AddrSubnetEither does not have any const methods so we need a method.
1584    pub(crate) fn test_addr_subnet_v4() -> AddrSubnetEither {
1585        net_addr_subnet!("192.0.2.1/24")
1586    }
1587
1588    // AddrSubnetEither does not have any const methods so we need a method.
1589    pub(crate) fn test_addr_subnet_v6() -> AddrSubnetEither {
1590        net_addr_subnet!("2001:db8::1/32")
1591    }
1592
1593    // AddrSubnetEither does not have any const methods so we need a method.
1594    pub(crate) fn add_test_addr_subnet_v4() -> AddrSubnetEither {
1595        net_addr_subnet!("192.0.2.2/24")
1596    }
1597
1598    // AddrSubnetEither does not have any const methods so we need a method.
1599    pub(crate) fn add_test_addr_subnet_v6() -> AddrSubnetEither {
1600        net_addr_subnet!("2001:db8::2/32")
1601    }
1602
1603    #[derive(Debug, PartialEq, Eq)]
1604    pub(crate) enum HandledLinkKind {
1605        New,
1606        Del,
1607    }
1608
1609    #[derive(Debug, PartialEq, Eq)]
1610    pub(crate) struct HandledLink {
1611        pub name: String,
1612        pub kind: HandledLinkKind,
1613    }
1614
1615    pub(crate) struct FakeInterfacesHandlerSink(Arc<Mutex<Vec<HandledLink>>>);
1616
1617    impl FakeInterfacesHandlerSink {
1618        pub(crate) fn take_handled(&mut self) -> Vec<HandledLink> {
1619            let Self(rc) = self;
1620            core::mem::take(&mut *rc.lock())
1621        }
1622    }
1623
1624    pub(crate) struct FakeInterfacesHandler(Arc<Mutex<Vec<HandledLink>>>);
1625
1626    impl FakeInterfacesHandler {
1627        pub(crate) fn new() -> (FakeInterfacesHandler, FakeInterfacesHandlerSink) {
1628            let inner = Arc::default();
1629            (FakeInterfacesHandler(Arc::clone(&inner)), FakeInterfacesHandlerSink(inner))
1630        }
1631    }
1632
1633    impl InterfacesHandler for FakeInterfacesHandler {
1634        fn handle_new_link(&mut self, name: &str, _interface_id: NonZeroU64) {
1635            let Self(rc) = self;
1636            rc.lock().push(HandledLink { name: name.to_string(), kind: HandledLinkKind::New })
1637        }
1638
1639        fn handle_deleted_link(&mut self, name: &str) {
1640            let Self(rc) = self;
1641            rc.lock().push(HandledLink { name: name.to_string(), kind: HandledLinkKind::Del })
1642        }
1643    }
1644
1645    enum OnlyInterfaces {}
1646    impl crate::route_eventloop::EventLoopSpec for OnlyInterfaces {
1647        type InterfacesProxy = Required;
1648        type InterfacesStateProxy = Required;
1649        type InterfacesHandler = Required;
1650        type RouteClients = Required;
1651
1652        type V4RoutesState = Optional;
1653        type V6RoutesState = Optional;
1654        type V4RoutesSetProvider = Optional;
1655        type V6RoutesSetProvider = Optional;
1656        type V4RouteTableProvider = Optional;
1657        type V6RouteTableProvider = Optional;
1658
1659        type InterfacesWorker = Required;
1660        type RoutesV4Worker = Optional;
1661        type RoutesV6Worker = Optional;
1662        type RuleV4Worker = Optional;
1663        type RuleV6Worker = Optional;
1664        type NduseroptWorker = Optional;
1665    }
1666
1667    pub(crate) struct Setup<E, W> {
1668        pub event_loop_fut: E,
1669        pub watcher_stream: W,
1670        pub request_sink:
1671            mpsc::Sender<crate::route_eventloop::UnifiedRequest<FakeSender<RouteNetlinkMessage>>>,
1672        pub interfaces_request_stream: fnet_root::InterfacesRequestStream,
1673        pub interfaces_handler_sink: FakeInterfacesHandlerSink,
1674        pub _async_work_sink: mpsc::UnboundedSender<AsyncWorkItem<NetlinkRouteNotifiedGroup>>,
1675    }
1676
1677    pub(crate) fn setup_with_route_clients(
1678        route_clients: ClientTable<NetlinkRoute, FakeSender<RouteNetlinkMessage>>,
1679    ) -> Setup<impl Future<Output = Never>, impl Stream<Item = fnet_interfaces::WatcherRequest>>
1680    {
1681        let (request_sink, request_stream) = mpsc::channel(1);
1682        let (interfaces_handler, interfaces_handler_sink) = FakeInterfacesHandler::new();
1683        let (interfaces_proxy, interfaces) =
1684            fidl::endpoints::create_proxy::<fnet_root::InterfacesMarker>();
1685        let (interfaces_state_proxy, interfaces_state) =
1686            fidl::endpoints::create_proxy::<fnet_interfaces::StateMarker>();
1687        let (async_work_sink, async_work_receiver) = mpsc::unbounded();
1688        let event_loop_inputs = crate::route_eventloop::EventLoopInputs::<_, _, OnlyInterfaces> {
1689            route_clients: EventLoopComponent::Present(route_clients),
1690            interfaces_handler: EventLoopComponent::Present(interfaces_handler),
1691            interfaces_proxy: EventLoopComponent::Present(interfaces_proxy),
1692            interfaces_state_proxy: EventLoopComponent::Present(interfaces_state_proxy),
1693            async_work_receiver,
1694
1695            v4_routes_state: EventLoopComponent::Absent(Optional),
1696            v6_routes_state: EventLoopComponent::Absent(Optional),
1697            v4_main_route_table: EventLoopComponent::Absent(Optional),
1698            v6_main_route_table: EventLoopComponent::Absent(Optional),
1699            v4_route_table_provider: EventLoopComponent::Absent(Optional),
1700            v6_route_table_provider: EventLoopComponent::Absent(Optional),
1701            v4_rule_table: EventLoopComponent::Absent(Optional),
1702            v6_rule_table: EventLoopComponent::Absent(Optional),
1703            ndp_option_watcher_provider: EventLoopComponent::Absent(Optional),
1704
1705            unified_request_stream: request_stream,
1706        };
1707
1708        let interfaces_request_stream = interfaces.into_stream();
1709        let if_stream = interfaces_state.into_stream();
1710        let watcher_stream = if_stream
1711            .and_then(|req| match req {
1712                fnet_interfaces::StateRequest::GetWatcher {
1713                    options: _,
1714                    watcher,
1715                    control_handle: _,
1716                } => futures::future::ready(Ok(watcher.into_stream())),
1717            })
1718            .try_flatten()
1719            .map(|res| res.expect("watcher stream error"));
1720
1721        Setup {
1722            event_loop_fut: async move {
1723                let event_loop = event_loop_inputs
1724                    .initialize(IncludedWorkers {
1725                        interfaces: EventLoopComponent::Present(()),
1726                        routes_v4: EventLoopComponent::Absent(Optional),
1727                        routes_v6: EventLoopComponent::Absent(Optional),
1728                        rules_v4: EventLoopComponent::Absent(Optional),
1729                        rules_v6: EventLoopComponent::Absent(Optional),
1730                        nduseropt: EventLoopComponent::Absent(Optional),
1731                    })
1732                    .await;
1733                event_loop.run().await
1734            },
1735            watcher_stream,
1736            request_sink,
1737            interfaces_request_stream,
1738            interfaces_handler_sink,
1739            _async_work_sink: async_work_sink,
1740        }
1741    }
1742
1743    pub(crate) async fn respond_to_watcher<S: Stream<Item = fnet_interfaces::WatcherRequest>>(
1744        stream: S,
1745        updates: impl IntoIterator<Item = fnet_interfaces::Event>,
1746    ) {
1747        stream
1748            .zip(futures::stream::iter(updates.into_iter()))
1749            .for_each(|(req, update)| async move {
1750                match req {
1751                    fnet_interfaces::WatcherRequest::Watch { responder } => {
1752                        responder.send(&update).expect("send watch response")
1753                    }
1754                }
1755            })
1756            .await
1757    }
1758
1759    pub(crate) fn create_netlink_link_message(
1760        id: u64,
1761        link_type: u16,
1762        flags: u32,
1763        nlas: Vec<LinkAttribute>,
1764    ) -> NetlinkLinkMessage {
1765        let mut link_header = LinkHeader::default();
1766        link_header.index = id.try_into().expect("should fit into u32");
1767        link_header.link_layer_type = LinkLayerType::from(link_type);
1768        link_header.flags = LinkFlags::from_bits(flags).unwrap();
1769        link_header.change_mask = LinkFlags::from_bits(u32::MAX).unwrap();
1770
1771        let mut link_message = LinkMessage::default();
1772        link_message.header = link_header;
1773        link_message.attributes = nlas;
1774
1775        NetlinkLinkMessage(link_message)
1776    }
1777
1778    pub(crate) fn create_nlas(
1779        name: String,
1780        link_type: u16,
1781        online: bool,
1782        mac: &Option<fnet::MacAddress>,
1783    ) -> Vec<LinkAttribute> {
1784        [
1785            LinkAttribute::IfName(name),
1786            LinkAttribute::Link(link_type.into()),
1787            LinkAttribute::OperState(if online { State::Up } else { State::Down }),
1788        ]
1789        .into_iter()
1790        .chain(mac.map(|fnet::MacAddress { octets }| LinkAttribute::Address(octets.to_vec())))
1791        .collect()
1792    }
1793
1794    pub(crate) fn create_address_message(
1795        interface_id: u32,
1796        subnet: fnet::Subnet,
1797        interface_name: String,
1798        flags: u32,
1799    ) -> NetlinkAddressMessage {
1800        let mut addr_header = AddressHeader::default();
1801        let (family, addr) = match subnet.addr {
1802            fnet::IpAddress::Ipv4(ip_addr) => {
1803                (AddressFamily::Inet, IpAddr::V4(ip_addr.addr.into()))
1804            }
1805            fnet::IpAddress::Ipv6(ip_addr) => {
1806                (AddressFamily::Inet6, IpAddr::V6(ip_addr.addr.into()))
1807            }
1808        };
1809        addr_header.family = family.into();
1810        addr_header.prefix_len = subnet.prefix_len;
1811        addr_header.flags = AddressHeaderFlags::from_bits(flags as u8).unwrap().bits();
1812        addr_header.index = interface_id.into();
1813
1814        let nlas = vec![
1815            AddressAttribute::Address(addr),
1816            AddressAttribute::Label(interface_name),
1817            AddressAttribute::Flags(AddressFlags::from_bits(flags).unwrap()),
1818        ];
1819
1820        let mut addr_message = AddressMessage::default();
1821        addr_message.header = addr_header;
1822        addr_message.attributes = nlas;
1823        NetlinkAddressMessage(addr_message)
1824    }
1825
1826    pub(crate) fn test_addr_with_assignment_state(
1827        addr: fnet::Subnet,
1828        assignment_state: fnet_interfaces::AddressAssignmentState,
1829    ) -> fnet_interfaces::Address {
1830        fnet_interfaces_ext::Address::<fnet_interfaces_ext::AllInterest> {
1831            addr,
1832            valid_until: fnet_interfaces_ext::PositiveMonotonicInstant::INFINITE_FUTURE,
1833            preferred_lifetime_info: fnet_interfaces_ext::PreferredLifetimeInfo::preferred_forever(
1834            ),
1835            assignment_state,
1836        }
1837        .into()
1838    }
1839
1840    pub(crate) fn test_addr(addr: fnet::Subnet) -> fnet_interfaces::Address {
1841        test_addr_with_assignment_state(addr, fnet_interfaces::AddressAssignmentState::Assigned)
1842    }
1843}
1844
1845#[cfg(test)]
1846mod tests {
1847    use super::testutil::*;
1848    use super::*;
1849
1850    use std::pin::{Pin, pin};
1851
1852    use fidl::endpoints::{ControlHandle as _, RequestStream as _, Responder as _};
1853    use fidl_fuchsia_net as fnet;
1854    use fnet_interfaces::AddressAssignmentState;
1855    use fuchsia_async::{self as fasync};
1856
1857    use assert_matches::assert_matches;
1858    use futures::FutureExt as _;
1859    use futures::sink::SinkExt as _;
1860    use futures::stream::Stream;
1861    use linux_uapi::{IFA_F_PERMANENT, IFA_F_TENTATIVE, rtnetlink_groups_RTNLGRP_IPV4_ROUTE};
1862    use pretty_assertions::assert_eq;
1863    use test_case::test_case;
1864
1865    use crate::messaging::testutil::SentMessage;
1866
1867    const TEST_SEQUENCE_NUMBER: u32 = 1234;
1868
1869    fn create_interface(
1870        id: u64,
1871        name: String,
1872        port_class: fnet_interfaces_ext::PortClass,
1873        online: bool,
1874        addresses: Vec<fnet_interfaces_ext::Address<fnet_interfaces_ext::AllInterest>>,
1875    ) -> fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest> {
1876        fnet_interfaces_ext::Properties {
1877            id: NonZeroU64::new(id).unwrap(),
1878            name,
1879            port_class,
1880            online,
1881            addresses,
1882            has_default_ipv4_route: false,
1883            has_default_ipv6_route: false,
1884            port_identity_koid: None,
1885        }
1886    }
1887
1888    fn create_interface_with_addresses(
1889        id: u64,
1890        name: String,
1891        port_class: fnet_interfaces_ext::PortClass,
1892        online: bool,
1893    ) -> fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest> {
1894        let addresses = vec![
1895            fnet_interfaces_ext::Address {
1896                addr: TEST_V4_ADDR,
1897                valid_until: fnet_interfaces_ext::PositiveMonotonicInstant::INFINITE_FUTURE,
1898                assignment_state: AddressAssignmentState::Assigned,
1899                preferred_lifetime_info:
1900                    fnet_interfaces_ext::PreferredLifetimeInfo::preferred_forever(),
1901            },
1902            fnet_interfaces_ext::Address {
1903                addr: TEST_V6_ADDR,
1904                valid_until: fnet_interfaces_ext::PositiveMonotonicInstant::INFINITE_FUTURE,
1905                assignment_state: AddressAssignmentState::Assigned,
1906                preferred_lifetime_info:
1907                    fnet_interfaces_ext::PreferredLifetimeInfo::preferred_forever(),
1908            },
1909        ];
1910        create_interface(id, name, port_class, online, addresses)
1911    }
1912
1913    fn create_default_address_messages(
1914        interface_id: u64,
1915        interface_name: String,
1916        flags: u32,
1917    ) -> BTreeMap<fnet::IpAddress, NetlinkAddressMessage> {
1918        let interface_id = interface_id.try_into().expect("should fit into u32");
1919        BTreeMap::from_iter([
1920            (
1921                TEST_V4_ADDR.addr,
1922                create_address_message(interface_id, TEST_V4_ADDR, interface_name.clone(), flags),
1923            ),
1924            (
1925                TEST_V6_ADDR.addr,
1926                create_address_message(interface_id, TEST_V6_ADDR, interface_name, flags),
1927            ),
1928        ])
1929    }
1930
1931    #[test_case(ETHERNET, false, 0, ARPHRD_ETHER)]
1932    #[test_case(ETHERNET, true, ONLINE_IF_FLAGS, ARPHRD_ETHER)]
1933    #[test_case(WLAN_CLIENT, false, net_device_flags_IFF_UP, ARPHRD_ETHER)]
1934    #[test_case(WLAN_CLIENT, true, ONLINE_IF_FLAGS, ARPHRD_ETHER)]
1935    #[test_case(WLAN_AP, false, 0, ARPHRD_ETHER)]
1936    #[test_case(WLAN_AP, true, ONLINE_IF_FLAGS, ARPHRD_ETHER)]
1937    #[test_case(PPP, false, 0, ARPHRD_PPP)]
1938    #[test_case(PPP, true, ONLINE_IF_FLAGS, ARPHRD_PPP)]
1939    #[test_case(LOOPBACK, false, net_device_flags_IFF_LOOPBACK, ARPHRD_LOOPBACK)]
1940    #[test_case(LOOPBACK, true, ONLINE_IF_FLAGS | net_device_flags_IFF_LOOPBACK, ARPHRD_LOOPBACK)]
1941    #[test_case(BRIDGE, false, 0, ARPHRD_ETHER)]
1942    #[test_case(BRIDGE, true, ONLINE_IF_FLAGS, ARPHRD_ETHER)]
1943    fn test_interface_conversion(
1944        port_class: fnet_interfaces_ext::PortClass,
1945        online: bool,
1946        flags: u32,
1947        expected_link_type: u32,
1948    ) {
1949        // This conversion is safe as the link type is actually a u16,
1950        // but our bindings generator declared it as a u32.
1951        let expected_link_type = expected_link_type as u16;
1952        let interface_name = LO_NAME.to_string();
1953        let interface =
1954            create_interface(LO_INTERFACE_ID, interface_name.clone(), port_class, online, vec![]);
1955        let actual: NetlinkLinkMessage =
1956            interface_properties_to_link_message(&interface, &LO_MAC.map(|a| a.octets.to_vec()))
1957                .unwrap();
1958
1959        let nlas = create_nlas(interface_name, expected_link_type, online, &LO_MAC);
1960        let expected =
1961            create_netlink_link_message(LO_INTERFACE_ID, expected_link_type, flags, nlas);
1962        pretty_assertions::assert_eq!(actual, expected);
1963    }
1964
1965    #[fuchsia::test]
1966    fn test_oversized_interface_id_link_address_conversion() {
1967        let invalid_interface_id = (u32::MAX as u64) + 1;
1968        let interface =
1969            create_interface(invalid_interface_id, "test".into(), ETHERNET, true, vec![]);
1970
1971        let actual_link_message = interface_properties_to_link_message(&interface, &None);
1972        assert_eq!(
1973            actual_link_message,
1974            Err(NetlinkLinkMessageConversionError::InvalidInterfaceId(invalid_interface_id))
1975        );
1976
1977        assert_eq!(
1978            interface_properties_to_address_messages(&interface),
1979            Err(NetlinkAddressMessageConversionError::InvalidInterfaceId(invalid_interface_id))
1980        );
1981    }
1982
1983    #[fuchsia::test]
1984    fn test_interface_to_address_conversion() {
1985        let interface_name: String = "test".into();
1986        let interface_id = 1;
1987
1988        let interface =
1989            create_interface_with_addresses(interface_id, interface_name.clone(), ETHERNET, true);
1990        let actual = interface_properties_to_address_messages(&interface).unwrap();
1991
1992        let expected =
1993            create_default_address_messages(interface_id, interface_name, IFA_F_PERMANENT);
1994        assert_eq!(actual, expected);
1995    }
1996
1997    #[test]
1998    fn test_into_rtnl_new_link_is_serializable() {
1999        let link = create_netlink_link_message(0, 0, 0, vec![]);
2000        let new_link_message = link.into_rtnl_new_link(UNSPECIFIED_SEQUENCE_NUMBER, false);
2001        let mut buf = vec![0; new_link_message.buffer_len()];
2002        // Serialize will panic if `new_route_message` is malformed.
2003        new_link_message.serialize(&mut buf);
2004    }
2005
2006    #[test]
2007    fn test_into_rtnl_del_link_is_serializable() {
2008        let link = create_netlink_link_message(0, 0, 0, vec![]);
2009        let del_link_message = link.into_rtnl_del_link(UNSPECIFIED_SEQUENCE_NUMBER);
2010        let mut buf = vec![0; del_link_message.buffer_len()];
2011        // Serialize will panic if `del_route_message` is malformed.
2012        del_link_message.serialize(&mut buf);
2013    }
2014
2015    #[fuchsia::test]
2016    async fn test_deliver_updates() {
2017        let (mut link_sink, link_client, _async_work_drain_task) =
2018            crate::client::testutil::new_fake_client::<NetlinkRoute>(
2019                crate::client::testutil::CLIENT_ID_1,
2020                [ModernGroup(rtnetlink_groups_RTNLGRP_LINK)],
2021            );
2022        let (mut addr4_sink, addr4_client, _async_work_drain_task) =
2023            crate::client::testutil::new_fake_client::<NetlinkRoute>(
2024                crate::client::testutil::CLIENT_ID_2,
2025                [ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR)],
2026            );
2027        let (mut addr6_sink, addr6_client, _async_work_drain_task) =
2028            crate::client::testutil::new_fake_client::<NetlinkRoute>(
2029                crate::client::testutil::CLIENT_ID_3,
2030                [ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR)],
2031            );
2032        let (mut other_sink, other_client, _async_work_drain_task) =
2033            crate::client::testutil::new_fake_client::<NetlinkRoute>(
2034                crate::client::testutil::CLIENT_ID_4,
2035                [ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE)],
2036            );
2037        let (mut all_sink, all_client, _async_work_drain_task) =
2038            crate::client::testutil::new_fake_client::<NetlinkRoute>(
2039                crate::client::testutil::CLIENT_ID_5,
2040                [
2041                    ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
2042                    ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2043                    ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR),
2044                ],
2045            );
2046        let Setup {
2047            event_loop_fut,
2048            mut watcher_stream,
2049            request_sink: _,
2050            interfaces_request_stream,
2051            mut interfaces_handler_sink,
2052            _async_work_sink: _,
2053        } = setup_with_route_clients({
2054            let route_clients = ClientTable::default();
2055            route_clients.add_client(link_client);
2056            route_clients.add_client(addr4_client);
2057            route_clients.add_client(addr6_client);
2058            route_clients.add_client(other_client);
2059            route_clients.add_client(all_client);
2060            route_clients
2061        });
2062        let event_loop_fut = event_loop_fut.fuse();
2063        let mut event_loop_fut = pin!(event_loop_fut);
2064        let root_interfaces_fut =
2065            handle_only_get_mac_root_requests_fut(interfaces_request_stream).fuse();
2066        let mut root_interfaces_fut = pin!(root_interfaces_fut);
2067
2068        // Existing events should never trigger messages to be sent.
2069        let watcher_stream_fut = respond_to_watcher(
2070            watcher_stream.by_ref(),
2071            [
2072                fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2073                    id: Some(LO_INTERFACE_ID),
2074                    name: Some(LO_NAME.to_string()),
2075                    port_class: Some(LOOPBACK.into()),
2076                    online: Some(false),
2077                    addresses: Some(vec![test_addr_with_assignment_state(
2078                        TEST_V4_ADDR,
2079                        fnet_interfaces::AddressAssignmentState::Assigned,
2080                    )]),
2081                    has_default_ipv4_route: Some(false),
2082                    has_default_ipv6_route: Some(false),
2083                    ..Default::default()
2084                }),
2085                fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2086                    id: Some(ETH_INTERFACE_ID),
2087                    name: Some(ETH_NAME.to_string()),
2088                    port_class: Some(ETHERNET.into()),
2089                    online: Some(false),
2090                    addresses: Some(vec![
2091                        test_addr_with_assignment_state(
2092                            TEST_V6_ADDR,
2093                            fnet_interfaces::AddressAssignmentState::Unavailable,
2094                        ),
2095                        test_addr_with_assignment_state(
2096                            TEST_V4_ADDR,
2097                            fnet_interfaces::AddressAssignmentState::Unavailable,
2098                        ),
2099                    ]),
2100                    has_default_ipv4_route: Some(false),
2101                    has_default_ipv6_route: Some(false),
2102                    ..Default::default()
2103                }),
2104                fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2105                    id: Some(PPP_INTERFACE_ID),
2106                    name: Some(PPP_NAME.to_string()),
2107                    port_class: Some(PPP.into()),
2108                    online: Some(false),
2109                    addresses: Some(vec![
2110                        test_addr_with_assignment_state(
2111                            TEST_V4_ADDR,
2112                            fnet_interfaces::AddressAssignmentState::Assigned,
2113                        ),
2114                        test_addr_with_assignment_state(
2115                            TEST_V6_ADDR,
2116                            fnet_interfaces::AddressAssignmentState::Assigned,
2117                        ),
2118                    ]),
2119                    has_default_ipv4_route: Some(false),
2120                    has_default_ipv6_route: Some(false),
2121                    ..Default::default()
2122                }),
2123                fnet_interfaces::Event::Idle(fnet_interfaces::Empty),
2124            ],
2125        );
2126        futures::select! {
2127            () = watcher_stream_fut.fuse() => {},
2128            () = root_interfaces_fut => {
2129                unreachable!("root interfaces request stream should never end")
2130            }
2131            err = event_loop_fut => unreachable!("eventloop should not return: {err:?}"),
2132        }
2133        assert_eq!(&link_sink.take_messages()[..], &[]);
2134        assert_eq!(&addr4_sink.take_messages()[..], &[]);
2135        assert_eq!(&addr6_sink.take_messages()[..], &[]);
2136        assert_eq!(&other_sink.take_messages()[..], &[]);
2137        assert_eq!(&all_sink.take_messages()[..], &[]);
2138
2139        let watcher_stream_fut = respond_to_watcher(
2140            watcher_stream.by_ref(),
2141            [
2142                fnet_interfaces::Event::Added(fnet_interfaces::Properties {
2143                    id: Some(WLAN_INTERFACE_ID),
2144                    name: Some(WLAN_NAME.to_string()),
2145                    port_class: Some(WLAN_CLIENT.into()),
2146                    online: Some(false),
2147                    addresses: Some(vec![
2148                        test_addr_with_assignment_state(
2149                            TEST_V4_ADDR,
2150                            fnet_interfaces::AddressAssignmentState::Tentative,
2151                        ),
2152                        test_addr_with_assignment_state(
2153                            TEST_V6_ADDR,
2154                            fnet_interfaces::AddressAssignmentState::Tentative,
2155                        ),
2156                    ]),
2157                    has_default_ipv4_route: Some(false),
2158                    has_default_ipv6_route: Some(false),
2159                    ..Default::default()
2160                }),
2161                fnet_interfaces::Event::Changed(fnet_interfaces::Properties {
2162                    id: Some(LO_INTERFACE_ID),
2163                    online: Some(true),
2164                    addresses: Some(vec![
2165                        test_addr_with_assignment_state(
2166                            TEST_V4_ADDR,
2167                            fnet_interfaces::AddressAssignmentState::Assigned,
2168                        ),
2169                        test_addr_with_assignment_state(
2170                            TEST_V6_ADDR,
2171                            fnet_interfaces::AddressAssignmentState::Assigned,
2172                        ),
2173                    ]),
2174                    ..Default::default()
2175                }),
2176                fnet_interfaces::Event::Removed(ETH_INTERFACE_ID),
2177                fnet_interfaces::Event::Changed(fnet_interfaces::Properties {
2178                    id: Some(PPP_INTERFACE_ID),
2179                    addresses: Some(Vec::new()),
2180                    ..Default::default()
2181                }),
2182                fnet_interfaces::Event::Changed(fnet_interfaces::Properties {
2183                    id: Some(WLAN_INTERFACE_ID),
2184                    has_default_ipv6_route: Some(true),
2185                    ..Default::default()
2186                }),
2187            ],
2188        );
2189
2190        futures::select! {
2191            () = watcher_stream_fut.fuse() => {},
2192            () = root_interfaces_fut => {
2193                unreachable!("root interfaces request stream should never end")
2194            }
2195            err = event_loop_fut => unreachable!("eventloop should not return: {err:?}"),
2196        }
2197
2198        // Poll the event loop to ensure it's had the opportunity to process the
2199        // events from the watcher. The event loop can never finish, so we
2200        // must see `None`.
2201        assert_matches!(event_loop_fut.now_or_never(), None);
2202
2203        assert_eq!(
2204            interfaces_handler_sink.take_handled(),
2205            [
2206                HandledLink { name: LO_NAME.to_string(), kind: HandledLinkKind::New },
2207                HandledLink { name: ETH_NAME.to_string(), kind: HandledLinkKind::New },
2208                HandledLink { name: PPP_NAME.to_string(), kind: HandledLinkKind::New },
2209                HandledLink { name: WLAN_NAME.to_string(), kind: HandledLinkKind::New },
2210                HandledLink { name: ETH_NAME.to_string(), kind: HandledLinkKind::Del },
2211            ],
2212        );
2213        // Conversion to u16 is safe because 1 < 65535
2214        let arphrd_ether_u16: u16 = ARPHRD_ETHER as u16;
2215        // Conversion to u16 is safe because 772 < 65535
2216        let arphrd_loopback_u16: u16 = ARPHRD_LOOPBACK as u16;
2217        let wlan_link = SentMessage::multicast(
2218            create_netlink_link_message(
2219                WLAN_INTERFACE_ID,
2220                arphrd_ether_u16,
2221                net_device_flags_IFF_UP, // For now, WLAN interfaces are always "IFF_UP"
2222                create_nlas(WLAN_NAME.to_string(), arphrd_ether_u16, false, &WLAN_MAC),
2223            )
2224            .into_rtnl_new_link(UNSPECIFIED_SEQUENCE_NUMBER, false),
2225            ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
2226        );
2227        let lo_link = SentMessage::multicast(
2228            create_netlink_link_message(
2229                LO_INTERFACE_ID,
2230                arphrd_loopback_u16,
2231                ONLINE_IF_FLAGS | net_device_flags_IFF_LOOPBACK,
2232                create_nlas(LO_NAME.to_string(), arphrd_loopback_u16, true, &LO_MAC),
2233            )
2234            .into_rtnl_new_link(UNSPECIFIED_SEQUENCE_NUMBER, false),
2235            ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
2236        );
2237        let eth_link = SentMessage::multicast(
2238            create_netlink_link_message(
2239                ETH_INTERFACE_ID,
2240                arphrd_ether_u16,
2241                0,
2242                create_nlas(ETH_NAME.to_string(), arphrd_ether_u16, false, &ETH_MAC),
2243            )
2244            .into_rtnl_del_link(UNSPECIFIED_SEQUENCE_NUMBER),
2245            ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
2246        );
2247        assert_eq!(
2248            &link_sink.take_messages()[..],
2249            &[wlan_link.clone(), lo_link.clone(), eth_link.clone(),],
2250        );
2251
2252        let wlan_v4_addr = SentMessage::multicast(
2253            create_address_message(
2254                WLAN_INTERFACE_ID.try_into().unwrap(),
2255                TEST_V4_ADDR,
2256                WLAN_NAME.to_string(),
2257                IFA_F_PERMANENT | IFA_F_TENTATIVE,
2258            )
2259            .to_rtnl_new_addr(UNSPECIFIED_SEQUENCE_NUMBER, false),
2260            ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR),
2261        );
2262        let eth_v4_addr = SentMessage::multicast(
2263            create_address_message(
2264                ETH_INTERFACE_ID.try_into().unwrap(),
2265                TEST_V4_ADDR,
2266                ETH_NAME.to_string(),
2267                IFA_F_PERMANENT | IFA_F_TENTATIVE,
2268            )
2269            .to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
2270            ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR),
2271        );
2272        let ppp_v4_addr = SentMessage::multicast(
2273            create_address_message(
2274                PPP_INTERFACE_ID.try_into().unwrap(),
2275                TEST_V4_ADDR,
2276                PPP_NAME.to_string(),
2277                IFA_F_PERMANENT,
2278            )
2279            .to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
2280            ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR),
2281        );
2282        assert_eq!(
2283            &addr4_sink.take_messages()[..],
2284            &[wlan_v4_addr.clone(), eth_v4_addr.clone(), ppp_v4_addr.clone(),],
2285        );
2286
2287        let wlan_v6_addr = SentMessage::multicast(
2288            create_address_message(
2289                WLAN_INTERFACE_ID.try_into().unwrap(),
2290                TEST_V6_ADDR,
2291                WLAN_NAME.to_string(),
2292                IFA_F_PERMANENT | IFA_F_TENTATIVE,
2293            )
2294            .to_rtnl_new_addr(UNSPECIFIED_SEQUENCE_NUMBER, false),
2295            ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2296        );
2297        let lo_v6_addr = SentMessage::multicast(
2298            create_address_message(
2299                LO_INTERFACE_ID.try_into().unwrap(),
2300                TEST_V6_ADDR,
2301                LO_NAME.to_string(),
2302                IFA_F_PERMANENT,
2303            )
2304            .to_rtnl_new_addr(UNSPECIFIED_SEQUENCE_NUMBER, false),
2305            ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2306        );
2307        let eth_v6_addr = SentMessage::multicast(
2308            create_address_message(
2309                ETH_INTERFACE_ID.try_into().unwrap(),
2310                TEST_V6_ADDR,
2311                ETH_NAME.to_string(),
2312                IFA_F_PERMANENT | IFA_F_TENTATIVE,
2313            )
2314            .to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
2315            ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2316        );
2317        let ppp_v6_addr = SentMessage::multicast(
2318            create_address_message(
2319                PPP_INTERFACE_ID.try_into().unwrap(),
2320                TEST_V6_ADDR,
2321                PPP_NAME.to_string(),
2322                IFA_F_PERMANENT,
2323            )
2324            .to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
2325            ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2326        );
2327        assert_eq!(
2328            &addr6_sink.take_messages()[..],
2329            &[wlan_v6_addr.clone(), lo_v6_addr.clone(), eth_v6_addr.clone(), ppp_v6_addr.clone(),],
2330        );
2331
2332        assert_eq!(
2333            &all_sink.take_messages()[..],
2334            &[
2335                // New links always appear before their addresses.
2336                wlan_link,
2337                wlan_v4_addr,
2338                wlan_v6_addr,
2339                lo_link,
2340                lo_v6_addr,
2341                // Removed addresses always appear before removed interfaces.
2342                eth_v4_addr,
2343                eth_v6_addr,
2344                eth_link,
2345                ppp_v4_addr,
2346                ppp_v6_addr,
2347            ],
2348        );
2349        assert_eq!(&other_sink.take_messages()[..], &[]);
2350    }
2351
2352    const LO_MAC: Option<fnet::MacAddress> = None;
2353    const ETH_MAC: Option<fnet::MacAddress> = Some(fnet::MacAddress { octets: [1, 1, 1, 1, 1, 1] });
2354    const PPP_MAC: Option<fnet::MacAddress> = Some(fnet::MacAddress { octets: [2, 2, 2, 2, 2, 2] });
2355    const WLAN_MAC: Option<fnet::MacAddress> =
2356        Some(fnet::MacAddress { octets: [3, 3, 3, 3, 3, 3] });
2357
2358    fn handle_get_mac_root_request_or_panic(req: fnet_root::InterfacesRequest) {
2359        match req {
2360            fnet_root::InterfacesRequest::GetMac { id, responder } => {
2361                let link_address = match id {
2362                    LO_INTERFACE_ID => LO_MAC,
2363                    ETH_INTERFACE_ID => ETH_MAC,
2364                    PPP_INTERFACE_ID => PPP_MAC,
2365                    WLAN_INTERFACE_ID => WLAN_MAC,
2366                    id => panic!("unexpected interface ID {id}"),
2367                };
2368
2369                responder.send(Ok(link_address.as_ref())).unwrap()
2370            }
2371            req => panic!("unexpected request {:?}", req),
2372        }
2373    }
2374
2375    fn expect_only_get_mac_root_requests(
2376        interfaces_request_stream: fnet_root::InterfacesRequestStream,
2377    ) -> impl Stream<Item = fnet_interfaces::Event> {
2378        futures::stream::unfold(interfaces_request_stream, |interfaces_request_stream| async move {
2379            interfaces_request_stream
2380                .for_each(|req| async move { handle_get_mac_root_request_or_panic(req.unwrap()) })
2381                .await;
2382
2383            None
2384        })
2385    }
2386
2387    async fn handle_only_get_mac_root_requests_fut(
2388        interfaces_request_stream: fnet_root::InterfacesRequestStream,
2389    ) {
2390        expect_only_get_mac_root_requests(interfaces_request_stream)
2391            .for_each(|item| async move { panic!("unexpected item = {item:?}") })
2392            .await
2393    }
2394
2395    #[derive(Debug, PartialEq)]
2396    struct TestRequestResult {
2397        messages: Vec<SentMessage<RouteNetlinkMessage>>,
2398        waiter_results: Vec<Result<(), RequestError>>,
2399    }
2400
2401    /// Test helper to handle a request.
2402    ///
2403    /// `root_handler` returns a future that returns an iterator of
2404    /// `fuchsia.net.interfaces/Event`s to feed to the netlink eventloop's
2405    /// interfaces watcher after a root API request is handled.
2406    async fn test_request<
2407        St: Stream<Item = fnet_interfaces::Event>,
2408        F: FnOnce(fnet_root::InterfacesRequestStream) -> St,
2409    >(
2410        args: impl IntoIterator<Item = RequestArgs>,
2411        root_handler: F,
2412    ) -> TestRequestResult {
2413        test_request_with_initial_state(
2414            args,
2415            root_handler,
2416            InitialState { eth_interface_online: false },
2417        )
2418        .await
2419    }
2420
2421    #[derive(Clone, Copy, Debug)]
2422    struct InitialState {
2423        eth_interface_online: bool,
2424    }
2425
2426    /// Test helper to handle a request.
2427    ///
2428    /// `root_handler` returns a future that returns an iterator of
2429    /// `fuchsia.net.interfaces/Event`s to feed to the netlink eventloop's
2430    /// interfaces watcher after a root API request is handled.
2431    /// `initial_state` parametrizes the initial state of the interfaces prior to the request being
2432    /// handled.
2433    async fn test_request_with_initial_state<
2434        St: Stream<Item = fnet_interfaces::Event>,
2435        F: FnOnce(fnet_root::InterfacesRequestStream) -> St,
2436    >(
2437        args: impl IntoIterator<Item = RequestArgs>,
2438        root_handler: F,
2439        initial_state: InitialState,
2440    ) -> TestRequestResult {
2441        let scope = fasync::Scope::new();
2442        let result = {
2443            let InitialState { eth_interface_online } = initial_state;
2444
2445            let (mut expected_sink, expected_client, async_work_drain_task) =
2446                crate::client::testutil::new_fake_client::<NetlinkRoute>(
2447                    crate::client::testutil::CLIENT_ID_1,
2448                    std::iter::empty(),
2449                );
2450            let _join_handle = scope.spawn(async_work_drain_task);
2451            let (mut other_sink, other_client, async_work_drain_task) =
2452                crate::client::testutil::new_fake_client::<NetlinkRoute>(
2453                    crate::client::testutil::CLIENT_ID_2,
2454                    std::iter::empty(),
2455                );
2456            let _join_handle = scope.spawn(async_work_drain_task);
2457            let Setup {
2458                event_loop_fut,
2459                mut watcher_stream,
2460                request_sink,
2461                interfaces_request_stream,
2462                interfaces_handler_sink: _,
2463                _async_work_sink: _,
2464            } = setup_with_route_clients({
2465                let route_clients = ClientTable::default();
2466                route_clients.add_client(expected_client.clone());
2467                route_clients.add_client(other_client);
2468                route_clients
2469            });
2470            let event_loop_fut = event_loop_fut.fuse();
2471            let mut event_loop_fut = pin!(event_loop_fut);
2472
2473            let watcher_stream_fut = respond_to_watcher(
2474                watcher_stream.by_ref(),
2475                [
2476                    fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2477                        id: Some(LO_INTERFACE_ID),
2478                        name: Some(LO_NAME.to_string()),
2479                        port_class: Some(LOOPBACK.into()),
2480                        online: Some(true),
2481                        addresses: Some(vec![test_addr(TEST_V6_ADDR), test_addr(TEST_V4_ADDR)]),
2482                        has_default_ipv4_route: Some(false),
2483                        has_default_ipv6_route: Some(false),
2484                        ..Default::default()
2485                    }),
2486                    fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2487                        id: Some(ETH_INTERFACE_ID),
2488                        name: Some(ETH_NAME.to_string()),
2489                        port_class: Some(ETHERNET.into()),
2490                        online: Some(eth_interface_online),
2491                        addresses: Some(vec![test_addr(TEST_V4_ADDR), test_addr(TEST_V6_ADDR)]),
2492                        has_default_ipv4_route: Some(false),
2493                        has_default_ipv6_route: Some(false),
2494                        ..Default::default()
2495                    }),
2496                    fnet_interfaces::Event::Idle(fnet_interfaces::Empty),
2497                ],
2498            );
2499            futures::select_biased! {
2500                err = event_loop_fut => unreachable!("eventloop should not return: {err:?}"),
2501                () = watcher_stream_fut.fuse() => {},
2502            }
2503            assert_eq!(&expected_sink.take_messages()[..], &[]);
2504            assert_eq!(&other_sink.take_messages()[..], &[]);
2505
2506            let expected_client = &expected_client;
2507            let fut = futures::stream::iter(args).fold(
2508                (Vec::new(), request_sink),
2509                |(mut results, mut request_sink), args| async move {
2510                    let (completer, waiter) = oneshot::channel();
2511                    request_sink
2512                        .send(crate::route_eventloop::UnifiedRequest::InterfacesRequest(Request {
2513                            args,
2514                            sequence_number: TEST_SEQUENCE_NUMBER,
2515                            client: expected_client.clone(),
2516                            completer,
2517                        }))
2518                        .await
2519                        .unwrap();
2520                    results.push(waiter.await.unwrap());
2521                    (results, request_sink)
2522                },
2523            );
2524            // Handle root API requests then feed the returned
2525            // `fuchsia.net.interfaces/Event`s to the watcher.
2526            let watcher_fut = root_handler(interfaces_request_stream).map(Ok).forward(
2527                futures::sink::unfold(watcher_stream.by_ref(), |st, event| async {
2528                    respond_to_watcher(st.by_ref(), [event]).await;
2529                    Ok::<_, std::convert::Infallible>(st)
2530                }),
2531            );
2532            let waiter_results = futures::select_biased! {
2533                res = futures::future::join(watcher_fut, event_loop_fut) => {
2534                    unreachable!("eventloop/watcher should not return: {res:?}")
2535                },
2536                (results, _request_sink) = fut.fuse() => results
2537            };
2538            assert_eq!(&other_sink.take_messages()[..], &[]);
2539
2540            TestRequestResult { messages: expected_sink.take_messages(), waiter_results }
2541        };
2542        scope.join().await;
2543        result
2544    }
2545
2546    #[test_case(
2547        GetLinkArgs::Dump,
2548        &[LO_INTERFACE_ID, ETH_INTERFACE_ID],
2549        Ok(()); "dump")]
2550    #[test_case(
2551        GetLinkArgs::Get(LinkSpecifier::Index(
2552            NonZeroU32::new(LO_INTERFACE_ID.try_into().unwrap()).unwrap())),
2553        &[LO_INTERFACE_ID],
2554        Ok(()); "id")]
2555    #[test_case(
2556        GetLinkArgs::Get(LinkSpecifier::Index(
2557            NonZeroU32::new(WLAN_INTERFACE_ID.try_into().unwrap()).unwrap())),
2558        &[],
2559        Err(RequestError::UnrecognizedInterface); "id_not_found")]
2560    #[test_case(
2561        GetLinkArgs::Get(LinkSpecifier::Name(LO_NAME.to_string())),
2562        &[LO_INTERFACE_ID],
2563        Ok(()); "name")]
2564    #[test_case(
2565        GetLinkArgs::Get(LinkSpecifier::Name(WLAN_NAME.to_string())),
2566        &[],
2567        Err(RequestError::UnrecognizedInterface); "name_not_found")]
2568    #[fuchsia::test]
2569    async fn test_get_link(
2570        args: GetLinkArgs,
2571        expected_new_links: &[u64],
2572        expected_result: Result<(), RequestError>,
2573    ) {
2574        let is_dump = match args {
2575            GetLinkArgs::Dump => true,
2576            GetLinkArgs::Get(_) => false,
2577        };
2578        // Conversion to u16 is safe because 1 <= 65535
2579        let arphrd_ether_u16: u16 = ARPHRD_ETHER as u16;
2580        // Conversion to u16 is safe because 772 <= 65535
2581        let arphrd_loopback_u16: u16 = ARPHRD_LOOPBACK as u16;
2582        let expected_messages = expected_new_links
2583            .iter()
2584            .map(|link_id| {
2585                let msg = match *link_id {
2586                    LO_INTERFACE_ID => create_netlink_link_message(
2587                        LO_INTERFACE_ID,
2588                        arphrd_loopback_u16,
2589                        ONLINE_IF_FLAGS | net_device_flags_IFF_LOOPBACK,
2590                        create_nlas(LO_NAME.to_string(), arphrd_loopback_u16, true, &LO_MAC),
2591                    ),
2592                    ETH_INTERFACE_ID => create_netlink_link_message(
2593                        ETH_INTERFACE_ID,
2594                        arphrd_ether_u16,
2595                        0,
2596                        create_nlas(ETH_NAME.to_string(), arphrd_ether_u16, false, &ETH_MAC),
2597                    ),
2598                    _ => unreachable!("GetLink should only be tested with loopback and ethernet"),
2599                };
2600                SentMessage::unicast(msg.into_rtnl_new_link(TEST_SEQUENCE_NUMBER, is_dump))
2601            })
2602            .collect();
2603
2604        assert_eq!(
2605            test_request(
2606                [RequestArgs::Link(LinkRequestArgs::Get(args))],
2607                expect_only_get_mac_root_requests,
2608            )
2609            .await,
2610            TestRequestResult {
2611                messages: expected_messages,
2612                waiter_results: vec![expected_result],
2613            },
2614        )
2615    }
2616
2617    fn handle_get_admin_for_eth_or_panic(
2618        req: Result<fnet_root::InterfacesRequest, fidl::Error>,
2619    ) -> impl Future<Output = Option<fnet_interfaces_admin::ControlRequestStream>> {
2620        futures::future::ready(match req.unwrap() {
2621            fnet_root::InterfacesRequest::GetAdmin { id, control, control_handle: _ } => {
2622                pretty_assertions::assert_eq!(id, ETH_INTERFACE_ID);
2623                Some(control.into_stream())
2624            }
2625            req => {
2626                handle_get_mac_root_request_or_panic(req);
2627                None
2628            }
2629        })
2630    }
2631
2632    /// Returns a `FnOnce` suitable for use with [`test_request`].
2633    ///
2634    /// The closure serves a single `GetAdmin` request for the Ethernet
2635    /// interface, and handles all subsequent
2636    /// [`fnet_interfaces_admin::ControlRequest`] by calling the provided
2637    /// handler.
2638    // TODO(https://github.com/rust-lang/rust/issues/99697): Remove the
2639    // `Pin<Box<dyn ...>>` from the return type once Rust supports
2640    // `impl Fn() -> impl <SomeTrait>` style declarations.
2641    fn expect_get_admin_with_handler<
2642        I: IntoIterator<Item = fnet_interfaces::Event> + 'static,
2643        H: FnMut(fnet_interfaces_admin::ControlRequest) -> I + 'static,
2644    >(
2645        admin_handler: H,
2646    ) -> impl FnOnce(
2647        fnet_root::InterfacesRequestStream,
2648    ) -> Pin<Box<dyn Stream<Item = fnet_interfaces::Event>>> {
2649        move |interfaces_request_stream: fnet_root::InterfacesRequestStream| {
2650            Box::pin(
2651                interfaces_request_stream
2652                    .filter_map(|req| handle_get_admin_for_eth_or_panic(req))
2653                    .into_future()
2654                    // This module's implementation is expected to only acquire one
2655                    // admin control handle per interface, so drop the remaining
2656                    // stream of admin control request streams.
2657                    .map(|(admin_control_stream, _stream_of_admin_control_streams)| {
2658                        admin_control_stream.unwrap()
2659                    })
2660                    .flatten_stream()
2661                    // Handle each Control request with the provided handler.
2662                    // `scan` transfers ownership of `admin_handle`, which
2663                    // circumvents some borrow chcker issues we would encounter
2664                    // with `map`.
2665                    .scan(admin_handler, |admin_handler, req| {
2666                        futures::future::ready(Some(futures::stream::iter(admin_handler(
2667                            req.unwrap(),
2668                        ))))
2669                    })
2670                    .flatten(),
2671            )
2672        }
2673    }
2674
2675    #[test_case(
2676        InitialState { eth_interface_online: false },
2677        SetLinkArgs{
2678            link: LinkSpecifier::Name(ETH_NAME.to_string()),
2679            enable: None,
2680        },
2681        Ok(true),
2682        Ok(()); "no_change")]
2683    #[test_case(
2684        InitialState { eth_interface_online: false },
2685        SetLinkArgs{
2686            link: LinkSpecifier::Name(WLAN_NAME.to_string()),
2687            enable: None,
2688        },
2689        Ok(true),
2690        Err(RequestError::UnrecognizedInterface); "no_change_name_not_found")]
2691    #[test_case(
2692        InitialState { eth_interface_online: false },
2693        SetLinkArgs {
2694            link: LinkSpecifier::Index(
2695                NonZeroU32::new(WLAN_INTERFACE_ID.try_into().unwrap()).unwrap()),
2696            enable: None,
2697        },
2698        Ok(true),
2699        Err(RequestError::UnrecognizedInterface); "no_change_id_not_found")]
2700    #[test_case(
2701        InitialState { eth_interface_online: false },
2702        SetLinkArgs{
2703            link: LinkSpecifier::Name(ETH_NAME.to_string()),
2704            enable: Some(true),
2705        },
2706        Ok(false),
2707        Ok(()); "enable_no_op_succeeds")]
2708    #[test_case(
2709        InitialState { eth_interface_online: false },
2710        SetLinkArgs{
2711            link: LinkSpecifier::Name(ETH_NAME.to_string()),
2712            enable: Some(true),
2713        },
2714        Ok(true),
2715        Ok(()); "enable_newly_succeeds")]
2716    #[test_case(
2717        InitialState { eth_interface_online: false },
2718        SetLinkArgs{
2719            link: LinkSpecifier::Name(WLAN_NAME.to_string()),
2720            enable: Some(true),
2721        },
2722        Ok(true),
2723        Err(RequestError::UnrecognizedInterface); "enable_not_found")]
2724    #[test_case(
2725        InitialState { eth_interface_online: false },
2726        SetLinkArgs{
2727            link: LinkSpecifier::Name(ETH_NAME.to_string()),
2728            enable: Some(true),
2729        },
2730        Err(()),
2731        Err(RequestError::Unknown); "enable_fails")]
2732    #[test_case(
2733        InitialState { eth_interface_online: false },
2734        SetLinkArgs{
2735            link: LinkSpecifier::Name(ETH_NAME.to_string()),
2736            enable: Some(false),
2737        },
2738        Ok(false),
2739        Ok(()); "disable_no_op_succeeds")]
2740    #[test_case(
2741        InitialState { eth_interface_online: true },
2742        SetLinkArgs{
2743            link: LinkSpecifier::Name(ETH_NAME.to_string()),
2744            enable: Some(false),
2745        },
2746        Ok(true),
2747        Ok(()); "disable_newly_succeeds")]
2748    #[test_case(
2749        InitialState { eth_interface_online: false },
2750        SetLinkArgs{
2751            link: LinkSpecifier::Name(WLAN_NAME.to_string()),
2752            enable: Some(false),
2753        },
2754        Ok(true),
2755        Err(RequestError::UnrecognizedInterface); "disable_not_found")]
2756    #[test_case(
2757        InitialState { eth_interface_online: false },
2758        SetLinkArgs{
2759            link: LinkSpecifier::Name(ETH_NAME.to_string()),
2760            enable: Some(false),
2761        },
2762        Err(()),
2763        Err(RequestError::Unknown); "disable_fails")]
2764    #[fuchsia::test]
2765    async fn test_set_link(
2766        initial_state: InitialState,
2767        args: SetLinkArgs,
2768        control_response: Result<bool, ()>,
2769        expected_result: Result<(), RequestError>,
2770    ) {
2771        let SetLinkArgs { link: _, enable } = args.clone();
2772        let request = RequestArgs::Link(LinkRequestArgs::Set(args));
2773
2774        let control_response_clone = control_response.clone();
2775        let handle_enable =
2776            move |req: fnet_interfaces_admin::ControlRequest| -> Option<fnet_interfaces::Event> {
2777                let responder = match req {
2778                    fnet_interfaces_admin::ControlRequest::Enable { responder } => responder,
2779                    _ => panic!("unexpected ControlRequest received"),
2780                };
2781                match control_response {
2782                    Err(()) => {
2783                        responder
2784                            .send(Err(fnet_interfaces_admin::ControlEnableError::unknown()))
2785                            .expect("should send response");
2786                        None
2787                    }
2788                    Ok(newly_enabled) => {
2789                        responder.send(Ok(newly_enabled)).expect("should send response");
2790                        newly_enabled.then_some(fnet_interfaces::Event::Changed(
2791                            fnet_interfaces::Properties {
2792                                id: Some(ETH_INTERFACE_ID),
2793                                online: Some(true),
2794                                ..fnet_interfaces::Properties::default()
2795                            },
2796                        ))
2797                    }
2798                }
2799            };
2800        let handle_disable =
2801            move |req: fnet_interfaces_admin::ControlRequest| -> Option<fnet_interfaces::Event> {
2802                let responder = match req {
2803                    fnet_interfaces_admin::ControlRequest::Disable { responder } => responder,
2804                    _ => panic!("unexpected ControlRequest received"),
2805                };
2806                match control_response_clone {
2807                    Err(()) => {
2808                        responder
2809                            .send(Err(fnet_interfaces_admin::ControlDisableError::unknown()))
2810                            .expect("should send response");
2811                        None
2812                    }
2813                    Ok(newly_disabled) => {
2814                        responder.send(Ok(newly_disabled)).expect("should send response");
2815                        newly_disabled.then_some(fnet_interfaces::Event::Changed(
2816                            fnet_interfaces::Properties {
2817                                id: Some(ETH_INTERFACE_ID),
2818                                online: Some(false),
2819                                ..fnet_interfaces::Properties::default()
2820                            },
2821                        ))
2822                    }
2823                }
2824            };
2825
2826        let test_result = match enable {
2827            None => {
2828                test_request_with_initial_state(
2829                    [request],
2830                    expect_only_get_mac_root_requests,
2831                    initial_state,
2832                )
2833                .await
2834            }
2835            Some(true) => {
2836                test_request_with_initial_state(
2837                    [request],
2838                    expect_get_admin_with_handler(handle_enable),
2839                    initial_state,
2840                )
2841                .await
2842            }
2843            Some(false) => {
2844                test_request_with_initial_state(
2845                    [request],
2846                    expect_get_admin_with_handler(handle_disable),
2847                    initial_state,
2848                )
2849                .await
2850            }
2851        };
2852
2853        assert_eq!(
2854            test_result,
2855            TestRequestResult {
2856                // SetLink requests never result in messages. Acks/errors
2857                // are handled by the caller.
2858                messages: vec![],
2859                waiter_results: vec![expected_result],
2860            },
2861        )
2862    }
2863
2864    #[test_case(Some(IpVersion::V4); "v4")]
2865    #[test_case(Some(IpVersion::V6); "v6")]
2866    #[test_case(None; "all")]
2867    #[fuchsia::test]
2868    async fn test_get_addr(ip_version_filter: Option<IpVersion>) {
2869        pretty_assertions::assert_eq!(
2870            test_request(
2871                [RequestArgs::Address(AddressRequestArgs::Get(GetAddressArgs::Dump {
2872                    ip_version_filter
2873                }))],
2874                expect_only_get_mac_root_requests,
2875            )
2876            .await,
2877            TestRequestResult {
2878                messages: [(LO_INTERFACE_ID, LO_NAME), (ETH_INTERFACE_ID, ETH_NAME)]
2879                    .into_iter()
2880                    .map(|(id, name)| {
2881                        [TEST_V4_ADDR, TEST_V6_ADDR]
2882                            .into_iter()
2883                            .filter(|fnet::Subnet { addr, prefix_len: _ }| {
2884                                ip_version_filter.map_or(true, |ip_version| {
2885                                    ip_version.eq(&match addr {
2886                                        fnet::IpAddress::Ipv4(_) => IpVersion::V4,
2887                                        fnet::IpAddress::Ipv6(_) => IpVersion::V6,
2888                                    })
2889                                })
2890                            })
2891                            .map(move |addr| {
2892                                SentMessage::unicast(
2893                                    create_address_message(
2894                                        id.try_into().unwrap(),
2895                                        addr,
2896                                        name.to_string(),
2897                                        IFA_F_PERMANENT,
2898                                    )
2899                                    .to_rtnl_new_addr(TEST_SEQUENCE_NUMBER, true),
2900                                )
2901                            })
2902                    })
2903                    .flatten()
2904                    .collect(),
2905                waiter_results: vec![Ok(())],
2906            },
2907        );
2908    }
2909
2910    /// Tests RTM_NEWADDR and RTM_DEL_ADDR when the interface is removed,
2911    /// indicated by the closure of the admin Control's server-end.
2912    #[test_case(
2913        test_addr_subnet_v4(),
2914        None,
2915        true; "v4_no_terminal_new")]
2916    #[test_case(
2917        test_addr_subnet_v6(),
2918        None,
2919        true; "v6_no_terminal_new")]
2920    #[test_case(
2921        test_addr_subnet_v4(),
2922        Some(InterfaceRemovedReason::PortClosed),
2923        true; "v4_port_closed_terminal_new")]
2924    #[test_case(
2925        test_addr_subnet_v6(),
2926        Some(InterfaceRemovedReason::PortClosed),
2927        true; "v6_port_closed_terminal_new")]
2928    #[test_case(
2929        test_addr_subnet_v4(),
2930        Some(InterfaceRemovedReason::User),
2931        true; "v4_user_terminal_new")]
2932    #[test_case(
2933        test_addr_subnet_v6(),
2934        Some(InterfaceRemovedReason::User),
2935        true; "v6_user_terminal_new")]
2936    #[test_case(
2937        test_addr_subnet_v4(),
2938        None,
2939        false; "v4_no_terminal_del")]
2940    #[test_case(
2941        test_addr_subnet_v6(),
2942        None,
2943        false; "v6_no_terminal_del")]
2944    #[test_case(
2945        test_addr_subnet_v4(),
2946        Some(InterfaceRemovedReason::PortClosed),
2947        false; "v4_port_closed_terminal_del")]
2948    #[test_case(
2949        test_addr_subnet_v6(),
2950        Some(InterfaceRemovedReason::PortClosed),
2951        false; "v6_port_closed_terminal_del")]
2952    #[test_case(
2953        test_addr_subnet_v4(),
2954        Some(InterfaceRemovedReason::User),
2955        false; "v4_user_terminal_del")]
2956    #[test_case(
2957        test_addr_subnet_v6(),
2958        Some(InterfaceRemovedReason::User),
2959        false; "v6_user_terminal_del")]
2960    #[fuchsia::test]
2961    async fn test_new_del_addr_interface_removed(
2962        address: AddrSubnetEither,
2963        removal_reason: Option<InterfaceRemovedReason>,
2964        is_new: bool,
2965    ) {
2966        let interface_id = NonZeroU32::new(LO_INTERFACE_ID.try_into().unwrap()).unwrap();
2967        let address_and_interface_id = AddressAndInterfaceArgs { address, interface_id };
2968        pretty_assertions::assert_eq!(
2969            test_request(
2970                [if is_new {
2971                    RequestArgs::Address(AddressRequestArgs::New(NewAddressArgs {
2972                        address_and_interface_id,
2973                        add_subnet_route: false,
2974                    }))
2975                } else {
2976                    RequestArgs::Address(AddressRequestArgs::Del(DelAddressArgs {
2977                        address_and_interface_id,
2978                    }))
2979                }],
2980                |interfaces_request_stream| futures::stream::unfold(
2981                    interfaces_request_stream,
2982                    |interfaces_request_stream| async move {
2983                        interfaces_request_stream
2984                            .for_each(|req| {
2985                                futures::future::ready(match req.unwrap() {
2986                                    fnet_root::InterfacesRequest::GetAdmin {
2987                                        id,
2988                                        control,
2989                                        control_handle: _,
2990                                    } => {
2991                                        pretty_assertions::assert_eq!(id, LO_INTERFACE_ID);
2992                                        let control = control.into_stream();
2993                                        let control = control.control_handle();
2994                                        if let Some(reason) = removal_reason {
2995                                            control.send_on_interface_removed(reason).unwrap()
2996                                        }
2997                                        control.shutdown();
2998                                    }
2999                                    req => handle_get_mac_root_request_or_panic(req),
3000                                })
3001                            })
3002                            .await;
3003
3004                        unreachable!("interfaces request stream should not end")
3005                    },
3006                ),
3007            )
3008            .await,
3009            TestRequestResult {
3010                messages: Vec::new(),
3011                waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
3012            },
3013        )
3014    }
3015
3016    enum AddressRequestKind {
3017        New { add_subnet_route: bool },
3018        Del,
3019    }
3020
3021    /// Test that a request for an interface the eventloop does not recognize
3022    /// fails with an unrecognized interface error.
3023    #[test_case(
3024        add_test_addr_subnet_v4(),
3025        AddressRequestKind::New { add_subnet_route: false }; "v4_new")]
3026    #[test_case(
3027        add_test_addr_subnet_v6(),
3028        AddressRequestKind::New { add_subnet_route: false }; "v6_new")]
3029    #[test_case(add_test_addr_subnet_v4(), AddressRequestKind::Del; "v4_del")]
3030    #[test_case(add_test_addr_subnet_v6(), AddressRequestKind::Del; "v6_del")]
3031    #[fuchsia::test]
3032    async fn test_unknown_interface_request(address: AddrSubnetEither, kind: AddressRequestKind) {
3033        let interface_id = NonZeroU32::new(WLAN_INTERFACE_ID.try_into().unwrap()).unwrap();
3034        let address_and_interface_id = AddressAndInterfaceArgs { address, interface_id };
3035        pretty_assertions::assert_eq!(
3036            test_request(
3037                [match kind {
3038                    AddressRequestKind::New { add_subnet_route } => {
3039                        RequestArgs::Address(AddressRequestArgs::New(NewAddressArgs {
3040                            address_and_interface_id,
3041                            add_subnet_route,
3042                        }))
3043                    }
3044                    AddressRequestKind::Del => {
3045                        RequestArgs::Address(AddressRequestArgs::Del(DelAddressArgs {
3046                            address_and_interface_id,
3047                        }))
3048                    }
3049                }],
3050                expect_only_get_mac_root_requests,
3051            )
3052            .await,
3053            TestRequestResult {
3054                messages: Vec::new(),
3055                waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
3056            },
3057        )
3058    }
3059
3060    struct TestInterfaceRequestCase<F> {
3061        address: AddrSubnetEither,
3062        kind: AddressRequestKind,
3063        control_request_handler: F,
3064    }
3065
3066    impl<F> TestInterfaceRequestCase<F> {
3067        fn into_request_args_and_handler(self, interface_id: NonZeroU32) -> (RequestArgs, F) {
3068            let Self { address, kind, control_request_handler } = self;
3069            let address_and_interface_id = AddressAndInterfaceArgs { address, interface_id };
3070            let args = match kind {
3071                AddressRequestKind::New { add_subnet_route } => {
3072                    RequestArgs::Address(AddressRequestArgs::New(NewAddressArgs {
3073                        address_and_interface_id,
3074                        add_subnet_route,
3075                    }))
3076                }
3077                AddressRequestKind::Del => {
3078                    RequestArgs::Address(AddressRequestArgs::Del(DelAddressArgs {
3079                        address_and_interface_id,
3080                    }))
3081                }
3082            };
3083
3084            (args, control_request_handler)
3085        }
3086    }
3087
3088    /// A test helper that calls the (up to two) test cases' callback with a
3089    /// [`fnet_interfaces_admin::ControlRequest`] as they arrive.
3090    ///
3091    /// This implementation makes sure that the the control handle for the
3092    /// interface is only requested once.
3093    async fn test_maybe_two_interface_requests_on_single_control<
3094        St1: Stream<Item = fnet_interfaces::Event>,
3095        F1: FnMut(fnet_interfaces_admin::ControlRequest) -> St1,
3096        St2: Stream<Item = fnet_interfaces::Event>,
3097        F2: FnMut(fnet_interfaces_admin::ControlRequest) -> St2,
3098    >(
3099        case1: TestInterfaceRequestCase<F1>,
3100        case2: Option<TestInterfaceRequestCase<F2>>,
3101    ) -> TestRequestResult {
3102        let interface_id = NonZeroU32::new(ETH_INTERFACE_ID.try_into().unwrap()).unwrap();
3103        let (args1, mut control_request_handler1) =
3104            case1.into_request_args_and_handler(interface_id);
3105
3106        let (args2, control_request_handler2) = if let Some(case) = case2 {
3107            let (args, control_request_handler) = case.into_request_args_and_handler(interface_id);
3108            (Some(args), Some(control_request_handler))
3109        } else {
3110            (None, None)
3111        };
3112
3113        test_request([args1].into_iter().chain(args2), |interfaces_request_stream| {
3114            interfaces_request_stream
3115                .filter_map(|req| handle_get_admin_for_eth_or_panic(req))
3116                .into_future()
3117                // This method supports tests that want to make sure that the
3118                // admin control is only requested once so we drop the remaining
3119                // stream of admin control request streams.
3120                .map(|(admin_control_stream, _stream_of_admin_control_streams)| {
3121                    admin_control_stream.unwrap()
3122                })
3123                .flatten_stream()
3124                .into_future()
3125                .map(|(admin_control_req, admin_control_stream)| {
3126                    control_request_handler1(admin_control_req.unwrap().unwrap()).chain(
3127                        futures::stream::iter(control_request_handler2.map(
3128                            |mut control_request_handler2| {
3129                                admin_control_stream
3130                                    .into_future()
3131                                    .map(move |(admin_control_req, _admin_control_stream)| {
3132                                        control_request_handler2(
3133                                            admin_control_req.unwrap().unwrap(),
3134                                        )
3135                                    })
3136                                    .flatten_stream()
3137                            },
3138                        ))
3139                        .flatten(),
3140                    )
3141                })
3142                .flatten_stream()
3143        })
3144        .await
3145    }
3146
3147    /// A test helper that calls the callback with a
3148    /// [`fnet_interfaces_admin::ControlRequest`] as they arrive.
3149    async fn test_interface_request<
3150        St: Stream<Item = fnet_interfaces::Event>,
3151        F: FnMut(fnet_interfaces_admin::ControlRequest) -> St,
3152    >(
3153        case: TestInterfaceRequestCase<F>,
3154    ) -> TestRequestResult {
3155        test_maybe_two_interface_requests_on_single_control(
3156            case,
3157            None::<TestInterfaceRequestCase<fn(_) -> futures::stream::Pending<_>>>,
3158        )
3159        .await
3160    }
3161
3162    /// An RTM_NEWADDR test helper that calls the callback with a stream of ASP
3163    /// requests.
3164    async fn test_new_addr_asp_helper<
3165        St: Stream<Item = fnet_interfaces::Event>,
3166        F: Fn(fnet_interfaces_admin::AddressStateProviderRequestStream) -> St,
3167    >(
3168        address: AddrSubnetEither,
3169        add_subnet_route: bool,
3170        asp_handler: F,
3171    ) -> TestRequestResult {
3172        test_interface_request(TestInterfaceRequestCase {
3173            address,
3174            kind: AddressRequestKind::New { add_subnet_route },
3175            control_request_handler: |req| match req {
3176                fnet_interfaces_admin::ControlRequest::AddAddress {
3177                    address: got_address,
3178                    parameters,
3179                    address_state_provider,
3180                    control_handle: _,
3181                } => {
3182                    pretty_assertions::assert_eq!(got_address, address.into_ext());
3183                    pretty_assertions::assert_eq!(
3184                        parameters,
3185                        fnet_interfaces_admin::AddressParameters {
3186                            add_subnet_route: Some(add_subnet_route),
3187                            ..fnet_interfaces_admin::AddressParameters::default()
3188                        },
3189                    );
3190                    asp_handler(address_state_provider.into_stream())
3191                }
3192                req => panic!("unexpected request {req:?}"),
3193            },
3194        })
3195        .await
3196    }
3197
3198    /// Tests RTM_NEWADDR when the ASP is dropped immediately (doesn't handle
3199    /// any request).
3200    #[test_case(test_addr_subnet_v4(); "v4")]
3201    #[test_case(test_addr_subnet_v6(); "v6")]
3202    #[fuchsia::test]
3203    async fn test_new_addr_drop_asp_immediately(address: AddrSubnetEither) {
3204        pretty_assertions::assert_eq!(
3205            test_new_addr_asp_helper(address, false, |_asp_request_stream| {
3206                futures::stream::empty()
3207            })
3208            .await,
3209            TestRequestResult {
3210                messages: Vec::new(),
3211                waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
3212            },
3213        )
3214    }
3215
3216    /// RTM_NEWADDR test helper that exercises the ASP being closed with a
3217    /// terminal event.
3218    async fn test_new_addr_failed_helper(
3219        address: AddrSubnetEither,
3220        reason: AddressRemovalReason,
3221    ) -> TestRequestResult {
3222        test_new_addr_asp_helper(address, true, |asp_request_stream| {
3223            asp_request_stream.control_handle().send_on_address_removed(reason).unwrap();
3224            futures::stream::empty()
3225        })
3226        .await
3227    }
3228
3229    /// Tests RTM_NEWADDR when the ASP is closed with an unexpected terminal
3230    /// event.
3231    #[test_case(
3232        test_addr_subnet_v4(),
3233        AddressRemovalReason::DadFailed; "v4_dad_failed")]
3234    #[test_case(
3235        test_addr_subnet_v6(),
3236        AddressRemovalReason::DadFailed; "v6_dad_failed")]
3237    #[test_case(
3238        test_addr_subnet_v4(),
3239        AddressRemovalReason::InterfaceRemoved; "v4_interface_removed")]
3240    #[test_case(
3241        test_addr_subnet_v6(),
3242        AddressRemovalReason::InterfaceRemoved; "v6_interface_removed")]
3243    #[test_case(
3244        test_addr_subnet_v4(),
3245        AddressRemovalReason::UserRemoved; "v4_user_removed")]
3246    #[test_case(
3247        test_addr_subnet_v6(),
3248        AddressRemovalReason::UserRemoved; "v6_user_removed")]
3249    #[should_panic(expected = "expected netstack to send initial state before removing")]
3250    #[fuchsia::test]
3251    async fn test_new_addr_failed_unexpected_reason(
3252        address: AddrSubnetEither,
3253        reason: AddressRemovalReason,
3254    ) {
3255        let _: TestRequestResult = test_new_addr_failed_helper(address, reason).await;
3256    }
3257
3258    /// Tests RTM_NEWADDR when the ASP is gracefully closed with a terminal event.
3259    #[test_case(
3260        test_addr_subnet_v4(),
3261        AddressRemovalReason::Invalid,
3262        RequestError::InvalidRequest; "v4_invalid")]
3263    #[test_case(
3264        test_addr_subnet_v6(),
3265        AddressRemovalReason::Invalid,
3266        RequestError::InvalidRequest; "v6_invalid")]
3267    #[test_case(
3268        test_addr_subnet_v4(),
3269        AddressRemovalReason::AlreadyAssigned,
3270        RequestError::AlreadyExists; "v4_exists")]
3271    #[test_case(
3272        test_addr_subnet_v6(),
3273        AddressRemovalReason::AlreadyAssigned,
3274        RequestError::AlreadyExists; "v6_exists")]
3275    #[fuchsia::test]
3276    async fn test_new_addr_failed(
3277        address: AddrSubnetEither,
3278        reason: AddressRemovalReason,
3279        expected_error: RequestError,
3280    ) {
3281        pretty_assertions::assert_eq!(
3282            test_new_addr_failed_helper(address, reason).await,
3283            TestRequestResult { messages: Vec::new(), waiter_results: vec![Err(expected_error)] },
3284        )
3285    }
3286
3287    /// An RTM_NEWADDR test helper that calls the callback with a stream of ASP
3288    /// requests after the Detach request is handled.
3289    async fn test_new_addr_asp_detach_handled_helper<
3290        St: Stream<Item = fnet_interfaces::Event>,
3291        F: Fn(fnet_interfaces_admin::AddressStateProviderRequestStream) -> St,
3292    >(
3293        address: AddrSubnetEither,
3294        add_subnet_route: bool,
3295        asp_handler: F,
3296    ) -> TestRequestResult {
3297        test_new_addr_asp_helper(address, add_subnet_route, |asp_request_stream| {
3298            asp_request_stream
3299                .into_future()
3300                .map(|(asp_request, asp_request_stream)| {
3301                    let _: fnet_interfaces_admin::AddressStateProviderControlHandle = asp_request
3302                        .expect("eventloop uses ASP before dropping")
3303                        .expect("unexpected error while waiting for Detach request")
3304                        .into_detach()
3305                        .expect("eventloop makes detach request immediately");
3306
3307                    asp_handler(asp_request_stream)
3308                })
3309                .flatten_stream()
3310        })
3311        .await
3312    }
3313
3314    /// Test RTM_NEWADDR when the ASP is dropped immediately after handling the
3315    /// Detach request (no assignment state update or terminal event).
3316    #[test_case(test_addr_subnet_v4(); "v4")]
3317    #[test_case(test_addr_subnet_v6(); "v6")]
3318    #[fuchsia::test]
3319    async fn test_new_addr_drop_asp_after_detach(address: AddrSubnetEither) {
3320        pretty_assertions::assert_eq!(
3321            test_new_addr_asp_detach_handled_helper(address, false, |_asp_stream| {
3322                futures::stream::empty()
3323            })
3324            .await,
3325            TestRequestResult {
3326                messages: Vec::new(),
3327                waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
3328            },
3329        )
3330    }
3331
3332    /// Test RTM_NEWADDR when the ASP yields an assignment state update.
3333    #[test_case(add_test_addr_subnet_v4(); "v4")]
3334    #[test_case(add_test_addr_subnet_v6(); "v6")]
3335    #[fuchsia::test]
3336    async fn test_new_addr_with_address_added_event(address: AddrSubnetEither) {
3337        pretty_assertions::assert_eq!(
3338            test_new_addr_asp_detach_handled_helper(address, true, |asp_request_stream| {
3339                asp_request_stream
3340                    .control_handle()
3341                    .send_on_address_added()
3342                    .expect("send address added");
3343
3344                // Send an update with the added address to complete the
3345                // request.
3346                futures::stream::iter([fnet_interfaces::Event::Changed(
3347                    fnet_interfaces::Properties {
3348                        id: Some(ETH_INTERFACE_ID.try_into().unwrap()),
3349                        addresses: Some(vec![test_addr(address.into_ext())]),
3350                        ..fnet_interfaces::Properties::default()
3351                    },
3352                )])
3353            })
3354            .await,
3355            TestRequestResult { messages: Vec::new(), waiter_results: vec![Ok(())] },
3356        )
3357    }
3358
3359    /// Test RTM_DELADDR when the interface is closed with an unexpected reaosn.
3360    #[test_case(
3361        test_addr_subnet_v4(),
3362        InterfaceRemovedReason::DuplicateName; "v4_duplicate_name")]
3363    #[test_case(
3364        test_addr_subnet_v6(),
3365        InterfaceRemovedReason::DuplicateName; "v6_duplicate_name")]
3366    #[test_case(
3367        test_addr_subnet_v4(),
3368        InterfaceRemovedReason::PortAlreadyBound; "v4_port_already_bound")]
3369    #[test_case(
3370        test_addr_subnet_v6(),
3371        InterfaceRemovedReason::PortAlreadyBound; "v6_port_already_bound")]
3372    #[test_case(
3373        test_addr_subnet_v4(),
3374        InterfaceRemovedReason::BadPort; "v4_bad_port")]
3375    #[test_case(
3376        test_addr_subnet_v6(),
3377        InterfaceRemovedReason::BadPort; "v6_bad_port")]
3378    #[should_panic(expected = "unexpected interface removed reason")]
3379    #[fuchsia::test]
3380    async fn test_del_addr_interface_closed_unexpected_reason(
3381        address: AddrSubnetEither,
3382        removal_reason: InterfaceRemovedReason,
3383    ) {
3384        let _: TestRequestResult = test_interface_request(TestInterfaceRequestCase {
3385            address,
3386            kind: AddressRequestKind::Del,
3387            control_request_handler: |req| match req {
3388                fnet_interfaces_admin::ControlRequest::RemoveAddress {
3389                    address: got_address,
3390                    responder,
3391                } => {
3392                    pretty_assertions::assert_eq!(got_address, address.into_ext());
3393                    let control_handle = responder.control_handle();
3394                    control_handle.send_on_interface_removed(removal_reason).unwrap();
3395                    control_handle.shutdown();
3396                    futures::stream::empty()
3397                }
3398                req => panic!("unexpected request {req:?}"),
3399            },
3400        })
3401        .await;
3402    }
3403
3404    fn del_addr_test_interface_case(
3405        address: AddrSubnetEither,
3406        response: Result<bool, fnet_interfaces_admin::ControlRemoveAddressError>,
3407        remaining_address: Option<AddrSubnetEither>,
3408    ) -> TestInterfaceRequestCase<
3409        impl FnMut(
3410            fnet_interfaces_admin::ControlRequest,
3411        ) -> futures::stream::Iter<core::array::IntoIter<fnet_interfaces::Event, 1>>,
3412    > {
3413        TestInterfaceRequestCase {
3414            address,
3415            kind: AddressRequestKind::Del,
3416            control_request_handler: move |req| {
3417                match req {
3418                    fnet_interfaces_admin::ControlRequest::RemoveAddress {
3419                        address: got_address,
3420                        responder,
3421                    } => {
3422                        pretty_assertions::assert_eq!(got_address, address.into_ext());
3423                        responder.send(response).unwrap();
3424
3425                        // Send an update without the deleted address to complete
3426                        // the request.
3427                        futures::stream::iter([fnet_interfaces::Event::Changed(
3428                            fnet_interfaces::Properties {
3429                                id: Some(ETH_INTERFACE_ID.try_into().unwrap()),
3430                                addresses: Some(remaining_address.map_or_else(Vec::new, |addr| {
3431                                    vec![test_addr(addr.into_ext())]
3432                                })),
3433                                ..fnet_interfaces::Properties::default()
3434                            },
3435                        )])
3436                    }
3437                    req => panic!("unexpected request {req:?}"),
3438                }
3439            },
3440        }
3441    }
3442
3443    /// Test RTM_DELADDR with all interesting responses to remove address.
3444    #[test_case(
3445        test_addr_subnet_v4(),
3446        Ok(true),
3447        Ok(()); "v4_did_remove")]
3448    #[test_case(
3449        test_addr_subnet_v6(),
3450        Ok(true),
3451        Ok(()); "v6_did_remove")]
3452    #[test_case(
3453        test_addr_subnet_v4(),
3454        Ok(false),
3455        Err(RequestError::AddressNotFound); "v4_did_not_remove")]
3456    #[test_case(
3457        test_addr_subnet_v6(),
3458        Ok(false),
3459        Err(RequestError::AddressNotFound); "v6_did_not_remove")]
3460    #[test_case(
3461        test_addr_subnet_v4(),
3462        Err(fnet_interfaces_admin::ControlRemoveAddressError::unknown()),
3463        Err(RequestError::InvalidRequest); "v4_unrecognized_error")]
3464    #[test_case(
3465        test_addr_subnet_v6(),
3466        Err(fnet_interfaces_admin::ControlRemoveAddressError::unknown()),
3467        Err(RequestError::InvalidRequest); "v6_unrecognized_error")]
3468    #[fuchsia::test]
3469    async fn test_del_addr(
3470        address: AddrSubnetEither,
3471        response: Result<bool, fnet_interfaces_admin::ControlRemoveAddressError>,
3472        waiter_result: Result<(), RequestError>,
3473    ) {
3474        pretty_assertions::assert_eq!(
3475            test_interface_request(del_addr_test_interface_case(address, response, None)).await,
3476            TestRequestResult { messages: Vec::new(), waiter_results: vec![waiter_result] },
3477        )
3478    }
3479
3480    /// Tests that multiple interface update requests result in only one
3481    /// admin handle being created for that interface.
3482    #[fuchsia::test]
3483    async fn test_single_get_admin_for_multiple_interface_requests() {
3484        let first_address = test_addr_subnet_v4();
3485        let second_address = test_addr_subnet_v6();
3486        pretty_assertions::assert_eq!(
3487            test_maybe_two_interface_requests_on_single_control(
3488                del_addr_test_interface_case(first_address, Ok(true), Some(second_address)),
3489                Some(del_addr_test_interface_case(second_address, Ok(true), None)),
3490            )
3491            .await,
3492            TestRequestResult { messages: Vec::new(), waiter_results: vec![Ok(()), Ok(())] },
3493        )
3494    }
3495}