policy_testing_common/
lib.rs

1// Copyright 2024 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
5use std::pin::pin;
6
7use fidl_fuchsia_net_interfaces as fnet_interfaces;
8use futures::future::{FutureExt as _, LocalBoxFuture};
9use netstack_testing_common::realms::{
10    KnownServiceProvider, Manager, ManagerConfig, Netstack, TestSandboxExt,
11};
12use netstack_testing_common::{
13    interfaces, wait_for_component_stopped, ASYNC_EVENT_POSITIVE_CHECK_TIMEOUT,
14};
15
16#[derive(Default)]
17pub struct NetcfgOwnedDeviceArgs {
18    // Whether to use the out of stack DHCP client.
19    pub use_out_of_stack_dhcp_client: bool,
20    // Whether to include the socketproxy protocols in netcfg.
21    pub use_socket_proxy: bool,
22    // Additional service providers to include in the realm.
23    pub extra_known_service_providers: Vec<KnownServiceProvider>,
24}
25
26/// Initialize a realm with a device that is owned by netcfg.
27/// The device is discovered through devfs and installed into
28/// the Netstack via netcfg. `after_interface_up` is called
29/// once the interface has been discovered via the Netstack
30/// interfaces watcher.
31pub async fn with_netcfg_owned_device<
32    M: Manager,
33    N: Netstack,
34    F: for<'a> FnOnce(
35        u64,
36        &'a netemul::TestNetwork<'a>,
37        &'a fnet_interfaces::StateProxy,
38        &'a netemul::TestRealm<'a>,
39        &'a netemul::TestSandbox,
40    ) -> LocalBoxFuture<'a, ()>,
41>(
42    name: &str,
43    manager_config: ManagerConfig,
44    additional_args: NetcfgOwnedDeviceArgs,
45    after_interface_up: F,
46) -> String {
47    let NetcfgOwnedDeviceArgs {
48        use_out_of_stack_dhcp_client,
49        use_socket_proxy,
50        extra_known_service_providers,
51    } = additional_args;
52    let sandbox = netemul::TestSandbox::new().expect("create sandbox");
53    let realm = sandbox
54        .create_netstack_realm_with::<N, _, _>(
55            name,
56            [
57                KnownServiceProvider::Manager {
58                    agent: M::MANAGEMENT_AGENT,
59                    use_dhcp_server: false,
60                    use_out_of_stack_dhcp_client,
61                    use_socket_proxy,
62                    config: manager_config,
63                },
64                KnownServiceProvider::DnsResolver,
65                KnownServiceProvider::FakeClock,
66            ]
67            .into_iter()
68            .chain(extra_known_service_providers)
69            // If the client requested an out of stack DHCP client or to use
70            // the socket proxy, add them to the list of service providers.
71            .chain(
72                use_out_of_stack_dhcp_client
73                    .then_some(KnownServiceProvider::DhcpClient)
74                    .into_iter(),
75            )
76            .chain(use_socket_proxy.then_some(KnownServiceProvider::SocketProxy).into_iter()),
77        )
78        .expect("create netstack realm");
79
80    // Add a device to the realm.
81    let network = sandbox.create_network(name).await.expect("create network");
82    let endpoint = network.create_endpoint(name).await.expect("create endpoint");
83    endpoint.set_link_up(true).await.expect("set link up");
84    let endpoint_mount_path = netemul::devfs_device_path("ep");
85    let endpoint_mount_path = endpoint_mount_path.as_path();
86    realm.add_virtual_device(&endpoint, endpoint_mount_path).await.unwrap_or_else(|e| {
87        panic!("add virtual device {}: {:?}", endpoint_mount_path.display(), e)
88    });
89
90    // Make sure the Netstack got the new device added.
91    let interface_state = realm
92        .connect_to_protocol::<fnet_interfaces::StateMarker>()
93        .expect("connect to fuchsia.net.interfaces/State service");
94    let wait_for_netmgr =
95        wait_for_component_stopped(&realm, M::MANAGEMENT_AGENT.get_component_name(), None).fuse();
96    let mut wait_for_netmgr = pin!(wait_for_netmgr);
97    let (if_id, if_name): (u64, String) = interfaces::wait_for_non_loopback_interface_up(
98        &interface_state,
99        &mut wait_for_netmgr,
100        None,
101        ASYNC_EVENT_POSITIVE_CHECK_TIMEOUT,
102    )
103    .await
104    .expect("wait for non loopback interface");
105
106    after_interface_up(if_id, &network, &interface_state, &realm, &sandbox).await;
107
108    // Wait for orderly shutdown of the test realm to complete before allowing
109    // test interfaces to be cleaned up.
110    //
111    // This is necessary to prevent test interfaces from being removed while
112    // NetCfg is still in the process of configuring them after adding them to
113    // the Netstack, which causes spurious errors.
114    realm.shutdown().await.expect("failed to shutdown realm");
115
116    if_name
117}