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