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