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    FakeNetcfg,
291}
292
293/// Constant properties of components used in networking integration tests, such
294/// as monikers and URLs.
295#[allow(missing_docs)]
296pub mod constants {
297    pub mod netstack {
298        pub const COMPONENT_NAME: &str = "netstack";
299    }
300    pub mod netcfg {
301        pub const COMPONENT_NAME: &str = "netcfg";
302        pub mod basic {
303            pub const COMPONENT_URL: &str = "#meta/netcfg-basic.cm";
304        }
305        pub mod advanced {
306            pub const COMPONENT_URL: &str = "#meta/netcfg-advanced.cm";
307        }
308        pub mod fake {
309            pub const COMPONENT_URL: &str = "#meta/fake_netcfg.cm";
310        }
311        // These capability names and filepaths should match the devfs capabilities used by netcfg
312        // in its component manifest, i.e. netcfg.cml.
313        pub const DEV_CLASS_NETWORK: &str = "dev-class-network";
314        pub const CLASS_NETWORK_PATH: &str = "class/network";
315    }
316    pub mod socket_proxy {
317        pub const COMPONENT_NAME: &str = "network-socket-proxy";
318        pub const COMPONENT_URL: &str = "#meta/network-socket-proxy.cm";
319    }
320    pub mod secure_stash {
321        pub const COMPONENT_NAME: &str = "stash_secure";
322        pub const COMPONENT_URL: &str = "#meta/stash_secure.cm";
323    }
324    pub mod dhcp_server {
325        pub const COMPONENT_NAME: &str = "dhcpd";
326        pub const COMPONENT_URL: &str = "#meta/dhcpv4_server.cm";
327    }
328    pub mod dhcp_client {
329        pub const COMPONENT_NAME: &str = "dhcp-client";
330        pub const COMPONENT_URL: &str = "#meta/dhcp-client.cm";
331    }
332    pub mod dhcpv6_client {
333        pub const COMPONENT_NAME: &str = "dhcpv6-client";
334        pub const COMPONENT_URL: &str = "#meta/dhcpv6-client.cm";
335    }
336    pub mod dns_resolver {
337        pub const COMPONENT_NAME: &str = "dns_resolver";
338        pub const COMPONENT_URL: &str = "#meta/dns_resolver_with_fake_time.cm";
339    }
340    pub mod reachability {
341        pub const COMPONENT_NAME: &str = "reachability";
342        pub const COMPONENT_URL: &str = "#meta/reachability_with_fake_time.cm";
343    }
344    pub mod network_test_realm {
345        pub const COMPONENT_NAME: &str = "controller";
346        pub const COMPONENT_URL: &str = "#meta/controller.cm";
347    }
348    pub mod fake_clock {
349        pub const COMPONENT_NAME: &str = "fake_clock";
350        pub const COMPONENT_URL: &str = "#meta/fake_clock.cm";
351    }
352    pub mod fake_socket_proxy {
353        pub const COMPONENT_NAME: &str = "fake_socket_proxy";
354        pub const COMPONENT_URL: &str = "#meta/fake_socket_proxy.cm";
355    }
356}
357
358fn protocol_dep<P>(component_name: &'static str) -> fnetemul::ChildDep
359where
360    P: fidl::endpoints::DiscoverableProtocolMarker,
361{
362    fnetemul::ChildDep {
363        name: Some(component_name.into()),
364        capability: Some(fnetemul::ExposedCapability::Protocol(P::PROTOCOL_NAME.to_string())),
365        ..Default::default()
366    }
367}
368
369impl From<KnownServiceProvider> for fnetemul::ChildDef {
370    fn from(s: KnownServiceProvider) -> Self {
371        (&s).into()
372    }
373}
374
375impl<'a> From<&'a KnownServiceProvider> for fnetemul::ChildDef {
376    fn from(s: &'a KnownServiceProvider) -> Self {
377        match s {
378            KnownServiceProvider::Netstack(version) => fnetemul::ChildDef {
379                name: Some(constants::netstack::COMPONENT_NAME.to_string()),
380                source: Some(fnetemul::ChildSource::Component(version.get_url().to_string())),
381                exposes: Some(
382                    version.get_services().iter().map(|service| service.to_string()).collect(),
383                ),
384                uses: {
385                    let mut uses = vec![fnetemul::Capability::LogSink(fnetemul::Empty {})];
386                    match version {
387                        // NB: intentionally do not route SecureStore; it is
388                        // intentionally not available in all tests to
389                        // ensure that its absence is handled gracefully.
390                        // Note also that netstack-debug does not have a use
391                        // declaration for this protocol for the same
392                        // reason.
393                        NetstackVersion::Netstack2 { tracing: false, fast_udp: _ } => {}
394                        NetstackVersion::Netstack2 { tracing: true, fast_udp: _ } => {
395                            uses.push(fnetemul::Capability::TracingProvider(fnetemul::Empty));
396                        }
397                        NetstackVersion::ProdNetstack2 => {
398                            uses.push(fnetemul::Capability::ChildDep(protocol_dep::<
399                                fstash::SecureStoreMarker,
400                            >(
401                                constants::secure_stash::COMPONENT_NAME,
402                            )));
403                        }
404                        NetstackVersion::Netstack3 | NetstackVersion::ProdNetstack3 => {
405                            uses.push(fnetemul::Capability::TracingProvider(fnetemul::Empty));
406                            uses.push(fnetemul::Capability::StorageDep(fnetemul::StorageDep {
407                                variant: Some(fnetemul::StorageVariant::Data),
408                                path: Some("/data".to_string()),
409                                ..Default::default()
410                            }));
411                        }
412                    }
413                    Some(fnetemul::ChildUses::Capabilities(uses))
414                },
415                ..Default::default()
416            },
417            KnownServiceProvider::Manager {
418                agent,
419                use_dhcp_server,
420                config,
421                use_out_of_stack_dhcp_client,
422                socket_proxy_type,
423            } => {
424                let enable_dhcpv6 = match config {
425                    ManagerConfig::Dhcpv6 => true,
426                    ManagerConfig::Forwarding
427                    | ManagerConfig::Empty
428                    | ManagerConfig::AllDelegated
429                    | ManagerConfig::IfacePrefix
430                    | ManagerConfig::DuplicateNames
431                    | ManagerConfig::EnableSocketProxy
432                    | ManagerConfig::EnableSocketProxyAllDelegated
433                    | ManagerConfig::PacketFilterEthernet
434                    | ManagerConfig::PacketFilterWlan
435                    | ManagerConfig::WithBlackhole
436                    | ManagerConfig::AllInterfaceLocalDelegated => false,
437                };
438
439                fnetemul::ChildDef {
440                    name: Some(constants::netcfg::COMPONENT_NAME.to_string()),
441                    source: Some(fnetemul::ChildSource::Component(agent.get_url().to_string())),
442                    program_args: Some(
443                        agent
444                            .get_program_args()
445                            .iter()
446                            .cloned()
447                            .chain(std::iter::once("--config-data"))
448                            .chain(std::iter::once(config.as_str()))
449                            .map(Into::into)
450                            .collect(),
451                    ),
452                    exposes: Some(
453                        agent.get_services().iter().map(|service| service.to_string()).collect(),
454                    ),
455                    uses: Some(fnetemul::ChildUses::Capabilities(
456                        (*use_dhcp_server)
457                            .then(|| {
458                                fnetemul::Capability::ChildDep(protocol_dep::<
459                                    fnet_dhcp::Server_Marker,
460                                >(
461                                    constants::dhcp_server::COMPONENT_NAME,
462                                ))
463                            })
464                            .into_iter()
465                            .chain(
466                                enable_dhcpv6
467                                    .then(|| {
468                                        fnetemul::Capability::ChildDep(protocol_dep::<
469                                            fnet_dhcpv6::ClientProviderMarker,
470                                        >(
471                                            constants::dhcpv6_client::COMPONENT_NAME,
472                                        ))
473                                    })
474                                    .into_iter(),
475                            )
476                            .chain(use_out_of_stack_dhcp_client.then(|| {
477                                fnetemul::Capability::ChildDep(protocol_dep::<
478                                    fnet_dhcp::ClientProviderMarker,
479                                >(
480                                    constants::dhcp_client::COMPONENT_NAME,
481                                ))
482                            }))
483                            .chain(
484                                socket_proxy_type
485                                    .component_name()
486                                    .map(|component_name| {
487                                        [
488                                            fnetemul::Capability::ChildDep(protocol_dep::<
489                                                fnp_socketproxy::FuchsiaNetworksMarker,
490                                            >(
491                                                component_name
492                                            )),
493                                            fnetemul::Capability::ChildDep(protocol_dep::<
494                                                fnp_socketproxy::DnsServerWatcherMarker,
495                                            >(
496                                                component_name
497                                            )),
498                                            fnetemul::Capability::ChildDep(protocol_dep::<
499                                                fnp_properties::DefaultNetworkWatcherMarker,
500                                            >(
501                                                component_name
502                                            )),
503                                        ]
504                                    })
505                                    .into_iter()
506                                    .flatten(),
507                            )
508                            .chain(
509                                [
510                                    fnetemul::Capability::LogSink(fnetemul::Empty {}),
511                                    fnetemul::Capability::ChildDep(protocol_dep::<
512                                        fnet_filter::ControlMarker,
513                                    >(
514                                        constants::netstack::COMPONENT_NAME,
515                                    )),
516                                    fnetemul::Capability::ChildDep(protocol_dep::<
517                                        fnet_filter_deprecated::FilterMarker,
518                                    >(
519                                        constants::netstack::COMPONENT_NAME,
520                                    )),
521                                    fnetemul::Capability::ChildDep(protocol_dep::<
522                                        fnet_interfaces::StateMarker,
523                                    >(
524                                        constants::netstack::COMPONENT_NAME,
525                                    )),
526                                    fnetemul::Capability::ChildDep(protocol_dep::<
527                                        fnet_interfaces_admin::InstallerMarker,
528                                    >(
529                                        constants::netstack::COMPONENT_NAME,
530                                    )),
531                                    fnetemul::Capability::ChildDep(protocol_dep::<
532                                        fnet_stack::StackMarker,
533                                    >(
534                                        constants::netstack::COMPONENT_NAME,
535                                    )),
536                                    fnetemul::Capability::ChildDep(protocol_dep::<
537                                        fnet_routes_admin::RouteTableV4Marker,
538                                    >(
539                                        constants::netstack::COMPONENT_NAME,
540                                    )),
541                                    fnetemul::Capability::ChildDep(protocol_dep::<
542                                        fnet_routes_admin::RouteTableV6Marker,
543                                    >(
544                                        constants::netstack::COMPONENT_NAME,
545                                    )),
546                                    fnetemul::Capability::ChildDep(protocol_dep::<
547                                        fnet_routes_admin::RuleTableV4Marker,
548                                    >(
549                                        constants::netstack::COMPONENT_NAME,
550                                    )),
551                                    fnetemul::Capability::ChildDep(protocol_dep::<
552                                        fnet_routes_admin::RuleTableV6Marker,
553                                    >(
554                                        constants::netstack::COMPONENT_NAME,
555                                    )),
556                                    fnetemul::Capability::ChildDep(protocol_dep::<
557                                        fnet_name::DnsServerWatcherMarker,
558                                    >(
559                                        constants::netstack::COMPONENT_NAME,
560                                    )),
561                                    fnetemul::Capability::ChildDep(protocol_dep::<
562                                        fnet_name::LookupAdminMarker,
563                                    >(
564                                        constants::dns_resolver::COMPONENT_NAME,
565                                    )),
566                                    fnetemul::Capability::ChildDep(protocol_dep::<
567                                        fnet_ndp::RouterAdvertisementOptionWatcherProviderMarker,
568                                    >(
569                                        constants::netstack::COMPONENT_NAME,
570                                    )),
571                                    fnetemul::Capability::NetemulDevfs(fnetemul::DevfsDep {
572                                        name: Some(
573                                            constants::netcfg::DEV_CLASS_NETWORK.to_string(),
574                                        ),
575                                        subdir: Some(
576                                            constants::netcfg::CLASS_NETWORK_PATH.to_string(),
577                                        ),
578                                        ..Default::default()
579                                    }),
580                                    fnetemul::Capability::StorageDep(fnetemul::StorageDep {
581                                        variant: Some(fnetemul::StorageVariant::Data),
582                                        path: Some("/data".to_string()),
583                                        ..Default::default()
584                                    }),
585                                ]
586                                .into_iter(),
587                            )
588                            .collect(),
589                    )),
590                    eager: Some(true),
591                    ..Default::default()
592                }
593            }
594            KnownServiceProvider::SecureStash => fnetemul::ChildDef {
595                name: Some(constants::secure_stash::COMPONENT_NAME.to_string()),
596                source: Some(fnetemul::ChildSource::Component(
597                    constants::secure_stash::COMPONENT_URL.to_string(),
598                )),
599                exposes: Some(vec![fstash::SecureStoreMarker::PROTOCOL_NAME.to_string()]),
600                uses: Some(fnetemul::ChildUses::Capabilities(vec![
601                    fnetemul::Capability::LogSink(fnetemul::Empty {}),
602                    fnetemul::Capability::StorageDep(fnetemul::StorageDep {
603                        variant: Some(fnetemul::StorageVariant::Data),
604                        path: Some("/data".to_string()),
605                        ..Default::default()
606                    }),
607                ])),
608                ..Default::default()
609            },
610            KnownServiceProvider::DhcpServer { persistent } => fnetemul::ChildDef {
611                name: Some(constants::dhcp_server::COMPONENT_NAME.to_string()),
612                source: Some(fnetemul::ChildSource::Component(
613                    constants::dhcp_server::COMPONENT_URL.to_string(),
614                )),
615                exposes: Some(vec![fnet_dhcp::Server_Marker::PROTOCOL_NAME.to_string()]),
616                uses: Some(fnetemul::ChildUses::Capabilities(
617                    [
618                        fnetemul::Capability::LogSink(fnetemul::Empty {}),
619                        fnetemul::Capability::ChildDep(protocol_dep::<
620                            fnet_neighbor::ControllerMarker,
621                        >(
622                            constants::netstack::COMPONENT_NAME
623                        )),
624                        fnetemul::Capability::ChildDep(
625                            protocol_dep::<fposix_socket::ProviderMarker>(
626                                constants::netstack::COMPONENT_NAME,
627                            ),
628                        ),
629                        fnetemul::Capability::ChildDep(protocol_dep::<
630                            fposix_socket_packet::ProviderMarker,
631                        >(
632                            constants::netstack::COMPONENT_NAME
633                        )),
634                    ]
635                    .into_iter()
636                    .chain(persistent.then_some(fnetemul::Capability::ChildDep(protocol_dep::<
637                        fstash::SecureStoreMarker,
638                    >(
639                        constants::secure_stash::COMPONENT_NAME,
640                    ))))
641                    .collect(),
642                )),
643                program_args: if *persistent {
644                    Some(vec![String::from("--persistent")])
645                } else {
646                    None
647                },
648                ..Default::default()
649            },
650            KnownServiceProvider::DhcpClient => fnetemul::ChildDef {
651                name: Some(constants::dhcp_client::COMPONENT_NAME.to_string()),
652                source: Some(fnetemul::ChildSource::Component(
653                    constants::dhcp_client::COMPONENT_URL.to_string(),
654                )),
655                exposes: Some(vec![fnet_dhcp::ClientProviderMarker::PROTOCOL_NAME.to_string()]),
656                uses: Some(fnetemul::ChildUses::Capabilities(vec![
657                    fnetemul::Capability::LogSink(fnetemul::Empty {}),
658                    fnetemul::Capability::ChildDep(protocol_dep::<fposix_socket::ProviderMarker>(
659                        constants::netstack::COMPONENT_NAME,
660                    )),
661                    fnetemul::Capability::ChildDep(protocol_dep::<
662                        fposix_socket_packet::ProviderMarker,
663                    >(
664                        constants::netstack::COMPONENT_NAME
665                    )),
666                ])),
667                program_args: None,
668                ..Default::default()
669            },
670            KnownServiceProvider::Dhcpv6Client => fnetemul::ChildDef {
671                name: Some(constants::dhcpv6_client::COMPONENT_NAME.to_string()),
672                source: Some(fnetemul::ChildSource::Component(
673                    constants::dhcpv6_client::COMPONENT_URL.to_string(),
674                )),
675                exposes: Some(vec![fnet_dhcpv6::ClientProviderMarker::PROTOCOL_NAME.to_string()]),
676                uses: Some(fnetemul::ChildUses::Capabilities(vec![
677                    fnetemul::Capability::LogSink(fnetemul::Empty {}),
678                    fnetemul::Capability::ChildDep(protocol_dep::<fposix_socket::ProviderMarker>(
679                        constants::netstack::COMPONENT_NAME,
680                    )),
681                ])),
682                ..Default::default()
683            },
684            KnownServiceProvider::DnsResolver => fnetemul::ChildDef {
685                name: Some(constants::dns_resolver::COMPONENT_NAME.to_string()),
686                source: Some(fnetemul::ChildSource::Component(
687                    constants::dns_resolver::COMPONENT_URL.to_string(),
688                )),
689                exposes: Some(vec![
690                    fnet_name::LookupAdminMarker::PROTOCOL_NAME.to_string(),
691                    fnet_name::LookupMarker::PROTOCOL_NAME.to_string(),
692                ]),
693                uses: Some(fnetemul::ChildUses::Capabilities(vec![
694                    fnetemul::Capability::LogSink(fnetemul::Empty {}),
695                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_routes::StateMarker>(
696                        constants::netstack::COMPONENT_NAME,
697                    )),
698                    fnetemul::Capability::ChildDep(protocol_dep::<fposix_socket::ProviderMarker>(
699                        constants::netstack::COMPONENT_NAME,
700                    )),
701                    fnetemul::Capability::ChildDep(protocol_dep::<
702                        fidl_fuchsia_testing::FakeClockMarker,
703                    >(
704                        constants::fake_clock::COMPONENT_NAME
705                    )),
706                ])),
707                ..Default::default()
708            },
709            KnownServiceProvider::Reachability { eager } => fnetemul::ChildDef {
710                name: Some(constants::reachability::COMPONENT_NAME.to_string()),
711                source: Some(fnetemul::ChildSource::Component(
712                    constants::reachability::COMPONENT_URL.to_string(),
713                )),
714                exposes: Some(vec![fnet_reachability::MonitorMarker::PROTOCOL_NAME.to_string()]),
715                uses: Some(fnetemul::ChildUses::Capabilities(vec![
716                    fnetemul::Capability::LogSink(fnetemul::Empty {}),
717                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_interfaces::StateMarker>(
718                        constants::netstack::COMPONENT_NAME,
719                    )),
720                    fnetemul::Capability::ChildDep(protocol_dep::<fposix_socket::ProviderMarker>(
721                        constants::netstack::COMPONENT_NAME,
722                    )),
723                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_name::LookupMarker>(
724                        constants::dns_resolver::COMPONENT_NAME,
725                    )),
726                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_neighbor::ViewMarker>(
727                        constants::netstack::COMPONENT_NAME,
728                    )),
729                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_debug::InterfacesMarker>(
730                        constants::netstack::COMPONENT_NAME,
731                    )),
732                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_root::InterfacesMarker>(
733                        constants::netstack::COMPONENT_NAME,
734                    )),
735                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_routes::StateV4Marker>(
736                        constants::netstack::COMPONENT_NAME,
737                    )),
738                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_routes::StateV6Marker>(
739                        constants::netstack::COMPONENT_NAME,
740                    )),
741                    fnetemul::Capability::ChildDep(protocol_dep::<fnet_debug::DiagnosticsMarker>(
742                        constants::netstack::COMPONENT_NAME,
743                    )),
744                    fnetemul::Capability::ChildDep(protocol_dep::<
745                        fidl_fuchsia_testing::FakeClockMarker,
746                    >(
747                        constants::fake_clock::COMPONENT_NAME
748                    )),
749                ])),
750                eager: Some(*eager),
751                ..Default::default()
752            },
753            KnownServiceProvider::SocketProxy => fnetemul::ChildDef {
754                name: Some(constants::socket_proxy::COMPONENT_NAME.to_string()),
755                source: Some(fnetemul::ChildSource::Component(
756                    constants::socket_proxy::COMPONENT_URL.to_string(),
757                )),
758                exposes: Some(vec![
759                    fposix_socket::ProviderMarker::PROTOCOL_NAME.to_string(),
760                    fposix_socket_raw::ProviderMarker::PROTOCOL_NAME.to_string(),
761                    fnp_socketproxy::StarnixNetworksMarker::PROTOCOL_NAME.to_string(),
762                    fnp_socketproxy::FuchsiaNetworksMarker::PROTOCOL_NAME.to_string(),
763                    fnp_socketproxy::DnsServerWatcherMarker::PROTOCOL_NAME.to_string(),
764                    fnp_properties::DefaultNetworkWatcherMarker::PROTOCOL_NAME.to_string(),
765                ]),
766                uses: Some(fnetemul::ChildUses::Capabilities(vec![
767                    fnetemul::Capability::ChildDep(protocol_dep::<fposix_socket::ProviderMarker>(
768                        constants::netstack::COMPONENT_NAME,
769                    )),
770                    fnetemul::Capability::ChildDep(
771                        protocol_dep::<fposix_socket_raw::ProviderMarker>(
772                            constants::netstack::COMPONENT_NAME,
773                        ),
774                    ),
775                ])),
776                ..Default::default()
777            },
778            KnownServiceProvider::NetworkTestRealm { require_outer_netstack } => {
779                fnetemul::ChildDef {
780                    name: Some(constants::network_test_realm::COMPONENT_NAME.to_string()),
781                    source: Some(fnetemul::ChildSource::Component(
782                        constants::network_test_realm::COMPONENT_URL.to_string(),
783                    )),
784                    exposes: Some(vec![
785                        fntr::ControllerMarker::PROTOCOL_NAME.to_string(),
786                        fcomponent::RealmMarker::PROTOCOL_NAME.to_string(),
787                    ]),
788                    uses: Some(fnetemul::ChildUses::Capabilities(
789                        std::iter::once(fnetemul::Capability::LogSink(fnetemul::Empty {}))
790                            .chain(
791                                require_outer_netstack
792                                    .then_some([
793                                        fnetemul::Capability::ChildDep(protocol_dep::<
794                                            fnet_stack::StackMarker,
795                                        >(
796                                            constants::netstack::COMPONENT_NAME,
797                                        )),
798                                        fnetemul::Capability::ChildDep(protocol_dep::<
799                                            fnet_debug::InterfacesMarker,
800                                        >(
801                                            constants::netstack::COMPONENT_NAME,
802                                        )),
803                                        fnetemul::Capability::ChildDep(protocol_dep::<
804                                            fnet_root::InterfacesMarker,
805                                        >(
806                                            constants::netstack::COMPONENT_NAME,
807                                        )),
808                                        fnetemul::Capability::ChildDep(protocol_dep::<
809                                            fnet_interfaces::StateMarker,
810                                        >(
811                                            constants::netstack::COMPONENT_NAME,
812                                        )),
813                                    ])
814                                    .into_iter()
815                                    .flatten(),
816                            )
817                            .collect::<Vec<_>>(),
818                    )),
819                    ..Default::default()
820                }
821            }
822            KnownServiceProvider::FakeClock => fnetemul::ChildDef {
823                name: Some(constants::fake_clock::COMPONENT_NAME.to_string()),
824                source: Some(fnetemul::ChildSource::Component(
825                    constants::fake_clock::COMPONENT_URL.to_string(),
826                )),
827                exposes: Some(vec![
828                    fidl_fuchsia_testing::FakeClockMarker::PROTOCOL_NAME.to_string(),
829                    fidl_fuchsia_testing::FakeClockControlMarker::PROTOCOL_NAME.to_string(),
830                ]),
831                uses: Some(fnetemul::ChildUses::Capabilities(vec![fnetemul::Capability::LogSink(
832                    fnetemul::Empty {},
833                )])),
834                ..Default::default()
835            },
836            KnownServiceProvider::FakeSocketProxy => fnetemul::ChildDef {
837                name: Some(constants::fake_socket_proxy::COMPONENT_NAME.to_string()),
838                source: Some(fnetemul::ChildSource::Component(
839                    constants::fake_socket_proxy::COMPONENT_URL.to_string(),
840                )),
841                exposes: Some(vec![
842                    fnp_properties::DefaultNetworkWatcherMarker::PROTOCOL_NAME.to_string(),
843                    fnp_socketproxy::DnsServerWatcherMarker::PROTOCOL_NAME.to_string(),
844                    fnp_socketproxy::FuchsiaNetworksMarker::PROTOCOL_NAME.to_string(),
845                    fnp_testing::FakeSocketProxy_Marker::PROTOCOL_NAME.to_string(),
846                ]),
847                ..Default::default()
848            },
849            KnownServiceProvider::FakeNetcfg => fnetemul::ChildDef {
850                name: Some(constants::netcfg::COMPONENT_NAME.to_string()),
851                source: Some(fnetemul::ChildSource::Component(
852                    constants::netcfg::fake::COMPONENT_URL.to_string(),
853                )),
854                exposes: Some(vec![
855                    fnp_properties::NetworksMarker::PROTOCOL_NAME.to_string(),
856                    fnp_testing::FakeNetcfgMarker::PROTOCOL_NAME.to_string(),
857                ]),
858                ..Default::default()
859            },
860        }
861    }
862}
863
864/// Set the `opaque_iids` structured configuration value for Netstack3.
865pub fn set_netstack3_opaque_iids(netstack: &mut fnetemul::ChildDef, value: bool) {
866    const KEY: &str = "opaque_iids";
867    set_structured_config_value(netstack, KEY.to_owned(), cm_rust::ConfigValue::from(value));
868}
869
870/// Set the `suspend_enabled` structured configuration value for Netstack3.
871pub fn set_netstack3_suspend_enabled(netstack: &mut fnetemul::ChildDef, value: bool) {
872    const KEY: &str = "suspend_enabled";
873    set_structured_config_value(netstack, KEY.to_owned(), cm_rust::ConfigValue::from(value));
874}
875
876/// Set a structured configuration value for the provided component.
877fn set_structured_config_value(
878    component: &mut fnetemul::ChildDef,
879    key: String,
880    value: cm_rust::ConfigValue,
881) {
882    component
883        .config_values
884        .get_or_insert_default()
885        .push(fnetemul::ChildConfigValue { key, value: value.native_into_fidl() });
886}
887
888/// Abstraction for a Fuchsia component which offers network stack services.
889pub trait Netstack: Copy + Clone {
890    /// The Netstack version.
891    const VERSION: NetstackVersion;
892}
893
894/// Uninstantiable type that represents Netstack2's implementation of a
895/// network stack.
896#[derive(Copy, Clone)]
897pub enum Netstack2 {}
898
899impl Netstack for Netstack2 {
900    const VERSION: NetstackVersion = NetstackVersion::Netstack2 { tracing: false, fast_udp: false };
901}
902
903/// Uninstantiable type that represents Netstack2's production implementation of
904/// a network stack.
905#[derive(Copy, Clone)]
906pub enum ProdNetstack2 {}
907
908impl Netstack for ProdNetstack2 {
909    const VERSION: NetstackVersion = NetstackVersion::ProdNetstack2;
910}
911
912/// Uninstantiable type that represents Netstack3's implementation of a
913/// network stack.
914#[derive(Copy, Clone)]
915pub enum Netstack3 {}
916
917impl Netstack for Netstack3 {
918    const VERSION: NetstackVersion = NetstackVersion::Netstack3;
919}
920
921/// Uninstantiable type that represents Netstack3's production implementation of
922/// a network stack.
923#[derive(Copy, Clone)]
924pub enum ProdNetstack3 {}
925
926impl Netstack for ProdNetstack3 {
927    const VERSION: NetstackVersion = NetstackVersion::ProdNetstack3;
928}
929
930/// Abstraction for a Fuchsia component which offers network configuration services.
931pub trait Manager: Copy + Clone {
932    /// The management agent to be used.
933    const MANAGEMENT_AGENT: ManagementAgent;
934}
935
936/// Uninstantiable type that represents netcfg_basic's implementation of a network manager.
937#[derive(Copy, Clone)]
938pub enum NetCfgBasic {}
939
940impl Manager for NetCfgBasic {
941    const MANAGEMENT_AGENT: ManagementAgent = ManagementAgent::NetCfg(NetCfgVersion::Basic);
942}
943
944/// Uninstantiable type that represents netcfg_advanced's implementation of a
945/// network manager.
946#[derive(Copy, Clone)]
947pub enum NetCfgAdvanced {}
948
949impl Manager for NetCfgAdvanced {
950    const MANAGEMENT_AGENT: ManagementAgent = ManagementAgent::NetCfg(NetCfgVersion::Advanced);
951}
952
953pub use netemul::{DhcpClient, DhcpClientVersion, InStack, OutOfStack};
954
955/// A combination of Netstack and DhcpClient guaranteed to be compatible with
956/// each other.
957pub trait NetstackAndDhcpClient: Copy + Clone {
958    /// The netstack to be used.
959    type Netstack: Netstack;
960    /// The DHCP client to be used.
961    type DhcpClient: DhcpClient;
962}
963
964/// Netstack2 with the in-stack DHCP client.
965#[derive(Copy, Clone)]
966pub enum Netstack2AndInStackDhcpClient {}
967
968impl NetstackAndDhcpClient for Netstack2AndInStackDhcpClient {
969    type Netstack = Netstack2;
970    type DhcpClient = InStack;
971}
972
973/// Netstack2 with the out-of-stack DHCP client.
974#[derive(Copy, Clone)]
975pub enum Netstack2AndOutOfStackDhcpClient {}
976
977impl NetstackAndDhcpClient for Netstack2AndOutOfStackDhcpClient {
978    type Netstack = Netstack2;
979    type DhcpClient = OutOfStack;
980}
981
982/// Netstack3 with the out-of-stack DHCP client.
983#[derive(Copy, Clone)]
984pub enum Netstack3AndOutOfStackDhcpClient {}
985
986impl NetstackAndDhcpClient for Netstack3AndOutOfStackDhcpClient {
987    type Netstack = Netstack3;
988    type DhcpClient = OutOfStack;
989}
990
991/// Helpers for `netemul::TestSandbox`.
992#[async_trait]
993pub trait TestSandboxExt {
994    /// Creates a realm with Netstack services.
995    fn create_netstack_realm<'a, N, S>(&'a self, name: S) -> Result<netemul::TestRealm<'a>>
996    where
997        N: Netstack,
998        S: Into<Cow<'a, str>>;
999
1000    /// Creates a realm with the base Netstack services plus additional ones in
1001    /// `children`.
1002    fn create_netstack_realm_with<'a, N, S, I>(
1003        &'a self,
1004        name: S,
1005        children: I,
1006    ) -> Result<netemul::TestRealm<'a>>
1007    where
1008        S: Into<Cow<'a, str>>,
1009        N: Netstack,
1010        I: IntoIterator,
1011        I::Item: Into<fnetemul::ChildDef>;
1012}
1013
1014#[async_trait]
1015impl TestSandboxExt for netemul::TestSandbox {
1016    fn create_netstack_realm<'a, N, S>(&'a self, name: S) -> Result<netemul::TestRealm<'a>>
1017    where
1018        N: Netstack,
1019        S: Into<Cow<'a, str>>,
1020    {
1021        self.create_netstack_realm_with::<N, _, _>(name, std::iter::empty::<fnetemul::ChildDef>())
1022    }
1023
1024    fn create_netstack_realm_with<'a, N, S, I>(
1025        &'a self,
1026        name: S,
1027        children: I,
1028    ) -> Result<netemul::TestRealm<'a>>
1029    where
1030        S: Into<Cow<'a, str>>,
1031        N: Netstack,
1032        I: IntoIterator,
1033        I::Item: Into<fnetemul::ChildDef>,
1034    {
1035        self.create_realm(
1036            name,
1037            [KnownServiceProvider::Netstack(N::VERSION)]
1038                .iter()
1039                .map(fnetemul::ChildDef::from)
1040                .chain(children.into_iter().map(Into::into)),
1041        )
1042    }
1043}
1044
1045/// Helpers for `netemul::TestRealm`.
1046#[async_trait]
1047pub trait TestRealmExt {
1048    /// Returns the properties of the loopback interface, or `None` if there is no
1049    /// loopback interface.
1050    async fn loopback_properties(
1051        &self,
1052    ) -> Result<Option<fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>>>;
1053
1054    /// Get a `fuchsia.net.interfaces.admin/Control` client proxy for the
1055    /// interface identified by [`id`] via `fuchsia.net.root`.
1056    ///
1057    /// Note that one should prefer to operate on a `TestInterface` if it is
1058    /// available; but this method exists in order to obtain a Control channel
1059    /// for interfaces such as loopback.
1060    fn interface_control(&self, id: u64) -> Result<fnet_interfaces_ext::admin::Control>;
1061}
1062
1063#[async_trait]
1064impl TestRealmExt for netemul::TestRealm<'_> {
1065    async fn loopback_properties(
1066        &self,
1067    ) -> Result<Option<fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>>> {
1068        let interface_state = self
1069            .connect_to_protocol::<fnet_interfaces::StateMarker>()
1070            .context("failed to connect to fuchsia.net.interfaces/State")?;
1071
1072        let properties = fnet_interfaces_ext::existing(
1073            fnet_interfaces_ext::event_stream_from_state(
1074                &interface_state,
1075                fnet_interfaces_ext::IncludedAddresses::OnlyAssigned,
1076            )
1077            .expect("create watcher event stream"),
1078            HashMap::<u64, fnet_interfaces_ext::PropertiesAndState<(), _>>::new(),
1079        )
1080        .await
1081        .context("failed to get existing interface properties from watcher")?
1082        .into_iter()
1083        .find_map(|(_id, properties_and_state): (u64, _)| {
1084            let fnet_interfaces_ext::PropertiesAndState {
1085                properties: properties @ fnet_interfaces_ext::Properties { port_class, .. },
1086                state: (),
1087            } = properties_and_state;
1088            port_class.is_loopback().then_some(properties)
1089        });
1090        Ok(properties)
1091    }
1092
1093    fn interface_control(&self, id: u64) -> Result<fnet_interfaces_ext::admin::Control> {
1094        let root_control = self
1095            .connect_to_protocol::<fnet_root::InterfacesMarker>()
1096            .context("connect to protocol")?;
1097
1098        let (control, server) = fnet_interfaces_ext::admin::Control::create_endpoints()
1099            .context("create Control proxy")?;
1100        let () = root_control.get_admin(id, server).context("get admin")?;
1101        Ok(control)
1102    }
1103}