Skip to main content

netlink/
interfaces.rs

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