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