Skip to main content

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