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