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