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    }
1665
1666    pub(crate) struct Setup<E, W> {
1667        pub event_loop_fut: E,
1668        pub watcher_stream: W,
1669        pub request_sink:
1670            mpsc::Sender<crate::route_eventloop::UnifiedRequest<FakeSender<RouteNetlinkMessage>>>,
1671        pub interfaces_request_stream: fnet_root::InterfacesRequestStream,
1672        pub interfaces_handler_sink: FakeInterfacesHandlerSink,
1673        pub _async_work_sink: mpsc::UnboundedSender<AsyncWorkItem<NetlinkRoute>>,
1674    }
1675
1676    pub(crate) fn setup_with_route_clients(
1677        route_clients: ClientTable<NetlinkRoute, FakeSender<RouteNetlinkMessage>>,
1678    ) -> Setup<impl Future<Output = Never>, impl Stream<Item = fnet_interfaces::WatcherRequest>>
1679    {
1680        let (request_sink, request_stream) = mpsc::channel(1);
1681        let (interfaces_handler, interfaces_handler_sink) = FakeInterfacesHandler::new();
1682        let (interfaces_proxy, interfaces) =
1683            fidl::endpoints::create_proxy::<fnet_root::InterfacesMarker>();
1684        let (interfaces_state_proxy, interfaces_state) =
1685            fidl::endpoints::create_proxy::<fnet_interfaces::StateMarker>();
1686        let (async_work_sink, async_work_receiver) = mpsc::unbounded();
1687        let event_loop_inputs = crate::route_eventloop::EventLoopInputs::<_, _, OnlyInterfaces> {
1688            route_clients: EventLoopComponent::Present(route_clients),
1689            interfaces_handler: EventLoopComponent::Present(interfaces_handler),
1690            interfaces_proxy: EventLoopComponent::Present(interfaces_proxy),
1691            interfaces_state_proxy: EventLoopComponent::Present(interfaces_state_proxy),
1692            async_work_receiver,
1693
1694            v4_routes_state: EventLoopComponent::Absent(Optional),
1695            v6_routes_state: EventLoopComponent::Absent(Optional),
1696            v4_main_route_table: EventLoopComponent::Absent(Optional),
1697            v6_main_route_table: EventLoopComponent::Absent(Optional),
1698            v4_route_table_provider: EventLoopComponent::Absent(Optional),
1699            v6_route_table_provider: EventLoopComponent::Absent(Optional),
1700            v4_rule_table: EventLoopComponent::Absent(Optional),
1701            v6_rule_table: EventLoopComponent::Absent(Optional),
1702            ndp_option_watcher_provider: EventLoopComponent::Absent(Optional),
1703
1704            unified_request_stream: request_stream,
1705        };
1706
1707        let interfaces_request_stream = interfaces.into_stream();
1708        let if_stream = interfaces_state.into_stream();
1709        let watcher_stream = if_stream
1710            .and_then(|req| match req {
1711                fnet_interfaces::StateRequest::GetWatcher {
1712                    options: _,
1713                    watcher,
1714                    control_handle: _,
1715                } => futures::future::ready(Ok(watcher.into_stream())),
1716            })
1717            .try_flatten()
1718            .map(|res| res.expect("watcher stream error"));
1719
1720        Setup {
1721            event_loop_fut: async move {
1722                let event_loop = event_loop_inputs
1723                    .initialize(IncludedWorkers {
1724                        interfaces: EventLoopComponent::Present(()),
1725                        routes_v4: EventLoopComponent::Absent(Optional),
1726                        routes_v6: EventLoopComponent::Absent(Optional),
1727                        rules_v4: EventLoopComponent::Absent(Optional),
1728                        rules_v6: EventLoopComponent::Absent(Optional),
1729                        nduseropt: EventLoopComponent::Absent(Optional),
1730                    })
1731                    .await;
1732                event_loop.run().await
1733            },
1734            watcher_stream,
1735            request_sink,
1736            interfaces_request_stream,
1737            interfaces_handler_sink,
1738            _async_work_sink: async_work_sink,
1739        }
1740    }
1741
1742    pub(crate) async fn respond_to_watcher<S: Stream<Item = fnet_interfaces::WatcherRequest>>(
1743        stream: S,
1744        updates: impl IntoIterator<Item = fnet_interfaces::Event>,
1745    ) {
1746        stream
1747            .zip(futures::stream::iter(updates.into_iter()))
1748            .for_each(|(req, update)| async move {
1749                match req {
1750                    fnet_interfaces::WatcherRequest::Watch { responder } => {
1751                        responder.send(&update).expect("send watch response")
1752                    }
1753                }
1754            })
1755            .await
1756    }
1757
1758    pub(crate) fn create_netlink_link_message(
1759        id: u64,
1760        link_type: u16,
1761        flags: u32,
1762        nlas: Vec<LinkAttribute>,
1763    ) -> NetlinkLinkMessage {
1764        let mut link_header = LinkHeader::default();
1765        link_header.index = id.try_into().expect("should fit into u32");
1766        link_header.link_layer_type = LinkLayerType::from(link_type);
1767        link_header.flags = LinkFlags::from_bits(flags).unwrap();
1768        link_header.change_mask = LinkFlags::from_bits(u32::MAX).unwrap();
1769
1770        let mut link_message = LinkMessage::default();
1771        link_message.header = link_header;
1772        link_message.attributes = nlas;
1773
1774        NetlinkLinkMessage(link_message)
1775    }
1776
1777    pub(crate) fn create_nlas(
1778        name: String,
1779        link_type: u16,
1780        online: bool,
1781        mac: &Option<fnet::MacAddress>,
1782    ) -> Vec<LinkAttribute> {
1783        [
1784            LinkAttribute::IfName(name),
1785            LinkAttribute::Link(link_type.into()),
1786            LinkAttribute::OperState(if online { State::Up } else { State::Down }),
1787        ]
1788        .into_iter()
1789        .chain(mac.map(|fnet::MacAddress { octets }| LinkAttribute::Address(octets.to_vec())))
1790        .collect()
1791    }
1792
1793    pub(crate) fn create_address_message(
1794        interface_id: u32,
1795        subnet: fnet::Subnet,
1796        interface_name: String,
1797        flags: u32,
1798    ) -> NetlinkAddressMessage {
1799        let mut addr_header = AddressHeader::default();
1800        let (family, addr) = match subnet.addr {
1801            fnet::IpAddress::Ipv4(ip_addr) => {
1802                (AddressFamily::Inet, IpAddr::V4(ip_addr.addr.into()))
1803            }
1804            fnet::IpAddress::Ipv6(ip_addr) => {
1805                (AddressFamily::Inet6, IpAddr::V6(ip_addr.addr.into()))
1806            }
1807        };
1808        addr_header.family = family.into();
1809        addr_header.prefix_len = subnet.prefix_len;
1810        addr_header.flags = AddressHeaderFlags::from_bits(flags as u8).unwrap().bits();
1811        addr_header.index = interface_id.into();
1812
1813        let nlas = vec![
1814            AddressAttribute::Address(addr),
1815            AddressAttribute::Label(interface_name),
1816            AddressAttribute::Flags(AddressFlags::from_bits(flags).unwrap()),
1817        ];
1818
1819        let mut addr_message = AddressMessage::default();
1820        addr_message.header = addr_header;
1821        addr_message.attributes = nlas;
1822        NetlinkAddressMessage(addr_message)
1823    }
1824
1825    pub(crate) fn test_addr_with_assignment_state(
1826        addr: fnet::Subnet,
1827        assignment_state: fnet_interfaces::AddressAssignmentState,
1828    ) -> fnet_interfaces::Address {
1829        fnet_interfaces_ext::Address::<fnet_interfaces_ext::AllInterest> {
1830            addr,
1831            valid_until: fnet_interfaces_ext::PositiveMonotonicInstant::INFINITE_FUTURE,
1832            preferred_lifetime_info: fnet_interfaces_ext::PreferredLifetimeInfo::preferred_forever(
1833            ),
1834            assignment_state,
1835        }
1836        .into()
1837    }
1838
1839    pub(crate) fn test_addr(addr: fnet::Subnet) -> fnet_interfaces::Address {
1840        test_addr_with_assignment_state(addr, fnet_interfaces::AddressAssignmentState::Assigned)
1841    }
1842}
1843
1844#[cfg(test)]
1845mod tests {
1846    use super::testutil::*;
1847    use super::*;
1848
1849    use std::pin::{Pin, pin};
1850
1851    use fidl::endpoints::{ControlHandle as _, RequestStream as _, Responder as _};
1852    use fidl_fuchsia_net as fnet;
1853    use fnet_interfaces::AddressAssignmentState;
1854    use fuchsia_async::{self as fasync};
1855
1856    use assert_matches::assert_matches;
1857    use futures::FutureExt as _;
1858    use futures::sink::SinkExt as _;
1859    use futures::stream::Stream;
1860    use linux_uapi::{IFA_F_PERMANENT, IFA_F_TENTATIVE, rtnetlink_groups_RTNLGRP_IPV4_ROUTE};
1861    use pretty_assertions::assert_eq;
1862    use test_case::test_case;
1863
1864    use crate::messaging::testutil::SentMessage;
1865
1866    const TEST_SEQUENCE_NUMBER: u32 = 1234;
1867
1868    fn create_interface(
1869        id: u64,
1870        name: String,
1871        port_class: fnet_interfaces_ext::PortClass,
1872        online: bool,
1873        addresses: Vec<fnet_interfaces_ext::Address<fnet_interfaces_ext::AllInterest>>,
1874    ) -> fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest> {
1875        fnet_interfaces_ext::Properties {
1876            id: NonZeroU64::new(id).unwrap(),
1877            name,
1878            port_class,
1879            online,
1880            addresses,
1881            has_default_ipv4_route: false,
1882            has_default_ipv6_route: false,
1883            port_identity_koid: None,
1884        }
1885    }
1886
1887    fn create_interface_with_addresses(
1888        id: u64,
1889        name: String,
1890        port_class: fnet_interfaces_ext::PortClass,
1891        online: bool,
1892    ) -> fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest> {
1893        let addresses = vec![
1894            fnet_interfaces_ext::Address {
1895                addr: TEST_V4_ADDR,
1896                valid_until: fnet_interfaces_ext::PositiveMonotonicInstant::INFINITE_FUTURE,
1897                assignment_state: AddressAssignmentState::Assigned,
1898                preferred_lifetime_info:
1899                    fnet_interfaces_ext::PreferredLifetimeInfo::preferred_forever(),
1900            },
1901            fnet_interfaces_ext::Address {
1902                addr: TEST_V6_ADDR,
1903                valid_until: fnet_interfaces_ext::PositiveMonotonicInstant::INFINITE_FUTURE,
1904                assignment_state: AddressAssignmentState::Assigned,
1905                preferred_lifetime_info:
1906                    fnet_interfaces_ext::PreferredLifetimeInfo::preferred_forever(),
1907            },
1908        ];
1909        create_interface(id, name, port_class, online, addresses)
1910    }
1911
1912    fn create_default_address_messages(
1913        interface_id: u64,
1914        interface_name: String,
1915        flags: u32,
1916    ) -> BTreeMap<fnet::IpAddress, NetlinkAddressMessage> {
1917        let interface_id = interface_id.try_into().expect("should fit into u32");
1918        BTreeMap::from_iter([
1919            (
1920                TEST_V4_ADDR.addr,
1921                create_address_message(interface_id, TEST_V4_ADDR, interface_name.clone(), flags),
1922            ),
1923            (
1924                TEST_V6_ADDR.addr,
1925                create_address_message(interface_id, TEST_V6_ADDR, interface_name, flags),
1926            ),
1927        ])
1928    }
1929
1930    #[test_case(ETHERNET, false, 0, ARPHRD_ETHER)]
1931    #[test_case(ETHERNET, true, ONLINE_IF_FLAGS, ARPHRD_ETHER)]
1932    #[test_case(WLAN_CLIENT, false, net_device_flags_IFF_UP, ARPHRD_ETHER)]
1933    #[test_case(WLAN_CLIENT, true, ONLINE_IF_FLAGS, ARPHRD_ETHER)]
1934    #[test_case(WLAN_AP, false, 0, ARPHRD_ETHER)]
1935    #[test_case(WLAN_AP, true, ONLINE_IF_FLAGS, ARPHRD_ETHER)]
1936    #[test_case(PPP, false, 0, ARPHRD_PPP)]
1937    #[test_case(PPP, true, ONLINE_IF_FLAGS, ARPHRD_PPP)]
1938    #[test_case(LOOPBACK, false, net_device_flags_IFF_LOOPBACK, ARPHRD_LOOPBACK)]
1939    #[test_case(LOOPBACK, true, ONLINE_IF_FLAGS | net_device_flags_IFF_LOOPBACK, ARPHRD_LOOPBACK)]
1940    #[test_case(BRIDGE, false, 0, ARPHRD_ETHER)]
1941    #[test_case(BRIDGE, true, ONLINE_IF_FLAGS, ARPHRD_ETHER)]
1942    fn test_interface_conversion(
1943        port_class: fnet_interfaces_ext::PortClass,
1944        online: bool,
1945        flags: u32,
1946        expected_link_type: u32,
1947    ) {
1948        // This conversion is safe as the link type is actually a u16,
1949        // but our bindings generator declared it as a u32.
1950        let expected_link_type = expected_link_type as u16;
1951        let interface_name = LO_NAME.to_string();
1952        let interface =
1953            create_interface(LO_INTERFACE_ID, interface_name.clone(), port_class, online, vec![]);
1954        let actual: NetlinkLinkMessage =
1955            interface_properties_to_link_message(&interface, &LO_MAC.map(|a| a.octets.to_vec()))
1956                .unwrap();
1957
1958        let nlas = create_nlas(interface_name, expected_link_type, online, &LO_MAC);
1959        let expected =
1960            create_netlink_link_message(LO_INTERFACE_ID, expected_link_type, flags, nlas);
1961        pretty_assertions::assert_eq!(actual, expected);
1962    }
1963
1964    #[fuchsia::test]
1965    fn test_oversized_interface_id_link_address_conversion() {
1966        let invalid_interface_id = (u32::MAX as u64) + 1;
1967        let interface =
1968            create_interface(invalid_interface_id, "test".into(), ETHERNET, true, vec![]);
1969
1970        let actual_link_message = interface_properties_to_link_message(&interface, &None);
1971        assert_eq!(
1972            actual_link_message,
1973            Err(NetlinkLinkMessageConversionError::InvalidInterfaceId(invalid_interface_id))
1974        );
1975
1976        assert_eq!(
1977            interface_properties_to_address_messages(&interface),
1978            Err(NetlinkAddressMessageConversionError::InvalidInterfaceId(invalid_interface_id))
1979        );
1980    }
1981
1982    #[fuchsia::test]
1983    fn test_interface_to_address_conversion() {
1984        let interface_name: String = "test".into();
1985        let interface_id = 1;
1986
1987        let interface =
1988            create_interface_with_addresses(interface_id, interface_name.clone(), ETHERNET, true);
1989        let actual = interface_properties_to_address_messages(&interface).unwrap();
1990
1991        let expected =
1992            create_default_address_messages(interface_id, interface_name, IFA_F_PERMANENT);
1993        assert_eq!(actual, expected);
1994    }
1995
1996    #[test]
1997    fn test_into_rtnl_new_link_is_serializable() {
1998        let link = create_netlink_link_message(0, 0, 0, vec![]);
1999        let new_link_message = link.into_rtnl_new_link(UNSPECIFIED_SEQUENCE_NUMBER, false);
2000        let mut buf = vec![0; new_link_message.buffer_len()];
2001        // Serialize will panic if `new_route_message` is malformed.
2002        new_link_message.serialize(&mut buf);
2003    }
2004
2005    #[test]
2006    fn test_into_rtnl_del_link_is_serializable() {
2007        let link = create_netlink_link_message(0, 0, 0, vec![]);
2008        let del_link_message = link.into_rtnl_del_link(UNSPECIFIED_SEQUENCE_NUMBER);
2009        let mut buf = vec![0; del_link_message.buffer_len()];
2010        // Serialize will panic if `del_route_message` is malformed.
2011        del_link_message.serialize(&mut buf);
2012    }
2013
2014    #[fuchsia::test]
2015    async fn test_deliver_updates() {
2016        let (mut link_sink, link_client, _async_work_drain_task) =
2017            crate::client::testutil::new_fake_client::<NetlinkRoute>(
2018                crate::client::testutil::CLIENT_ID_1,
2019                [ModernGroup(rtnetlink_groups_RTNLGRP_LINK)],
2020            );
2021        let (mut addr4_sink, addr4_client, _async_work_drain_task) =
2022            crate::client::testutil::new_fake_client::<NetlinkRoute>(
2023                crate::client::testutil::CLIENT_ID_2,
2024                [ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR)],
2025            );
2026        let (mut addr6_sink, addr6_client, _async_work_drain_task) =
2027            crate::client::testutil::new_fake_client::<NetlinkRoute>(
2028                crate::client::testutil::CLIENT_ID_3,
2029                [ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR)],
2030            );
2031        let (mut other_sink, other_client, _async_work_drain_task) =
2032            crate::client::testutil::new_fake_client::<NetlinkRoute>(
2033                crate::client::testutil::CLIENT_ID_4,
2034                [ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_ROUTE)],
2035            );
2036        let (mut all_sink, all_client, _async_work_drain_task) =
2037            crate::client::testutil::new_fake_client::<NetlinkRoute>(
2038                crate::client::testutil::CLIENT_ID_5,
2039                [
2040                    ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
2041                    ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2042                    ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR),
2043                ],
2044            );
2045        let Setup {
2046            event_loop_fut,
2047            mut watcher_stream,
2048            request_sink: _,
2049            interfaces_request_stream,
2050            mut interfaces_handler_sink,
2051            _async_work_sink: _,
2052        } = setup_with_route_clients({
2053            let route_clients = ClientTable::default();
2054            route_clients.add_client(link_client);
2055            route_clients.add_client(addr4_client);
2056            route_clients.add_client(addr6_client);
2057            route_clients.add_client(other_client);
2058            route_clients.add_client(all_client);
2059            route_clients
2060        });
2061        let event_loop_fut = event_loop_fut.fuse();
2062        let mut event_loop_fut = pin!(event_loop_fut);
2063        let root_interfaces_fut =
2064            handle_only_get_mac_root_requests_fut(interfaces_request_stream).fuse();
2065        let mut root_interfaces_fut = pin!(root_interfaces_fut);
2066
2067        // Existing events should never trigger messages to be sent.
2068        let watcher_stream_fut = respond_to_watcher(
2069            watcher_stream.by_ref(),
2070            [
2071                fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2072                    id: Some(LO_INTERFACE_ID),
2073                    name: Some(LO_NAME.to_string()),
2074                    port_class: Some(LOOPBACK.into()),
2075                    online: Some(false),
2076                    addresses: Some(vec![test_addr_with_assignment_state(
2077                        TEST_V4_ADDR,
2078                        fnet_interfaces::AddressAssignmentState::Assigned,
2079                    )]),
2080                    has_default_ipv4_route: Some(false),
2081                    has_default_ipv6_route: Some(false),
2082                    ..Default::default()
2083                }),
2084                fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2085                    id: Some(ETH_INTERFACE_ID),
2086                    name: Some(ETH_NAME.to_string()),
2087                    port_class: Some(ETHERNET.into()),
2088                    online: Some(false),
2089                    addresses: Some(vec![
2090                        test_addr_with_assignment_state(
2091                            TEST_V6_ADDR,
2092                            fnet_interfaces::AddressAssignmentState::Unavailable,
2093                        ),
2094                        test_addr_with_assignment_state(
2095                            TEST_V4_ADDR,
2096                            fnet_interfaces::AddressAssignmentState::Unavailable,
2097                        ),
2098                    ]),
2099                    has_default_ipv4_route: Some(false),
2100                    has_default_ipv6_route: Some(false),
2101                    ..Default::default()
2102                }),
2103                fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2104                    id: Some(PPP_INTERFACE_ID),
2105                    name: Some(PPP_NAME.to_string()),
2106                    port_class: Some(PPP.into()),
2107                    online: Some(false),
2108                    addresses: Some(vec![
2109                        test_addr_with_assignment_state(
2110                            TEST_V4_ADDR,
2111                            fnet_interfaces::AddressAssignmentState::Assigned,
2112                        ),
2113                        test_addr_with_assignment_state(
2114                            TEST_V6_ADDR,
2115                            fnet_interfaces::AddressAssignmentState::Assigned,
2116                        ),
2117                    ]),
2118                    has_default_ipv4_route: Some(false),
2119                    has_default_ipv6_route: Some(false),
2120                    ..Default::default()
2121                }),
2122                fnet_interfaces::Event::Idle(fnet_interfaces::Empty),
2123            ],
2124        );
2125        futures::select! {
2126            () = watcher_stream_fut.fuse() => {},
2127            () = root_interfaces_fut => {
2128                unreachable!("root interfaces request stream should never end")
2129            }
2130            err = event_loop_fut => unreachable!("eventloop should not return: {err:?}"),
2131        }
2132        assert_eq!(&link_sink.take_messages()[..], &[]);
2133        assert_eq!(&addr4_sink.take_messages()[..], &[]);
2134        assert_eq!(&addr6_sink.take_messages()[..], &[]);
2135        assert_eq!(&other_sink.take_messages()[..], &[]);
2136        assert_eq!(&all_sink.take_messages()[..], &[]);
2137
2138        let watcher_stream_fut = respond_to_watcher(
2139            watcher_stream.by_ref(),
2140            [
2141                fnet_interfaces::Event::Added(fnet_interfaces::Properties {
2142                    id: Some(WLAN_INTERFACE_ID),
2143                    name: Some(WLAN_NAME.to_string()),
2144                    port_class: Some(WLAN_CLIENT.into()),
2145                    online: Some(false),
2146                    addresses: Some(vec![
2147                        test_addr_with_assignment_state(
2148                            TEST_V4_ADDR,
2149                            fnet_interfaces::AddressAssignmentState::Tentative,
2150                        ),
2151                        test_addr_with_assignment_state(
2152                            TEST_V6_ADDR,
2153                            fnet_interfaces::AddressAssignmentState::Tentative,
2154                        ),
2155                    ]),
2156                    has_default_ipv4_route: Some(false),
2157                    has_default_ipv6_route: Some(false),
2158                    ..Default::default()
2159                }),
2160                fnet_interfaces::Event::Changed(fnet_interfaces::Properties {
2161                    id: Some(LO_INTERFACE_ID),
2162                    online: Some(true),
2163                    addresses: Some(vec![
2164                        test_addr_with_assignment_state(
2165                            TEST_V4_ADDR,
2166                            fnet_interfaces::AddressAssignmentState::Assigned,
2167                        ),
2168                        test_addr_with_assignment_state(
2169                            TEST_V6_ADDR,
2170                            fnet_interfaces::AddressAssignmentState::Assigned,
2171                        ),
2172                    ]),
2173                    ..Default::default()
2174                }),
2175                fnet_interfaces::Event::Removed(ETH_INTERFACE_ID),
2176                fnet_interfaces::Event::Changed(fnet_interfaces::Properties {
2177                    id: Some(PPP_INTERFACE_ID),
2178                    addresses: Some(Vec::new()),
2179                    ..Default::default()
2180                }),
2181                fnet_interfaces::Event::Changed(fnet_interfaces::Properties {
2182                    id: Some(WLAN_INTERFACE_ID),
2183                    has_default_ipv6_route: Some(true),
2184                    ..Default::default()
2185                }),
2186            ],
2187        );
2188
2189        futures::select! {
2190            () = watcher_stream_fut.fuse() => {},
2191            () = root_interfaces_fut => {
2192                unreachable!("root interfaces request stream should never end")
2193            }
2194            err = event_loop_fut => unreachable!("eventloop should not return: {err:?}"),
2195        }
2196
2197        // Poll the event loop to ensure it's had the opportunity to process the
2198        // events from the watcher. The event loop can never finish, so we
2199        // must see `None`.
2200        assert_matches!(event_loop_fut.now_or_never(), None);
2201
2202        assert_eq!(
2203            interfaces_handler_sink.take_handled(),
2204            [
2205                HandledLink { name: LO_NAME.to_string(), kind: HandledLinkKind::New },
2206                HandledLink { name: ETH_NAME.to_string(), kind: HandledLinkKind::New },
2207                HandledLink { name: PPP_NAME.to_string(), kind: HandledLinkKind::New },
2208                HandledLink { name: WLAN_NAME.to_string(), kind: HandledLinkKind::New },
2209                HandledLink { name: ETH_NAME.to_string(), kind: HandledLinkKind::Del },
2210            ],
2211        );
2212        // Conversion to u16 is safe because 1 < 65535
2213        let arphrd_ether_u16: u16 = ARPHRD_ETHER as u16;
2214        // Conversion to u16 is safe because 772 < 65535
2215        let arphrd_loopback_u16: u16 = ARPHRD_LOOPBACK as u16;
2216        let wlan_link = SentMessage::multicast(
2217            create_netlink_link_message(
2218                WLAN_INTERFACE_ID,
2219                arphrd_ether_u16,
2220                net_device_flags_IFF_UP, // For now, WLAN interfaces are always "IFF_UP"
2221                create_nlas(WLAN_NAME.to_string(), arphrd_ether_u16, false, &WLAN_MAC),
2222            )
2223            .into_rtnl_new_link(UNSPECIFIED_SEQUENCE_NUMBER, false),
2224            ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
2225        );
2226        let lo_link = SentMessage::multicast(
2227            create_netlink_link_message(
2228                LO_INTERFACE_ID,
2229                arphrd_loopback_u16,
2230                ONLINE_IF_FLAGS | net_device_flags_IFF_LOOPBACK,
2231                create_nlas(LO_NAME.to_string(), arphrd_loopback_u16, true, &LO_MAC),
2232            )
2233            .into_rtnl_new_link(UNSPECIFIED_SEQUENCE_NUMBER, false),
2234            ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
2235        );
2236        let eth_link = SentMessage::multicast(
2237            create_netlink_link_message(
2238                ETH_INTERFACE_ID,
2239                arphrd_ether_u16,
2240                0,
2241                create_nlas(ETH_NAME.to_string(), arphrd_ether_u16, false, &ETH_MAC),
2242            )
2243            .into_rtnl_del_link(UNSPECIFIED_SEQUENCE_NUMBER),
2244            ModernGroup(rtnetlink_groups_RTNLGRP_LINK),
2245        );
2246        assert_eq!(
2247            &link_sink.take_messages()[..],
2248            &[wlan_link.clone(), lo_link.clone(), eth_link.clone(),],
2249        );
2250
2251        let wlan_v4_addr = SentMessage::multicast(
2252            create_address_message(
2253                WLAN_INTERFACE_ID.try_into().unwrap(),
2254                TEST_V4_ADDR,
2255                WLAN_NAME.to_string(),
2256                IFA_F_PERMANENT | IFA_F_TENTATIVE,
2257            )
2258            .to_rtnl_new_addr(UNSPECIFIED_SEQUENCE_NUMBER, false),
2259            ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR),
2260        );
2261        let eth_v4_addr = SentMessage::multicast(
2262            create_address_message(
2263                ETH_INTERFACE_ID.try_into().unwrap(),
2264                TEST_V4_ADDR,
2265                ETH_NAME.to_string(),
2266                IFA_F_PERMANENT | IFA_F_TENTATIVE,
2267            )
2268            .to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
2269            ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR),
2270        );
2271        let ppp_v4_addr = SentMessage::multicast(
2272            create_address_message(
2273                PPP_INTERFACE_ID.try_into().unwrap(),
2274                TEST_V4_ADDR,
2275                PPP_NAME.to_string(),
2276                IFA_F_PERMANENT,
2277            )
2278            .to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
2279            ModernGroup(rtnetlink_groups_RTNLGRP_IPV4_IFADDR),
2280        );
2281        assert_eq!(
2282            &addr4_sink.take_messages()[..],
2283            &[wlan_v4_addr.clone(), eth_v4_addr.clone(), ppp_v4_addr.clone(),],
2284        );
2285
2286        let wlan_v6_addr = SentMessage::multicast(
2287            create_address_message(
2288                WLAN_INTERFACE_ID.try_into().unwrap(),
2289                TEST_V6_ADDR,
2290                WLAN_NAME.to_string(),
2291                IFA_F_PERMANENT | IFA_F_TENTATIVE,
2292            )
2293            .to_rtnl_new_addr(UNSPECIFIED_SEQUENCE_NUMBER, false),
2294            ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2295        );
2296        let lo_v6_addr = SentMessage::multicast(
2297            create_address_message(
2298                LO_INTERFACE_ID.try_into().unwrap(),
2299                TEST_V6_ADDR,
2300                LO_NAME.to_string(),
2301                IFA_F_PERMANENT,
2302            )
2303            .to_rtnl_new_addr(UNSPECIFIED_SEQUENCE_NUMBER, false),
2304            ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2305        );
2306        let eth_v6_addr = SentMessage::multicast(
2307            create_address_message(
2308                ETH_INTERFACE_ID.try_into().unwrap(),
2309                TEST_V6_ADDR,
2310                ETH_NAME.to_string(),
2311                IFA_F_PERMANENT | IFA_F_TENTATIVE,
2312            )
2313            .to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
2314            ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2315        );
2316        let ppp_v6_addr = SentMessage::multicast(
2317            create_address_message(
2318                PPP_INTERFACE_ID.try_into().unwrap(),
2319                TEST_V6_ADDR,
2320                PPP_NAME.to_string(),
2321                IFA_F_PERMANENT,
2322            )
2323            .to_rtnl_del_addr(UNSPECIFIED_SEQUENCE_NUMBER),
2324            ModernGroup(rtnetlink_groups_RTNLGRP_IPV6_IFADDR),
2325        );
2326        assert_eq!(
2327            &addr6_sink.take_messages()[..],
2328            &[wlan_v6_addr.clone(), lo_v6_addr.clone(), eth_v6_addr.clone(), ppp_v6_addr.clone(),],
2329        );
2330
2331        assert_eq!(
2332            &all_sink.take_messages()[..],
2333            &[
2334                // New links always appear before their addresses.
2335                wlan_link,
2336                wlan_v4_addr,
2337                wlan_v6_addr,
2338                lo_link,
2339                lo_v6_addr,
2340                // Removed addresses always appear before removed interfaces.
2341                eth_v4_addr,
2342                eth_v6_addr,
2343                eth_link,
2344                ppp_v4_addr,
2345                ppp_v6_addr,
2346            ],
2347        );
2348        assert_eq!(&other_sink.take_messages()[..], &[]);
2349    }
2350
2351    const LO_MAC: Option<fnet::MacAddress> = None;
2352    const ETH_MAC: Option<fnet::MacAddress> = Some(fnet::MacAddress { octets: [1, 1, 1, 1, 1, 1] });
2353    const PPP_MAC: Option<fnet::MacAddress> = Some(fnet::MacAddress { octets: [2, 2, 2, 2, 2, 2] });
2354    const WLAN_MAC: Option<fnet::MacAddress> =
2355        Some(fnet::MacAddress { octets: [3, 3, 3, 3, 3, 3] });
2356
2357    fn handle_get_mac_root_request_or_panic(req: fnet_root::InterfacesRequest) {
2358        match req {
2359            fnet_root::InterfacesRequest::GetMac { id, responder } => {
2360                let link_address = match id {
2361                    LO_INTERFACE_ID => LO_MAC,
2362                    ETH_INTERFACE_ID => ETH_MAC,
2363                    PPP_INTERFACE_ID => PPP_MAC,
2364                    WLAN_INTERFACE_ID => WLAN_MAC,
2365                    id => panic!("unexpected interface ID {id}"),
2366                };
2367
2368                responder.send(Ok(link_address.as_ref())).unwrap()
2369            }
2370            req => panic!("unexpected request {:?}", req),
2371        }
2372    }
2373
2374    fn expect_only_get_mac_root_requests(
2375        interfaces_request_stream: fnet_root::InterfacesRequestStream,
2376    ) -> impl Stream<Item = fnet_interfaces::Event> {
2377        futures::stream::unfold(interfaces_request_stream, |interfaces_request_stream| async move {
2378            interfaces_request_stream
2379                .for_each(|req| async move { handle_get_mac_root_request_or_panic(req.unwrap()) })
2380                .await;
2381
2382            None
2383        })
2384    }
2385
2386    async fn handle_only_get_mac_root_requests_fut(
2387        interfaces_request_stream: fnet_root::InterfacesRequestStream,
2388    ) {
2389        expect_only_get_mac_root_requests(interfaces_request_stream)
2390            .for_each(|item| async move { panic!("unexpected item = {item:?}") })
2391            .await
2392    }
2393
2394    #[derive(Debug, PartialEq)]
2395    struct TestRequestResult {
2396        messages: Vec<SentMessage<RouteNetlinkMessage>>,
2397        waiter_results: Vec<Result<(), RequestError>>,
2398    }
2399
2400    /// Test helper to handle a request.
2401    ///
2402    /// `root_handler` returns a future that returns an iterator of
2403    /// `fuchsia.net.interfaces/Event`s to feed to the netlink eventloop's
2404    /// interfaces watcher after a root API request is handled.
2405    async fn test_request<
2406        St: Stream<Item = fnet_interfaces::Event>,
2407        F: FnOnce(fnet_root::InterfacesRequestStream) -> St,
2408    >(
2409        args: impl IntoIterator<Item = RequestArgs>,
2410        root_handler: F,
2411    ) -> TestRequestResult {
2412        test_request_with_initial_state(
2413            args,
2414            root_handler,
2415            InitialState { eth_interface_online: false },
2416        )
2417        .await
2418    }
2419
2420    #[derive(Clone, Copy, Debug)]
2421    struct InitialState {
2422        eth_interface_online: bool,
2423    }
2424
2425    /// Test helper to handle a request.
2426    ///
2427    /// `root_handler` returns a future that returns an iterator of
2428    /// `fuchsia.net.interfaces/Event`s to feed to the netlink eventloop's
2429    /// interfaces watcher after a root API request is handled.
2430    /// `initial_state` parametrizes the initial state of the interfaces prior to the request being
2431    /// handled.
2432    async fn test_request_with_initial_state<
2433        St: Stream<Item = fnet_interfaces::Event>,
2434        F: FnOnce(fnet_root::InterfacesRequestStream) -> St,
2435    >(
2436        args: impl IntoIterator<Item = RequestArgs>,
2437        root_handler: F,
2438        initial_state: InitialState,
2439    ) -> TestRequestResult {
2440        let scope = fasync::Scope::new();
2441        let result = {
2442            let InitialState { eth_interface_online } = initial_state;
2443
2444            let (mut expected_sink, expected_client, async_work_drain_task) =
2445                crate::client::testutil::new_fake_client::<NetlinkRoute>(
2446                    crate::client::testutil::CLIENT_ID_1,
2447                    std::iter::empty(),
2448                );
2449            let _join_handle = scope.spawn(async_work_drain_task);
2450            let (mut other_sink, other_client, async_work_drain_task) =
2451                crate::client::testutil::new_fake_client::<NetlinkRoute>(
2452                    crate::client::testutil::CLIENT_ID_2,
2453                    std::iter::empty(),
2454                );
2455            let _join_handle = scope.spawn(async_work_drain_task);
2456            let Setup {
2457                event_loop_fut,
2458                mut watcher_stream,
2459                request_sink,
2460                interfaces_request_stream,
2461                interfaces_handler_sink: _,
2462                _async_work_sink: _,
2463            } = setup_with_route_clients({
2464                let route_clients = ClientTable::default();
2465                route_clients.add_client(expected_client.clone());
2466                route_clients.add_client(other_client);
2467                route_clients
2468            });
2469            let event_loop_fut = event_loop_fut.fuse();
2470            let mut event_loop_fut = pin!(event_loop_fut);
2471
2472            let watcher_stream_fut = respond_to_watcher(
2473                watcher_stream.by_ref(),
2474                [
2475                    fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2476                        id: Some(LO_INTERFACE_ID),
2477                        name: Some(LO_NAME.to_string()),
2478                        port_class: Some(LOOPBACK.into()),
2479                        online: Some(true),
2480                        addresses: Some(vec![test_addr(TEST_V6_ADDR), test_addr(TEST_V4_ADDR)]),
2481                        has_default_ipv4_route: Some(false),
2482                        has_default_ipv6_route: Some(false),
2483                        ..Default::default()
2484                    }),
2485                    fnet_interfaces::Event::Existing(fnet_interfaces::Properties {
2486                        id: Some(ETH_INTERFACE_ID),
2487                        name: Some(ETH_NAME.to_string()),
2488                        port_class: Some(ETHERNET.into()),
2489                        online: Some(eth_interface_online),
2490                        addresses: Some(vec![test_addr(TEST_V4_ADDR), test_addr(TEST_V6_ADDR)]),
2491                        has_default_ipv4_route: Some(false),
2492                        has_default_ipv6_route: Some(false),
2493                        ..Default::default()
2494                    }),
2495                    fnet_interfaces::Event::Idle(fnet_interfaces::Empty),
2496                ],
2497            );
2498            futures::select_biased! {
2499                err = event_loop_fut => unreachable!("eventloop should not return: {err:?}"),
2500                () = watcher_stream_fut.fuse() => {},
2501            }
2502            assert_eq!(&expected_sink.take_messages()[..], &[]);
2503            assert_eq!(&other_sink.take_messages()[..], &[]);
2504
2505            let expected_client = &expected_client;
2506            let fut = futures::stream::iter(args).fold(
2507                (Vec::new(), request_sink),
2508                |(mut results, mut request_sink), args| async move {
2509                    let (completer, waiter) = oneshot::channel();
2510                    request_sink
2511                        .send(crate::route_eventloop::UnifiedRequest::InterfacesRequest(Request {
2512                            args,
2513                            sequence_number: TEST_SEQUENCE_NUMBER,
2514                            client: expected_client.clone(),
2515                            completer,
2516                        }))
2517                        .await
2518                        .unwrap();
2519                    results.push(waiter.await.unwrap());
2520                    (results, request_sink)
2521                },
2522            );
2523            // Handle root API requests then feed the returned
2524            // `fuchsia.net.interfaces/Event`s to the watcher.
2525            let watcher_fut = root_handler(interfaces_request_stream).map(Ok).forward(
2526                futures::sink::unfold(watcher_stream.by_ref(), |st, event| async {
2527                    respond_to_watcher(st.by_ref(), [event]).await;
2528                    Ok::<_, std::convert::Infallible>(st)
2529                }),
2530            );
2531            let waiter_results = futures::select_biased! {
2532                res = futures::future::join(watcher_fut, event_loop_fut) => {
2533                    unreachable!("eventloop/watcher should not return: {res:?}")
2534                },
2535                (results, _request_sink) = fut.fuse() => results
2536            };
2537            assert_eq!(&other_sink.take_messages()[..], &[]);
2538
2539            TestRequestResult { messages: expected_sink.take_messages(), waiter_results }
2540        };
2541        scope.join().await;
2542        result
2543    }
2544
2545    #[test_case(
2546        GetLinkArgs::Dump,
2547        &[LO_INTERFACE_ID, ETH_INTERFACE_ID],
2548        Ok(()); "dump")]
2549    #[test_case(
2550        GetLinkArgs::Get(LinkSpecifier::Index(
2551            NonZeroU32::new(LO_INTERFACE_ID.try_into().unwrap()).unwrap())),
2552        &[LO_INTERFACE_ID],
2553        Ok(()); "id")]
2554    #[test_case(
2555        GetLinkArgs::Get(LinkSpecifier::Index(
2556            NonZeroU32::new(WLAN_INTERFACE_ID.try_into().unwrap()).unwrap())),
2557        &[],
2558        Err(RequestError::UnrecognizedInterface); "id_not_found")]
2559    #[test_case(
2560        GetLinkArgs::Get(LinkSpecifier::Name(LO_NAME.to_string())),
2561        &[LO_INTERFACE_ID],
2562        Ok(()); "name")]
2563    #[test_case(
2564        GetLinkArgs::Get(LinkSpecifier::Name(WLAN_NAME.to_string())),
2565        &[],
2566        Err(RequestError::UnrecognizedInterface); "name_not_found")]
2567    #[fuchsia::test]
2568    async fn test_get_link(
2569        args: GetLinkArgs,
2570        expected_new_links: &[u64],
2571        expected_result: Result<(), RequestError>,
2572    ) {
2573        let is_dump = match args {
2574            GetLinkArgs::Dump => true,
2575            GetLinkArgs::Get(_) => false,
2576        };
2577        // Conversion to u16 is safe because 1 <= 65535
2578        let arphrd_ether_u16: u16 = ARPHRD_ETHER as u16;
2579        // Conversion to u16 is safe because 772 <= 65535
2580        let arphrd_loopback_u16: u16 = ARPHRD_LOOPBACK as u16;
2581        let expected_messages = expected_new_links
2582            .iter()
2583            .map(|link_id| {
2584                let msg = match *link_id {
2585                    LO_INTERFACE_ID => create_netlink_link_message(
2586                        LO_INTERFACE_ID,
2587                        arphrd_loopback_u16,
2588                        ONLINE_IF_FLAGS | net_device_flags_IFF_LOOPBACK,
2589                        create_nlas(LO_NAME.to_string(), arphrd_loopback_u16, true, &LO_MAC),
2590                    ),
2591                    ETH_INTERFACE_ID => create_netlink_link_message(
2592                        ETH_INTERFACE_ID,
2593                        arphrd_ether_u16,
2594                        0,
2595                        create_nlas(ETH_NAME.to_string(), arphrd_ether_u16, false, &ETH_MAC),
2596                    ),
2597                    _ => unreachable!("GetLink should only be tested with loopback and ethernet"),
2598                };
2599                SentMessage::unicast(msg.into_rtnl_new_link(TEST_SEQUENCE_NUMBER, is_dump))
2600            })
2601            .collect();
2602
2603        assert_eq!(
2604            test_request(
2605                [RequestArgs::Link(LinkRequestArgs::Get(args))],
2606                expect_only_get_mac_root_requests,
2607            )
2608            .await,
2609            TestRequestResult {
2610                messages: expected_messages,
2611                waiter_results: vec![expected_result],
2612            },
2613        )
2614    }
2615
2616    fn handle_get_admin_for_eth_or_panic(
2617        req: Result<fnet_root::InterfacesRequest, fidl::Error>,
2618    ) -> impl Future<Output = Option<fnet_interfaces_admin::ControlRequestStream>> {
2619        futures::future::ready(match req.unwrap() {
2620            fnet_root::InterfacesRequest::GetAdmin { id, control, control_handle: _ } => {
2621                pretty_assertions::assert_eq!(id, ETH_INTERFACE_ID);
2622                Some(control.into_stream())
2623            }
2624            req => {
2625                handle_get_mac_root_request_or_panic(req);
2626                None
2627            }
2628        })
2629    }
2630
2631    /// Returns a `FnOnce` suitable for use with [`test_request`].
2632    ///
2633    /// The closure serves a single `GetAdmin` request for the Ethernet
2634    /// interface, and handles all subsequent
2635    /// [`fnet_interfaces_admin::ControlRequest`] by calling the provided
2636    /// handler.
2637    // TODO(https://github.com/rust-lang/rust/issues/99697): Remove the
2638    // `Pin<Box<dyn ...>>` from the return type once Rust supports
2639    // `impl Fn() -> impl <SomeTrait>` style declarations.
2640    fn expect_get_admin_with_handler<
2641        I: IntoIterator<Item = fnet_interfaces::Event> + 'static,
2642        H: FnMut(fnet_interfaces_admin::ControlRequest) -> I + 'static,
2643    >(
2644        admin_handler: H,
2645    ) -> impl FnOnce(
2646        fnet_root::InterfacesRequestStream,
2647    ) -> Pin<Box<dyn Stream<Item = fnet_interfaces::Event>>> {
2648        move |interfaces_request_stream: fnet_root::InterfacesRequestStream| {
2649            Box::pin(
2650                interfaces_request_stream
2651                    .filter_map(|req| handle_get_admin_for_eth_or_panic(req))
2652                    .into_future()
2653                    // This module's implementation is expected to only acquire one
2654                    // admin control handle per interface, so drop the remaining
2655                    // stream of admin control request streams.
2656                    .map(|(admin_control_stream, _stream_of_admin_control_streams)| {
2657                        admin_control_stream.unwrap()
2658                    })
2659                    .flatten_stream()
2660                    // Handle each Control request with the provided handler.
2661                    // `scan` transfers ownership of `admin_handle`, which
2662                    // circumvents some borrow chcker issues we would encounter
2663                    // with `map`.
2664                    .scan(admin_handler, |admin_handler, req| {
2665                        futures::future::ready(Some(futures::stream::iter(admin_handler(
2666                            req.unwrap(),
2667                        ))))
2668                    })
2669                    .flatten(),
2670            )
2671        }
2672    }
2673
2674    #[test_case(
2675        InitialState { eth_interface_online: false },
2676        SetLinkArgs{
2677            link: LinkSpecifier::Name(ETH_NAME.to_string()),
2678            enable: None,
2679        },
2680        Ok(true),
2681        Ok(()); "no_change")]
2682    #[test_case(
2683        InitialState { eth_interface_online: false },
2684        SetLinkArgs{
2685            link: LinkSpecifier::Name(WLAN_NAME.to_string()),
2686            enable: None,
2687        },
2688        Ok(true),
2689        Err(RequestError::UnrecognizedInterface); "no_change_name_not_found")]
2690    #[test_case(
2691        InitialState { eth_interface_online: false },
2692        SetLinkArgs {
2693            link: LinkSpecifier::Index(
2694                NonZeroU32::new(WLAN_INTERFACE_ID.try_into().unwrap()).unwrap()),
2695            enable: None,
2696        },
2697        Ok(true),
2698        Err(RequestError::UnrecognizedInterface); "no_change_id_not_found")]
2699    #[test_case(
2700        InitialState { eth_interface_online: false },
2701        SetLinkArgs{
2702            link: LinkSpecifier::Name(ETH_NAME.to_string()),
2703            enable: Some(true),
2704        },
2705        Ok(false),
2706        Ok(()); "enable_no_op_succeeds")]
2707    #[test_case(
2708        InitialState { eth_interface_online: false },
2709        SetLinkArgs{
2710            link: LinkSpecifier::Name(ETH_NAME.to_string()),
2711            enable: Some(true),
2712        },
2713        Ok(true),
2714        Ok(()); "enable_newly_succeeds")]
2715    #[test_case(
2716        InitialState { eth_interface_online: false },
2717        SetLinkArgs{
2718            link: LinkSpecifier::Name(WLAN_NAME.to_string()),
2719            enable: Some(true),
2720        },
2721        Ok(true),
2722        Err(RequestError::UnrecognizedInterface); "enable_not_found")]
2723    #[test_case(
2724        InitialState { eth_interface_online: false },
2725        SetLinkArgs{
2726            link: LinkSpecifier::Name(ETH_NAME.to_string()),
2727            enable: Some(true),
2728        },
2729        Err(()),
2730        Err(RequestError::Unknown); "enable_fails")]
2731    #[test_case(
2732        InitialState { eth_interface_online: false },
2733        SetLinkArgs{
2734            link: LinkSpecifier::Name(ETH_NAME.to_string()),
2735            enable: Some(false),
2736        },
2737        Ok(false),
2738        Ok(()); "disable_no_op_succeeds")]
2739    #[test_case(
2740        InitialState { eth_interface_online: true },
2741        SetLinkArgs{
2742            link: LinkSpecifier::Name(ETH_NAME.to_string()),
2743            enable: Some(false),
2744        },
2745        Ok(true),
2746        Ok(()); "disable_newly_succeeds")]
2747    #[test_case(
2748        InitialState { eth_interface_online: false },
2749        SetLinkArgs{
2750            link: LinkSpecifier::Name(WLAN_NAME.to_string()),
2751            enable: Some(false),
2752        },
2753        Ok(true),
2754        Err(RequestError::UnrecognizedInterface); "disable_not_found")]
2755    #[test_case(
2756        InitialState { eth_interface_online: false },
2757        SetLinkArgs{
2758            link: LinkSpecifier::Name(ETH_NAME.to_string()),
2759            enable: Some(false),
2760        },
2761        Err(()),
2762        Err(RequestError::Unknown); "disable_fails")]
2763    #[fuchsia::test]
2764    async fn test_set_link(
2765        initial_state: InitialState,
2766        args: SetLinkArgs,
2767        control_response: Result<bool, ()>,
2768        expected_result: Result<(), RequestError>,
2769    ) {
2770        let SetLinkArgs { link: _, enable } = args.clone();
2771        let request = RequestArgs::Link(LinkRequestArgs::Set(args));
2772
2773        let control_response_clone = control_response.clone();
2774        let handle_enable =
2775            move |req: fnet_interfaces_admin::ControlRequest| -> Option<fnet_interfaces::Event> {
2776                let responder = match req {
2777                    fnet_interfaces_admin::ControlRequest::Enable { responder } => responder,
2778                    _ => panic!("unexpected ControlRequest received"),
2779                };
2780                match control_response {
2781                    Err(()) => {
2782                        responder
2783                            .send(Err(fnet_interfaces_admin::ControlEnableError::unknown()))
2784                            .expect("should send response");
2785                        None
2786                    }
2787                    Ok(newly_enabled) => {
2788                        responder.send(Ok(newly_enabled)).expect("should send response");
2789                        newly_enabled.then_some(fnet_interfaces::Event::Changed(
2790                            fnet_interfaces::Properties {
2791                                id: Some(ETH_INTERFACE_ID),
2792                                online: Some(true),
2793                                ..fnet_interfaces::Properties::default()
2794                            },
2795                        ))
2796                    }
2797                }
2798            };
2799        let handle_disable =
2800            move |req: fnet_interfaces_admin::ControlRequest| -> Option<fnet_interfaces::Event> {
2801                let responder = match req {
2802                    fnet_interfaces_admin::ControlRequest::Disable { responder } => responder,
2803                    _ => panic!("unexpected ControlRequest received"),
2804                };
2805                match control_response_clone {
2806                    Err(()) => {
2807                        responder
2808                            .send(Err(fnet_interfaces_admin::ControlDisableError::unknown()))
2809                            .expect("should send response");
2810                        None
2811                    }
2812                    Ok(newly_disabled) => {
2813                        responder.send(Ok(newly_disabled)).expect("should send response");
2814                        newly_disabled.then_some(fnet_interfaces::Event::Changed(
2815                            fnet_interfaces::Properties {
2816                                id: Some(ETH_INTERFACE_ID),
2817                                online: Some(false),
2818                                ..fnet_interfaces::Properties::default()
2819                            },
2820                        ))
2821                    }
2822                }
2823            };
2824
2825        let test_result = match enable {
2826            None => {
2827                test_request_with_initial_state(
2828                    [request],
2829                    expect_only_get_mac_root_requests,
2830                    initial_state,
2831                )
2832                .await
2833            }
2834            Some(true) => {
2835                test_request_with_initial_state(
2836                    [request],
2837                    expect_get_admin_with_handler(handle_enable),
2838                    initial_state,
2839                )
2840                .await
2841            }
2842            Some(false) => {
2843                test_request_with_initial_state(
2844                    [request],
2845                    expect_get_admin_with_handler(handle_disable),
2846                    initial_state,
2847                )
2848                .await
2849            }
2850        };
2851
2852        assert_eq!(
2853            test_result,
2854            TestRequestResult {
2855                // SetLink requests never result in messages. Acks/errors
2856                // are handled by the caller.
2857                messages: vec![],
2858                waiter_results: vec![expected_result],
2859            },
2860        )
2861    }
2862
2863    #[test_case(Some(IpVersion::V4); "v4")]
2864    #[test_case(Some(IpVersion::V6); "v6")]
2865    #[test_case(None; "all")]
2866    #[fuchsia::test]
2867    async fn test_get_addr(ip_version_filter: Option<IpVersion>) {
2868        pretty_assertions::assert_eq!(
2869            test_request(
2870                [RequestArgs::Address(AddressRequestArgs::Get(GetAddressArgs::Dump {
2871                    ip_version_filter
2872                }))],
2873                expect_only_get_mac_root_requests,
2874            )
2875            .await,
2876            TestRequestResult {
2877                messages: [(LO_INTERFACE_ID, LO_NAME), (ETH_INTERFACE_ID, ETH_NAME)]
2878                    .into_iter()
2879                    .map(|(id, name)| {
2880                        [TEST_V4_ADDR, TEST_V6_ADDR]
2881                            .into_iter()
2882                            .filter(|fnet::Subnet { addr, prefix_len: _ }| {
2883                                ip_version_filter.map_or(true, |ip_version| {
2884                                    ip_version.eq(&match addr {
2885                                        fnet::IpAddress::Ipv4(_) => IpVersion::V4,
2886                                        fnet::IpAddress::Ipv6(_) => IpVersion::V6,
2887                                    })
2888                                })
2889                            })
2890                            .map(move |addr| {
2891                                SentMessage::unicast(
2892                                    create_address_message(
2893                                        id.try_into().unwrap(),
2894                                        addr,
2895                                        name.to_string(),
2896                                        IFA_F_PERMANENT,
2897                                    )
2898                                    .to_rtnl_new_addr(TEST_SEQUENCE_NUMBER, true),
2899                                )
2900                            })
2901                    })
2902                    .flatten()
2903                    .collect(),
2904                waiter_results: vec![Ok(())],
2905            },
2906        );
2907    }
2908
2909    /// Tests RTM_NEWADDR and RTM_DEL_ADDR when the interface is removed,
2910    /// indicated by the closure of the admin Control's server-end.
2911    #[test_case(
2912        test_addr_subnet_v4(),
2913        None,
2914        true; "v4_no_terminal_new")]
2915    #[test_case(
2916        test_addr_subnet_v6(),
2917        None,
2918        true; "v6_no_terminal_new")]
2919    #[test_case(
2920        test_addr_subnet_v4(),
2921        Some(InterfaceRemovedReason::PortClosed),
2922        true; "v4_port_closed_terminal_new")]
2923    #[test_case(
2924        test_addr_subnet_v6(),
2925        Some(InterfaceRemovedReason::PortClosed),
2926        true; "v6_port_closed_terminal_new")]
2927    #[test_case(
2928        test_addr_subnet_v4(),
2929        Some(InterfaceRemovedReason::User),
2930        true; "v4_user_terminal_new")]
2931    #[test_case(
2932        test_addr_subnet_v6(),
2933        Some(InterfaceRemovedReason::User),
2934        true; "v6_user_terminal_new")]
2935    #[test_case(
2936        test_addr_subnet_v4(),
2937        None,
2938        false; "v4_no_terminal_del")]
2939    #[test_case(
2940        test_addr_subnet_v6(),
2941        None,
2942        false; "v6_no_terminal_del")]
2943    #[test_case(
2944        test_addr_subnet_v4(),
2945        Some(InterfaceRemovedReason::PortClosed),
2946        false; "v4_port_closed_terminal_del")]
2947    #[test_case(
2948        test_addr_subnet_v6(),
2949        Some(InterfaceRemovedReason::PortClosed),
2950        false; "v6_port_closed_terminal_del")]
2951    #[test_case(
2952        test_addr_subnet_v4(),
2953        Some(InterfaceRemovedReason::User),
2954        false; "v4_user_terminal_del")]
2955    #[test_case(
2956        test_addr_subnet_v6(),
2957        Some(InterfaceRemovedReason::User),
2958        false; "v6_user_terminal_del")]
2959    #[fuchsia::test]
2960    async fn test_new_del_addr_interface_removed(
2961        address: AddrSubnetEither,
2962        removal_reason: Option<InterfaceRemovedReason>,
2963        is_new: bool,
2964    ) {
2965        let interface_id = NonZeroU32::new(LO_INTERFACE_ID.try_into().unwrap()).unwrap();
2966        let address_and_interface_id = AddressAndInterfaceArgs { address, interface_id };
2967        pretty_assertions::assert_eq!(
2968            test_request(
2969                [if is_new {
2970                    RequestArgs::Address(AddressRequestArgs::New(NewAddressArgs {
2971                        address_and_interface_id,
2972                        add_subnet_route: false,
2973                    }))
2974                } else {
2975                    RequestArgs::Address(AddressRequestArgs::Del(DelAddressArgs {
2976                        address_and_interface_id,
2977                    }))
2978                }],
2979                |interfaces_request_stream| futures::stream::unfold(
2980                    interfaces_request_stream,
2981                    |interfaces_request_stream| async move {
2982                        interfaces_request_stream
2983                            .for_each(|req| {
2984                                futures::future::ready(match req.unwrap() {
2985                                    fnet_root::InterfacesRequest::GetAdmin {
2986                                        id,
2987                                        control,
2988                                        control_handle: _,
2989                                    } => {
2990                                        pretty_assertions::assert_eq!(id, LO_INTERFACE_ID);
2991                                        let control = control.into_stream();
2992                                        let control = control.control_handle();
2993                                        if let Some(reason) = removal_reason {
2994                                            control.send_on_interface_removed(reason).unwrap()
2995                                        }
2996                                        control.shutdown();
2997                                    }
2998                                    req => handle_get_mac_root_request_or_panic(req),
2999                                })
3000                            })
3001                            .await;
3002
3003                        unreachable!("interfaces request stream should not end")
3004                    },
3005                ),
3006            )
3007            .await,
3008            TestRequestResult {
3009                messages: Vec::new(),
3010                waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
3011            },
3012        )
3013    }
3014
3015    enum AddressRequestKind {
3016        New { add_subnet_route: bool },
3017        Del,
3018    }
3019
3020    /// Test that a request for an interface the eventloop does not recognize
3021    /// fails with an unrecognized interface error.
3022    #[test_case(
3023        add_test_addr_subnet_v4(),
3024        AddressRequestKind::New { add_subnet_route: false }; "v4_new")]
3025    #[test_case(
3026        add_test_addr_subnet_v6(),
3027        AddressRequestKind::New { add_subnet_route: false }; "v6_new")]
3028    #[test_case(add_test_addr_subnet_v4(), AddressRequestKind::Del; "v4_del")]
3029    #[test_case(add_test_addr_subnet_v6(), AddressRequestKind::Del; "v6_del")]
3030    #[fuchsia::test]
3031    async fn test_unknown_interface_request(address: AddrSubnetEither, kind: AddressRequestKind) {
3032        let interface_id = NonZeroU32::new(WLAN_INTERFACE_ID.try_into().unwrap()).unwrap();
3033        let address_and_interface_id = AddressAndInterfaceArgs { address, interface_id };
3034        pretty_assertions::assert_eq!(
3035            test_request(
3036                [match kind {
3037                    AddressRequestKind::New { add_subnet_route } => {
3038                        RequestArgs::Address(AddressRequestArgs::New(NewAddressArgs {
3039                            address_and_interface_id,
3040                            add_subnet_route,
3041                        }))
3042                    }
3043                    AddressRequestKind::Del => {
3044                        RequestArgs::Address(AddressRequestArgs::Del(DelAddressArgs {
3045                            address_and_interface_id,
3046                        }))
3047                    }
3048                }],
3049                expect_only_get_mac_root_requests,
3050            )
3051            .await,
3052            TestRequestResult {
3053                messages: Vec::new(),
3054                waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
3055            },
3056        )
3057    }
3058
3059    struct TestInterfaceRequestCase<F> {
3060        address: AddrSubnetEither,
3061        kind: AddressRequestKind,
3062        control_request_handler: F,
3063    }
3064
3065    impl<F> TestInterfaceRequestCase<F> {
3066        fn into_request_args_and_handler(self, interface_id: NonZeroU32) -> (RequestArgs, F) {
3067            let Self { address, kind, control_request_handler } = self;
3068            let address_and_interface_id = AddressAndInterfaceArgs { address, interface_id };
3069            let args = match kind {
3070                AddressRequestKind::New { add_subnet_route } => {
3071                    RequestArgs::Address(AddressRequestArgs::New(NewAddressArgs {
3072                        address_and_interface_id,
3073                        add_subnet_route,
3074                    }))
3075                }
3076                AddressRequestKind::Del => {
3077                    RequestArgs::Address(AddressRequestArgs::Del(DelAddressArgs {
3078                        address_and_interface_id,
3079                    }))
3080                }
3081            };
3082
3083            (args, control_request_handler)
3084        }
3085    }
3086
3087    /// A test helper that calls the (up to two) test cases' callback with a
3088    /// [`fnet_interfaces_admin::ControlRequest`] as they arrive.
3089    ///
3090    /// This implementation makes sure that the the control handle for the
3091    /// interface is only requested once.
3092    async fn test_maybe_two_interface_requests_on_single_control<
3093        St1: Stream<Item = fnet_interfaces::Event>,
3094        F1: FnMut(fnet_interfaces_admin::ControlRequest) -> St1,
3095        St2: Stream<Item = fnet_interfaces::Event>,
3096        F2: FnMut(fnet_interfaces_admin::ControlRequest) -> St2,
3097    >(
3098        case1: TestInterfaceRequestCase<F1>,
3099        case2: Option<TestInterfaceRequestCase<F2>>,
3100    ) -> TestRequestResult {
3101        let interface_id = NonZeroU32::new(ETH_INTERFACE_ID.try_into().unwrap()).unwrap();
3102        let (args1, mut control_request_handler1) =
3103            case1.into_request_args_and_handler(interface_id);
3104
3105        let (args2, control_request_handler2) = if let Some(case) = case2 {
3106            let (args, control_request_handler) = case.into_request_args_and_handler(interface_id);
3107            (Some(args), Some(control_request_handler))
3108        } else {
3109            (None, None)
3110        };
3111
3112        test_request([args1].into_iter().chain(args2), |interfaces_request_stream| {
3113            interfaces_request_stream
3114                .filter_map(|req| handle_get_admin_for_eth_or_panic(req))
3115                .into_future()
3116                // This method supports tests that want to make sure that the
3117                // admin control is only requested once so we drop the remaining
3118                // stream of admin control request streams.
3119                .map(|(admin_control_stream, _stream_of_admin_control_streams)| {
3120                    admin_control_stream.unwrap()
3121                })
3122                .flatten_stream()
3123                .into_future()
3124                .map(|(admin_control_req, admin_control_stream)| {
3125                    control_request_handler1(admin_control_req.unwrap().unwrap()).chain(
3126                        futures::stream::iter(control_request_handler2.map(
3127                            |mut control_request_handler2| {
3128                                admin_control_stream
3129                                    .into_future()
3130                                    .map(move |(admin_control_req, _admin_control_stream)| {
3131                                        control_request_handler2(
3132                                            admin_control_req.unwrap().unwrap(),
3133                                        )
3134                                    })
3135                                    .flatten_stream()
3136                            },
3137                        ))
3138                        .flatten(),
3139                    )
3140                })
3141                .flatten_stream()
3142        })
3143        .await
3144    }
3145
3146    /// A test helper that calls the callback with a
3147    /// [`fnet_interfaces_admin::ControlRequest`] as they arrive.
3148    async fn test_interface_request<
3149        St: Stream<Item = fnet_interfaces::Event>,
3150        F: FnMut(fnet_interfaces_admin::ControlRequest) -> St,
3151    >(
3152        case: TestInterfaceRequestCase<F>,
3153    ) -> TestRequestResult {
3154        test_maybe_two_interface_requests_on_single_control(
3155            case,
3156            None::<TestInterfaceRequestCase<fn(_) -> futures::stream::Pending<_>>>,
3157        )
3158        .await
3159    }
3160
3161    /// An RTM_NEWADDR test helper that calls the callback with a stream of ASP
3162    /// requests.
3163    async fn test_new_addr_asp_helper<
3164        St: Stream<Item = fnet_interfaces::Event>,
3165        F: Fn(fnet_interfaces_admin::AddressStateProviderRequestStream) -> St,
3166    >(
3167        address: AddrSubnetEither,
3168        add_subnet_route: bool,
3169        asp_handler: F,
3170    ) -> TestRequestResult {
3171        test_interface_request(TestInterfaceRequestCase {
3172            address,
3173            kind: AddressRequestKind::New { add_subnet_route },
3174            control_request_handler: |req| match req {
3175                fnet_interfaces_admin::ControlRequest::AddAddress {
3176                    address: got_address,
3177                    parameters,
3178                    address_state_provider,
3179                    control_handle: _,
3180                } => {
3181                    pretty_assertions::assert_eq!(got_address, address.into_ext());
3182                    pretty_assertions::assert_eq!(
3183                        parameters,
3184                        fnet_interfaces_admin::AddressParameters {
3185                            add_subnet_route: Some(add_subnet_route),
3186                            ..fnet_interfaces_admin::AddressParameters::default()
3187                        },
3188                    );
3189                    asp_handler(address_state_provider.into_stream())
3190                }
3191                req => panic!("unexpected request {req:?}"),
3192            },
3193        })
3194        .await
3195    }
3196
3197    /// Tests RTM_NEWADDR when the ASP is dropped immediately (doesn't handle
3198    /// any request).
3199    #[test_case(test_addr_subnet_v4(); "v4")]
3200    #[test_case(test_addr_subnet_v6(); "v6")]
3201    #[fuchsia::test]
3202    async fn test_new_addr_drop_asp_immediately(address: AddrSubnetEither) {
3203        pretty_assertions::assert_eq!(
3204            test_new_addr_asp_helper(address, false, |_asp_request_stream| {
3205                futures::stream::empty()
3206            })
3207            .await,
3208            TestRequestResult {
3209                messages: Vec::new(),
3210                waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
3211            },
3212        )
3213    }
3214
3215    /// RTM_NEWADDR test helper that exercises the ASP being closed with a
3216    /// terminal event.
3217    async fn test_new_addr_failed_helper(
3218        address: AddrSubnetEither,
3219        reason: AddressRemovalReason,
3220    ) -> TestRequestResult {
3221        test_new_addr_asp_helper(address, true, |asp_request_stream| {
3222            asp_request_stream.control_handle().send_on_address_removed(reason).unwrap();
3223            futures::stream::empty()
3224        })
3225        .await
3226    }
3227
3228    /// Tests RTM_NEWADDR when the ASP is closed with an unexpected terminal
3229    /// event.
3230    #[test_case(
3231        test_addr_subnet_v4(),
3232        AddressRemovalReason::DadFailed; "v4_dad_failed")]
3233    #[test_case(
3234        test_addr_subnet_v6(),
3235        AddressRemovalReason::DadFailed; "v6_dad_failed")]
3236    #[test_case(
3237        test_addr_subnet_v4(),
3238        AddressRemovalReason::InterfaceRemoved; "v4_interface_removed")]
3239    #[test_case(
3240        test_addr_subnet_v6(),
3241        AddressRemovalReason::InterfaceRemoved; "v6_interface_removed")]
3242    #[test_case(
3243        test_addr_subnet_v4(),
3244        AddressRemovalReason::UserRemoved; "v4_user_removed")]
3245    #[test_case(
3246        test_addr_subnet_v6(),
3247        AddressRemovalReason::UserRemoved; "v6_user_removed")]
3248    #[should_panic(expected = "expected netstack to send initial state before removing")]
3249    #[fuchsia::test]
3250    async fn test_new_addr_failed_unexpected_reason(
3251        address: AddrSubnetEither,
3252        reason: AddressRemovalReason,
3253    ) {
3254        let _: TestRequestResult = test_new_addr_failed_helper(address, reason).await;
3255    }
3256
3257    /// Tests RTM_NEWADDR when the ASP is gracefully closed with a terminal event.
3258    #[test_case(
3259        test_addr_subnet_v4(),
3260        AddressRemovalReason::Invalid,
3261        RequestError::InvalidRequest; "v4_invalid")]
3262    #[test_case(
3263        test_addr_subnet_v6(),
3264        AddressRemovalReason::Invalid,
3265        RequestError::InvalidRequest; "v6_invalid")]
3266    #[test_case(
3267        test_addr_subnet_v4(),
3268        AddressRemovalReason::AlreadyAssigned,
3269        RequestError::AlreadyExists; "v4_exists")]
3270    #[test_case(
3271        test_addr_subnet_v6(),
3272        AddressRemovalReason::AlreadyAssigned,
3273        RequestError::AlreadyExists; "v6_exists")]
3274    #[fuchsia::test]
3275    async fn test_new_addr_failed(
3276        address: AddrSubnetEither,
3277        reason: AddressRemovalReason,
3278        expected_error: RequestError,
3279    ) {
3280        pretty_assertions::assert_eq!(
3281            test_new_addr_failed_helper(address, reason).await,
3282            TestRequestResult { messages: Vec::new(), waiter_results: vec![Err(expected_error)] },
3283        )
3284    }
3285
3286    /// An RTM_NEWADDR test helper that calls the callback with a stream of ASP
3287    /// requests after the Detach request is handled.
3288    async fn test_new_addr_asp_detach_handled_helper<
3289        St: Stream<Item = fnet_interfaces::Event>,
3290        F: Fn(fnet_interfaces_admin::AddressStateProviderRequestStream) -> St,
3291    >(
3292        address: AddrSubnetEither,
3293        add_subnet_route: bool,
3294        asp_handler: F,
3295    ) -> TestRequestResult {
3296        test_new_addr_asp_helper(address, add_subnet_route, |asp_request_stream| {
3297            asp_request_stream
3298                .into_future()
3299                .map(|(asp_request, asp_request_stream)| {
3300                    let _: fnet_interfaces_admin::AddressStateProviderControlHandle = asp_request
3301                        .expect("eventloop uses ASP before dropping")
3302                        .expect("unexpected error while waiting for Detach request")
3303                        .into_detach()
3304                        .expect("eventloop makes detach request immediately");
3305
3306                    asp_handler(asp_request_stream)
3307                })
3308                .flatten_stream()
3309        })
3310        .await
3311    }
3312
3313    /// Test RTM_NEWADDR when the ASP is dropped immediately after handling the
3314    /// Detach request (no assignment state update or terminal event).
3315    #[test_case(test_addr_subnet_v4(); "v4")]
3316    #[test_case(test_addr_subnet_v6(); "v6")]
3317    #[fuchsia::test]
3318    async fn test_new_addr_drop_asp_after_detach(address: AddrSubnetEither) {
3319        pretty_assertions::assert_eq!(
3320            test_new_addr_asp_detach_handled_helper(address, false, |_asp_stream| {
3321                futures::stream::empty()
3322            })
3323            .await,
3324            TestRequestResult {
3325                messages: Vec::new(),
3326                waiter_results: vec![Err(RequestError::UnrecognizedInterface)],
3327            },
3328        )
3329    }
3330
3331    /// Test RTM_NEWADDR when the ASP yields an assignment state update.
3332    #[test_case(add_test_addr_subnet_v4(); "v4")]
3333    #[test_case(add_test_addr_subnet_v6(); "v6")]
3334    #[fuchsia::test]
3335    async fn test_new_addr_with_address_added_event(address: AddrSubnetEither) {
3336        pretty_assertions::assert_eq!(
3337            test_new_addr_asp_detach_handled_helper(address, true, |asp_request_stream| {
3338                asp_request_stream
3339                    .control_handle()
3340                    .send_on_address_added()
3341                    .expect("send address added");
3342
3343                // Send an update with the added address to complete the
3344                // request.
3345                futures::stream::iter([fnet_interfaces::Event::Changed(
3346                    fnet_interfaces::Properties {
3347                        id: Some(ETH_INTERFACE_ID.try_into().unwrap()),
3348                        addresses: Some(vec![test_addr(address.into_ext())]),
3349                        ..fnet_interfaces::Properties::default()
3350                    },
3351                )])
3352            })
3353            .await,
3354            TestRequestResult { messages: Vec::new(), waiter_results: vec![Ok(())] },
3355        )
3356    }
3357
3358    /// Test RTM_DELADDR when the interface is closed with an unexpected reaosn.
3359    #[test_case(
3360        test_addr_subnet_v4(),
3361        InterfaceRemovedReason::DuplicateName; "v4_duplicate_name")]
3362    #[test_case(
3363        test_addr_subnet_v6(),
3364        InterfaceRemovedReason::DuplicateName; "v6_duplicate_name")]
3365    #[test_case(
3366        test_addr_subnet_v4(),
3367        InterfaceRemovedReason::PortAlreadyBound; "v4_port_already_bound")]
3368    #[test_case(
3369        test_addr_subnet_v6(),
3370        InterfaceRemovedReason::PortAlreadyBound; "v6_port_already_bound")]
3371    #[test_case(
3372        test_addr_subnet_v4(),
3373        InterfaceRemovedReason::BadPort; "v4_bad_port")]
3374    #[test_case(
3375        test_addr_subnet_v6(),
3376        InterfaceRemovedReason::BadPort; "v6_bad_port")]
3377    #[should_panic(expected = "unexpected interface removed reason")]
3378    #[fuchsia::test]
3379    async fn test_del_addr_interface_closed_unexpected_reason(
3380        address: AddrSubnetEither,
3381        removal_reason: InterfaceRemovedReason,
3382    ) {
3383        let _: TestRequestResult = test_interface_request(TestInterfaceRequestCase {
3384            address,
3385            kind: AddressRequestKind::Del,
3386            control_request_handler: |req| match req {
3387                fnet_interfaces_admin::ControlRequest::RemoveAddress {
3388                    address: got_address,
3389                    responder,
3390                } => {
3391                    pretty_assertions::assert_eq!(got_address, address.into_ext());
3392                    let control_handle = responder.control_handle();
3393                    control_handle.send_on_interface_removed(removal_reason).unwrap();
3394                    control_handle.shutdown();
3395                    futures::stream::empty()
3396                }
3397                req => panic!("unexpected request {req:?}"),
3398            },
3399        })
3400        .await;
3401    }
3402
3403    fn del_addr_test_interface_case(
3404        address: AddrSubnetEither,
3405        response: Result<bool, fnet_interfaces_admin::ControlRemoveAddressError>,
3406        remaining_address: Option<AddrSubnetEither>,
3407    ) -> TestInterfaceRequestCase<
3408        impl FnMut(
3409            fnet_interfaces_admin::ControlRequest,
3410        ) -> futures::stream::Iter<core::array::IntoIter<fnet_interfaces::Event, 1>>,
3411    > {
3412        TestInterfaceRequestCase {
3413            address,
3414            kind: AddressRequestKind::Del,
3415            control_request_handler: move |req| {
3416                match req {
3417                    fnet_interfaces_admin::ControlRequest::RemoveAddress {
3418                        address: got_address,
3419                        responder,
3420                    } => {
3421                        pretty_assertions::assert_eq!(got_address, address.into_ext());
3422                        responder.send(response).unwrap();
3423
3424                        // Send an update without the deleted address to complete
3425                        // the request.
3426                        futures::stream::iter([fnet_interfaces::Event::Changed(
3427                            fnet_interfaces::Properties {
3428                                id: Some(ETH_INTERFACE_ID.try_into().unwrap()),
3429                                addresses: Some(remaining_address.map_or_else(Vec::new, |addr| {
3430                                    vec![test_addr(addr.into_ext())]
3431                                })),
3432                                ..fnet_interfaces::Properties::default()
3433                            },
3434                        )])
3435                    }
3436                    req => panic!("unexpected request {req:?}"),
3437                }
3438            },
3439        }
3440    }
3441
3442    /// Test RTM_DELADDR with all interesting responses to remove address.
3443    #[test_case(
3444        test_addr_subnet_v4(),
3445        Ok(true),
3446        Ok(()); "v4_did_remove")]
3447    #[test_case(
3448        test_addr_subnet_v6(),
3449        Ok(true),
3450        Ok(()); "v6_did_remove")]
3451    #[test_case(
3452        test_addr_subnet_v4(),
3453        Ok(false),
3454        Err(RequestError::AddressNotFound); "v4_did_not_remove")]
3455    #[test_case(
3456        test_addr_subnet_v6(),
3457        Ok(false),
3458        Err(RequestError::AddressNotFound); "v6_did_not_remove")]
3459    #[test_case(
3460        test_addr_subnet_v4(),
3461        Err(fnet_interfaces_admin::ControlRemoveAddressError::unknown()),
3462        Err(RequestError::InvalidRequest); "v4_unrecognized_error")]
3463    #[test_case(
3464        test_addr_subnet_v6(),
3465        Err(fnet_interfaces_admin::ControlRemoveAddressError::unknown()),
3466        Err(RequestError::InvalidRequest); "v6_unrecognized_error")]
3467    #[fuchsia::test]
3468    async fn test_del_addr(
3469        address: AddrSubnetEither,
3470        response: Result<bool, fnet_interfaces_admin::ControlRemoveAddressError>,
3471        waiter_result: Result<(), RequestError>,
3472    ) {
3473        pretty_assertions::assert_eq!(
3474            test_interface_request(del_addr_test_interface_case(address, response, None)).await,
3475            TestRequestResult { messages: Vec::new(), waiter_results: vec![waiter_result] },
3476        )
3477    }
3478
3479    /// Tests that multiple interface update requests result in only one
3480    /// admin handle being created for that interface.
3481    #[fuchsia::test]
3482    async fn test_single_get_admin_for_multiple_interface_requests() {
3483        let first_address = test_addr_subnet_v4();
3484        let second_address = test_addr_subnet_v6();
3485        pretty_assertions::assert_eq!(
3486            test_maybe_two_interface_requests_on_single_control(
3487                del_addr_test_interface_case(first_address, Ok(true), Some(second_address)),
3488                Some(del_addr_test_interface_case(second_address, Ok(true), None)),
3489            )
3490            .await,
3491            TestRequestResult { messages: Vec::new(), waiter_results: vec![Ok(()), Ok(())] },
3492        )
3493    }
3494}