netstack_testing_common/
realms.rs

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