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