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