netlink/
interfaces.rs

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