netlink/
interfaces.rs

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