Skip to main content

netstack_testing_common/
realms.rs

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