1use std::pin::pin;
6
7use fidl::endpoints::ProtocolMarker;
8use fidl_fuchsia_net_interfaces as fnet_interfaces;
9use fidl_fuchsia_net_resources as fnet_resources;
10use fidl_fuchsia_net_routes as fnet_routes;
11use fidl_fuchsia_net_routes_ext::admin::FidlRouteAdminIpExt;
12use fidl_fuchsia_net_routes_ext::{self as fnet_routes_ext, FidlRouteIpExt};
13use fidl_fuchsia_netemul_network as fnetemul_network;
14use futures::future::{FutureExt as _, LocalBoxFuture};
15use net_types::ip::Ip;
16use netemul::{TestEndpoint, TestNetwork, TestRealm};
17use netstack_testing_common::realms::{
18 KnownServiceProvider, ManagerConfig, Netstack3, SocketProxyType, TestRealmExt, TestSandboxExt,
19 constants,
20};
21use netstack_testing_common::{
22 ASYNC_EVENT_POSITIVE_CHECK_TIMEOUT, interfaces, wait_for_component_stopped,
23};
24
25#[derive(Default)]
26pub struct NetcfgOwnedDeviceArgs {
27 pub socket_proxy_type: SocketProxyType,
29 pub extra_known_service_providers: Vec<KnownServiceProvider>,
31}
32
33pub async fn with_netcfg_owned_device<
39 F: for<'a> FnOnce(
40 u64,
41 &'a netemul::TestNetwork<'a>,
42 &'a fnet_interfaces::StateProxy,
43 &'a netemul::TestRealm<'a>,
44 &'a netemul::TestSandbox,
45 ) -> LocalBoxFuture<'a, ()>,
46>(
47 name: &str,
48 manager_config: ManagerConfig,
49 additional_args: NetcfgOwnedDeviceArgs,
50 after_interface_up: F,
51) -> String {
52 let NetcfgOwnedDeviceArgs { socket_proxy_type, extra_known_service_providers } =
53 additional_args;
54 let sandbox = netemul::TestSandbox::new().expect("create sandbox");
55 let realm = sandbox
56 .create_netstack_realm_with::<Netstack3, _, _>(
57 name,
58 [
59 KnownServiceProvider::Manager {
60 use_dhcp_server: false,
61 socket_proxy_type,
62 config: manager_config,
63 },
64 KnownServiceProvider::DhcpClient,
65 KnownServiceProvider::DnsResolver,
66 KnownServiceProvider::FakeClock,
67 ]
68 .into_iter()
69 .chain(extra_known_service_providers)
70 .chain(socket_proxy_type.known_service_provider().into_iter()),
71 )
72 .expect("create netstack realm");
73 let network = sandbox.create_network(name).await.expect("create network");
75 let _endpoint = add_device_to_devfs(&network, &realm, name.to_string(), None).await;
76
77 let (interface_state, if_id, if_name) = verify_interface_added(&realm).await;
79
80 after_interface_up(if_id, &network, &interface_state, &realm, &sandbox).await;
81
82 realm.shutdown().await.expect("failed to shutdown realm");
89
90 if_name
91}
92
93pub async fn add_device_to_devfs<'a>(
100 network: &'a TestNetwork<'a>,
101 realm: &'a TestRealm<'a>,
102 name: String,
103 endpoint_config: Option<fnetemul_network::EndpointConfig>,
104) -> TestEndpoint<'a> {
105 let endpoint = match endpoint_config {
106 Some(config) => network.create_endpoint_with(name, config).await,
107 None => network.create_endpoint(name).await,
108 }
109 .expect("create endpoint");
110 endpoint.set_link_up(true).await.expect("set link up");
111 let endpoint_mount_path = netemul::devfs_device_path("ep");
112 let endpoint_mount_path = endpoint_mount_path.as_path();
113 realm.add_virtual_device(&endpoint, endpoint_mount_path).await.unwrap_or_else(|e| {
114 panic!("add virtual device {}: {:?}", endpoint_mount_path.display(), e)
115 });
116 endpoint
117}
118
119pub async fn verify_interface_added<'a>(
123 realm: &'a TestRealm<'a>,
124) -> (fnet_interfaces::StateProxy, u64, String) {
125 let interface_state = realm
126 .connect_to_protocol::<fnet_interfaces::StateMarker>()
127 .expect("connect to fuchsia.net.interfaces/State service");
128 let wait_for_netmgr =
129 wait_for_component_stopped(&realm, constants::netcfg::COMPONENT_NAME, None).fuse();
130 let mut wait_for_netmgr = pin!(wait_for_netmgr);
131 let (if_id, if_name): (u64, String) = interfaces::wait_for_non_loopback_interface_up(
132 &interface_state,
133 &mut wait_for_netmgr,
134 None,
135 ASYNC_EVENT_POSITIVE_CHECK_TIMEOUT,
136 )
137 .await
138 .expect("wait for non loopback interface");
139 (interface_state, if_id, if_name.clone())
140}
141
142pub async fn add_default_route<'a, I>(
147 realm: &'a TestRealm<'a>,
148 if_id: u64,
149 route_set: &<I::RouteSetMarker as ProtocolMarker>::Proxy,
150) -> bool
151where
152 I: Ip + FidlRouteAdminIpExt + FidlRouteIpExt,
153{
154 let client_interface_control =
158 realm.interface_control(if_id).expect("get client interface Control");
159
160 let fnet_resources::GrantForInterfaceAuthorization { interface_id, token } =
161 client_interface_control
162 .get_authorization_for_interface()
163 .await
164 .expect("get authorization");
165
166 fnet_routes_ext::admin::authenticate_for_interface::<I>(
167 route_set,
168 fnet_resources::ProofOfInterfaceAuthorization { interface_id, token },
169 )
170 .await
171 .expect("authenticate for interface should not see FIDL error")
172 .expect("authenticate for interface should succeed");
173
174 let default_route = fnet_routes_ext::Route {
175 destination: I::ALL_ADDRS_SUBNET,
176 action: fnet_routes_ext::RouteAction::Forward(fnet_routes_ext::RouteTarget::<I> {
177 outbound_interface: interface_id,
178 next_hop: None,
179 }),
180 properties: fnet_routes_ext::RouteProperties {
181 specified_properties: fnet_routes_ext::SpecifiedRouteProperties {
182 metric: fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
183 },
184 },
185 };
186
187 fnet_routes_ext::admin::add_route::<I>(
188 route_set,
189 &default_route.try_into().expect("convert into fidl route"),
190 )
191 .await
192 .expect("should not see RouteSet FIDL error")
193 .expect("should not see RouteSet error")
194}