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