netstack_testing_common/
realms.rs

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