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