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