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