1use std::collections::hash_map::DefaultHasher;
7use std::collections::{HashMap, HashSet};
8use std::hash::{Hash, Hasher};
9use std::net::{IpAddr, SocketAddr};
10use std::ops::Add;
11use std::pin::Pin;
12use std::str::FromStr as _;
13use std::time::Duration;
14
15use fidl::endpoints::ServerEnd;
16use fidl_fuchsia_net as fnet;
17use fidl_fuchsia_net_dhcpv6::{
18 ClientMarker, ClientRequest, ClientRequestStream, ClientWatchAddressResponder,
19 ClientWatchPrefixesResponder, ClientWatchServersResponder, Duid, Empty, Lifetimes,
20 LinkLayerAddress, LinkLayerAddressPlusTime, Prefix, PrefixDelegationConfig,
21 RELAY_AGENT_AND_SERVER_LINK_LOCAL_MULTICAST_ADDRESS, RELAY_AGENT_AND_SERVER_PORT,
22};
23use fidl_fuchsia_net_dhcpv6_ext::{
24 AddressConfig, ClientConfig, InformationConfig, NewClientParams,
25};
26use fidl_fuchsia_net_ext as fnet_ext;
27use fidl_fuchsia_net_name as fnet_name;
28use fuchsia_async as fasync;
29use futures::{Future, FutureExt as _, StreamExt as _, TryStreamExt as _, select, stream};
30
31use anyhow::{Context as _, Result};
32use assert_matches::assert_matches;
33use byteorder::{NetworkEndian, WriteBytesExt as _};
34use dns_server_watcher::DEFAULT_DNS_PORT;
35use log::{debug, warn};
36use net_types::MulticastAddress as _;
37use net_types::ip::{Ip as _, Ipv6, Ipv6Addr, Subnet, SubnetError};
38use packet::ParsablePacket;
39use packet_formats_dhcp::v6;
40use rand::rngs::StdRng;
41
42#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)]
44pub(crate) struct MonotonicInstant(zx::MonotonicInstant);
45
46impl MonotonicInstant {
47 fn now() -> MonotonicInstant {
48 MonotonicInstant(zx::MonotonicInstant::get())
49 }
50}
51
52impl dhcpv6_core::Instant for MonotonicInstant {
53 fn duration_since(&self, MonotonicInstant(earlier): MonotonicInstant) -> Duration {
54 let Self(this) = *self;
55
56 let diff: zx::MonotonicDuration = this - earlier;
57
58 Duration::from_nanos(diff.into_nanos().try_into().unwrap_or_else(|e| {
59 panic!(
60 "failed to calculate duration since {:?} with instant {:?}: {}",
61 earlier, this, e,
62 )
63 }))
64 }
65
66 fn checked_add(&self, duration: Duration) -> Option<MonotonicInstant> {
67 Some(self.add(duration))
68 }
69}
70
71impl Add<Duration> for MonotonicInstant {
72 type Output = MonotonicInstant;
73
74 fn add(self, duration: Duration) -> MonotonicInstant {
75 let MonotonicInstant(this) = self;
76 MonotonicInstant(this + duration.into())
77 }
78}
79
80#[derive(Debug, thiserror::Error)]
81pub enum ClientError {
82 #[error("fidl error")]
83 Fidl(#[source] fidl::Error),
84 #[error("got watch request while the previous one is pending")]
85 DoubleWatch,
86 #[error("unsupported DHCPv6 configuration")]
87 UnsupportedConfigs,
88 #[error("socket create error")]
89 SocketCreate(std::io::Error),
90 #[error("socket receive error")]
91 SocketRecv(std::io::Error),
92 #[error("unimplemented DHCPv6 functionality: {:?}()", _0)]
93 Unimplemented(String),
94}
95
96const MAX_UDP_DATAGRAM_SIZE: usize = 65_535;
100
101#[pin_project::pin_project]
102struct Timers {
103 #[pin]
104 retransmission: fasync::Timer,
105 #[pin]
106 refresh: fasync::Timer,
107 #[pin]
108 renew: fasync::Timer,
109 #[pin]
110 rebind: fasync::Timer,
111 #[pin]
112 restart_server_discovery: fasync::Timer,
113
114 #[cfg(test)]
115 scheduled: HashSet<dhcpv6_core::client::ClientTimerType>,
116}
117
118impl Default for Timers {
119 fn default() -> Self {
120 let unscheduled = || fasync::Timer::new(fasync::MonotonicInstant::INFINITE);
121 Self {
122 retransmission: unscheduled(),
123 refresh: unscheduled(),
124 renew: unscheduled(),
125 rebind: unscheduled(),
126 restart_server_discovery: unscheduled(),
127 #[cfg(test)]
128 scheduled: Default::default(),
129 }
130 }
131}
132
133pub(crate) struct Client<S: for<'a> AsyncSocket<'a>> {
135 interface_id: fnet::InterfaceId,
137 last_observed_dns_hash: u64,
142 dns_responder: Option<ClientWatchServersResponder>,
144 address_responder: Option<ClientWatchAddressResponder>,
146 prefixes: HashMap<fnet::Ipv6AddressWithPrefix, Lifetimes>,
148 prefixes_changed: bool,
150 prefixes_responder: Option<ClientWatchPrefixesResponder>,
152 state_machine: dhcpv6_core::client::ClientStateMachine<MonotonicInstant, StdRng>,
154 socket: S,
156 server_addr: SocketAddr,
158 timers: Pin<Box<Timers>>,
160 request_stream: ClientRequestStream,
162}
163
164pub(crate) trait AsyncSocket<'a> {
166 type RecvFromFut: Future<Output = Result<(usize, SocketAddr), std::io::Error>> + 'a;
167 type SendToFut: Future<Output = Result<usize, std::io::Error>> + 'a;
168
169 fn recv_from(&'a self, buf: &'a mut [u8]) -> Self::RecvFromFut;
170 fn send_to(&'a self, buf: &'a [u8], addr: SocketAddr) -> Self::SendToFut;
171}
172
173impl<'a> AsyncSocket<'a> for fasync::net::UdpSocket {
174 type RecvFromFut = fasync::net::UdpRecvFrom<'a>;
175 type SendToFut = fasync::net::SendTo<'a>;
176
177 fn recv_from(&'a self, buf: &'a mut [u8]) -> Self::RecvFromFut {
178 self.recv_from(buf)
179 }
180 fn send_to(&'a self, buf: &'a [u8], addr: SocketAddr) -> Self::SendToFut {
181 self.send_to(buf, addr)
182 }
183}
184
185fn to_dhcpv6_option_codes(
187 InformationConfig { dns_servers }: InformationConfig,
188) -> Vec<v6::OptionCode> {
189 dns_servers.then_some(v6::OptionCode::DnsServers).into_iter().collect()
190}
191
192fn to_configured_addresses(
193 AddressConfig { address_count, preferred_addresses }: AddressConfig,
194) -> Result<HashMap<v6::IAID, HashSet<Ipv6Addr>>, ClientError> {
195 let preferred_addresses = preferred_addresses.unwrap_or(Vec::new());
196 if preferred_addresses.len() > address_count.into() {
197 return Err(ClientError::UnsupportedConfigs);
198 }
199
200 Ok((0..)
203 .map(v6::IAID::new)
204 .zip(
205 preferred_addresses
206 .into_iter()
207 .map(|fnet::Ipv6Address { addr, .. }| HashSet::from([Ipv6Addr::from(addr)]))
208 .chain(std::iter::repeat_with(HashSet::new)),
209 )
210 .take(address_count.into())
211 .collect())
212}
213
214const IA_PD_IAID: v6::IAID = v6::IAID::new(0);
218
219fn create_state_machine(
221 duid: Option<dhcpv6_core::ClientDuid>,
222 ClientConfig {
223 information_config,
224 non_temporary_address_config,
225 prefix_delegation_config,
226 }: ClientConfig,
227) -> Result<
228 (
229 dhcpv6_core::client::ClientStateMachine<MonotonicInstant, StdRng>,
230 dhcpv6_core::client::Actions<MonotonicInstant>,
231 ),
232 ClientError,
233> {
234 let information_option_codes = to_dhcpv6_option_codes(information_config);
235 let configured_non_temporary_addresses = to_configured_addresses(non_temporary_address_config)?;
236 let configured_delegated_prefixes = prefix_delegation_config
237 .map(|prefix_delegation_config| {
238 let prefix = match prefix_delegation_config {
239 PrefixDelegationConfig::Empty(Empty {}) => Ok(None),
240 PrefixDelegationConfig::PrefixLength(prefix_len) => {
241 if prefix_len == 0 {
242 return Err(ClientError::UnsupportedConfigs);
244 }
245
246 Subnet::new(Ipv6::UNSPECIFIED_ADDRESS, prefix_len).map(Some)
247 }
248 PrefixDelegationConfig::Prefix(fnet::Ipv6AddressWithPrefix {
249 addr: fnet::Ipv6Address { addr, .. },
250 prefix_len,
251 }) => {
252 let addr = Ipv6Addr::from_bytes(addr);
253 if addr == Ipv6::UNSPECIFIED_ADDRESS {
254 return Err(ClientError::UnsupportedConfigs);
256 }
257
258 Subnet::new(addr, prefix_len).map(Some)
259 }
260 };
261
262 match prefix {
263 Ok(o) => Ok(HashMap::from([(IA_PD_IAID, HashSet::from_iter(o.into_iter()))])),
264 Err(SubnetError::PrefixTooLong | SubnetError::HostBitsSet) => {
265 Err(ClientError::UnsupportedConfigs)
266 }
267 }
268 })
269 .transpose()?;
270
271 let now = MonotonicInstant::now();
272 match (
273 information_option_codes.is_empty(),
274 configured_non_temporary_addresses.is_empty(),
275 configured_delegated_prefixes,
276 ) {
277 (true, true, None) => Err(ClientError::UnsupportedConfigs),
278 (false, true, None) => {
279 if duid.is_some() {
280 Err(ClientError::UnsupportedConfigs)
281 } else {
282 Ok(dhcpv6_core::client::ClientStateMachine::start_stateless(
283 information_option_codes,
284 rand::make_rng(),
285 now,
286 ))
287 }
288 }
289 (
290 _request_information,
291 _configure_non_temporary_addresses,
292 configured_delegated_prefixes,
293 ) => Ok(dhcpv6_core::client::ClientStateMachine::start_stateful(
294 if let Some(duid) = duid {
295 duid
296 } else {
297 return Err(ClientError::UnsupportedConfigs);
298 },
299 configured_non_temporary_addresses,
300 configured_delegated_prefixes.unwrap_or_else(Default::default),
301 information_option_codes,
302 rand::make_rng(),
303 now,
304 )),
305 }
306}
307
308fn hash<H: Hash>(h: &H) -> u64 {
310 let mut dh = DefaultHasher::new();
311 h.hash(&mut dh);
312 dh.finish()
313}
314
315fn subnet_to_address_with_prefix(prefix: Subnet<Ipv6Addr>) -> fnet::Ipv6AddressWithPrefix {
316 fnet::Ipv6AddressWithPrefix {
317 addr: fnet::Ipv6Address { addr: prefix.network().ipv6_bytes() },
318 prefix_len: prefix.prefix(),
319 }
320}
321
322impl<S: for<'a> AsyncSocket<'a>> Client<S> {
323 pub(crate) async fn start(
325 duid: Option<dhcpv6_core::ClientDuid>,
326 config: ClientConfig,
327 interface_id: fnet::InterfaceId,
328 socket_fn: impl FnOnce() -> std::io::Result<S>,
329 server_addr: SocketAddr,
330 request_stream: ClientRequestStream,
331 ) -> Result<Self, ClientError> {
332 let (state_machine, actions) = create_state_machine(duid, config)?;
333 let mut client = Self {
334 state_machine,
335 interface_id,
336 socket: socket_fn().map_err(ClientError::SocketCreate)?,
337 server_addr,
338 request_stream,
339 last_observed_dns_hash: hash(&Vec::<Ipv6Addr>::new()),
342 dns_responder: None,
343 address_responder: None,
344 prefixes: Default::default(),
345 prefixes_changed: false,
346 prefixes_responder: None,
347 timers: Box::pin(Default::default()),
348 };
349 client.run_actions(actions).await?;
350 Ok(client)
351 }
352
353 async fn run_actions(
355 &mut self,
356 actions: dhcpv6_core::client::Actions<MonotonicInstant>,
357 ) -> Result<(), ClientError> {
358 stream::iter(actions)
359 .map(Ok)
360 .try_fold(self, |client, action| async move {
361 match action {
362 dhcpv6_core::client::Action::SendMessage(buf) => {
363 match client.socket.send_to(&buf, client.server_addr).await {
364 Ok(size) => assert_eq!(size, buf.len()),
365 Err(e) => warn!(
366 "failed to send message to {}: {}; will retransmit later",
367 client.server_addr, e
368 ),
369 };
370 }
371 dhcpv6_core::client::Action::ScheduleTimer(timer_type, timeout) => {
372 client.schedule_timer(timer_type, timeout)
373 }
374 dhcpv6_core::client::Action::CancelTimer(timer_type) => {
375 client.cancel_timer(timer_type)
376 }
377 dhcpv6_core::client::Action::UpdateDnsServers(servers) => {
378 client.maybe_send_dns_server_updates(servers)?;
379 }
380 dhcpv6_core::client::Action::IaNaUpdates(_) => {
381 }
388 dhcpv6_core::client::Action::IaPdUpdates(mut updates) => {
389 let updates = {
390 let ret =
391 updates.remove(&IA_PD_IAID).expect("Update missing for IAID");
392 debug_assert_eq!(updates, HashMap::new());
393 ret
394 };
395
396 let Self { prefixes, prefixes_changed, .. } = client;
397
398 let now = zx::MonotonicInstant::get();
399 let nonzero_timevalue_to_zx_time = |tv| match tv {
400 v6::NonZeroTimeValue::Finite(tv) => {
401 now + zx::MonotonicDuration::from_seconds(tv.get().into())
402 }
403 v6::NonZeroTimeValue::Infinity => zx::MonotonicInstant::INFINITE,
404 };
405
406 let calculate_lifetimes = |dhcpv6_core::client::Lifetimes {
407 preferred_lifetime,
408 valid_lifetime,
409 }| {
410 Lifetimes {
411 preferred_until: match preferred_lifetime {
412 v6::TimeValue::Zero => zx::MonotonicInstant::ZERO,
413 v6::TimeValue::NonZero(preferred_lifetime) => {
414 nonzero_timevalue_to_zx_time(preferred_lifetime)
415 },
416 }.into_nanos(),
417 valid_until: nonzero_timevalue_to_zx_time(valid_lifetime)
418 .into_nanos(),
419 }
420 };
421
422 for (prefix, update) in updates.into_iter() {
423 let fidl_prefix = subnet_to_address_with_prefix(prefix);
424
425 match update {
426 dhcpv6_core::client::IaValueUpdateKind::Added(lifetimes) => {
427 assert_matches!(
428 prefixes.insert(
429 fidl_prefix,
430 calculate_lifetimes(lifetimes)
431 ),
432 None,
433 "must not know about prefix {} to add it with lifetimes {:?}",
434 prefix, lifetimes,
435 );
436 }
437 dhcpv6_core::client::IaValueUpdateKind::UpdatedLifetimes(updated_lifetimes) => {
438 assert_matches!(
439 prefixes.get_mut(&fidl_prefix),
440 Some(lifetimes) => {
441 *lifetimes = calculate_lifetimes(updated_lifetimes);
442 },
443 "must know about prefix {} to update lifetimes with {:?}",
444 prefix, updated_lifetimes,
445 );
446 }
447 dhcpv6_core::client::IaValueUpdateKind::Removed => {
448 assert_matches!(
449 prefixes.remove(&fidl_prefix),
450 Some(_),
451 "must know about prefix {} to remove it",
452 prefix
453 );
454 }
455 }
456 }
457
458 *prefixes_changed = true;
461 client.maybe_send_prefixes()?;
462 }
463 };
464 Ok(client)
465 })
466 .await
467 .map(|_: &mut Client<S>| ())
468 }
469
470 fn maybe_send_dns_server_updates(&mut self, servers: Vec<Ipv6Addr>) -> Result<(), ClientError> {
473 let servers_hash = hash(&servers);
474 if servers_hash == self.last_observed_dns_hash {
475 Ok(())
476 } else {
477 Ok(match self.dns_responder.take() {
478 Some(responder) => {
479 self.send_dns_server_updates(responder, servers, servers_hash)?
480 }
481 None => (),
482 })
483 }
484 }
485
486 fn maybe_send_prefixes(&mut self) -> Result<(), ClientError> {
487 let Self { prefixes, prefixes_changed, prefixes_responder, .. } = self;
488
489 if !*prefixes_changed {
490 return Ok(());
491 }
492
493 let responder = if let Some(responder) = prefixes_responder.take() {
494 responder
495 } else {
496 return Ok(());
497 };
498
499 let prefixes = prefixes
500 .iter()
501 .map(|(prefix, lifetimes)| Prefix { prefix: *prefix, lifetimes: *lifetimes })
502 .collect::<Vec<_>>();
503
504 responder.send(&prefixes).map_err(ClientError::Fidl)?;
505 *prefixes_changed = false;
506 Ok(())
507 }
508
509 fn send_dns_server_updates(
512 &mut self,
513 responder: ClientWatchServersResponder,
514 servers: Vec<Ipv6Addr>,
515 hash: u64,
516 ) -> Result<(), ClientError> {
517 let response: Vec<_> = servers
518 .iter()
519 .map(|addr| {
520 let address = fnet::Ipv6Address { addr: addr.ipv6_bytes() };
521 let zone_index = if addr.is_unicast_link_local() { self.interface_id } else { 0 };
522
523 fnet_name::DnsServer_ {
524 address: Some(fnet::SocketAddress::Ipv6(fnet::Ipv6SocketAddress {
525 address,
526 zone_index,
527 port: DEFAULT_DNS_PORT,
528 })),
529 source: Some(fnet_name::DnsServerSource::Dhcpv6(
530 fnet_name::Dhcpv6DnsServerSource {
531 source_interface: Some(self.interface_id),
532 ..Default::default()
533 },
534 )),
535 ..Default::default()
536 }
537 })
538 .collect();
539 responder
540 .send(&response)
541 .map_err(ClientError::Fidl)?;
543 self.last_observed_dns_hash = hash;
544 Ok(())
545 }
546
547 fn schedule_timer(
552 &mut self,
553 timer_type: dhcpv6_core::client::ClientTimerType,
554 MonotonicInstant(instant): MonotonicInstant,
555 ) {
556 let timers = self.timers.as_mut().project();
557 let timer = match timer_type {
558 dhcpv6_core::client::ClientTimerType::Retransmission => timers.retransmission,
559 dhcpv6_core::client::ClientTimerType::Refresh => timers.refresh,
560 dhcpv6_core::client::ClientTimerType::Renew => timers.renew,
561 dhcpv6_core::client::ClientTimerType::Rebind => timers.rebind,
562 dhcpv6_core::client::ClientTimerType::RestartServerDiscovery => {
563 timers.restart_server_discovery
564 }
565 };
566 #[cfg(test)]
567 let _: bool = if instant == zx::MonotonicInstant::INFINITE {
568 timers.scheduled.remove(&timer_type)
569 } else {
570 timers.scheduled.insert(timer_type)
571 };
572 timer.reset(fasync::MonotonicInstant::from_zx(instant));
573 }
574
575 fn cancel_timer(&mut self, timer_type: dhcpv6_core::client::ClientTimerType) {
580 self.schedule_timer(timer_type, MonotonicInstant(zx::MonotonicInstant::INFINITE))
581 }
582
583 async fn handle_timeout(
585 &mut self,
586 timer_type: dhcpv6_core::client::ClientTimerType,
587 ) -> Result<(), ClientError> {
588 self.cancel_timer(timer_type);
590
591 let actions = self.state_machine.handle_timeout(timer_type, MonotonicInstant::now());
592 self.run_actions(actions).await
593 }
594
595 async fn handle_message_recv(&mut self, mut msg: &[u8]) -> Result<(), ClientError> {
597 let msg = match v6::Message::parse(&mut msg, ()) {
598 Ok(msg) => msg,
599 Err(e) => {
600 warn!("failed to parse received message: {}", e);
604 return Ok(());
605 }
606 };
607 let actions = self.state_machine.handle_message_receive(msg, MonotonicInstant::now());
608 self.run_actions(actions).await
609 }
610
611 fn handle_client_request(&mut self, request: ClientRequest) -> Result<(), ClientError> {
613 debug!("handling client request: {:?}", request);
614 match request {
615 ClientRequest::WatchServers { responder } => match self.dns_responder {
616 Some(_) => {
617 self.dns_responder = None;
619 Err(ClientError::DoubleWatch)
621 }
622 None => {
623 let dns_servers = self.state_machine.get_dns_servers();
624 let servers_hash = hash(&dns_servers);
625 if servers_hash != self.last_observed_dns_hash {
626 let () =
628 self.send_dns_server_updates(responder, dns_servers, servers_hash)?;
629 } else {
630 self.dns_responder = Some(responder);
632 }
633 Ok(())
634 }
635 },
636 ClientRequest::WatchAddress { responder } => match self.address_responder.take() {
637 Some(ClientWatchAddressResponder { .. }) => Err(ClientError::DoubleWatch),
639 None => {
640 warn!("WatchAddress call will block forever as it is unimplemented");
642 self.address_responder = Some(responder);
643 Ok(())
644 }
645 },
646 ClientRequest::WatchPrefixes { responder } => match self.prefixes_responder.take() {
647 Some(ClientWatchPrefixesResponder { .. }) => Err(ClientError::DoubleWatch),
649 None => {
650 self.prefixes_responder = Some(responder);
651 self.maybe_send_prefixes()
652 }
653 },
654 ClientRequest::Shutdown { responder: _ } => {
656 Err(ClientError::Unimplemented("Shutdown".to_string()))
657 }
658 }
659 }
660
661 async fn handle_next_event(&mut self, buf: &mut [u8]) -> Result<Option<()>, ClientError> {
667 let timers = self.timers.as_mut().project();
668 let timer_type = select! {
669 () = timers.retransmission => {
670 dhcpv6_core::client::ClientTimerType::Retransmission
671 },
672 () = timers.refresh => {
673 dhcpv6_core::client::ClientTimerType::Refresh
674 },
675 () = timers.renew => {
676 dhcpv6_core::client::ClientTimerType::Renew
677 },
678 () = timers.rebind => {
679 dhcpv6_core::client::ClientTimerType::Rebind
680 },
681 () = timers.restart_server_discovery => {
682 dhcpv6_core::client::ClientTimerType::RestartServerDiscovery
683 },
684 recv_from_res = self.socket.recv_from(buf).fuse() => {
685 let (size, _addr) = recv_from_res.map_err(ClientError::SocketRecv)?;
686 self.handle_message_recv(&buf[..size]).await?;
687 return Ok(Some(()));
688 },
689 request = self.request_stream.try_next() => {
690 let request = request.map_err(ClientError::Fidl)?;
691 return request.map(|request| self.handle_client_request(request)).transpose();
692 }
693 };
694 self.handle_timeout(timer_type).await?;
695 Ok(Some(()))
696 }
697
698 #[cfg(test)]
699 fn assert_scheduled(
700 &self,
701 timers: impl IntoIterator<Item = dhcpv6_core::client::ClientTimerType>,
702 ) {
703 assert_eq!(self.timers.as_ref().scheduled, timers.into_iter().collect())
704 }
705}
706
707fn create_socket(
710 addr: SocketAddr,
711 interface_id: fnet::InterfaceId,
712) -> std::io::Result<fasync::net::UdpSocket> {
713 let socket = socket2::Socket::new(
714 socket2::Domain::IPV6,
715 socket2::Type::DGRAM,
716 Some(socket2::Protocol::UDP),
717 )?;
718 socket.set_reuse_port(true)?;
720
721 let interface_id = u32::try_from(interface_id).map_err(|_| {
723 std::io::Error::new(std::io::ErrorKind::InvalidInput, "interface ID does not fit in u32")
724 })?;
725 let name = fuchsia_nix::net::if_::if_indextoname(interface_id)
726 .map_err(Into::<std::io::Error>::into)?;
727 socket.bind_device(Some(name.as_bytes()))?;
728
729 socket.bind(&addr.into())?;
730 fasync::net::UdpSocket::from_socket(socket.into())
731}
732
733fn duid_from_fidl(duid: Duid) -> Result<dhcpv6_core::ClientDuid, ()> {
734 const DUID_TYPE_LLT: [u8; 2] = [0, 1];
738 const DUID_TYPE_LL: [u8; 2] = [0, 3];
742 const DUID_TYPE_UUID: [u8; 2] = [0, 4];
746 const HARDWARE_TYPE_ETHERNET: [u8; 2] = [0, 1];
752 match duid {
753 Duid::LinkLayerAddressPlusTime(LinkLayerAddressPlusTime {
758 time,
759 link_layer_address: LinkLayerAddress::Ethernet(mac),
760 }) => {
761 let mut duid = dhcpv6_core::ClientDuid::new();
762 duid.try_extend_from_slice(&DUID_TYPE_LLT).unwrap();
763 duid.try_extend_from_slice(&HARDWARE_TYPE_ETHERNET).unwrap();
764 duid.write_u32::<NetworkEndian>(time).unwrap();
765 duid.try_extend_from_slice(&mac.octets).unwrap();
766 Ok(duid)
767 }
768 Duid::LinkLayerAddress(LinkLayerAddress::Ethernet(mac)) => Ok(DUID_TYPE_LL
772 .into_iter()
773 .chain(HARDWARE_TYPE_ETHERNET.into_iter())
774 .chain(mac.octets.into_iter())
775 .collect()),
776 Duid::Uuid(uuid) => Ok(DUID_TYPE_UUID.into_iter().chain(uuid.into_iter()).collect()),
779 _ => Err(()),
780 }
781}
782
783pub(crate) async fn serve_client(
787 NewClientParams { interface_id, address, duid, config }: NewClientParams,
788 request: ServerEnd<ClientMarker>,
789) -> Result<()> {
790 let std_addr = Ipv6Addr::from(address.address.addr);
791 if std_addr.is_multicast()
792 || (std_addr.is_unicast_link_local() && address.zone_index != interface_id)
793 {
794 return request
795 .close_with_epitaph(zx::Status::INVALID_ARGS)
796 .context("closing request channel with epitaph");
797 }
798
799 let fnet_ext::SocketAddress(addr) = fnet::SocketAddress::Ipv6(address).into();
800 let servers_addr = IpAddr::from_str(RELAY_AGENT_AND_SERVER_LINK_LOCAL_MULTICAST_ADDRESS)
801 .with_context(|| {
802 format!(
803 "{} should be a valid IPv6 address",
804 RELAY_AGENT_AND_SERVER_LINK_LOCAL_MULTICAST_ADDRESS,
805 )
806 })?;
807 let duid = match duid.map(|fidl| duid_from_fidl(fidl)).transpose() {
808 Ok(duid) => duid,
809 Err(()) => {
810 return request
811 .close_with_epitaph(zx::Status::INVALID_ARGS)
812 .context("closing request channel with epitaph");
813 }
814 };
815 let (request_stream, control_handle) = request.into_stream_and_control_handle();
816 let mut client = match Client::<fasync::net::UdpSocket>::start(
817 duid,
818 config,
819 interface_id,
820 || create_socket(addr, interface_id),
821 SocketAddr::new(servers_addr, RELAY_AGENT_AND_SERVER_PORT),
822 request_stream,
823 )
824 .await
825 {
826 Ok(client) => client,
827 Err(ClientError::UnsupportedConfigs) => {
828 control_handle.shutdown_with_epitaph(zx::Status::INVALID_ARGS);
829 return Ok(());
830 }
831 Err(e) => {
832 return Err(e.into());
833 }
834 };
835 let mut buf = vec![0u8; MAX_UDP_DATAGRAM_SIZE];
836 loop {
837 match client.handle_next_event(&mut buf).await? {
838 Some(()) => (),
839 None => break Ok(()),
840 }
841 }
842}
843
844#[cfg(test)]
845mod tests {
846 use std::pin::pin;
847 use std::task::Poll;
848
849 use fidl::endpoints::{
850 ClientEnd, create_proxy, create_proxy_and_stream, create_request_stream,
851 };
852 use fidl_fuchsia_net_dhcpv6::{self as fnet_dhcpv6, ClientProxy, DEFAULT_CLIENT_PORT};
853 use fuchsia_async as fasync;
854 use futures::{TryFutureExt as _, join, poll};
855
856 use assert_matches::assert_matches;
857 use net_declare::{
858 fidl_ip_v6, fidl_ip_v6_with_prefix, fidl_mac, fidl_socket_addr, fidl_socket_addr_v6,
859 net_ip_v6, net_subnet_v6, std_socket_addr,
860 };
861 use net_types::ip::IpAddress as _;
862 use packet::serialize::InnerPacketBuilder;
863 use test_case::test_case;
864
865 use super::*;
866
867 fn create_test_socket() -> (fasync::net::UdpSocket, SocketAddr) {
869 let addr: SocketAddr = std_socket_addr!("[::1]:0");
870 let socket = std::net::UdpSocket::bind(addr).expect("failed to create test socket");
871 let addr = socket.local_addr().expect("failed to get address of test socket");
872 (fasync::net::UdpSocket::from_socket(socket).expect("failed to create test socket"), addr)
873 }
874
875 struct ReceivedMessage {
876 transaction_id: [u8; 3],
877 client_id: Option<Vec<u8>>,
893 }
894
895 async fn assert_received_message(
898 socket: &fasync::net::UdpSocket,
899 want_from_addr: SocketAddr,
900 msg_type: v6::MessageType,
901 ) -> ReceivedMessage {
902 let mut buf = vec![0u8; MAX_UDP_DATAGRAM_SIZE];
903 let (size, from_addr) =
904 socket.recv_from(&mut buf).await.expect("failed to receive on test server socket");
905 assert_eq!(from_addr, want_from_addr);
906 let buf = &mut &buf[..size]; let msg = v6::Message::parse(buf, ()).expect("failed to parse message");
908 assert_eq!(msg.msg_type(), msg_type);
909
910 let mut client_id = None;
911 for opt in msg.options() {
912 match opt {
913 v6::ParsedDhcpOption::ClientId(id) => {
914 assert_eq!(core::mem::replace(&mut client_id, Some(id.to_vec())), None)
915 }
916 _ => {}
917 }
918 }
919
920 ReceivedMessage { transaction_id: *msg.transaction_id(), client_id: client_id }
921 }
922
923 const TEST_MAC: fnet::MacAddress = fidl_mac!("00:01:02:03:04:05");
924
925 #[test_case(
926 Duid::LinkLayerAddress(LinkLayerAddress::Ethernet(TEST_MAC)),
927 &[0, 3, 0, 1, 0, 1, 2, 3, 4, 5];
928 "ll"
929 )]
930 #[test_case(
931 Duid::LinkLayerAddressPlusTime(LinkLayerAddressPlusTime {
932 time: 0,
933 link_layer_address: LinkLayerAddress::Ethernet(TEST_MAC),
934 }),
935 &[0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5];
936 "llt"
937 )]
938 #[test_case(
939 Duid::Uuid([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]),
940 &[0, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
941 "uuid"
942 )]
943 #[fuchsia::test]
944 fn test_duid_from_fidl(duid: Duid, want: &[u8]) {
945 assert_eq!(duid_from_fidl(duid), Ok(dhcpv6_core::ClientDuid::try_from(want).unwrap()));
946 }
947
948 #[fuchsia::test]
949 fn test_create_client_with_unsupported_config() {
950 let prefix_delegation_configs = [
951 None,
952 Some(PrefixDelegationConfig::PrefixLength(0)),
954 Some(PrefixDelegationConfig::PrefixLength(Ipv6Addr::BYTES * 8 + 1)),
956 Some(PrefixDelegationConfig::Prefix(fidl_ip_v6_with_prefix!("::/64"))),
958 Some(PrefixDelegationConfig::Prefix(fidl_ip_v6_with_prefix!("a::1/64"))),
960 ];
961
962 for prefix_delegation_config in prefix_delegation_configs.iter() {
963 assert_matches!(
964 create_state_machine(
965 prefix_delegation_config.is_some().then(|| CLIENT_ID.into()),
966 ClientConfig {
967 information_config: Default::default(),
968 non_temporary_address_config: Default::default(),
969 prefix_delegation_config: prefix_delegation_config.clone(),
970 }
971 ),
972 Err(ClientError::UnsupportedConfigs),
973 "prefix_delegation_config={:?}",
974 prefix_delegation_config
975 );
976 }
977 }
978
979 const STATELESS_CLIENT_CONFIG: ClientConfig = ClientConfig {
980 information_config: InformationConfig { dns_servers: true },
981 non_temporary_address_config: AddressConfig { address_count: 0, preferred_addresses: None },
982 prefix_delegation_config: None,
983 };
984
985 #[fuchsia::test]
986 async fn test_client_stops_on_channel_close() {
987 let (client_proxy, server_end) = create_proxy::<ClientMarker>();
988
989 let ((), client_res) = join!(
990 async { drop(client_proxy) },
991 serve_client(
992 NewClientParams {
993 interface_id: 1,
994 address: fidl_socket_addr_v6!("[::1]:546"),
995 config: STATELESS_CLIENT_CONFIG,
996 duid: None,
997 },
998 server_end,
999 ),
1000 );
1001 client_res.expect("client future should return with Ok");
1002 }
1003
1004 fn client_proxy_watch_servers(
1005 client_proxy: &fnet_dhcpv6::ClientProxy,
1006 ) -> impl Future<Output = Result<(), fidl::Error>> {
1007 client_proxy.watch_servers().map_ok(|_: Vec<fidl_fuchsia_net_name::DnsServer_>| ())
1008 }
1009
1010 fn client_proxy_watch_address(
1011 client_proxy: &fnet_dhcpv6::ClientProxy,
1012 ) -> impl Future<Output = Result<(), fidl::Error>> {
1013 client_proxy.watch_address().map_ok(
1014 |_: (
1015 fnet::Subnet,
1016 fidl_fuchsia_net_interfaces_admin::AddressParameters,
1017 fidl::endpoints::ServerEnd<
1018 fidl_fuchsia_net_interfaces_admin::AddressStateProviderMarker,
1019 >,
1020 )| (),
1021 )
1022 }
1023
1024 fn client_proxy_watch_prefixes(
1025 client_proxy: &fnet_dhcpv6::ClientProxy,
1026 ) -> impl Future<Output = Result<(), fidl::Error>> {
1027 client_proxy.watch_prefixes().map_ok(|_: Vec<fnet_dhcpv6::Prefix>| ())
1028 }
1029
1030 #[test_case(client_proxy_watch_servers; "watch_servers")]
1031 #[test_case(client_proxy_watch_address; "watch_address")]
1032 #[test_case(client_proxy_watch_prefixes; "watch_prefixes")]
1033 #[fuchsia::test]
1034 async fn test_client_should_return_error_on_double_watch<F>(watch: F)
1035 where
1036 F: AsyncFn(&fnet_dhcpv6::ClientProxy) -> Result<(), fidl::Error>,
1037 {
1038 let (client_proxy, server_end) = create_proxy::<ClientMarker>();
1039
1040 let (caller1_res, caller2_res, client_res) = join!(
1041 watch(&client_proxy),
1042 watch(&client_proxy),
1043 serve_client(
1044 NewClientParams {
1045 interface_id: 1,
1046 address: fidl_socket_addr_v6!("[::1]:546"),
1047 config: STATELESS_CLIENT_CONFIG,
1048 duid: None,
1049 },
1050 server_end,
1051 )
1052 );
1053
1054 assert_matches!(
1055 caller1_res,
1056 Err(fidl::Error::ClientChannelClosed { epitaph: fidl::Epitaph::PeerClosed, .. })
1057 );
1058 assert_matches!(
1059 caller2_res,
1060 Err(fidl::Error::ClientChannelClosed { epitaph: fidl::Epitaph::PeerClosed, .. })
1061 );
1062 assert!(
1063 client_res
1064 .expect_err("client should fail with double watch error")
1065 .to_string()
1066 .contains("got watch request while the previous one is pending")
1067 );
1068 }
1069
1070 const VALID_INFORMATION_CONFIGS: [InformationConfig; 2] =
1071 [InformationConfig { dns_servers: false }, InformationConfig { dns_servers: true }];
1072
1073 const VALID_DELEGATED_PREFIX_CONFIGS: [Option<PrefixDelegationConfig>; 4] = [
1074 Some(PrefixDelegationConfig::Empty(Empty {})),
1075 Some(PrefixDelegationConfig::PrefixLength(1)),
1076 Some(PrefixDelegationConfig::PrefixLength(127)),
1077 Some(PrefixDelegationConfig::Prefix(fidl_ip_v6_with_prefix!("a::/64"))),
1078 ];
1079
1080 fn get_valid_non_temporary_address_configs() -> [AddressConfig; 5] {
1082 [
1083 Default::default(),
1084 AddressConfig { address_count: 1, preferred_addresses: None },
1085 AddressConfig { address_count: 1, preferred_addresses: Some(Vec::new()) },
1086 AddressConfig {
1087 address_count: 1,
1088 preferred_addresses: Some(vec![fidl_ip_v6!("a::1")]),
1089 },
1090 AddressConfig {
1091 address_count: 2,
1092 preferred_addresses: Some(vec![fidl_ip_v6!("a::2")]),
1093 },
1094 ]
1095 }
1096
1097 #[fuchsia::test]
1098 fn test_client_starts_with_valid_args() {
1099 for information_config in VALID_INFORMATION_CONFIGS {
1100 for non_temporary_address_config in get_valid_non_temporary_address_configs() {
1101 for prefix_delegation_config in VALID_DELEGATED_PREFIX_CONFIGS {
1102 let mut exec = fasync::TestExecutor::new();
1103
1104 let (client_proxy, server_end) = create_proxy::<ClientMarker>();
1105
1106 let test_fut = async {
1107 join!(
1108 client_proxy.watch_servers(),
1109 serve_client(
1110 NewClientParams {
1111 interface_id: 1,
1112 address: fidl_socket_addr_v6!("[::1]:546"),
1113 config: ClientConfig {
1114 information_config: information_config.clone(),
1115 non_temporary_address_config: non_temporary_address_config
1116 .clone(),
1117 prefix_delegation_config: prefix_delegation_config.clone(),
1118 },
1119 duid: (non_temporary_address_config.address_count != 0
1120 || prefix_delegation_config.is_some())
1121 .then(|| fnet_dhcpv6::Duid::LinkLayerAddress(
1122 fnet_dhcpv6::LinkLayerAddress::Ethernet(fidl_mac!(
1123 "00:11:22:33:44:55"
1124 ))
1125 )),
1126 },
1127 server_end
1128 )
1129 )
1130 };
1131 let mut test_fut = pin!(test_fut);
1132 assert_matches!(
1133 exec.run_until_stalled(&mut test_fut),
1134 Poll::Pending,
1135 "information_config={:?}, non_temporary_address_config={:?}, prefix_delegation_config={:?}",
1136 information_config,
1137 non_temporary_address_config,
1138 prefix_delegation_config
1139 );
1140 }
1141 }
1142 }
1143 }
1144
1145 const CLIENT_ID: [u8; 18] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17];
1146
1147 #[fuchsia::test]
1148 async fn test_client_starts_in_correct_mode() {
1149 for information_config @ InformationConfig { dns_servers } in VALID_INFORMATION_CONFIGS {
1150 for non_temporary_address_config @ AddressConfig {
1151 address_count,
1152 preferred_addresses: _,
1153 } in get_valid_non_temporary_address_configs()
1154 {
1155 for prefix_delegation_config in VALID_DELEGATED_PREFIX_CONFIGS {
1156 let (stateful, want_msg_type) =
1157 if address_count == 0 && prefix_delegation_config.is_none() {
1158 if !dns_servers {
1159 continue;
1160 } else {
1161 (false, v6::MessageType::InformationRequest)
1162 }
1163 } else {
1164 (true, v6::MessageType::Solicit)
1165 };
1166
1167 let (_, client_stream): (ClientEnd<ClientMarker>, _) =
1168 create_request_stream::<ClientMarker>();
1169
1170 let (client_socket, client_addr) = create_test_socket();
1171 let (server_socket, server_addr) = create_test_socket();
1172 println!(
1173 "{:?} {:?} {:?}",
1174 information_config, non_temporary_address_config, prefix_delegation_config
1175 );
1176 let _: Client<fasync::net::UdpSocket> = Client::start(
1177 stateful.then(|| CLIENT_ID.into()),
1178 ClientConfig {
1179 information_config: information_config.clone(),
1180 non_temporary_address_config: non_temporary_address_config.clone(),
1181 prefix_delegation_config: prefix_delegation_config.clone(),
1182 },
1183 1, || Ok(client_socket),
1185 server_addr,
1186 client_stream,
1187 )
1188 .await
1189 .unwrap_or_else(|e| panic!(
1190 "failed to create test client: {}; information_config={:?}, non_temporary_address_config={:?}, prefix_delegation_config={:?}",
1191 e, information_config, non_temporary_address_config, prefix_delegation_config
1192 ));
1193
1194 let _: ReceivedMessage =
1195 assert_received_message(&server_socket, client_addr, want_msg_type).await;
1196 }
1197 }
1198 }
1199 }
1200
1201 #[fuchsia::test]
1204 async fn test_client_fails_to_start_with_invalid_args() {
1205 for params in vec![
1206 NewClientParams {
1208 interface_id: 2,
1209 address: fnet::Ipv6SocketAddress {
1210 address: fidl_ip_v6!("fe80::1"),
1211 port: DEFAULT_CLIENT_PORT,
1212 zone_index: 1,
1213 },
1214 config: STATELESS_CLIENT_CONFIG,
1215 duid: None,
1216 },
1217 NewClientParams {
1219 interface_id: 1,
1220 address: fnet::Ipv6SocketAddress {
1221 address: fidl_ip_v6!("ff01::1"),
1222 port: DEFAULT_CLIENT_PORT,
1223 zone_index: 1,
1224 },
1225 config: STATELESS_CLIENT_CONFIG,
1226 duid: None,
1227 },
1228 NewClientParams {
1230 interface_id: 1,
1231 address: fidl_socket_addr_v6!("[2001:db8::1]:12345"),
1232 config: STATELESS_CLIENT_CONFIG,
1233 duid: Some(fnet_dhcpv6::Duid::LinkLayerAddress(
1234 fnet_dhcpv6::LinkLayerAddress::Ethernet(fidl_mac!("00:11:22:33:44:55")),
1235 )),
1236 },
1237 NewClientParams {
1239 interface_id: 1,
1240 address: fidl_socket_addr_v6!("[2001:db8::1]:12345"),
1241 config: ClientConfig {
1242 information_config: InformationConfig { dns_servers: true },
1243 non_temporary_address_config: AddressConfig {
1244 address_count: 1,
1245 preferred_addresses: None,
1246 },
1247 prefix_delegation_config: None,
1248 },
1249 duid: None,
1250 },
1251 ] {
1252 let (client_proxy, server_end) = create_proxy::<ClientMarker>();
1253 let () =
1254 serve_client(params, server_end).await.expect("start server failed unexpectedly");
1255 assert_matches!(
1258 client_proxy.watch_servers().await,
1259 Err(fidl::Error::ClientChannelClosed { epitaph, .. })
1260 if epitaph == zx::Status::INVALID_ARGS
1261 );
1262 }
1263 }
1264
1265 fn create_test_dns_server(
1266 address: fnet::Ipv6Address,
1267 source_interface: u64,
1268 zone_index: u64,
1269 ) -> fnet_name::DnsServer_ {
1270 fnet_name::DnsServer_ {
1271 address: Some(fnet::SocketAddress::Ipv6(fnet::Ipv6SocketAddress {
1272 address,
1273 zone_index,
1274 port: DEFAULT_DNS_PORT,
1275 })),
1276 source: Some(fnet_name::DnsServerSource::Dhcpv6(fnet_name::Dhcpv6DnsServerSource {
1277 source_interface: Some(source_interface),
1278 ..Default::default()
1279 })),
1280 ..Default::default()
1281 }
1282 }
1283
1284 async fn send_msg_with_options(
1285 socket: &fasync::net::UdpSocket,
1286 to_addr: SocketAddr,
1287 transaction_id: [u8; 3],
1288 msg_type: v6::MessageType,
1289 options: &[v6::DhcpOption<'_>],
1290 ) -> Result<()> {
1291 let builder = v6::MessageBuilder::new(msg_type, transaction_id, options);
1292 let mut buf = vec![0u8; builder.bytes_len()];
1293 builder.serialize(&mut buf);
1294 let size = socket.send_to(&buf, to_addr).await?;
1295 assert_eq!(size, buf.len());
1296 Ok(())
1297 }
1298
1299 #[fuchsia::test]
1300 fn test_client_should_respond_to_dns_watch_requests() {
1301 let mut exec = fasync::TestExecutor::new();
1302
1303 let (client_proxy, client_stream) = create_proxy_and_stream::<ClientMarker>();
1304
1305 let (client_socket, client_addr) = create_test_socket();
1306 let (server_socket, server_addr) = create_test_socket();
1307 let mut client = exec
1308 .run_singlethreaded(Client::<fasync::net::UdpSocket>::start(
1309 None,
1310 STATELESS_CLIENT_CONFIG,
1311 1, || Ok(client_socket),
1313 server_addr,
1314 client_stream,
1315 ))
1316 .expect("failed to create test client");
1317
1318 let ReceivedMessage { transaction_id: initial_transaction_id, client_id: _ } = exec
1319 .run_singlethreaded(assert_received_message(
1320 &server_socket,
1321 client_addr,
1322 v6::MessageType::InformationRequest,
1323 ));
1324
1325 type WatchServersResponseFut = <fnet_dhcpv6::ClientProxy as fnet_dhcpv6::ClientProxyInterface>::WatchServersResponseFut;
1326 type WatchServersResponse = <WatchServersResponseFut as Future>::Output;
1327
1328 struct Test<'a> {
1329 client: &'a mut Client<fasync::net::UdpSocket>,
1330 buf: Vec<u8>,
1331 watcher_fut: WatchServersResponseFut,
1332 }
1333
1334 impl<'a> Test<'a> {
1335 fn new(
1336 client: &'a mut Client<fasync::net::UdpSocket>,
1337 client_proxy: &ClientProxy,
1338 ) -> Self {
1339 Self {
1340 client,
1341 buf: vec![0u8; MAX_UDP_DATAGRAM_SIZE],
1342 watcher_fut: client_proxy.watch_servers(),
1343 }
1344 }
1345
1346 async fn handle_next_event(&mut self) {
1347 self.client
1348 .handle_next_event(&mut self.buf)
1349 .await
1350 .expect("test client failed to handle next event")
1351 .expect("request stream closed");
1352 }
1353
1354 async fn refresh_client(
1355 &mut self,
1356 server_socket: &fasync::net::UdpSocket,
1357 client_addr: SocketAddr,
1358 ) -> [u8; 3] {
1359 if self
1363 .client
1364 .timers
1365 .as_ref()
1366 .scheduled
1367 .contains(&dhcpv6_core::client::ClientTimerType::Refresh)
1368 {
1369 self.client
1370 .handle_timeout(dhcpv6_core::client::ClientTimerType::Refresh)
1371 .await
1372 .expect("test client failed to handle timeout");
1373 let ReceivedMessage { transaction_id, client_id: _ } = assert_received_message(
1374 server_socket,
1375 client_addr,
1376 v6::MessageType::InformationRequest,
1377 )
1378 .await;
1379 transaction_id
1380 } else {
1381 panic!("no refresh timer is scheduled and refresh is requested in test");
1382 }
1383 }
1384
1385 fn run(&mut self) -> impl Future<Output = WatchServersResponse> + use<'_, 'a> {
1389 let Self { client, buf, watcher_fut } = self;
1390 async move {
1391 let client_fut = async {
1392 loop {
1393 client
1394 .handle_next_event(buf)
1395 .await
1396 .expect("test client failed to handle next event")
1397 .expect("request stream closed");
1398 }
1399 }
1400 .fuse();
1401 let mut client_fut = pin!(client_fut);
1402 let mut watcher_fut = watcher_fut.fuse();
1403 select! {
1404 () = client_fut => panic!("test client returned unexpectedly"),
1405 r = watcher_fut => r,
1406 }
1407 }
1408 }
1409 }
1410
1411 {
1412 let mut test = Test::new(&mut client, &client_proxy);
1414
1415 exec.run_singlethreaded(test.handle_next_event());
1417 assert!(
1418 test.client.dns_responder.is_some(),
1419 "WatchServers responder should be present"
1420 );
1421
1422 exec.run_singlethreaded(send_msg_with_options(
1424 &server_socket,
1425 client_addr,
1426 initial_transaction_id,
1427 v6::MessageType::Reply,
1428 &[v6::DhcpOption::ServerId(&[1, 2, 3]), v6::DhcpOption::DnsServers(&[])],
1429 ))
1430 .expect("failed to send test reply");
1431 exec.run_singlethreaded(test.handle_next_event());
1437 assert_matches!(exec.run_until_stalled(&mut pin!(test.run())), Poll::Pending);
1438
1439 let transaction_id =
1441 exec.run_singlethreaded(test.refresh_client(&server_socket, client_addr));
1442 let dns_servers = [net_ip_v6!("fe80::1:2")];
1443 exec.run_singlethreaded(send_msg_with_options(
1444 &server_socket,
1445 client_addr,
1446 transaction_id,
1447 v6::MessageType::Reply,
1448 &[v6::DhcpOption::ServerId(&[1, 2, 3]), v6::DhcpOption::DnsServers(&dns_servers)],
1449 ))
1450 .expect("failed to send test reply");
1451 let want_servers = vec![create_test_dns_server(
1452 fidl_ip_v6!("fe80::1:2"),
1453 1, 1, )];
1456 let servers = exec.run_singlethreaded(test.run()).expect("get servers");
1457 assert_eq!(servers, want_servers);
1458 } {
1461 let mut test = Test::new(&mut client, &client_proxy);
1463
1464 exec.run_singlethreaded(test.handle_next_event());
1466 assert!(
1467 test.client.dns_responder.is_some(),
1468 "WatchServers responder should be present"
1469 );
1470
1471 let transaction_id =
1473 exec.run_singlethreaded(test.refresh_client(&server_socket, client_addr));
1474 let dns_servers = [net_ip_v6!("fe80::1:2")];
1475 exec.run_singlethreaded(send_msg_with_options(
1476 &server_socket,
1477 client_addr,
1478 transaction_id,
1479 v6::MessageType::Reply,
1480 &[v6::DhcpOption::ServerId(&[1, 2, 3]), v6::DhcpOption::DnsServers(&dns_servers)],
1481 ))
1482 .expect("failed to send test reply");
1483 exec.run_singlethreaded(test.handle_next_event());
1489 assert_matches!(exec.run_until_stalled(&mut pin!(test.run())), Poll::Pending);
1490
1491 let transaction_id =
1493 exec.run_singlethreaded(test.refresh_client(&server_socket, client_addr));
1494 let dns_servers = [net_ip_v6!("fe80::1:2"), net_ip_v6!("1234::5:6")];
1495 exec.run_singlethreaded(send_msg_with_options(
1496 &server_socket,
1497 client_addr,
1498 transaction_id,
1499 v6::MessageType::Reply,
1500 &[v6::DhcpOption::ServerId(&[1, 2, 3]), v6::DhcpOption::DnsServers(&dns_servers)],
1501 ))
1502 .expect("failed to send test reply");
1503 let want_servers = vec![
1504 create_test_dns_server(
1505 fidl_ip_v6!("fe80::1:2"),
1506 1, 1, ),
1509 create_test_dns_server(
1511 fidl_ip_v6!("1234::5:6"),
1512 1, 0, ),
1515 ];
1516 let servers = exec.run_singlethreaded(test.run()).expect("get servers");
1517 assert_eq!(servers, want_servers);
1518 } {
1521 let mut test = Test::new(&mut client, &client_proxy);
1525
1526 let transaction_id =
1527 exec.run_singlethreaded(test.refresh_client(&server_socket, client_addr));
1528 exec.run_singlethreaded(send_msg_with_options(
1529 &server_socket,
1530 client_addr,
1531 transaction_id,
1532 v6::MessageType::Reply,
1533 &[v6::DhcpOption::ServerId(&[1, 2, 3]), v6::DhcpOption::DnsServers(&[])],
1534 ))
1535 .expect("failed to send test reply");
1536 let want_servers = Vec::<fnet_name::DnsServer_>::new();
1537 assert_eq!(exec.run_singlethreaded(test.run()).expect("get servers"), want_servers);
1538 } }
1540
1541 #[fuchsia::test]
1542 async fn test_client_should_respond_with_dns_servers_on_first_watch_if_non_empty() {
1543 let (client_proxy, client_stream) = create_proxy_and_stream::<ClientMarker>();
1544
1545 let (client_socket, client_addr) = create_test_socket();
1546 let (server_socket, server_addr) = create_test_socket();
1547 let client = Client::<fasync::net::UdpSocket>::start(
1548 None,
1549 STATELESS_CLIENT_CONFIG,
1550 1, || Ok(client_socket),
1552 server_addr,
1553 client_stream,
1554 )
1555 .await
1556 .expect("failed to create test client");
1557
1558 let ReceivedMessage { transaction_id: initial_txid, client_id: _ } =
1559 assert_received_message(
1560 &server_socket,
1561 client_addr,
1562 v6::MessageType::InformationRequest,
1563 )
1564 .await;
1565
1566 let dns_servers = [net_ip_v6!("fe80::1:2"), net_ip_v6!("1234::5:6")];
1567 send_msg_with_options(
1568 &server_socket,
1569 client_addr,
1570 initial_txid,
1571 v6::MessageType::Reply,
1572 &[v6::DhcpOption::ServerId(&[4, 5, 6]), v6::DhcpOption::DnsServers(&dns_servers)],
1573 )
1574 .await
1575 .expect("failed to send test message");
1576
1577 let buf = vec![0u8; MAX_UDP_DATAGRAM_SIZE];
1578 let handle_client_events_fut =
1579 futures::stream::try_unfold((client, buf), |(mut client, mut buf)| async {
1580 client
1581 .handle_next_event(&mut buf)
1582 .await
1583 .map(|res| res.map(|()| ((), (client, buf))))
1584 })
1585 .try_fold((), |(), ()| futures::future::ready(Ok(())))
1586 .fuse();
1587 let mut handle_client_events_fut = pin!(handle_client_events_fut);
1588
1589 let want_servers = vec![
1590 create_test_dns_server(
1591 fidl_ip_v6!("fe80::1:2"),
1592 1, 1, ),
1595 create_test_dns_server(
1596 fidl_ip_v6!("1234::5:6"),
1597 1, 0, ),
1600 ];
1601 let found_servers = select!(
1602 status = handle_client_events_fut => panic!("client unexpectedly exited: {status:?}"),
1603 found_servers = client_proxy.watch_servers() => found_servers.expect(
1604 "watch servers should succeed"),
1605 );
1606 assert_eq!(found_servers, want_servers);
1607 }
1608
1609 #[fuchsia::test]
1610 async fn watch_prefixes() {
1611 const SERVER_ID: [u8; 3] = [3, 4, 5];
1612 const PREFERRED_LIFETIME_SECS: u32 = 1000;
1613 const VALID_LIFETIME_SECS: u32 = 2000;
1614 const T1: u32 = 1;
1617 const T2: u32 = 2000;
1618
1619 let (client_proxy, client_stream) = create_proxy_and_stream::<ClientMarker>();
1620
1621 let (client_socket, client_addr) = create_test_socket();
1622 let (server_socket, server_addr) = create_test_socket();
1623 let mut client = Client::<fasync::net::UdpSocket>::start(
1624 Some(CLIENT_ID.into()),
1625 ClientConfig {
1626 information_config: Default::default(),
1627 non_temporary_address_config: Default::default(),
1628 prefix_delegation_config: Some(PrefixDelegationConfig::Empty(Empty {})),
1629 },
1630 1, || Ok(client_socket),
1632 server_addr,
1633 client_stream,
1634 )
1635 .await
1636 .expect("failed to create test client");
1637
1638 let client_fut = async {
1639 let mut buf = vec![0u8; MAX_UDP_DATAGRAM_SIZE];
1640 loop {
1641 select! {
1642 res = client.handle_next_event(&mut buf).fuse() => {
1643 match res.expect("test client failed to handle next event") {
1644 Some(()) => (),
1645 None => break (),
1646 };
1647 }
1648 }
1649 }
1650 }
1651 .fuse();
1652 let mut client_fut = pin!(client_fut);
1653
1654 let update_prefix = net_subnet_v6!("a::/64");
1655 let remove_prefix = net_subnet_v6!("b::/64");
1656 let add_prefix = net_subnet_v6!("c::/64");
1657
1658 let client_id = {
1660 let ReceivedMessage { client_id, transaction_id } =
1661 assert_received_message(&server_socket, client_addr, v6::MessageType::Solicit)
1662 .await;
1663 let client_id = client_id.unwrap();
1665
1666 let ia_prefix = [
1667 v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
1668 PREFERRED_LIFETIME_SECS,
1669 VALID_LIFETIME_SECS,
1670 update_prefix,
1671 &[],
1672 )),
1673 v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
1674 PREFERRED_LIFETIME_SECS,
1675 VALID_LIFETIME_SECS,
1676 remove_prefix,
1677 &[],
1678 )),
1679 ];
1680 send_msg_with_options(
1681 &server_socket,
1682 client_addr,
1683 transaction_id,
1684 v6::MessageType::Advertise,
1685 &[
1686 v6::DhcpOption::ServerId(&SERVER_ID),
1687 v6::DhcpOption::ClientId(&client_id),
1688 v6::DhcpOption::Preference(u8::MAX),
1689 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(IA_PD_IAID, T1, T2, &ia_prefix)),
1690 ],
1691 )
1692 .await
1693 .expect("failed to send adv message");
1694
1695 let transaction_id = select! {
1698 () = client_fut => panic!("should never return"),
1699 res = assert_received_message(
1700 &server_socket,
1701 client_addr,
1702 v6::MessageType::Request,
1703 ).fuse() => {
1704 let ReceivedMessage { client_id: req_client_id, transaction_id } = res;
1705 assert_eq!(Some(&client_id), req_client_id.as_ref());
1706 transaction_id
1707 },
1708 };
1709
1710 send_msg_with_options(
1711 &server_socket,
1712 client_addr,
1713 transaction_id,
1714 v6::MessageType::Reply,
1715 &[
1716 v6::DhcpOption::ServerId(&SERVER_ID),
1717 v6::DhcpOption::ClientId(&client_id),
1718 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(IA_PD_IAID, T1, T2, &ia_prefix)),
1719 ],
1720 )
1721 .await
1722 .expect("failed to send reply message");
1723
1724 client_id
1725 };
1726
1727 let check_watch_prefixes_result =
1728 |res: Result<Vec<Prefix>, _>,
1729 before_handling_reply,
1730 preferred_lifetime_secs: u32,
1731 valid_lifetime_secs: u32,
1732 expected_prefixes| {
1733 assert_matches!(
1734 res.unwrap()[..],
1735 [
1736 Prefix {
1737 prefix: got_prefix1,
1738 lifetimes: Lifetimes {
1739 preferred_until: preferred_until1,
1740 valid_until: valid_until1,
1741 },
1742 },
1743 Prefix {
1744 prefix: got_prefix2,
1745 lifetimes: Lifetimes {
1746 preferred_until: preferred_until2,
1747 valid_until: valid_until2,
1748 },
1749 },
1750 ] => {
1751 let now = zx::MonotonicInstant::get();
1752 let preferred_until = zx::MonotonicInstant::from_nanos(preferred_until1);
1753 let valid_until = zx::MonotonicInstant::from_nanos(valid_until1);
1754
1755 let preferred_for = zx::MonotonicDuration::from_seconds(
1756 preferred_lifetime_secs.into(),
1757 );
1758 let valid_for = zx::MonotonicDuration::from_seconds(valid_lifetime_secs.into());
1759
1760 assert_eq!(
1761 HashSet::from([got_prefix1, got_prefix2]),
1762 HashSet::from(expected_prefixes),
1763 );
1764 assert!(preferred_until >= before_handling_reply + preferred_for);
1765 assert!(preferred_until <= now + preferred_for);
1766 assert!(valid_until >= before_handling_reply + valid_for);
1767 assert!(valid_until <= now + valid_for);
1768
1769 assert_eq!(preferred_until1, preferred_until2);
1770 assert_eq!(valid_until1, valid_until2);
1771 }
1772 )
1773 };
1774
1775 {
1778 let mut watch_prefixes = client_proxy.watch_prefixes().fuse();
1781 assert_matches!(poll!(&mut watch_prefixes), Poll::Pending);
1782 let before_handling_reply = zx::MonotonicInstant::get();
1783 select! {
1784 () = client_fut => panic!("should never return"),
1785 res = watch_prefixes => check_watch_prefixes_result(
1786 res,
1787 before_handling_reply,
1788 PREFERRED_LIFETIME_SECS,
1789 VALID_LIFETIME_SECS,
1790 [
1791 subnet_to_address_with_prefix(update_prefix),
1792 subnet_to_address_with_prefix(remove_prefix),
1793 ],
1794 ),
1795 }
1796 }
1797
1798 {
1801 let transaction_id = select! {
1802 () = client_fut => panic!("should never return"),
1803 res = assert_received_message(
1804 &server_socket,
1805 client_addr,
1806 v6::MessageType::Renew,
1807 ).fuse() => {
1808 let ReceivedMessage { client_id: ren_client_id, transaction_id } = res;
1809 assert_eq!(ren_client_id.as_ref(), Some(&client_id));
1810 transaction_id
1811 },
1812 };
1813
1814 const NEW_PREFERRED_LIFETIME_SECS: u32 = 2 * PREFERRED_LIFETIME_SECS;
1815 const NEW_VALID_LIFETIME_SECS: u32 = 2 * VALID_LIFETIME_SECS;
1816 let ia_prefix = [
1817 v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
1818 NEW_PREFERRED_LIFETIME_SECS,
1819 NEW_VALID_LIFETIME_SECS,
1820 update_prefix,
1821 &[],
1822 )),
1823 v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
1824 NEW_PREFERRED_LIFETIME_SECS,
1825 NEW_VALID_LIFETIME_SECS,
1826 add_prefix,
1827 &[],
1828 )),
1829 v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(0, 0, remove_prefix, &[])),
1830 ];
1831
1832 send_msg_with_options(
1833 &server_socket,
1834 client_addr,
1835 transaction_id,
1836 v6::MessageType::Reply,
1837 &[
1838 v6::DhcpOption::ServerId(&SERVER_ID),
1839 v6::DhcpOption::ClientId(&client_id),
1840 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
1841 v6::IAID::new(0),
1842 T1,
1843 T2,
1844 &ia_prefix,
1845 )),
1846 ],
1847 )
1848 .await
1849 .expect("failed to send reply message");
1850
1851 let before_handling_reply = zx::MonotonicInstant::get();
1852 select! {
1853 () = client_fut => panic!("should never return"),
1854 res = client_proxy.watch_prefixes().fuse() => check_watch_prefixes_result(
1855 res,
1856 before_handling_reply,
1857 NEW_PREFERRED_LIFETIME_SECS,
1858 NEW_VALID_LIFETIME_SECS,
1859 [
1860 subnet_to_address_with_prefix(update_prefix),
1861 subnet_to_address_with_prefix(add_prefix),
1862 ],
1863 ),
1864 }
1865 }
1866 }
1867
1868 #[fuchsia::test]
1869 async fn test_client_schedule_and_cancel_timers() {
1870 let (_client_end, client_stream) = create_request_stream::<ClientMarker>();
1871
1872 let (client_socket, _client_addr) = create_test_socket();
1873 let (_server_socket, server_addr) = create_test_socket();
1874 let mut client = Client::<fasync::net::UdpSocket>::start(
1875 None,
1876 STATELESS_CLIENT_CONFIG,
1877 1, || Ok(client_socket),
1879 server_addr,
1880 client_stream,
1881 )
1882 .await
1883 .expect("failed to create test client");
1884
1885 client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
1887
1888 client.cancel_timer(dhcpv6_core::client::ClientTimerType::Retransmission);
1889 client.assert_scheduled([]);
1890
1891 let now = MonotonicInstant::now();
1892 client.schedule_timer(
1893 dhcpv6_core::client::ClientTimerType::Refresh,
1894 now + Duration::from_nanos(1),
1895 );
1896 client.schedule_timer(
1897 dhcpv6_core::client::ClientTimerType::Retransmission,
1898 now + Duration::from_nanos(2),
1899 );
1900 client.assert_scheduled([
1901 dhcpv6_core::client::ClientTimerType::Retransmission,
1902 dhcpv6_core::client::ClientTimerType::Refresh,
1903 ]);
1904
1905 let now = MonotonicInstant::now();
1907 client.schedule_timer(
1908 dhcpv6_core::client::ClientTimerType::Refresh,
1909 now + Duration::from_nanos(1),
1910 );
1911 client.schedule_timer(
1912 dhcpv6_core::client::ClientTimerType::Retransmission,
1913 now + Duration::from_nanos(2),
1914 );
1915
1916 client.cancel_timer(dhcpv6_core::client::ClientTimerType::Refresh);
1917 client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
1918
1919 client.cancel_timer(dhcpv6_core::client::ClientTimerType::Refresh);
1921
1922 client.cancel_timer(dhcpv6_core::client::ClientTimerType::Retransmission);
1923 client.assert_scheduled([]);
1924
1925 client.cancel_timer(dhcpv6_core::client::ClientTimerType::Retransmission);
1927 }
1928
1929 #[fuchsia::test]
1930 async fn test_handle_next_event_on_stateless_client() {
1931 let (client_proxy, client_stream) = create_proxy_and_stream::<ClientMarker>();
1932
1933 let (client_socket, client_addr) = create_test_socket();
1934 let (server_socket, server_addr) = create_test_socket();
1935 let mut client = Client::<fasync::net::UdpSocket>::start(
1936 None,
1937 STATELESS_CLIENT_CONFIG,
1938 1, || Ok(client_socket),
1940 server_addr,
1941 client_stream,
1942 )
1943 .await
1944 .expect("failed to create test client");
1945
1946 let ReceivedMessage { client_id, transaction_id: initial_txid } = assert_received_message(
1948 &server_socket,
1949 client_addr,
1950 v6::MessageType::InformationRequest,
1951 )
1952 .await;
1953 client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
1954
1955 let mut buf = vec![0u8; MAX_UDP_DATAGRAM_SIZE];
1956 assert_matches!(client.handle_next_event(&mut buf).await, Ok(Some(())));
1958 let ReceivedMessage { client_id: got_client_id, transaction_id: _ } =
1959 assert_received_message(
1960 &server_socket,
1961 client_addr,
1962 v6::MessageType::InformationRequest,
1963 )
1964 .await;
1965 assert_eq!(got_client_id, client_id);
1966 client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
1967
1968 send_msg_with_options(&server_socket, client_addr, [5, 6, 7], v6::MessageType::Reply, &[])
1970 .await
1971 .expect("failed to send test message");
1972 assert_matches!(client.handle_next_event(&mut buf).await, Ok(Some(())));
1973 client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
1974
1975 let size =
1977 server_socket.send_to(&[], client_addr).await.expect("failed to send test message");
1978 assert_eq!(size, 0);
1979 assert_matches!(client.handle_next_event(&mut buf).await, Ok(Some(())));
1980 client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
1981
1982 send_msg_with_options(
1984 &server_socket,
1985 client_addr,
1986 initial_txid,
1987 v6::MessageType::Reply,
1988 &[v6::DhcpOption::ServerId(&[4, 5, 6])],
1989 )
1990 .await
1991 .expect("failed to send test message");
1992 assert_matches!(client.handle_next_event(&mut buf).await, Ok(Some(())));
1993 client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Refresh]);
1994
1995 client.schedule_timer(
1997 dhcpv6_core::client::ClientTimerType::Refresh,
1998 MonotonicInstant::now() + Duration::from_nanos(1),
1999 );
2000
2001 assert_matches!(client.handle_next_event(&mut buf).await, Ok(Some(())));
2003 let ReceivedMessage { client_id, transaction_id: _ } = assert_received_message(
2004 &server_socket,
2005 client_addr,
2006 v6::MessageType::InformationRequest,
2007 )
2008 .await;
2009 assert_eq!(got_client_id, client_id,);
2010 client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
2011
2012 let test_fut = async {
2013 assert_matches!(client.handle_next_event(&mut buf).await, Ok(Some(())));
2014 client
2015 .dns_responder
2016 .take()
2017 .expect("test client did not get a channel responder")
2018 .send(&[fnet_name::DnsServer_ {
2019 address: Some(fidl_socket_addr!("[fe01::2:3]:42")),
2020 source: Some(fnet_name::DnsServerSource::Dhcpv6(
2021 fnet_name::Dhcpv6DnsServerSource {
2022 source_interface: Some(42),
2023 ..Default::default()
2024 },
2025 )),
2026 ..Default::default()
2027 }])
2028 .expect("failed to send response on test channel");
2029 };
2030 let (watcher_res, ()) = join!(client_proxy.watch_servers(), test_fut);
2031 let servers = watcher_res.expect("failed to watch servers");
2032 assert_eq!(
2033 servers,
2034 vec![fnet_name::DnsServer_ {
2035 address: Some(fidl_socket_addr!("[fe01::2:3]:42")),
2036 source: Some(fnet_name::DnsServerSource::Dhcpv6(
2037 fnet_name::Dhcpv6DnsServerSource {
2038 source_interface: Some(42),
2039 ..Default::default()
2040 },
2041 )),
2042 ..Default::default()
2043 }]
2044 );
2045
2046 drop(client_proxy);
2048 assert_matches!(client.handle_next_event(&mut buf).await, Ok(None));
2049 }
2050
2051 #[fuchsia::test]
2052 async fn test_handle_next_event_on_stateful_client() {
2053 let (client_proxy, client_stream) = create_proxy_and_stream::<ClientMarker>();
2054
2055 let (client_socket, client_addr) = create_test_socket();
2056 let (server_socket, server_addr) = create_test_socket();
2057 let mut client = Client::<fasync::net::UdpSocket>::start(
2058 Some(CLIENT_ID.into()),
2059 ClientConfig {
2060 information_config: Default::default(),
2061 non_temporary_address_config: AddressConfig {
2062 address_count: 1,
2063 preferred_addresses: None,
2064 },
2065 prefix_delegation_config: None,
2066 },
2067 1, || Ok(client_socket),
2069 server_addr,
2070 client_stream,
2071 )
2072 .await
2073 .expect("failed to create test client");
2074
2075 let _: ReceivedMessage =
2077 assert_received_message(&server_socket, client_addr, v6::MessageType::Solicit).await;
2078 client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
2079
2080 let mut buf = vec![0u8; MAX_UDP_DATAGRAM_SIZE];
2081 drop(client_proxy);
2083 assert_matches!(client.handle_next_event(&mut buf).await, Ok(None));
2084 }
2085
2086 #[fuchsia::test]
2087 #[should_panic = "received unexpected refresh timeout in state InformationRequesting"]
2088 async fn test_handle_next_event_respects_timer_order() {
2089 let (_client_end, client_stream) = create_request_stream::<ClientMarker>();
2090
2091 let (client_socket, client_addr) = create_test_socket();
2092 let (server_socket, server_addr) = create_test_socket();
2093 let mut client = Client::<fasync::net::UdpSocket>::start(
2094 None,
2095 STATELESS_CLIENT_CONFIG,
2096 1, || Ok(client_socket),
2098 server_addr,
2099 client_stream,
2100 )
2101 .await
2102 .expect("failed to create test client");
2103
2104 let mut buf = vec![0u8; MAX_UDP_DATAGRAM_SIZE];
2105 client.schedule_timer(
2108 dhcpv6_core::client::ClientTimerType::Retransmission,
2109 MonotonicInstant::now() + Duration::from_secs(1_000_000),
2110 );
2111 client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
2112
2113 send_msg_with_options(&server_socket, client_addr, [5, 6, 7], v6::MessageType::Reply, &[])
2116 .await
2117 .expect("failed to send test message");
2118 assert_matches!(client.handle_next_event(&mut buf).await, Ok(Some(())));
2121 client.assert_scheduled([dhcpv6_core::client::ClientTimerType::Retransmission]);
2123
2124 client.schedule_timer(
2126 dhcpv6_core::client::ClientTimerType::Refresh,
2127 MonotonicInstant::now() + Duration::from_nanos(1),
2128 );
2129 client.assert_scheduled([
2131 dhcpv6_core::client::ClientTimerType::Retransmission,
2132 dhcpv6_core::client::ClientTimerType::Refresh,
2133 ]);
2134
2135 let unreachable = client.handle_next_event(&mut buf).await;
2139 panic!("{unreachable:?}");
2140 }
2141
2142 #[fuchsia::test]
2143 async fn test_handle_next_event_fails_on_recv_err() {
2144 struct StubSocket {}
2145 impl<'a> AsyncSocket<'a> for StubSocket {
2146 type RecvFromFut = futures::future::Ready<Result<(usize, SocketAddr), std::io::Error>>;
2147 type SendToFut = futures::future::Ready<Result<usize, std::io::Error>>;
2148
2149 fn recv_from(&'a self, _buf: &'a mut [u8]) -> Self::RecvFromFut {
2150 futures::future::ready(Err(std::io::Error::other("test recv error")))
2151 }
2152 fn send_to(&'a self, buf: &'a [u8], _addr: SocketAddr) -> Self::SendToFut {
2153 futures::future::ready(Ok(buf.len()))
2154 }
2155 }
2156
2157 let (_client_end, client_stream) = create_request_stream::<ClientMarker>();
2158
2159 let mut client = Client::<StubSocket>::start(
2160 None,
2161 STATELESS_CLIENT_CONFIG,
2162 1, || Ok(StubSocket {}),
2164 std_socket_addr!("[::1]:0"),
2165 client_stream,
2166 )
2167 .await
2168 .expect("failed to create test client");
2169
2170 assert_matches!(
2171 client.handle_next_event(&mut [0u8]).await,
2172 Err(ClientError::SocketRecv(err)) if err.kind() == std::io::ErrorKind::Other
2173 );
2174 }
2175}