Skip to main content

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