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