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