1#![warn(missing_docs)]
6
7use super::Result;
11
12use anyhow::Context as _;
13use fuchsia_async::{DurationExt as _, TimeoutExt as _};
14
15use futures::future::{FusedFuture, Future, FutureExt as _, TryFutureExt as _};
16use std::collections::{HashMap, HashSet};
17use std::pin::pin;
18
19pub async fn wait_for_non_loopback_interface_up<
26 F: Unpin + FusedFuture + Future<Output = Result<component_events::events::Stopped>>,
27>(
28 interface_state: &fidl_fuchsia_net_interfaces::StateProxy,
29 mut wait_for_netmgr: &mut F,
30 exclude_ids: Option<&HashSet<u64>>,
31 timeout: zx::MonotonicDuration,
32) -> Result<(u64, String)> {
33 let mut if_map =
34 HashMap::<u64, fidl_fuchsia_net_interfaces_ext::PropertiesAndState<(), _>>::new();
35 let mut wait_for_interface = pin!(
36 fidl_fuchsia_net_interfaces_ext::wait_interface(
37 fidl_fuchsia_net_interfaces_ext::event_stream_from_state::<
38 fidl_fuchsia_net_interfaces_ext::DefaultInterest,
39 >(
40 interface_state, fidl_fuchsia_net_interfaces_ext::IncludedAddresses::OnlyAssigned,
41 )?,
42 &mut if_map,
43 |if_map| {
44 if_map.iter().find_map(
45 |(
46 id,
47 fidl_fuchsia_net_interfaces_ext::PropertiesAndState {
48 properties:
49 fidl_fuchsia_net_interfaces_ext::Properties {
50 name,
51 port_class,
52 online,
53 ..
54 },
55 state: _,
56 },
57 )| {
58 (*port_class != fidl_fuchsia_net_interfaces_ext::PortClass::Loopback
59 && *online
60 && exclude_ids.map_or(true, |ids| !ids.contains(id)))
61 .then(|| (*id, name.clone()))
62 },
63 )
64 },
65 )
66 .map_err(anyhow::Error::from)
67 .on_timeout(timeout.after_now(), || Err(anyhow::anyhow!("timed out")))
68 .map(|r| r.context("failed to wait for non-loopback interface up"))
69 .fuse()
70 );
71 futures::select! {
72 wait_for_interface_res = wait_for_interface => {
73 wait_for_interface_res
74 }
75 stopped_event = wait_for_netmgr => {
76 Err(anyhow::anyhow!("the network manager unexpectedly stopped with event = {:?}", stopped_event))
77 }
78 }
79}
80
81pub async fn add_address_wait_assigned(
83 control: &fidl_fuchsia_net_interfaces_ext::admin::Control,
84 address: fidl_fuchsia_net::Subnet,
85 address_parameters: fidl_fuchsia_net_interfaces_admin::AddressParameters,
86) -> std::result::Result<
87 fidl_fuchsia_net_interfaces_admin::AddressStateProviderProxy,
88 fidl_fuchsia_net_interfaces_ext::admin::AddressStateProviderError,
89> {
90 let (address_state_provider, server) = fidl::endpoints::create_proxy::<
91 fidl_fuchsia_net_interfaces_admin::AddressStateProviderMarker,
92 >();
93 let () = control
94 .add_address(&address, &address_parameters, server)
95 .expect("Control.AddAddress FIDL error");
96
97 fidl_fuchsia_net_interfaces_ext::admin::wait_for_address_added_event(
98 &mut address_state_provider.take_event_stream(),
99 )
100 .await?;
101
102 {
103 let mut state_stream =
104 pin!(fidl_fuchsia_net_interfaces_ext::admin::assignment_state_stream(
105 address_state_provider.clone(),
106 ));
107 let () = fidl_fuchsia_net_interfaces_ext::admin::wait_assignment_state(
108 &mut state_stream,
109 fidl_fuchsia_net_interfaces::AddressAssignmentState::Assigned,
110 )
111 .await?;
112 }
113 Ok(address_state_provider)
114}
115
116pub async fn remove_subnet_address_and_route<'a>(
118 iface: &'a netemul::TestInterface<'a>,
119 subnet: fidl_fuchsia_net::Subnet,
120) -> Result<bool> {
121 iface.del_address_and_subnet_route(subnet).await
122}
123
124pub async fn wait_for_v4_and_v6_ll(
134 interfaces_state: &fidl_fuchsia_net_interfaces::StateProxy,
135 id: u64,
136) -> Result<(net_types::ip::Ipv4Addr, net_types::ip::Ipv6Addr)> {
137 wait_for_addresses(interfaces_state, id, |addresses| {
138 let (v4, v6) = addresses.into_iter().fold(
139 (None, None),
140 |(v4, v6),
141 &fidl_fuchsia_net_interfaces_ext::Address {
142 addr: fidl_fuchsia_net::Subnet { addr, prefix_len: _ },
143 valid_until: _,
144 preferred_lifetime_info: _,
145 assignment_state,
146 }| {
147 assert_eq!(
148 assignment_state,
149 fidl_fuchsia_net_interfaces::AddressAssignmentState::Assigned
150 );
151 match addr {
152 fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address { addr }) => {
153 (Some(net_types::ip::Ipv4Addr::from(addr)), v6)
154 }
155 fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address { addr }) => {
156 let v6_addr = net_types::ip::Ipv6Addr::from_bytes(addr);
157 (v4, if v6_addr.is_unicast_link_local() { Some(v6_addr) } else { v6 })
158 }
159 }
160 },
161 );
162 match (v4, v6) {
163 (Some(v4), Some(v6)) => Some((v4, v6)),
164 _ => None,
165 }
166 })
167 .await
168 .context("wait for addresses")
169}
170
171pub async fn wait_for_v6_ll(
181 interfaces_state: &fidl_fuchsia_net_interfaces::StateProxy,
182 id: u64,
183) -> Result<net_types::ip::Ipv6Addr> {
184 wait_for_addresses(interfaces_state, id, |addresses| {
185 addresses.into_iter().find_map(
186 |&fidl_fuchsia_net_interfaces_ext::Address {
187 addr: fidl_fuchsia_net::Subnet { addr, prefix_len: _ },
188 valid_until: _,
189 preferred_lifetime_info: _,
190 assignment_state,
191 }| {
192 assert_eq!(
193 assignment_state,
194 fidl_fuchsia_net_interfaces::AddressAssignmentState::Assigned
195 );
196 match addr {
197 fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address {
198 addr: _,
199 }) => None,
200 fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address { addr }) => {
201 let v6_addr = net_types::ip::Ipv6Addr::from_bytes(addr);
202 v6_addr.is_unicast_link_local().then(|| v6_addr)
203 }
204 }
205 },
206 )
207 })
208 .await
209 .context("wait for IPv6 link-local address")
210}
211
212pub async fn wait_for_addresses<T, F>(
215 interfaces_state: &fidl_fuchsia_net_interfaces::StateProxy,
216 id: u64,
217 mut predicate: F,
218) -> Result<T>
219where
220 F: FnMut(
221 &[fidl_fuchsia_net_interfaces_ext::Address<fidl_fuchsia_net_interfaces_ext::AllInterest>],
222 ) -> Option<T>,
223{
224 let mut state =
225 fidl_fuchsia_net_interfaces_ext::InterfaceState::<(), _>::Unknown(u64::from(id));
226 fidl_fuchsia_net_interfaces_ext::wait_interface_with_id(
227 fidl_fuchsia_net_interfaces_ext::event_stream_from_state::<
228 fidl_fuchsia_net_interfaces_ext::AllInterest,
229 >(
230 &interfaces_state, fidl_fuchsia_net_interfaces_ext::IncludedAddresses::OnlyAssigned
231 )
232 .context("get interface event stream")?,
233 &mut state,
234 |properties_and_state| predicate(&properties_and_state.properties.addresses),
235 )
236 .await
237 .context("wait for address")
238}
239
240pub async fn wait_for_online(
242 interfaces_state: &fidl_fuchsia_net_interfaces::StateProxy,
243 id: u64,
244 want_online: bool,
245) -> Result<()> {
246 let mut state =
247 fidl_fuchsia_net_interfaces_ext::InterfaceState::<(), _>::Unknown(u64::from(id));
248 fidl_fuchsia_net_interfaces_ext::wait_interface_with_id(
249 fidl_fuchsia_net_interfaces_ext::event_stream_from_state::<
250 fidl_fuchsia_net_interfaces_ext::DefaultInterest,
251 >(
252 &interfaces_state, fidl_fuchsia_net_interfaces_ext::IncludedAddresses::OnlyAssigned
253 )
254 .context("get interface event stream")?,
255 &mut state,
256 |properties_and_state| {
257 (properties_and_state.properties.online == want_online).then_some(())
258 },
259 )
260 .await
261 .with_context(|| format!("wait for online {}", want_online))
262}
263
264#[async_trait::async_trait]
266pub trait TestInterfaceExt {
267 async fn apply_nud_flake_workaround(&self) -> Result;
269}
270
271#[async_trait::async_trait]
272impl<'a> TestInterfaceExt for netemul::TestInterface<'a> {
273 async fn apply_nud_flake_workaround(&self) -> Result {
274 crate::nud::apply_nud_flake_workaround(self.control()).await
275 }
276}