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