Skip to main content

netstack_testing_common/
realms.rs

1// Copyright 2020 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//! Provides utilities for test realms.
6
7use std::borrow::Cow;
8use std::collections::HashMap;
9use std::future::Future;
10
11use cm_rust::NativeIntoFidl as _;
12use fidl::endpoints::{DiscoverableProtocolMarker as _, ServiceMarker};
13use fidl_fuchsia_component as fcomponent;
14use fidl_fuchsia_hardware_network as fhwnet;
15use fidl_fuchsia_net_debug as fnet_debug;
16use fidl_fuchsia_net_dhcp as fnet_dhcp;
17use fidl_fuchsia_net_dhcpv6 as fnet_dhcpv6;
18use fidl_fuchsia_net_filter as fnet_filter;
19use fidl_fuchsia_net_filter_deprecated as fnet_filter_deprecated;
20use fidl_fuchsia_net_interfaces as fnet_interfaces;
21use fidl_fuchsia_net_interfaces_admin as fnet_interfaces_admin;
22use fidl_fuchsia_net_interfaces_ext as fnet_interfaces_ext;
23use fidl_fuchsia_net_masquerade as fnet_masquerade;
24use fidl_fuchsia_net_multicast_admin as fnet_multicast_admin;
25use fidl_fuchsia_net_name as fnet_name;
26use fidl_fuchsia_net_ndp as fnet_ndp;
27use fidl_fuchsia_net_neighbor as fnet_neighbor;
28use fidl_fuchsia_net_policy_properties as fnp_properties;
29use fidl_fuchsia_net_policy_socketproxy as fnp_socketproxy;
30use fidl_fuchsia_net_power as fnet_power;
31use fidl_fuchsia_net_reachability as fnet_reachability;
32use fidl_fuchsia_net_root as fnet_root;
33use fidl_fuchsia_net_routes as fnet_routes;
34use fidl_fuchsia_net_routes_admin as fnet_routes_admin;
35use fidl_fuchsia_net_settings as fnet_settings;
36use fidl_fuchsia_net_sockets as fnet_sockets;
37use fidl_fuchsia_net_stack as fnet_stack;
38use fidl_fuchsia_net_test_realm as fntr;
39use fidl_fuchsia_net_virtualization as fnet_virtualization;
40use fidl_fuchsia_netemul as fnetemul;
41use fidl_fuchsia_posix_socket as fposix_socket;
42use fidl_fuchsia_posix_socket_packet as fposix_socket_packet;
43use fidl_fuchsia_posix_socket_raw as fposix_socket_raw;
44use fidl_fuchsia_scheduler as fscheduler;
45use fidl_fuchsia_stash as fstash;
46use fidl_fuchsia_update_verify as fupdate_verify;
47
48use anyhow::Context as _;
49
50use crate::Result;
51
52/// The Netstack version. Used to specify which Netstack version to use in a
53/// [`KnownServiceProvider::Netstack`].
54#[derive(Copy, Clone, Eq, PartialEq, Debug)]
55#[allow(missing_docs)]
56pub enum NetstackVersion {
57    Netstack2 { tracing: bool, fast_udp: bool },
58    Netstack3,
59    ProdNetstack2,
60    ProdNetstack3,
61}
62
63impl NetstackVersion {
64    /// Gets the Fuchsia URL for this Netstack component.
65    pub fn get_url(&self) -> &'static str {
66        match self {
67            NetstackVersion::Netstack2 { tracing, fast_udp } => match (tracing, fast_udp) {
68                (false, false) => "#meta/netstack-debug.cm",
69                (false, true) => "#meta/netstack-with-fast-udp-debug.cm",
70                (true, false) => "#meta/netstack-with-tracing.cm",
71                (true, true) => "#meta/netstack-with-fast-udp-tracing.cm",
72            },
73            NetstackVersion::Netstack3 => "#meta/netstack3-debug.cm",
74            NetstackVersion::ProdNetstack2 => "#meta/netstack.cm",
75            NetstackVersion::ProdNetstack3 => "#meta/netstack3.cm",
76        }
77    }
78
79    /// Gets the services exposed by this Netstack component.
80    pub fn get_services(&self) -> &[&'static str] {
81        macro_rules! common_services_and {
82            ($($name:expr),*) => {[
83                fnet_debug::InterfacesMarker::PROTOCOL_NAME,
84                fnet_interfaces_admin::InstallerMarker::PROTOCOL_NAME,
85                fnet_interfaces::StateMarker::PROTOCOL_NAME,
86                fnet_multicast_admin::Ipv4RoutingTableControllerMarker::PROTOCOL_NAME,
87                fnet_multicast_admin::Ipv6RoutingTableControllerMarker::PROTOCOL_NAME,
88                fnet_name::DnsServerWatcherMarker::PROTOCOL_NAME,
89                fnet_neighbor::ControllerMarker::PROTOCOL_NAME,
90                fnet_neighbor::ViewMarker::PROTOCOL_NAME,
91                fnet_root::InterfacesMarker::PROTOCOL_NAME,
92                fnet_root::RoutesV4Marker::PROTOCOL_NAME,
93                fnet_root::RoutesV6Marker::PROTOCOL_NAME,
94                fnet_routes::StateMarker::PROTOCOL_NAME,
95                fnet_routes::StateV4Marker::PROTOCOL_NAME,
96                fnet_routes::StateV6Marker::PROTOCOL_NAME,
97                fnet_routes_admin::RouteTableProviderV4Marker::PROTOCOL_NAME,
98                fnet_routes_admin::RouteTableProviderV6Marker::PROTOCOL_NAME,
99                fnet_routes_admin::RouteTableV4Marker::PROTOCOL_NAME,
100                fnet_routes_admin::RouteTableV6Marker::PROTOCOL_NAME,
101                fnet_routes_admin::RuleTableV4Marker::PROTOCOL_NAME,
102                fnet_routes_admin::RuleTableV6Marker::PROTOCOL_NAME,
103                fnet_stack::StackMarker::PROTOCOL_NAME,
104                fposix_socket_packet::ProviderMarker::PROTOCOL_NAME,
105                fposix_socket_raw::ProviderMarker::PROTOCOL_NAME,
106                fposix_socket::ProviderMarker::PROTOCOL_NAME,
107                fnet_debug::DiagnosticsMarker::PROTOCOL_NAME,
108                fnet_debug::PacketCaptureProviderMarker::PROTOCOL_NAME,
109
110                fupdate_verify::ComponentOtaHealthCheckMarker::PROTOCOL_NAME,
111                $($name),*
112            ]};
113            // Strip trailing comma.
114            ($($name:expr),*,) => {common_services_and!($($name),*)}
115        }
116        match self {
117            NetstackVersion::Netstack2 { tracing: _, fast_udp: _ }
118            | NetstackVersion::ProdNetstack2 => &common_services_and!(
119                fnet_filter_deprecated::FilterMarker::PROTOCOL_NAME,
120                fnet_stack::LogMarker::PROTOCOL_NAME,
121            ),
122            NetstackVersion::Netstack3 | NetstackVersion::ProdNetstack3 => &common_services_and!(
123                fnet_filter::ControlMarker::PROTOCOL_NAME,
124                fnet_filter::StateMarker::PROTOCOL_NAME,
125                fnet_ndp::RouterAdvertisementOptionWatcherProviderMarker::PROTOCOL_NAME,
126                fnet_power::WakeGroupProviderMarker::PROTOCOL_NAME,
127                fnet_root::FilterMarker::PROTOCOL_NAME,
128                fnet_settings::StateMarker::PROTOCOL_NAME,
129                fnet_settings::ControlMarker::PROTOCOL_NAME,
130                fnet_sockets::DiagnosticsMarker::PROTOCOL_NAME,
131                fnet_sockets::ControlMarker::PROTOCOL_NAME,
132            ),
133        }
134    }
135
136    /// Returns true if this is a netstack3 version.
137    pub const fn is_netstack3(&self) -> bool {
138        match self {
139            Self::Netstack3 | Self::ProdNetstack3 => true,
140            Self::Netstack2 { .. } | Self::ProdNetstack2 => false,
141        }
142    }
143}
144
145/// An extension trait for [`Netstack`].
146pub trait NetstackExt {
147    /// Whether to use the out of stack DHCP client for the given Netstack.
148    const USE_OUT_OF_STACK_DHCP_CLIENT: bool;
149}
150
151impl<N: Netstack> NetstackExt for N {
152    const USE_OUT_OF_STACK_DHCP_CLIENT: bool = match Self::VERSION {
153        NetstackVersion::Netstack3 | NetstackVersion::ProdNetstack3 => true,
154        NetstackVersion::Netstack2 { .. } | NetstackVersion::ProdNetstack2 => false,
155    };
156}
157
158/// The NetCfg version.
159#[derive(Copy, Clone, Eq, PartialEq, Debug)]
160pub enum NetCfgVersion {
161    /// The basic NetCfg version.
162    Basic,
163    /// The advanced NetCfg version.
164    Advanced,
165}
166
167/// The network manager to use in a [`KnownServiceProvider::Manager`].
168#[derive(Copy, Clone, Eq, PartialEq, Debug)]
169pub enum ManagementAgent {
170    /// A version of netcfg.
171    NetCfg(NetCfgVersion),
172}
173
174impl ManagementAgent {
175    /// Gets the URL for this network manager component.
176    pub fn get_url(&self) -> &'static str {
177        match self {
178            Self::NetCfg(NetCfgVersion::Basic) => constants::netcfg::basic::COMPONENT_URL,
179            Self::NetCfg(NetCfgVersion::Advanced) => constants::netcfg::advanced::COMPONENT_URL,
180        }
181    }
182
183    /// Default arguments that should be passed to the component when run in a
184    /// test realm.
185    pub fn get_program_args(&self) -> &[&'static str] {
186        match self {
187            Self::NetCfg(NetCfgVersion::Basic) | Self::NetCfg(NetCfgVersion::Advanced) => {
188                &["--min-severity", "DEBUG"]
189            }
190        }
191    }
192
193    /// Gets the services exposed by this management agent.
194    pub fn get_services(&self) -> &[&'static str] {
195        match self {
196            Self::NetCfg(NetCfgVersion::Basic) => &[
197                fnet_dhcpv6::PrefixProviderMarker::PROTOCOL_NAME,
198                fnet_masquerade::FactoryMarker::PROTOCOL_NAME,
199                fnet_name::DnsServerWatcherMarker::PROTOCOL_NAME,
200                fnp_properties::NetworksMarker::PROTOCOL_NAME,
201                fnp_socketproxy::NetworkRegistryMarker::PROTOCOL_NAME,
202                fnp_properties::NetworkTokenResolverMarker::PROTOCOL_NAME,
203                fnet_reachability::MonitorMarker::PROTOCOL_NAME,
204            ],
205            Self::NetCfg(NetCfgVersion::Advanced) => &[
206                fnet_dhcpv6::PrefixProviderMarker::PROTOCOL_NAME,
207                fnet_masquerade::FactoryMarker::PROTOCOL_NAME,
208                fnet_name::DnsServerWatcherMarker::PROTOCOL_NAME,
209                fnet_virtualization::ControlMarker::PROTOCOL_NAME,
210                fnp_properties::NetworksMarker::PROTOCOL_NAME,
211                fnp_socketproxy::NetworkRegistryMarker::PROTOCOL_NAME,
212                fnp_properties::NetworkTokenResolverMarker::PROTOCOL_NAME,
213                fnet_reachability::MonitorMarker::PROTOCOL_NAME,
214            ],
215        }
216    }
217}
218
219/// Available configurations for a Manager.
220#[derive(Clone, Eq, PartialEq, Debug)]
221#[allow(missing_docs)]
222pub enum ManagerConfig {
223    Empty,
224    Dhcpv6,
225    Forwarding,
226    AllDelegated,
227    IfacePrefix,
228    DuplicateNames,
229    EnableSocketProxy,
230    EnableSocketProxyAllDelegated,
231    PacketFilterEthernet,
232    PacketFilterWlan,
233    WithBlackhole,
234    AllInterfaceLocalDelegated,
235}
236
237impl ManagerConfig {
238    fn as_str(&self) -> &'static str {
239        match self {
240            ManagerConfig::Empty => "/pkg/netcfg/empty.json",
241            ManagerConfig::Dhcpv6 => "/pkg/netcfg/dhcpv6.json",
242            ManagerConfig::Forwarding => "/pkg/netcfg/forwarding.json",
243            ManagerConfig::AllDelegated => "/pkg/netcfg/all_delegated.json",
244            ManagerConfig::IfacePrefix => "/pkg/netcfg/iface_prefix.json",
245            ManagerConfig::DuplicateNames => "/pkg/netcfg/duplicate_names.json",
246            ManagerConfig::EnableSocketProxy => "/pkg/netcfg/enable_socket_proxy.json",
247            ManagerConfig::EnableSocketProxyAllDelegated => {
248                "/pkg/netcfg/enable_socket_proxy_all_delegated.json"
249            }
250            ManagerConfig::PacketFilterEthernet => "/pkg/netcfg/packet_filter_ethernet.json",
251            ManagerConfig::PacketFilterWlan => "/pkg/netcfg/packet_filter_wlan.json",
252            ManagerConfig::WithBlackhole => "/pkg/netcfg/with_blackhole.json",
253            ManagerConfig::AllInterfaceLocalDelegated => {
254                "/pkg/netcfg/all_interface_local_delegated.json"
255            }
256        }
257    }
258}
259
260#[derive(Copy, Clone, Default, Eq, PartialEq, Debug)]
261/// The type of `socket-proxy` implementation to use.
262pub enum SocketProxyType {
263    #[default]
264    /// No socket proxy is present.
265    None,
266    /// Use the real socket proxy implementation.
267    Real,
268    /// Use the fake socket proxy implementation to allow mocking behavior.
269    Fake,
270}
271
272impl SocketProxyType {
273    /// Returns the appropriate `KnownServiceProvider` variant for this type.
274    pub fn known_service_provider(&self) -> Option<KnownServiceProvider> {
275        match self {
276            SocketProxyType::None => None,
277            SocketProxyType::Real => Some(KnownServiceProvider::SocketProxy),
278            SocketProxyType::Fake => Some(KnownServiceProvider::FakeSocketProxy),
279        }
280    }
281}
282
283/// Components that provide known services used in tests.
284#[derive(Clone, Eq, PartialEq, Debug)]
285#[allow(missing_docs)]
286pub enum KnownServiceProvider {
287    Netstack(NetstackVersion),
288    Manager {
289        agent: ManagementAgent,
290        config: ManagerConfig,
291        use_dhcp_server: bool,
292        use_out_of_stack_dhcp_client: bool,
293        socket_proxy_type: SocketProxyType,
294    },
295    SecureStash,
296    DhcpServer {
297        persistent: bool,
298    },
299    DhcpClient,
300    Dhcpv6Client,
301    DnsResolver,
302    Reachability {
303        eager: bool,
304    },
305    SocketProxy,
306    NetworkTestRealm {
307        require_outer_netstack: bool,
308    },
309    FakeClock,
310    FakeSocketProxy,
311    FakeNetcfg,
312}
313
314/// Constant properties of components used in networking integration tests, such
315/// as monikers and URLs.
316#[allow(missing_docs)]
317pub mod constants {
318    pub mod netstack {
319        pub const COMPONENT_NAME: &str = "netstack";
320    }
321    pub mod netcfg {
322        pub const COMPONENT_NAME: &str = "netcfg";
323        pub mod basic {
324            pub const COMPONENT_URL: &str = "#meta/netcfg-basic.cm";
325        }
326        pub mod advanced {
327            pub const COMPONENT_URL: &str = "#meta/netcfg-advanced.cm";
328        }
329        pub mod fake {
330            pub const COMPONENT_URL: &str = "#meta/fake_netcfg.cm";
331        }
332        // These capability names and filepaths should match the devfs capabilities used by netcfg
333        // in its component manifest, i.e. netcfg.cml.
334        pub const DEV_CLASS_NETWORK: &str = "dev-class-network";
335        pub const CLASS_NETWORK_PATH: &str = "class/network";
336    }
337    pub mod socket_proxy {
338        pub const COMPONENT_NAME: &str = "network-socket-proxy";
339        pub const COMPONENT_URL: &str = "#meta/network-socket-proxy.cm";
340    }
341    pub mod secure_stash {
342        pub const COMPONENT_NAME: &str = "stash_secure";
343        pub const COMPONENT_URL: &str = "#meta/stash_secure.cm";
344    }
345    pub mod dhcp_server {
346        pub const COMPONENT_NAME: &str = "dhcpd";
347        pub const COMPONENT_URL: &str = "#meta/dhcpv4_server.cm";
348    }
349    pub mod dhcp_client {
350        pub const COMPONENT_NAME: &str = "dhcp-client";
351        pub const COMPONENT_URL: &str = "#meta/dhcp-client.cm";
352    }
353    pub mod dhcpv6_client {
354        pub const COMPONENT_NAME: &str = "dhcpv6-client";
355        pub const COMPONENT_URL: &str = "#meta/dhcpv6-client.cm";
356    }
357    pub mod dns_resolver {
358        pub const COMPONENT_NAME: &str = "dns_resolver";
359        pub const COMPONENT_URL: &str = "#meta/dns_resolver_with_fake_time.cm";
360    }
361    pub mod reachability {
362        pub const COMPONENT_NAME: &str = "reachability";
363        pub const COMPONENT_URL: &str = "#meta/reachability_with_fake_time.cm";
364    }
365    pub mod network_test_realm {
366        pub const COMPONENT_NAME: &str = "controller";
367        pub const COMPONENT_URL: &str = "#meta/controller.cm";
368    }
369    pub mod fake_clock {
370        pub const COMPONENT_NAME: &str = "fake_clock";
371        pub const COMPONENT_URL: &str = "#meta/fake_clock.cm";
372    }
373    pub mod fake_socket_proxy {
374        pub const COMPONENT_NAME: &str = "fake_socket_proxy";
375        pub const COMPONENT_URL: &str = "#meta/fake_socket_proxy.cm";
376    }
377}
378
379fn protocol_dep<P>(component_name: &'static str) -> fnetemul::ChildDep
380where
381    P: fidl::endpoints::DiscoverableProtocolMarker,
382{
383    fnetemul::ChildDep {
384        name: Some(component_name.into()),
385        capability: Some(fnetemul::ExposedCapability::Protocol(P::PROTOCOL_NAME.to_string())),
386        ..Default::default()
387    }
388}
389
390fn or_void_protocol_dep<P>(
391    component_name: &'static str,
392    is_child_present: bool,
393) -> fnetemul::ChildDep
394where
395    P: fidl::endpoints::DiscoverableProtocolMarker,
396{
397    if is_child_present { protocol_dep::<P>(component_name) } else { void_protocol_dep::<P>() }
398}
399
400fn void_protocol_dep<P>() -> fnetemul::ChildDep
401where
402    P: fidl::endpoints::DiscoverableProtocolMarker,
403{
404    fnetemul::ChildDep {
405        name: None,
406        capability: Some(fnetemul::ExposedCapability::Protocol(P::PROTOCOL_NAME.to_string())),
407        ..Default::default()
408    }
409}
410
411impl From<KnownServiceProvider> for fnetemul::ChildDef {
412    fn from(s: KnownServiceProvider) -> Self {
413        (&s).into()
414    }
415}
416
417impl<'a> From<&'a KnownServiceProvider> for fnetemul::ChildDef {
418    fn from(s: &'a KnownServiceProvider) -> Self {
419        match s {
420            KnownServiceProvider::Netstack(version) => fnetemul::ChildDef {
421                name: Some(constants::netstack::COMPONENT_NAME.to_string()),
422                source: Some(fnetemul::ChildSource::Component(version.get_url().to_string())),
423                exposes: Some(
424                    version.get_services().iter().map(|service| service.to_string()).collect(),
425                ),
426                uses: {
427                    let mut uses = vec![fnetemul::Capability::LogSink(fnetemul::Empty {})];
428                    match version {
429                        // NB: intentionally do not route SecureStore; it is
430                        // intentionally not available in all tests to
431                        // ensure that its absence is handled gracefully.
432                        // Note also that netstack-debug does not have a use
433                        // declaration for this protocol for the same
434                        // reason.
435                        NetstackVersion::Netstack2 { tracing: false, fast_udp: _ } => {}
436                        NetstackVersion::Netstack2 { tracing: true, fast_udp: _ } => {
437                            uses.push(fnetemul::Capability::TracingProvider(fnetemul::Empty));
438                        }
439                        NetstackVersion::ProdNetstack2 => {
440                            uses.push(fnetemul::Capability::ChildDep(protocol_dep::<
441                                fstash::SecureStoreMarker,
442                            >(
443                                constants::secure_stash::COMPONENT_NAME,
444                            )));
445                        }
446                        NetstackVersion::Netstack3 | NetstackVersion::ProdNetstack3 => {
447                            uses.push(fnetemul::Capability::TracingProvider(fnetemul::Empty));
448                            uses.push(fnetemul::Capability::StorageDep(fnetemul::StorageDep {
449                                variant: Some(fnetemul::StorageVariant::Data),
450                                path: Some("/data".to_string()),
451                                ..Default::default()
452                            }));
453                        }
454                    }
455                    Some(fnetemul::ChildUses::Capabilities(uses))
456                },
457                ..Default::default()
458            },
459            KnownServiceProvider::Manager {
460                agent,
461                use_dhcp_server,
462                config,
463                use_out_of_stack_dhcp_client,
464                socket_proxy_type: _,
465            } => {
466                let enable_dhcpv6 = match config {
467                    ManagerConfig::Dhcpv6 => true,
468                    ManagerConfig::Forwarding
469                    | ManagerConfig::Empty
470                    | ManagerConfig::AllDelegated
471                    | ManagerConfig::IfacePrefix
472                    | ManagerConfig::DuplicateNames
473                    | ManagerConfig::EnableSocketProxy
474                    | ManagerConfig::EnableSocketProxyAllDelegated
475                    | ManagerConfig::PacketFilterEthernet
476                    | ManagerConfig::PacketFilterWlan
477                    | ManagerConfig::WithBlackhole
478                    | ManagerConfig::AllInterfaceLocalDelegated => false,
479                };
480
481                fnetemul::ChildDef {
482                    name: Some(constants::netcfg::COMPONENT_NAME.to_string()),
483                    source: Some(fnetemul::ChildSource::Component(agent.get_url().to_string())),
484                    program_args: Some(
485                        agent
486                            .get_program_args()
487                            .iter()
488                            .cloned()
489                            .chain(std::iter::once("--config-data"))
490                            .chain(std::iter::once(config.as_str()))
491                            .map(Into::into)
492                            .collect(),
493                    ),
494                    exposes: Some(
495                        agent.get_services().iter().map(|service| service.to_string()).collect(),
496                    ),
497                    uses: Some(fnetemul::ChildUses::Capabilities(
498                        std::iter::once(fnetemul::Capability::ChildDep(or_void_protocol_dep::<
499                            fnet_dhcp::Server_Marker,
500                        >(
501                            constants::dhcp_server::COMPONENT_NAME,
502                            *use_dhcp_server,
503                        )))
504                        .chain(std::iter::once(fnetemul::Capability::ChildDep(
505                            or_void_protocol_dep::<fnet_dhcpv6::ClientProviderMarker>(
506                                constants::dhcpv6_client::COMPONENT_NAME,
507                                enable_dhcpv6,
508                            ),
509                        )))
510                        .chain(std::iter::once(fnetemul::Capability::ChildDep(
511                            or_void_protocol_dep::<fnet_dhcp::ClientProviderMarker>(
512                                constants::dhcp_client::COMPONENT_NAME,
513                                *use_out_of_stack_dhcp_client,
514                            ),
515                        )))
516                        .chain(
517                            [
518                                fnetemul::Capability::LogSink(fnetemul::Empty {}),
519                                fnetemul::Capability::ChildDep(fnetemul::ChildDep {
520                                    dynamically_offer_from_void: Some(true),
521                                    ..protocol_dep::<fnet_filter::ControlMarker>(
522                                        constants::netstack::COMPONENT_NAME,
523                                    )
524                                }),
525                                fnetemul::Capability::ChildDep(fnetemul::ChildDep {
526                                    dynamically_offer_from_void: Some(true),
527                                    ..protocol_dep::<fnet_filter_deprecated::FilterMarker>(
528                                        constants::netstack::COMPONENT_NAME,
529                                    )
530                                }),
531                                fnetemul::Capability::ChildDep(protocol_dep::<
532                                    fnet_interfaces::StateMarker,
533                                >(
534                                    constants::netstack::COMPONENT_NAME,
535                                )),
536                                fnetemul::Capability::ChildDep(protocol_dep::<
537                                    fnet_interfaces_admin::InstallerMarker,
538                                >(
539                                    constants::netstack::COMPONENT_NAME,
540                                )),
541                                fnetemul::Capability::ChildDep(protocol_dep::<
542                                    fnet_stack::StackMarker,
543                                >(
544                                    constants::netstack::COMPONENT_NAME,
545                                )),
546                                fnetemul::Capability::ChildDep(protocol_dep::<
547                                    fnet_routes_admin::RouteTableV4Marker,
548                                >(
549                                    constants::netstack::COMPONENT_NAME,
550                                )),
551                                fnetemul::Capability::ChildDep(protocol_dep::<
552                                    fnet_routes_admin::RouteTableV6Marker,
553                                >(
554                                    constants::netstack::COMPONENT_NAME,
555                                )),
556                                fnetemul::Capability::ChildDep(protocol_dep::<
557                                    fnet_routes_admin::RuleTableV4Marker,
558                                >(
559                                    constants::netstack::COMPONENT_NAME,
560                                )),
561                                fnetemul::Capability::ChildDep(protocol_dep::<
562                                    fnet_routes_admin::RuleTableV6Marker,
563                                >(
564                                    constants::netstack::COMPONENT_NAME,
565                                )),
566                                fnetemul::Capability::ChildDep(protocol_dep::<
567                                    fnet_name::DnsServerWatcherMarker,
568                                >(
569                                    constants::netstack::COMPONENT_NAME,
570                                )),
571                                fnetemul::Capability::ChildDep(protocol_dep::<
572                                    fnet_name::LookupAdminMarker,
573                                >(
574                                    constants::dns_resolver::COMPONENT_NAME,
575                                )),
576                                fnetemul::Capability::ChildDep(protocol_dep::<
577                                    fnet_ndp::RouterAdvertisementOptionWatcherProviderMarker,
578                                >(
579                                    constants::netstack::COMPONENT_NAME,
580                                )),
581                                fnetemul::Capability::ChildDep(fnetemul::ChildDep {
582                                    name: Some(
583                                        fnetemul::NETEMUL_SERVICES_COMPONENT_NAME.to_string(),
584                                    ),
585                                    capability: Some(fnetemul::ExposedCapability::Service(
586                                        fhwnet::ServiceMarker::SERVICE_NAME.to_string(),
587                                    )),
588                                    ..Default::default()
589                                }),
590                                fnetemul::Capability::StorageDep(fnetemul::StorageDep {
591                                    variant: Some(fnetemul::StorageVariant::Data),
592                                    path: Some("/data".to_string()),
593                                    ..Default::default()
594                                }),
595                            ]
596                            .into_iter(),
597                        )
598                        .collect(),
599                    )),
600                    eager: Some(true),
601                    ..Default::default()
602                }
603            }
604            KnownServiceProvider::SecureStash => fnetemul::ChildDef {
605                name: Some(constants::secure_stash::COMPONENT_NAME.to_string()),
606                source: Some(fnetemul::ChildSource::Component(
607                    constants::secure_stash::COMPONENT_URL.to_string(),
608                )),
609                exposes: Some(vec![fstash::SecureStoreMarker::PROTOCOL_NAME.to_string()]),
610                uses: Some(fnetemul::ChildUses::Capabilities(vec![
611                    fnetemul::Capability::LogSink(fnetemul::Empty {}),
612                    fnetemul::Capability::StorageDep(fnetemul::StorageDep {
613                        variant: Some(fnetemul::StorageVariant::Data),
614                        path: Some("/data".to_string()),
615                        ..Default::default()
616                    }),
617                ])),
618                ..Default::default()
619            },
620            KnownServiceProvider::DhcpServer { persistent } => fnetemul::ChildDef {
621                name: Some(constants::dhcp_server::COMPONENT_NAME.to_string()),
622                source: Some(fnetemul::ChildSource::Component(
623                    constants::dhcp_server::COMPONENT_URL.to_string(),
624                )),
625                exposes: Some(vec![fnet_dhcp::Server_Marker::PROTOCOL_NAME.to_string()]),
626                uses: Some(fnetemul::ChildUses::Capabilities(
627                    [
628                        fnetemul::Capability::LogSink(fnetemul::Empty {}),
629                        fnetemul::Capability::ChildDep(protocol_dep::<
630                            fnet_neighbor::ControllerMarker,
631                        >(
632                            constants::netstack::COMPONENT_NAME
633                        )),
634                        fnetemul::Capability::ChildDep(
635                            protocol_dep::<fposix_socket::ProviderMarker>(
636                                constants::netstack::COMPONENT_NAME,
637                            ),
638                        ),
639                        fnetemul::Capability::ChildDep(protocol_dep::<
640                            fposix_socket_packet::ProviderMarker,
641                        >(
642                            constants::netstack::COMPONENT_NAME
643                        )),
644                    ]
645                    .into_iter()
646                    .chain(persistent.then_some(fnetemul::Capability::ChildDep(protocol_dep::<
647                        fstash::SecureStoreMarker,
648                    >(
649                        constants::secure_stash::COMPONENT_NAME,
650                    ))))
651                    .collect(),
652                )),
653                program_args: if *persistent {
654                    Some(vec![String::from("--persistent")])
655                } else {
656                    None
657                },
658                ..Default::default()
659            },
660            KnownServiceProvider::DhcpClient => fnetemul::ChildDef {
661                name: Some(constants::dhcp_client::COMPONENT_NAME.to_string()),
662                source: Some(fnetemul::ChildSource::Component(
663                    constants::dhcp_client::COMPONENT_URL.to_string(),
664                )),
665                exposes: Some(vec![fnet_dhcp::ClientProviderMarker::PROTOCOL_NAME.to_string()]),
666                uses: Some(fnetemul::ChildUses::Capabilities(vec![
667                    fnetemul::Capability::LogSink(fnetemul::Empty {}),
668                    fnetemul::Capability::ChildDep(protocol_dep::<fposix_socket::ProviderMarker>(
669                        constants::netstack::COMPONENT_NAME,
670                    )),
671                    fnetemul::Capability::ChildDep(protocol_dep::<
672                        fposix_socket_packet::ProviderMarker,
673                    >(
674                        constants::netstack::COMPONENT_NAME
675                    )),
676                ])),
677                program_args: None,
678                ..Default::default()
679            },
680            KnownServiceProvider::Dhcpv6Client => fnetemul::ChildDef {
681                name: Some(constants::dhcpv6_client::COMPONENT_NAME.to_string()),
682                source: Some(fnetemul::ChildSource::Component(
683                    constants::dhcpv6_client::COMPONENT_URL.to_string(),
684                )),
685                exposes: Some(vec![fnet_dhcpv6::ClientProviderMarker::PROTOCOL_NAME.to_string()]),
686                uses: Some(fnetemul::ChildUses::Capabilities(vec![
687                    fnetemul::Capability::LogSink(fnetemul::Empty {}),
688                    fnetemul::Capability::ChildDep(protocol_dep::<fposix_socket::ProviderMarker>(
689                        constants::netstack::COMPONENT_NAME,
690                    )),
691                ])),
692                ..Default::default()
693            },
694            KnownServiceProvider::DnsResolver => fnetemul::ChildDef {
695                name: Some(constants::dns_resolver::COMPONENT_NAME.to_string()),
696                source: Some(fnetemul::ChildSource::Component(
697                    constants::dns_resolver::COMPONENT_URL.to_string(),
698                )),
699                exposes: Some(vec![
700                    fnet_name::LookupAdminMarker::PROTOCOL_NAME.to_string(),
701                    fnet_name::LookupMarker::PROTOCOL_NAME.to_string(),
702                ]),
703                uses: Some(fnetemul::ChildUses::Capabilities(vec![
704                    fnetemul::Capability::LogSink(fnetemul::Empty {}),
705                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_routes::StateMarker>(
706                        constants::netstack::COMPONENT_NAME,
707                    )),
708                    fnetemul::Capability::ChildDep(protocol_dep::<fposix_socket::ProviderMarker>(
709                        constants::netstack::COMPONENT_NAME,
710                    )),
711                    fnetemul::Capability::ChildDep(protocol_dep::<
712                        fidl_fuchsia_testing::FakeClockMarker,
713                    >(
714                        constants::fake_clock::COMPONENT_NAME
715                    )),
716                    fnetemul::Capability::ChildDep(void_protocol_dep::<
717                        fscheduler::RoleManagerMarker,
718                    >()),
719                ])),
720                ..Default::default()
721            },
722            KnownServiceProvider::Reachability { eager } => fnetemul::ChildDef {
723                name: Some(constants::reachability::COMPONENT_NAME.to_string()),
724                source: Some(fnetemul::ChildSource::Component(
725                    constants::reachability::COMPONENT_URL.to_string(),
726                )),
727                exposes: Some(vec![fnet_reachability::MonitorMarker::PROTOCOL_NAME.to_string()]),
728                uses: Some(fnetemul::ChildUses::Capabilities(vec![
729                    fnetemul::Capability::LogSink(fnetemul::Empty {}),
730                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_interfaces::StateMarker>(
731                        constants::netstack::COMPONENT_NAME,
732                    )),
733                    fnetemul::Capability::ChildDep(protocol_dep::<fposix_socket::ProviderMarker>(
734                        constants::netstack::COMPONENT_NAME,
735                    )),
736                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_name::LookupMarker>(
737                        constants::dns_resolver::COMPONENT_NAME,
738                    )),
739                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_neighbor::ViewMarker>(
740                        constants::netstack::COMPONENT_NAME,
741                    )),
742                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_debug::InterfacesMarker>(
743                        constants::netstack::COMPONENT_NAME,
744                    )),
745                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_root::InterfacesMarker>(
746                        constants::netstack::COMPONENT_NAME,
747                    )),
748                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_routes::StateV4Marker>(
749                        constants::netstack::COMPONENT_NAME,
750                    )),
751                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_routes::StateV6Marker>(
752                        constants::netstack::COMPONENT_NAME,
753                    )),
754                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_debug::DiagnosticsMarker>(
755                        constants::netstack::COMPONENT_NAME,
756                    )),
757                    fnetemul::Capability::ChildDep(protocol_dep::<
758                        fidl_fuchsia_testing::FakeClockMarker,
759                    >(
760                        constants::fake_clock::COMPONENT_NAME
761                    )),
762                ])),
763                eager: Some(*eager),
764                ..Default::default()
765            },
766            KnownServiceProvider::SocketProxy => fnetemul::ChildDef {
767                name: Some(constants::socket_proxy::COMPONENT_NAME.to_string()),
768                source: Some(fnetemul::ChildSource::Component(
769                    constants::socket_proxy::COMPONENT_URL.to_string(),
770                )),
771                exposes: Some(vec![
772                    fposix_socket::ProviderMarker::PROTOCOL_NAME.to_string(),
773                    fposix_socket_raw::ProviderMarker::PROTOCOL_NAME.to_string(),
774                    fnp_socketproxy::StarnixNetworksMarker::PROTOCOL_NAME.to_string(),
775                ]),
776                uses: Some(fnetemul::ChildUses::Capabilities(vec![
777                    fnetemul::Capability::ChildDep(protocol_dep::<fposix_socket::ProviderMarker>(
778                        constants::netstack::COMPONENT_NAME,
779                    )),
780                    fnetemul::Capability::ChildDep(
781                        protocol_dep::<fposix_socket_raw::ProviderMarker>(
782                            constants::netstack::COMPONENT_NAME,
783                        ),
784                    ),
785                    fnetemul::Capability::ChildDep(fnetemul::ChildDep {
786                        is_weak: Some(true),
787                        ..protocol_dep::<fnp_properties::NetworksMarker>(
788                            constants::netcfg::COMPONENT_NAME,
789                        )
790                    }),
791                    fnetemul::Capability::ChildDep(fnetemul::ChildDep {
792                        is_weak: Some(true),
793                        ..protocol_dep::<fnp_socketproxy::NetworkRegistryMarker>(
794                            constants::netcfg::COMPONENT_NAME,
795                        )
796                    }),
797                ])),
798                ..Default::default()
799            },
800            KnownServiceProvider::NetworkTestRealm { require_outer_netstack } => {
801                fnetemul::ChildDef {
802                    name: Some(constants::network_test_realm::COMPONENT_NAME.to_string()),
803                    source: Some(fnetemul::ChildSource::Component(
804                        constants::network_test_realm::COMPONENT_URL.to_string(),
805                    )),
806                    exposes: Some(vec![
807                        fntr::ControllerMarker::PROTOCOL_NAME.to_string(),
808                        fcomponent::RealmMarker::PROTOCOL_NAME.to_string(),
809                    ]),
810                    uses: Some(fnetemul::ChildUses::Capabilities(
811                        [
812                            fnetemul::Capability::LogSink(fnetemul::Empty {}),
813                            fnetemul::Capability::ChildDep(fnetemul::ChildDep {
814                                name: Some(fnetemul::NETEMUL_SERVICES_COMPONENT_NAME.to_string()),
815                                capability: Some(fnetemul::ExposedCapability::Service(
816                                    fhwnet::ServiceMarker::SERVICE_NAME.to_string(),
817                                )),
818                                ..Default::default()
819                            }),
820                        ]
821                        .into_iter()
822                        .chain(
823                            require_outer_netstack
824                                .then_some([
825                                    fnetemul::Capability::ChildDep(protocol_dep::<
826                                        fnet_stack::StackMarker,
827                                    >(
828                                        constants::netstack::COMPONENT_NAME,
829                                    )),
830                                    fnetemul::Capability::ChildDep(protocol_dep::<
831                                        fnet_debug::InterfacesMarker,
832                                    >(
833                                        constants::netstack::COMPONENT_NAME,
834                                    )),
835                                    fnetemul::Capability::ChildDep(protocol_dep::<
836                                        fnet_root::InterfacesMarker,
837                                    >(
838                                        constants::netstack::COMPONENT_NAME,
839                                    )),
840                                    fnetemul::Capability::ChildDep(protocol_dep::<
841                                        fnet_interfaces::StateMarker,
842                                    >(
843                                        constants::netstack::COMPONENT_NAME,
844                                    )),
845                                ])
846                                .into_iter()
847                                .flatten(),
848                        )
849                        .collect::<Vec<_>>(),
850                    )),
851                    ..Default::default()
852                }
853            }
854            KnownServiceProvider::FakeClock => fnetemul::ChildDef {
855                name: Some(constants::fake_clock::COMPONENT_NAME.to_string()),
856                source: Some(fnetemul::ChildSource::Component(
857                    constants::fake_clock::COMPONENT_URL.to_string(),
858                )),
859                exposes: Some(vec![
860                    fidl_fuchsia_testing::FakeClockMarker::PROTOCOL_NAME.to_string(),
861                    fidl_fuchsia_testing::FakeClockControlMarker::PROTOCOL_NAME.to_string(),
862                ]),
863                uses: Some(fnetemul::ChildUses::Capabilities(vec![fnetemul::Capability::LogSink(
864                    fnetemul::Empty {},
865                )])),
866                ..Default::default()
867            },
868            KnownServiceProvider::FakeSocketProxy => fnetemul::ChildDef {
869                name: Some(constants::fake_socket_proxy::COMPONENT_NAME.to_string()),
870                source: Some(fnetemul::ChildSource::Component(
871                    constants::fake_socket_proxy::COMPONENT_URL.to_string(),
872                )),
873                exposes: Some(vec![
874                    fnp_socketproxy::NetworkRegistryMarker::PROTOCOL_NAME.to_string(),
875                ]),
876                uses: Some(fnetemul::ChildUses::Capabilities(vec![
877                    fnetemul::Capability::ChildDep(fnetemul::ChildDep {
878                        is_weak: Some(true),
879                        ..protocol_dep::<fnp_socketproxy::NetworkRegistryMarker>(
880                            constants::netcfg::COMPONENT_NAME,
881                        )
882                    }),
883                ])),
884                ..Default::default()
885            },
886            KnownServiceProvider::FakeNetcfg => fnetemul::ChildDef {
887                name: Some(constants::netcfg::COMPONENT_NAME.to_string()),
888                source: Some(fnetemul::ChildSource::Component(
889                    constants::netcfg::fake::COMPONENT_URL.to_string(),
890                )),
891                exposes: Some(vec![
892                    fnp_properties::NetworksMarker::PROTOCOL_NAME.to_string(),
893                    fnp_socketproxy::NetworkRegistryMarker::PROTOCOL_NAME.to_string(),
894                ]),
895                ..Default::default()
896            },
897        }
898    }
899}
900
901/// Set the `opaque_iids` structured configuration value for Netstack3.
902pub fn set_netstack3_opaque_iids(netstack: &mut fnetemul::ChildDef, value: bool) {
903    const KEY: &str = "opaque_iids";
904    set_structured_config_value(netstack, KEY.to_owned(), cm_rust::ConfigValue::from(value));
905}
906
907/// Set the `suspend_enabled` structured configuration value for Netstack3.
908pub fn set_netstack3_suspend_enabled(netstack: &mut fnetemul::ChildDef, value: bool) {
909    const KEY: &str = "suspend_enabled";
910    set_structured_config_value(netstack, KEY.to_owned(), cm_rust::ConfigValue::from(value));
911}
912
913/// Set a structured configuration value for the provided component.
914fn set_structured_config_value(
915    component: &mut fnetemul::ChildDef,
916    key: String,
917    value: cm_rust::ConfigValue,
918) {
919    component
920        .config_values
921        .get_or_insert_default()
922        .push(fnetemul::ChildConfigValue { key, value: value.native_into_fidl() });
923}
924
925/// Abstraction for a Fuchsia component which offers network stack services.
926pub trait Netstack: Copy + Clone {
927    /// The Netstack version.
928    const VERSION: NetstackVersion;
929}
930
931/// Uninstantiable type that represents Netstack2's implementation of a
932/// network stack.
933#[derive(Copy, Clone)]
934pub enum Netstack2 {}
935
936impl Netstack for Netstack2 {
937    const VERSION: NetstackVersion = NetstackVersion::Netstack2 { tracing: false, fast_udp: false };
938}
939
940/// Uninstantiable type that represents Netstack2's production implementation of
941/// a network stack.
942#[derive(Copy, Clone)]
943pub enum ProdNetstack2 {}
944
945impl Netstack for ProdNetstack2 {
946    const VERSION: NetstackVersion = NetstackVersion::ProdNetstack2;
947}
948
949/// Uninstantiable type that represents Netstack3's implementation of a
950/// network stack.
951#[derive(Copy, Clone)]
952pub enum Netstack3 {}
953
954impl Netstack for Netstack3 {
955    const VERSION: NetstackVersion = NetstackVersion::Netstack3;
956}
957
958/// Uninstantiable type that represents Netstack3's production implementation of
959/// a network stack.
960#[derive(Copy, Clone)]
961pub enum ProdNetstack3 {}
962
963impl Netstack for ProdNetstack3 {
964    const VERSION: NetstackVersion = NetstackVersion::ProdNetstack3;
965}
966
967/// Abstraction for a Fuchsia component which offers network configuration services.
968pub trait Manager: Copy + Clone {
969    /// The management agent to be used.
970    const MANAGEMENT_AGENT: ManagementAgent;
971}
972
973/// Uninstantiable type that represents netcfg_basic's implementation of a network manager.
974#[derive(Copy, Clone)]
975pub enum NetCfgBasic {}
976
977impl Manager for NetCfgBasic {
978    const MANAGEMENT_AGENT: ManagementAgent = ManagementAgent::NetCfg(NetCfgVersion::Basic);
979}
980
981/// Uninstantiable type that represents netcfg_advanced's implementation of a
982/// network manager.
983#[derive(Copy, Clone)]
984pub enum NetCfgAdvanced {}
985
986impl Manager for NetCfgAdvanced {
987    const MANAGEMENT_AGENT: ManagementAgent = ManagementAgent::NetCfg(NetCfgVersion::Advanced);
988}
989
990pub use netemul::{DhcpClient, DhcpClientVersion, InStack, OutOfStack};
991
992/// A combination of Netstack and DhcpClient guaranteed to be compatible with
993/// each other.
994pub trait NetstackAndDhcpClient: Copy + Clone {
995    /// The netstack to be used.
996    type Netstack: Netstack;
997    /// The DHCP client to be used.
998    type DhcpClient: DhcpClient;
999}
1000
1001/// Netstack2 with the in-stack DHCP client.
1002#[derive(Copy, Clone)]
1003pub enum Netstack2AndInStackDhcpClient {}
1004
1005impl NetstackAndDhcpClient for Netstack2AndInStackDhcpClient {
1006    type Netstack = Netstack2;
1007    type DhcpClient = InStack;
1008}
1009
1010/// Netstack2 with the out-of-stack DHCP client.
1011#[derive(Copy, Clone)]
1012pub enum Netstack2AndOutOfStackDhcpClient {}
1013
1014impl NetstackAndDhcpClient for Netstack2AndOutOfStackDhcpClient {
1015    type Netstack = Netstack2;
1016    type DhcpClient = OutOfStack;
1017}
1018
1019/// Netstack3 with the out-of-stack DHCP client.
1020#[derive(Copy, Clone)]
1021pub enum Netstack3AndOutOfStackDhcpClient {}
1022
1023impl NetstackAndDhcpClient for Netstack3AndOutOfStackDhcpClient {
1024    type Netstack = Netstack3;
1025    type DhcpClient = OutOfStack;
1026}
1027
1028/// Helpers for `netemul::TestSandbox`.
1029pub trait TestSandboxExt {
1030    /// Creates a realm with Netstack services.
1031    fn create_netstack_realm<'a, N, S>(&'a self, name: S) -> Result<netemul::TestRealm<'a>>
1032    where
1033        N: Netstack,
1034        S: Into<Cow<'a, str>>;
1035
1036    /// Creates a realm with the base Netstack services plus additional ones in
1037    /// `children`.
1038    fn create_netstack_realm_with<'a, N, S, I>(
1039        &'a self,
1040        name: S,
1041        children: I,
1042    ) -> Result<netemul::TestRealm<'a>>
1043    where
1044        S: Into<Cow<'a, str>>,
1045        N: Netstack,
1046        I: IntoIterator,
1047        I::Item: Into<fnetemul::ChildDef>;
1048}
1049
1050impl TestSandboxExt for netemul::TestSandbox {
1051    fn create_netstack_realm<'a, N, S>(&'a self, name: S) -> Result<netemul::TestRealm<'a>>
1052    where
1053        N: Netstack,
1054        S: Into<Cow<'a, str>>,
1055    {
1056        self.create_netstack_realm_with::<N, _, _>(name, std::iter::empty::<fnetemul::ChildDef>())
1057    }
1058
1059    fn create_netstack_realm_with<'a, N, S, I>(
1060        &'a self,
1061        name: S,
1062        children: I,
1063    ) -> Result<netemul::TestRealm<'a>>
1064    where
1065        S: Into<Cow<'a, str>>,
1066        N: Netstack,
1067        I: IntoIterator,
1068        I::Item: Into<fnetemul::ChildDef>,
1069    {
1070        self.create_realm(
1071            name,
1072            [KnownServiceProvider::Netstack(N::VERSION)]
1073                .iter()
1074                .map(fnetemul::ChildDef::from)
1075                .chain(children.into_iter().map(Into::into)),
1076        )
1077    }
1078}
1079
1080/// Helpers for `netemul::TestRealm`.
1081pub trait TestRealmExt {
1082    /// Returns the properties of the loopback interface, or `None` if there is no
1083    /// loopback interface.
1084    fn loopback_properties(
1085        &self,
1086    ) -> impl Future<
1087        Output = Result<Option<fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>>>,
1088    >;
1089
1090    /// Get a `fuchsia.net.interfaces.admin/Control` client proxy for the
1091    /// interface identified by [`id`] via `fuchsia.net.root`.
1092    ///
1093    /// Note that one should prefer to operate on a `TestInterface` if it is
1094    /// available; but this method exists in order to obtain a Control channel
1095    /// for interfaces such as loopback.
1096    fn interface_control(&self, id: u64) -> Result<fnet_interfaces_ext::admin::Control>;
1097}
1098
1099impl TestRealmExt for netemul::TestRealm<'_> {
1100    async fn loopback_properties(
1101        &self,
1102    ) -> Result<Option<fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>>> {
1103        let interface_state = self
1104            .connect_to_protocol::<fnet_interfaces::StateMarker>()
1105            .context("failed to connect to fuchsia.net.interfaces/State")?;
1106
1107        let properties = fnet_interfaces_ext::existing(
1108            fnet_interfaces_ext::event_stream_from_state(&interface_state, Default::default())
1109                .expect("create watcher event stream"),
1110            HashMap::<u64, fnet_interfaces_ext::PropertiesAndState<(), _>>::new(),
1111        )
1112        .await
1113        .context("failed to get existing interface properties from watcher")?
1114        .into_iter()
1115        .find_map(|(_id, properties_and_state): (u64, _)| {
1116            let fnet_interfaces_ext::PropertiesAndState {
1117                properties: properties @ fnet_interfaces_ext::Properties { port_class, .. },
1118                state: (),
1119            } = properties_and_state;
1120            port_class.is_loopback().then_some(properties)
1121        });
1122        Ok(properties)
1123    }
1124
1125    fn interface_control(&self, id: u64) -> Result<fnet_interfaces_ext::admin::Control> {
1126        let root_control = self
1127            .connect_to_protocol::<fnet_root::InterfacesMarker>()
1128            .context("connect to protocol")?;
1129
1130        let (control, server) = fnet_interfaces_ext::admin::Control::create_endpoints()
1131            .context("create Control proxy")?;
1132        root_control.get_admin(id, server).context("get admin")?;
1133        Ok(control)
1134    }
1135}