fidl_fuchsia_net_dhcpv6_ext/
lib.rs
1#![deny(missing_docs)]
6
7use fidl_table_validation::{ValidFidlTable, Validate};
10use futures::future::Either;
11use futures::FutureExt as _;
12
13#[derive(ValidFidlTable, Debug, Clone, PartialEq)]
17#[fidl_table_src(fidl_fuchsia_net_dhcpv6::NewClientParams)]
18#[fidl_table_strict]
19pub struct NewClientParams {
20 pub interface_id: u64,
24 pub address: fidl_fuchsia_net::Ipv6SocketAddress,
41 pub config: ClientConfig,
58 #[fidl_field_type(optional)]
59 pub duid: Option<fidl_fuchsia_net_dhcpv6::Duid>,
66}
67
68#[derive(ValidFidlTable, Debug, Clone, PartialEq)]
72#[fidl_table_src(fidl_fuchsia_net_dhcpv6::ClientConfig)]
73#[fidl_table_strict]
74pub struct ClientConfig {
75 #[fidl_field_type(default)]
76 pub information_config: InformationConfig,
80 #[fidl_field_type(default)]
81 pub non_temporary_address_config: AddressConfig,
91 #[fidl_field_type(optional)]
92 pub prefix_delegation_config: Option<fidl_fuchsia_net_dhcpv6::PrefixDelegationConfig>,
105}
106
107#[derive(ValidFidlTable, Debug, Clone, PartialEq, Default)]
111#[fidl_table_src(fidl_fuchsia_net_dhcpv6::InformationConfig)]
112#[fidl_table_strict]
113pub struct InformationConfig {
114 #[fidl_field_type(default)]
115 pub dns_servers: bool,
117}
118
119#[derive(thiserror::Error, Debug)]
121pub enum AddressConfigCustomValidationError {
122 #[error("more preferred addresses in {preferred_addresses:?} than count {address_count}")]
124 TooManyPreferredAddresses {
125 address_count: u8,
127 preferred_addresses: Vec<fidl_fuchsia_net::Ipv6Address>,
129 },
130}
131
132pub struct AddressConfigValidator;
134
135impl Validate<AddressConfig> for AddressConfigValidator {
136 type Error = AddressConfigCustomValidationError;
137
138 fn validate(
139 AddressConfig { address_count, preferred_addresses }: &AddressConfig,
140 ) -> Result<(), Self::Error> {
141 match preferred_addresses.as_ref() {
142 Some(preferred_addresses) => {
143 if preferred_addresses.len() > (*address_count).into() {
144 Err(AddressConfigCustomValidationError::TooManyPreferredAddresses {
145 address_count: *address_count,
146 preferred_addresses: preferred_addresses.clone(),
147 })
148 } else {
149 Ok(())
150 }
151 }
152 None => Ok(()),
153 }
154 }
155}
156
157#[derive(ValidFidlTable, Debug, Clone, PartialEq, Default)]
161#[fidl_table_src(fidl_fuchsia_net_dhcpv6::AddressConfig)]
162#[fidl_table_validator(AddressConfigValidator)]
163#[fidl_table_strict]
164pub struct AddressConfig {
165 #[fidl_field_type(default)]
166 pub address_count: u8,
174 #[fidl_field_type(optional)]
175 pub preferred_addresses: Option<Vec<fidl_fuchsia_net::Ipv6Address>>,
190}
191
192#[derive(Debug)]
194pub enum WatchItem {
195 DnsServers(Vec<fidl_fuchsia_net_name::DnsServer_>),
197 Address {
199 addr: fidl_fuchsia_net::Subnet,
201 parameters: fidl_fuchsia_net_interfaces_admin::AddressParameters,
203 address_state_provider_server_end: fidl::endpoints::ServerEnd<
206 fidl_fuchsia_net_interfaces_admin::AddressStateProviderMarker,
207 >,
208 },
209}
210
211impl WatchItem {
212 pub fn new_address(
214 addr: fidl_fuchsia_net::Subnet,
215 parameters: fidl_fuchsia_net_interfaces_admin::AddressParameters,
216 address_state_provider_server_end: fidl::endpoints::ServerEnd<
217 fidl_fuchsia_net_interfaces_admin::AddressStateProviderMarker,
218 >,
219 ) -> Self {
220 Self::Address { addr, parameters, address_state_provider_server_end }
221 }
222}
223
224pub fn into_watch_stream(
233 client_proxy: fidl_fuchsia_net_dhcpv6::ClientProxy,
234) -> impl futures::Stream<Item = Result<WatchItem, fidl::Error>> + Unpin {
235 let watch_servers_fut = client_proxy.watch_servers();
236 let watch_address_fut = client_proxy.watch_address();
237 futures::stream::try_unfold(
238 (client_proxy, watch_servers_fut, watch_address_fut),
239 |(client_proxy, watch_servers_fut, watch_address_fut)| {
240 futures::future::select(watch_servers_fut, watch_address_fut).map(|either| {
241 match either {
242 Either::Left((servers_res, watch_address_fut)) => servers_res.map(|servers| {
243 let watch_servers_fut = client_proxy.watch_servers();
244 Some((
245 WatchItem::DnsServers(servers),
246 (client_proxy, watch_servers_fut, watch_address_fut),
247 ))
248 }),
249 Either::Right((address_res, watch_servers_fut)) => {
250 address_res.map(|(addr, parameters, address_state_provider_server_end)| {
251 let watch_address_fut = client_proxy.watch_address();
252 Some((
253 WatchItem::new_address(
254 addr,
255 parameters,
256 address_state_provider_server_end,
257 ),
258 (client_proxy, watch_servers_fut, watch_address_fut),
259 ))
260 })
261 }
262 }
263 .or_else(|e| if e.is_closed() { Ok(None) } else { Err(e) })
264 })
265 },
266 )
267}
268
269#[cfg(test)]
270mod tests {
271 use super::{into_watch_stream, WatchItem};
272
273 use assert_matches::assert_matches;
274 use futures::{StreamExt as _, TryStreamExt as _};
275 use net_declare::fidl_ip_v6;
276 use test_case::test_case;
277
278 #[test_case(fidl_fuchsia_net_dhcpv6::AddressConfig {
279 address_count: Some(0),
280 preferred_addresses: Some(vec![fidl_ip_v6!("2001:db8::1")]),
281 ..Default::default()
282 })]
283 #[test_case(fidl_fuchsia_net_dhcpv6::AddressConfig {
284 address_count: Some(1),
285 preferred_addresses: Some(vec![fidl_ip_v6!("2001:db8::1"), fidl_ip_v6!("2001:db8::2")]),
286 ..Default::default()
287 })]
288 #[fuchsia::test]
289 fn address_config_custom_validation(address_config: fidl_fuchsia_net_dhcpv6::AddressConfig) {
290 let (want_address_count, want_preferred_addresses) = assert_matches!(
291 address_config.clone(),
292 fidl_fuchsia_net_dhcpv6::AddressConfig {
293 address_count: Some(address_count),
294 preferred_addresses: Some(preferred_addresses),
295 ..
296 } => (address_count, preferred_addresses));
297
298 assert_matches!(
299 crate::AddressConfig::try_from(address_config),
300 Err(crate::AddressConfigValidationError::Logical(
301 crate::AddressConfigCustomValidationError::TooManyPreferredAddresses {
302 address_count,
303 preferred_addresses,
304 }
305 )) => {
306 assert_eq!(address_count, want_address_count);
307 assert_eq!(preferred_addresses, want_preferred_addresses);
308 }
309 );
310 }
311
312 #[derive(Debug, Clone, Copy)]
313 enum WatchType {
314 DnsServers,
315 Address,
316 }
317
318 const SUBNET: fidl_fuchsia_net::Subnet = net_declare::fidl_subnet!("abcd::1/128");
319
320 async fn run_fake_server(
323 request_stream: &mut fidl_fuchsia_net_dhcpv6::ClientRequestStream,
324 response_types: &[WatchType],
325 ) {
326 let (_, _, _): (
327 &mut fidl_fuchsia_net_dhcpv6::ClientRequestStream,
328 Option<fidl_fuchsia_net_dhcpv6::ClientWatchServersResponder>,
329 Option<fidl_fuchsia_net_dhcpv6::ClientWatchAddressResponder>,
330 ) = futures::stream::iter(response_types)
331 .fold(
332 (request_stream, None, None),
333 |(request_stream, mut dns_servers_responder, mut address_responder),
334 watch_type_to_unblock| async move {
335 while dns_servers_responder.is_none() || address_responder.is_none() {
336 match request_stream
337 .try_next()
338 .await
339 .expect("FIDL error")
340 .expect("request stream ended")
341 {
342 fidl_fuchsia_net_dhcpv6::ClientRequest::WatchServers { responder } => {
343 assert_matches!(dns_servers_responder.replace(responder), None);
344 }
345 fidl_fuchsia_net_dhcpv6::ClientRequest::WatchAddress { responder } => {
346 assert_matches!(address_responder.replace(responder), None);
347 }
348 fidl_fuchsia_net_dhcpv6::ClientRequest::WatchPrefixes {
349 responder: _,
350 } => {
351 panic!("WatchPrefix method should not be called");
352 }
353 fidl_fuchsia_net_dhcpv6::ClientRequest::Shutdown { responder: _ } => {
354 panic!("Shutdown method should not be called");
355 }
356 }
357 }
358 match watch_type_to_unblock {
359 WatchType::Address => {
360 let (_, server_end) = fidl::endpoints::create_endpoints::<
361 fidl_fuchsia_net_interfaces_admin::AddressStateProviderMarker,
362 >();
363 address_responder
364 .take()
365 .expect("must have address responder")
366 .send(&SUBNET, &Default::default(), server_end)
367 .expect("FIDL error");
368 }
369 WatchType::DnsServers => {
370 dns_servers_responder
371 .take()
372 .expect("must have DNS servers responder")
373 .send(&[])
374 .expect("FIDL error");
375 }
376 };
377 (request_stream, dns_servers_responder, address_responder)
378 },
379 )
380 .await;
381 }
382
383 #[test_case(&[WatchType::DnsServers, WatchType::DnsServers]; "dns_servers")]
387 #[test_case(&[WatchType::Address, WatchType::Address]; "address")]
388 #[test_case(&[WatchType::DnsServers, WatchType::Address]; "dns_servers_then_address")]
389 #[test_case(&[WatchType::Address, WatchType::DnsServers]; "address_then_dns_servers")]
390 #[fuchsia::test]
391 async fn watch_stream(watch_types: &[WatchType]) {
392 let (client_proxy, mut request_stream) =
393 fidl::endpoints::create_proxy_and_stream::<fidl_fuchsia_net_dhcpv6::ClientMarker>();
394 let mut watch_stream = into_watch_stream(client_proxy);
395 let client_fut = watch_stream.by_ref().take(watch_types.len()).try_collect::<Vec<_>>();
396 let (r, ()) = futures::future::join(
397 client_fut,
398 run_fake_server(request_stream.by_ref(), watch_types),
399 )
400 .await;
401 let items = r.expect("watch stream error");
402 assert_eq!(items.len(), watch_types.len());
403 for (item, watch_type) in items.into_iter().zip(watch_types) {
404 match watch_type {
405 WatchType::Address => {
406 assert_matches!(
407 item,
408 WatchItem::Address {
409 addr,
410 parameters: fidl_fuchsia_net_interfaces_admin::AddressParameters {
411 initial_properties: None,
412 temporary: None,
413 ..
414 },
415 address_state_provider_server_end: _,
416 } if addr == SUBNET
417 );
418 }
419 WatchType::DnsServers => {
420 assert_matches!(
421 item,
422 WatchItem::DnsServers(dns_servers) if dns_servers.is_empty()
423 );
424 }
425 }
426 }
427
428 drop(request_stream);
429 assert_matches!(
430 watch_stream.try_collect::<Vec<_>>().await.expect("watch stream error").as_slice(),
431 &[]
432 );
433 }
434}