1use assert_matches::assert_matches;
8use derivative::Derivative;
9use log::{debug, info, warn};
10use net_types::ip::{Ipv6Addr, Subnet};
11use num::CheckedMul;
12use num::rational::Ratio;
13use packet::serialize::InnerPacketBuilder;
14use packet_formats_dhcp::v6;
15use rand::Rng;
16use std::cmp::{Eq, Ord, PartialEq, PartialOrd};
17use std::collections::hash_map::Entry;
18use std::collections::{BinaryHeap, HashMap, HashSet};
19use std::fmt::Debug;
20use std::hash::Hash;
21use std::marker::PhantomData;
22use std::time::Duration;
23use zerocopy::SplitByteSlice;
24
25use crate::{ClientDuid, Instant, InstantExt as _};
26
27const INITIAL_INFO_REQ_TIMEOUT: Duration = Duration::from_secs(1);
31const MAX_INFO_REQ_TIMEOUT: Duration = Duration::from_secs(3600);
35const IRT_DEFAULT: Duration = Duration::from_secs(86400);
39
40const MAX_DURATION: Duration = Duration::from_secs(u64::MAX);
45
46const INITIAL_SOLICIT_TIMEOUT: Duration = Duration::from_secs(1);
50
51const MAX_SOLICIT_TIMEOUT: Duration = Duration::from_secs(3600);
55
56const VALID_MAX_SOLICIT_TIMEOUT_RANGE: std::ops::RangeInclusive<u32> = 60..=86400;
60
61const ADVERTISE_MAX_PREFERENCE: u8 = std::u8::MAX;
67
68const ELAPSED_TIME_DENOMINATOR: u128 = 10;
73
74const RANDOMIZATION_FACTOR_MIN: f64 = -0.1;
79
80const RANDOMIZATION_FACTOR_MAX: f64 = 0.1;
85
86const INITIAL_REQUEST_TIMEOUT: Duration = Duration::from_secs(1);
90
91const MAX_REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
95
96const REQUEST_MAX_RC: u8 = 10;
100
101const T1_MIN_LIFETIME_RATIO: Ratio<u32> = Ratio::new_raw(1, 2);
112
113const T2_T1_RATIO: Ratio<u32> = Ratio::new_raw(8, 5);
124
125const INITIAL_RENEW_TIMEOUT: Duration = Duration::from_secs(10);
129
130const MAX_RENEW_TIMEOUT: Duration = Duration::from_secs(600);
134
135const INITIAL_REBIND_TIMEOUT: Duration = Duration::from_secs(10);
139
140const MAX_REBIND_TIMEOUT: Duration = Duration::from_secs(600);
144
145const IA_NA_NAME: &'static str = "IA_NA";
146const IA_PD_NAME: &'static str = "IA_PD";
147
148fn retransmission_timeout<R: Rng>(
178 prev_retrans_timeout: Duration,
179 initial_retrans_timeout: Duration,
180 max_retrans_timeout: Duration,
181 rng: &mut R,
182) -> Duration {
183 let rand = rng.random_range(RANDOMIZATION_FACTOR_MIN..RANDOMIZATION_FACTOR_MAX);
184
185 let next_rt = if prev_retrans_timeout.as_nanos() == 0 {
186 let irt = initial_retrans_timeout.as_secs_f64();
187 irt + rand * irt
188 } else {
189 let rt = prev_retrans_timeout.as_secs_f64();
190 2. * rt + rand * rt
191 };
192
193 if max_retrans_timeout.as_nanos() == 0 || next_rt < max_retrans_timeout.as_secs_f64() {
194 clipped_duration(next_rt)
195 } else {
196 let mrt = max_retrans_timeout.as_secs_f64();
197 clipped_duration(mrt + rand * mrt)
198 }
199}
200
201fn clipped_duration(secs: f64) -> Duration {
203 if secs <= 0. {
204 Duration::from_nanos(0)
205 } else if secs >= MAX_DURATION.as_secs_f64() {
206 MAX_DURATION
207 } else {
208 Duration::from_secs_f64(secs)
209 }
210}
211
212fn transaction_id<R: Rng>(rng: &mut R) -> [u8; 3] {
217 let mut id = [0u8; 3];
218 rng.fill(&mut id[..]);
219 id
220}
221
222#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
224pub enum ClientTimerType {
225 Retransmission,
226 Refresh,
227 Renew,
228 Rebind,
229 RestartServerDiscovery,
230}
231
232#[derive(Debug, PartialEq, Clone)]
234pub enum Action<I> {
235 SendMessage(Vec<u8>),
236 ScheduleTimer(ClientTimerType, I),
241 CancelTimer(ClientTimerType),
246 UpdateDnsServers(Vec<Ipv6Addr>),
247 IaNaUpdates(HashMap<v6::IAID, HashMap<Ipv6Addr, IaValueUpdateKind>>),
258 IaPdUpdates(HashMap<v6::IAID, HashMap<Subnet<Ipv6Addr>, IaValueUpdateKind>>),
269}
270
271pub type Actions<I> = Vec<Action<I>>;
272
273#[derive(Debug)]
276struct InformationRequesting<I> {
277 retrans_timeout: Duration,
278 _marker: PhantomData<I>,
279}
280
281impl<I: Instant> InformationRequesting<I> {
282 fn start<R: Rng>(options_to_request: &[v6::OptionCode], rng: &mut R, now: I) -> Transition<I> {
286 let transaction_id = transaction_id(rng);
287 let info_req = Self { retrans_timeout: Default::default(), _marker: Default::default() };
288 info_req.send_and_schedule_retransmission(transaction_id, options_to_request, rng, now)
289 }
290
291 fn retransmission_timeout<R: Rng>(&self, rng: &mut R) -> Duration {
296 let Self { retrans_timeout, _marker } = self;
297 retransmission_timeout(
298 *retrans_timeout,
299 INITIAL_INFO_REQ_TIMEOUT,
300 MAX_INFO_REQ_TIMEOUT,
301 rng,
302 )
303 }
304
305 fn send_and_schedule_retransmission<R: Rng>(
308 self,
309 transaction_id: [u8; 3],
310 options_to_request: &[v6::OptionCode],
311 rng: &mut R,
312 now: I,
313 ) -> Transition<I> {
314 let options_array = [v6::DhcpOption::Oro(options_to_request)];
315 let options = if options_to_request.is_empty() { &[][..] } else { &options_array[..] };
316
317 let builder =
318 v6::MessageBuilder::new(v6::MessageType::InformationRequest, transaction_id, options);
319 let mut buf = vec![0; builder.bytes_len()];
320 builder.serialize(&mut buf);
321
322 let retrans_timeout = self.retransmission_timeout(rng);
323
324 Transition {
325 state: ClientState::InformationRequesting(InformationRequesting {
326 retrans_timeout,
327 _marker: Default::default(),
328 }),
329 actions: vec![
330 Action::SendMessage(buf),
331 Action::ScheduleTimer(ClientTimerType::Retransmission, now.add(retrans_timeout)),
332 ],
333 transaction_id: Some(transaction_id),
334 }
335 }
336
337 fn retransmission_timer_expired<R: Rng>(
339 self,
340 transaction_id: [u8; 3],
341 options_to_request: &[v6::OptionCode],
342 rng: &mut R,
343 now: I,
344 ) -> Transition<I> {
345 self.send_and_schedule_retransmission(transaction_id, options_to_request, rng, now)
346 }
347
348 fn reply_message_received<B: SplitByteSlice>(
352 self,
353 msg: v6::Message<'_, B>,
354 now: I,
355 ) -> Transition<I> {
356 let ProcessedOptions { server_id, solicit_max_rt_opt: _, result } = match process_options(
360 &msg,
361 ExchangeType::ReplyToInformationRequest,
362 None,
363 &NoIaRequested,
364 &NoIaRequested,
365 ) {
366 Ok(processed_options) => processed_options,
367 Err(e) => {
368 warn!("ignoring Reply to Information-Request: {}", e);
369 return Transition {
370 state: ClientState::InformationRequesting(self),
371 actions: Vec::new(),
372 transaction_id: None,
373 };
374 }
375 };
376
377 let Options {
378 success_status_message,
379 next_contact_time,
380 preference: _,
381 non_temporary_addresses: _,
382 delegated_prefixes: _,
383 dns_servers,
384 } = match result {
385 Ok(options) => options,
386 Err(e) => {
387 warn!(
388 "Reply to Information-Request from server {:?} error status code: {}",
389 server_id, e
390 );
391 return Transition {
392 state: ClientState::InformationRequesting(self),
393 actions: Vec::new(),
394 transaction_id: None,
395 };
396 }
397 };
398
399 let information_refresh_time = assert_matches!(
405 next_contact_time,
406 NextContactTime::InformationRefreshTime(option) => option
407 )
408 .map(|t| Duration::from_secs(t.into()))
409 .unwrap_or(IRT_DEFAULT);
410
411 if let Some(success_status_message) = success_status_message {
412 if !success_status_message.is_empty() {
413 info!(
414 "Reply to Information-Request from server {:?} \
415 contains success status code message: {}",
416 server_id, success_status_message,
417 );
418 }
419 }
420
421 let actions = [
422 Action::CancelTimer(ClientTimerType::Retransmission),
423 Action::ScheduleTimer(ClientTimerType::Refresh, now.add(information_refresh_time)),
424 ]
425 .into_iter()
426 .chain(dns_servers.clone().map(|server_addrs| Action::UpdateDnsServers(server_addrs)))
427 .collect::<Vec<_>>();
428
429 Transition {
430 state: ClientState::InformationReceived(InformationReceived {
431 dns_servers: dns_servers.unwrap_or(Vec::new()),
432 _marker: Default::default(),
433 }),
434 actions,
435 transaction_id: None,
436 }
437 }
438}
439
440#[derive(Debug)]
442struct InformationReceived<I> {
443 dns_servers: Vec<Ipv6Addr>,
445 _marker: PhantomData<I>,
446}
447
448impl<I: Instant> InformationReceived<I> {
449 fn refresh_timer_expired<R: Rng>(
451 self,
452 options_to_request: &[v6::OptionCode],
453 rng: &mut R,
454 now: I,
455 ) -> Transition<I> {
456 InformationRequesting::start(options_to_request, rng, now)
457 }
458}
459
460enum IaKind {
461 Address,
462 Prefix,
463}
464
465trait IaValue: Copy + Clone + Debug + PartialEq + Eq + Hash {
466 const KIND: IaKind;
467}
468
469impl IaValue for Ipv6Addr {
470 const KIND: IaKind = IaKind::Address;
471}
472
473impl IaValue for Subnet<Ipv6Addr> {
474 const KIND: IaKind = IaKind::Prefix;
475}
476
477#[derive(Debug, Clone)]
479struct AdvertiseMessage<I> {
480 server_id: Vec<u8>,
481 non_temporary_addresses: HashMap<v6::IAID, HashSet<Ipv6Addr>>,
485 delegated_prefixes: HashMap<v6::IAID, HashSet<Subnet<Ipv6Addr>>>,
489 dns_servers: Vec<Ipv6Addr>,
490 preference: u8,
491 receive_time: I,
492 preferred_non_temporary_addresses_count: usize,
493 preferred_delegated_prefixes_count: usize,
494}
495
496impl<I> AdvertiseMessage<I> {
497 fn has_ias(&self) -> bool {
498 let Self {
499 server_id: _,
500 non_temporary_addresses,
501 delegated_prefixes,
502 dns_servers: _,
503 preference: _,
504 receive_time: _,
505 preferred_non_temporary_addresses_count: _,
506 preferred_delegated_prefixes_count: _,
507 } = self;
508 !(non_temporary_addresses.is_empty() && delegated_prefixes.is_empty())
515 }
516}
517
518impl<I: Instant> Ord for AdvertiseMessage<I> {
528 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
529 #[derive(PartialEq, Eq, PartialOrd, Ord)]
530 struct Candidate<I> {
531 has_ia_na: bool,
533 has_ia_pd: bool,
535 ia_na_count: usize,
537 ia_pd_count: usize,
539 preferred_ia_na_address_count: usize,
542 preferred_ia_pd_prefix_count: usize,
545 server_preference: u8,
547 dns_server_count: usize,
550 other_candidate_rcv_time: I,
552 }
553
554 impl<I: Instant> Candidate<I> {
555 fn from_advertisements(
556 candidate: &AdvertiseMessage<I>,
557 other_candidate: &AdvertiseMessage<I>,
558 ) -> Self {
559 let AdvertiseMessage {
560 server_id: _,
561 non_temporary_addresses,
562 delegated_prefixes,
563 dns_servers,
564 preference,
565 receive_time: _,
566 preferred_non_temporary_addresses_count,
567 preferred_delegated_prefixes_count,
568 } = candidate;
569 let AdvertiseMessage {
570 server_id: _,
571 non_temporary_addresses: _,
572 delegated_prefixes: _,
573 dns_servers: _,
574 preference: _,
575 receive_time: other_receive_time,
576 preferred_non_temporary_addresses_count: _,
577 preferred_delegated_prefixes_count: _,
578 } = other_candidate;
579
580 Self {
581 has_ia_na: !non_temporary_addresses.is_empty(),
582 has_ia_pd: !delegated_prefixes.is_empty(),
583 ia_na_count: non_temporary_addresses.len(),
584 ia_pd_count: delegated_prefixes.len(),
585 preferred_ia_na_address_count: *preferred_non_temporary_addresses_count,
586 preferred_ia_pd_prefix_count: *preferred_delegated_prefixes_count,
587 server_preference: *preference,
588 dns_server_count: dns_servers.len(),
589 other_candidate_rcv_time: *other_receive_time,
590 }
591 }
592 }
593
594 Candidate::from_advertisements(self, other)
595 .cmp(&Candidate::from_advertisements(other, self))
596 }
597}
598
599impl<I: Instant> PartialOrd for AdvertiseMessage<I> {
600 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
601 Some(self.cmp(other))
602 }
603}
604
605impl<I: Instant> PartialEq for AdvertiseMessage<I> {
606 fn eq(&self, other: &Self) -> bool {
607 self.cmp(other) == std::cmp::Ordering::Equal
608 }
609}
610
611impl<I: Instant> Eq for AdvertiseMessage<I> {}
612
613fn compute_preferred_ia_count<V: IaValue>(
616 got: &HashMap<v6::IAID, HashSet<V>>,
617 configured: &HashMap<v6::IAID, HashSet<V>>,
618) -> usize {
619 got.iter()
620 .map(|(iaid, got_values)| {
621 configured
622 .get(iaid)
623 .map_or(0, |configured_values| got_values.intersection(configured_values).count())
624 })
625 .sum()
626}
627
628fn elapsed_time_in_centisecs<I: Instant>(start_time: I, now: I) -> u16 {
630 u16::try_from(
631 now.duration_since(start_time)
632 .as_millis()
633 .checked_div(ELAPSED_TIME_DENOMINATOR)
634 .expect("division should succeed, denominator is non-zero"),
635 )
636 .unwrap_or(u16::MAX)
637}
638
639fn get_common_value(values: &Vec<u32>) -> Option<Duration> {
642 if !values.is_empty() && values.iter().all(|value| *value == values[0]) {
643 return Some(Duration::from_secs(values[0].into()));
644 }
645 None
646}
647
648#[derive(thiserror::Error, Copy, Clone, Debug)]
649#[cfg_attr(test, derive(PartialEq))]
650enum LifetimesError {
651 #[error("valid lifetime is zero")]
652 ValidLifetimeZero,
653 #[error("preferred lifetime greater than valid lifetime: {0:?}")]
654 PreferredLifetimeGreaterThanValidLifetime(Lifetimes),
655}
656
657#[derive(Copy, Clone, Debug, PartialEq)]
659pub struct Lifetimes {
660 pub preferred_lifetime: v6::TimeValue,
661 pub valid_lifetime: v6::NonZeroTimeValue,
662}
663
664#[derive(Debug)]
665struct IaValueOption<V> {
666 value: V,
667 lifetimes: Result<Lifetimes, LifetimesError>,
668}
669
670#[derive(thiserror::Error, Debug)]
671enum IaOptionError<V: IaValue> {
672 #[error("T1={t1:?} greater than T2={t2:?}")]
673 T1GreaterThanT2 { t1: v6::TimeValue, t2: v6::TimeValue },
674 #[error("status code error: {0}")]
675 StatusCode(#[from] StatusCodeError),
676 #[error("invalid option: {0:?}")]
679 InvalidOption(String),
680 #[error(
681 "IA value={value:?} appeared twice with first={first_lifetimes:?} and second={second_lifetimes:?}"
682 )]
683 DuplicateIaValue {
684 value: V,
685 first_lifetimes: Result<Lifetimes, LifetimesError>,
686 second_lifetimes: Result<Lifetimes, LifetimesError>,
687 },
688}
689
690#[derive(Debug)]
691#[cfg_attr(test, derive(PartialEq))]
692enum IaOption<V: IaValue> {
693 Success {
694 status_message: Option<String>,
695 t1: v6::TimeValue,
696 t2: v6::TimeValue,
697 ia_values: HashMap<V, Result<Lifetimes, LifetimesError>>,
698 },
699 Failure(ErrorStatusCode),
700}
701
702type IaNaOption = IaOption<Ipv6Addr>;
703
704#[derive(thiserror::Error, Debug)]
705enum StatusCodeError {
706 #[error("unknown status code {0}")]
707 InvalidStatusCode(u16),
708 #[error("duplicate Status Code option {0:?} and {1:?}")]
709 DuplicateStatusCode((v6::StatusCode, String), (v6::StatusCode, String)),
710}
711
712fn check_lifetimes(
713 valid_lifetime: v6::TimeValue,
714 preferred_lifetime: v6::TimeValue,
715) -> Result<Lifetimes, LifetimesError> {
716 match valid_lifetime {
717 v6::TimeValue::Zero => Err(LifetimesError::ValidLifetimeZero),
718 vl @ v6::TimeValue::NonZero(valid_lifetime) => {
719 if preferred_lifetime > vl {
732 Err(LifetimesError::PreferredLifetimeGreaterThanValidLifetime(Lifetimes {
733 preferred_lifetime,
734 valid_lifetime,
735 }))
736 } else {
737 Ok(Lifetimes { preferred_lifetime, valid_lifetime })
738 }
739 }
740 }
741}
742
743fn process_ia<
746 'a,
747 V: IaValue,
748 E: From<IaOptionError<V>> + Debug,
749 F: Fn(&v6::ParsedDhcpOption<'a>) -> Result<IaValueOption<V>, E>,
750>(
751 t1: v6::TimeValue,
752 t2: v6::TimeValue,
753 options: impl Iterator<Item = v6::ParsedDhcpOption<'a>>,
754 check: F,
755) -> Result<IaOption<V>, E> {
756 match (t1, t2) {
772 (v6::TimeValue::Zero, _) | (_, v6::TimeValue::Zero) => {}
773 (t1, t2) => {
774 if t1 > t2 {
775 return Err(IaOptionError::T1GreaterThanT2 { t1, t2 }.into());
776 }
777 }
778 }
779
780 let mut ia_values = HashMap::new();
781 let mut success_status_message = None;
782 for opt in options {
783 match opt {
784 v6::ParsedDhcpOption::StatusCode(code, msg) => {
785 let mut status_code = || {
786 let status_code = code.get().try_into().map_err(|e| match e {
787 v6::ParseError::InvalidStatusCode(code) => {
788 StatusCodeError::InvalidStatusCode(code)
789 }
790 e => unreachable!("unreachable status code parse error: {}", e),
791 })?;
792 if let Some(existing) = success_status_message.take() {
793 return Err(StatusCodeError::DuplicateStatusCode(
794 (v6::StatusCode::Success, existing),
795 (status_code, msg.to_string()),
796 ));
797 }
798
799 Ok(status_code)
800 };
801 let status_code = status_code().map_err(IaOptionError::StatusCode)?;
802 match status_code.into_result() {
803 Ok(()) => {
804 success_status_message = Some(msg.to_string());
805 }
806 Err(error_status_code) => {
807 return Ok(IaOption::Failure(ErrorStatusCode(
808 error_status_code,
809 msg.to_string(),
810 )));
811 }
812 }
813 }
814 opt @ (v6::ParsedDhcpOption::IaAddr(_) | v6::ParsedDhcpOption::IaPrefix(_)) => {
815 let IaValueOption { value, lifetimes } = check(&opt)?;
816 if let Some(first_lifetimes) = ia_values.insert(value, lifetimes) {
817 return Err(IaOptionError::DuplicateIaValue {
818 value,
819 first_lifetimes,
820 second_lifetimes: lifetimes,
821 }
822 .into());
823 }
824 }
825 v6::ParsedDhcpOption::ClientId(_)
826 | v6::ParsedDhcpOption::ServerId(_)
827 | v6::ParsedDhcpOption::SolMaxRt(_)
828 | v6::ParsedDhcpOption::Preference(_)
829 | v6::ParsedDhcpOption::Iana(_)
830 | v6::ParsedDhcpOption::InformationRefreshTime(_)
831 | v6::ParsedDhcpOption::IaPd(_)
832 | v6::ParsedDhcpOption::Oro(_)
833 | v6::ParsedDhcpOption::ElapsedTime(_)
834 | v6::ParsedDhcpOption::DnsServers(_)
835 | v6::ParsedDhcpOption::DomainList(_) => {
836 return Err(IaOptionError::InvalidOption(format!("{:?}", opt)).into());
837 }
838 }
839 }
840
841 Ok(IaOption::Success { status_message: success_status_message, t1, t2, ia_values })
847}
848
849fn process_ia_na(
852 ia_na_data: &v6::IanaData<&'_ [u8]>,
853) -> Result<IaNaOption, IaOptionError<Ipv6Addr>> {
854 process_ia(ia_na_data.t1(), ia_na_data.t2(), ia_na_data.iter_options(), |opt| match opt {
855 v6::ParsedDhcpOption::IaAddr(ia_addr_data) => Ok(IaValueOption {
856 value: ia_addr_data.addr(),
857 lifetimes: check_lifetimes(
858 ia_addr_data.valid_lifetime(),
859 ia_addr_data.preferred_lifetime(),
860 ),
861 }),
862 opt @ v6::ParsedDhcpOption::IaPrefix(_) => {
863 Err(IaOptionError::InvalidOption(format!("{:?}", opt)))
864 }
865 opt => unreachable!(
866 "other options should be handled before this fn is called; got = {:?}",
867 opt
868 ),
869 })
870}
871
872#[derive(thiserror::Error, Debug)]
873enum IaPdOptionError {
874 #[error("generic IA Option error: {0}")]
875 IaOptionError(#[from] IaOptionError<Subnet<Ipv6Addr>>),
876 #[error("invalid subnet")]
877 InvalidSubnet,
878}
879
880type IaPdOption = IaOption<Subnet<Ipv6Addr>>;
881
882fn process_ia_pd(ia_pd_data: &v6::IaPdData<&'_ [u8]>) -> Result<IaPdOption, IaPdOptionError> {
885 process_ia(ia_pd_data.t1(), ia_pd_data.t2(), ia_pd_data.iter_options(), |opt| match opt {
886 v6::ParsedDhcpOption::IaPrefix(ia_prefix_data) => ia_prefix_data
887 .prefix()
888 .map_err(|_| IaPdOptionError::InvalidSubnet)
889 .map(|prefix| IaValueOption {
890 value: prefix,
891 lifetimes: check_lifetimes(
892 ia_prefix_data.valid_lifetime(),
893 ia_prefix_data.preferred_lifetime(),
894 ),
895 }),
896 opt @ v6::ParsedDhcpOption::IaAddr(_) => {
897 Err(IaOptionError::InvalidOption(format!("{:?}", opt)).into())
898 }
899 opt => unreachable!(
900 "other options should be handled before this fn is called; got = {:?}",
901 opt
902 ),
903 })
904}
905
906#[derive(Debug)]
907enum NextContactTime {
908 InformationRefreshTime(Option<u32>),
909 RenewRebind { t1: v6::NonZeroTimeValue, t2: v6::NonZeroTimeValue },
910}
911
912#[derive(Debug)]
913struct Options {
914 success_status_message: Option<String>,
915 next_contact_time: NextContactTime,
916 preference: Option<u8>,
917 non_temporary_addresses: HashMap<v6::IAID, IaNaOption>,
918 delegated_prefixes: HashMap<v6::IAID, IaPdOption>,
919 dns_servers: Option<Vec<Ipv6Addr>>,
920}
921
922#[derive(Debug)]
923struct ProcessedOptions {
924 server_id: Vec<u8>,
925 solicit_max_rt_opt: Option<u32>,
926 result: Result<Options, ErrorStatusCode>,
927}
928
929#[derive(thiserror::Error, Debug)]
930#[cfg_attr(test, derive(PartialEq))]
931#[error("error status code={0}, message='{1}'")]
932struct ErrorStatusCode(v6::ErrorStatusCode, String);
933
934#[derive(thiserror::Error, Debug)]
935enum OptionsError {
936 #[error("duplicate option with code {0:?} {1} and {2}")]
939 DuplicateOption(v6::OptionCode, String, String),
940 #[error("unknown status code {0} with message '{1}'")]
941 InvalidStatusCode(u16, String),
942 #[error("IA_NA option error")]
943 IaNaError(#[from] IaOptionError<Ipv6Addr>),
944 #[error("IA_PD option error")]
945 IaPdError(#[from] IaPdOptionError),
946 #[error("duplicate IA_NA option with IAID={0:?} {1:?} and {2:?}")]
947 DuplicateIaNaId(v6::IAID, IaNaOption, IaNaOption),
948 #[error("duplicate IA_PD option with IAID={0:?} {1:?} and {2:?}")]
949 DuplicateIaPdId(v6::IAID, IaPdOption, IaPdOption),
950 #[error("IA_NA with unexpected IAID")]
951 UnexpectedIaNa(v6::IAID, IaNaOption),
952 #[error("IA_PD with unexpected IAID")]
953 UnexpectedIaPd(v6::IAID, IaPdOption),
954 #[error("missing Server Id option")]
955 MissingServerId,
956 #[error("missing Client Id option")]
957 MissingClientId,
958 #[error("got Client ID option {got:?} but want {want:?}")]
959 MismatchedClientId { got: Vec<u8>, want: Vec<u8> },
960 #[error("unexpected Client ID in Reply to anonymous Information-Request: {0:?}")]
961 UnexpectedClientId(Vec<u8>),
962 #[error("invalid option found: {0:?}")]
965 InvalidOption(String),
966}
967
968#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
971enum RequestLeasesMessageType {
972 Request,
973 Renew,
974 Rebind,
975}
976
977impl std::fmt::Display for RequestLeasesMessageType {
978 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
979 match self {
980 Self::Request => write!(f, "Request"),
981 Self::Renew => write!(f, "Renew"),
982 Self::Rebind => write!(f, "Rebind"),
983 }
984 }
985}
986
987#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
988enum ExchangeType {
989 ReplyToInformationRequest,
990 AdvertiseToSolicit,
991 ReplyWithLeases(RequestLeasesMessageType),
992}
993
994trait IaChecker {
995 fn was_ia_requested(&self, id: &v6::IAID) -> bool;
997}
998
999struct NoIaRequested;
1000
1001impl IaChecker for NoIaRequested {
1002 fn was_ia_requested(&self, _id: &v6::IAID) -> bool {
1003 false
1004 }
1005}
1006
1007impl<V> IaChecker for HashMap<v6::IAID, V> {
1008 fn was_ia_requested(&self, id: &v6::IAID) -> bool {
1009 self.get(id).is_some()
1010 }
1011}
1012
1013#[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
1018fn process_options<B: SplitByteSlice, IaNaChecker: IaChecker, IaPdChecker: IaChecker>(
1038 msg: &v6::Message<'_, B>,
1039 exchange_type: ExchangeType,
1040 want_client_id: Option<&[u8]>,
1041 iana_checker: &IaNaChecker,
1042 iapd_checker: &IaPdChecker,
1043) -> Result<ProcessedOptions, OptionsError> {
1044 let mut solicit_max_rt_option = None;
1045 let mut server_id_option = None;
1046 let mut client_id_option = None;
1047 let mut preference = None;
1048 let mut non_temporary_addresses = HashMap::new();
1049 let mut delegated_prefixes = HashMap::new();
1050 let mut status_code_option = None;
1051 let mut dns_servers = None;
1052 let mut refresh_time_option = None;
1053 let mut min_t1 = v6::TimeValue::Zero;
1054 let mut min_t2 = v6::TimeValue::Zero;
1055 let mut min_preferred_lifetime = v6::TimeValue::Zero;
1056 let mut min_valid_lifetime = v6::NonZeroTimeValue::Infinity;
1060
1061 let mut update_min_preferred_valid_lifetimes = |preferred_lifetime, valid_lifetime| {
1064 min_preferred_lifetime = maybe_get_nonzero_min(min_preferred_lifetime, preferred_lifetime);
1065 min_valid_lifetime = std::cmp::min(min_valid_lifetime, valid_lifetime);
1066 };
1067
1068 let mut update_min_t1_t2 = |t1, t2| {
1069 min_t1 = maybe_get_nonzero_min(min_t1, t1);
1083 min_t2 = maybe_get_nonzero_min(min_t2, t2);
1084 };
1085
1086 #[derive(Copy, Clone)]
1087 enum OptionAction {
1088 Accept,
1089 Ignore,
1090 Drop,
1091 }
1092 impl OptionAction {
1093 fn accept_option<'a>(
1094 self,
1095 opt: &v6::ParsedDhcpOption<'a>,
1096 exchange_type: ExchangeType,
1097 ) -> Result<bool, OptionsError> {
1098 match self {
1099 Self::Drop => Err(OptionsError::InvalidOption(format!("{:?}", opt))),
1100 Self::Ignore => {
1101 warn!("{:?}: ignoring invalid option {:?}", exchange_type, opt);
1102 Ok(false)
1103 }
1104 Self::Accept => Ok(true),
1105 }
1106 }
1107 }
1108 struct OptionActions {
1109 preference: OptionAction,
1110 information_refresh_time: OptionAction,
1111 identity_association: OptionAction,
1112 }
1113 let OptionActions {
1116 preference: preference_action,
1117 information_refresh_time: information_refresh_time_action,
1118 identity_association: identity_association_action,
1119 } = match exchange_type {
1120 ExchangeType::ReplyToInformationRequest => OptionActions {
1121 preference: OptionAction::Ignore,
1122 information_refresh_time: OptionAction::Accept,
1123 identity_association: OptionAction::Drop,
1134 },
1135 ExchangeType::AdvertiseToSolicit => OptionActions {
1136 preference: OptionAction::Accept,
1137 information_refresh_time: OptionAction::Ignore,
1138 identity_association: OptionAction::Accept,
1139 },
1140 ExchangeType::ReplyWithLeases(
1141 RequestLeasesMessageType::Request
1142 | RequestLeasesMessageType::Renew
1143 | RequestLeasesMessageType::Rebind,
1144 ) => OptionActions {
1145 preference: OptionAction::Ignore,
1146 information_refresh_time: OptionAction::Ignore,
1147 identity_association: OptionAction::Accept,
1148 },
1149 };
1150
1151 for opt in msg.options() {
1152 match opt {
1153 v6::ParsedDhcpOption::ClientId(client_id) => {
1154 if let Some(existing) = client_id_option {
1155 return Err(OptionsError::DuplicateOption(
1156 v6::OptionCode::ClientId,
1157 format!("{:?}", existing),
1158 format!("{:?}", client_id.to_vec()),
1159 ));
1160 }
1161 client_id_option = Some(client_id.to_vec());
1162 }
1163 v6::ParsedDhcpOption::ServerId(server_id_opt) => {
1164 if let Some(existing) = server_id_option {
1165 return Err(OptionsError::DuplicateOption(
1166 v6::OptionCode::ServerId,
1167 format!("{:?}", existing),
1168 format!("{:?}", server_id_opt.to_vec()),
1169 ));
1170 }
1171 server_id_option = Some(server_id_opt.to_vec());
1172 }
1173 v6::ParsedDhcpOption::SolMaxRt(sol_max_rt_opt) => {
1174 if let Some(existing) = solicit_max_rt_option {
1175 return Err(OptionsError::DuplicateOption(
1176 v6::OptionCode::SolMaxRt,
1177 format!("{:?}", existing),
1178 format!("{:?}", sol_max_rt_opt.get()),
1179 ));
1180 }
1181 if !VALID_MAX_SOLICIT_TIMEOUT_RANGE.contains(&sol_max_rt_opt.get()) {
1188 warn!(
1189 "{:?}: ignoring SOL_MAX_RT value {} outside of range {:?}",
1190 exchange_type,
1191 sol_max_rt_opt.get(),
1192 VALID_MAX_SOLICIT_TIMEOUT_RANGE,
1193 );
1194 } else {
1195 solicit_max_rt_option = Some(sol_max_rt_opt.get());
1198 }
1199 }
1200 v6::ParsedDhcpOption::Preference(preference_opt) => {
1201 if !preference_action.accept_option(&opt, exchange_type)? {
1202 continue;
1203 }
1204 if let Some(existing) = preference {
1205 return Err(OptionsError::DuplicateOption(
1206 v6::OptionCode::Preference,
1207 format!("{:?}", existing),
1208 format!("{:?}", preference_opt),
1209 ));
1210 }
1211 preference = Some(preference_opt);
1212 }
1213 v6::ParsedDhcpOption::Iana(ref iana_data) => {
1214 if !identity_association_action.accept_option(&opt, exchange_type)? {
1215 continue;
1216 }
1217 let iaid = v6::IAID::new(iana_data.iaid());
1218 let processed_ia_na = match process_ia_na(iana_data) {
1219 Ok(o) => o,
1220 Err(IaOptionError::T1GreaterThanT2 { t1: _, t2: _ }) => {
1221 continue;
1229 }
1230 Err(
1231 e @ IaOptionError::StatusCode(_)
1232 | e @ IaOptionError::InvalidOption(_)
1233 | e @ IaOptionError::DuplicateIaValue {
1234 value: _,
1235 first_lifetimes: _,
1236 second_lifetimes: _,
1237 },
1238 ) => {
1239 return Err(OptionsError::IaNaError(e));
1240 }
1241 };
1242 if !iana_checker.was_ia_requested(&iaid) {
1243 return Err(OptionsError::UnexpectedIaNa(iaid, processed_ia_na));
1249 }
1250 match processed_ia_na {
1251 IaNaOption::Failure(_) => {}
1252 IaNaOption::Success { status_message: _, t1, t2, ref ia_values } => {
1253 let mut update_t1_t2 = false;
1254 for (_value, lifetimes) in ia_values {
1255 match lifetimes {
1256 Err(_) => {}
1257 Ok(Lifetimes { preferred_lifetime, valid_lifetime }) => {
1258 update_min_preferred_valid_lifetimes(
1259 *preferred_lifetime,
1260 *valid_lifetime,
1261 );
1262 update_t1_t2 = true;
1263 }
1264 }
1265 }
1266 if update_t1_t2 {
1267 update_min_t1_t2(t1, t2);
1268 }
1269 }
1270 }
1271
1272 match non_temporary_addresses.entry(iaid) {
1278 Entry::Occupied(entry) => {
1279 return Err(OptionsError::DuplicateIaNaId(
1280 iaid,
1281 entry.remove(),
1282 processed_ia_na,
1283 ));
1284 }
1285 Entry::Vacant(entry) => {
1286 let _: &mut IaNaOption = entry.insert(processed_ia_na);
1287 }
1288 };
1289 }
1290 v6::ParsedDhcpOption::StatusCode(code, message) => {
1291 let status_code = match v6::StatusCode::try_from(code.get()) {
1292 Ok(status_code) => status_code,
1293 Err(v6::ParseError::InvalidStatusCode(invalid)) => {
1294 return Err(OptionsError::InvalidStatusCode(invalid, message.to_string()));
1295 }
1296 Err(e) => {
1297 unreachable!("unreachable status code parse error: {}", e);
1298 }
1299 };
1300 if let Some(existing) = status_code_option {
1301 return Err(OptionsError::DuplicateOption(
1302 v6::OptionCode::StatusCode,
1303 format!("{:?}", existing),
1304 format!("{:?}", (status_code, message.to_string())),
1305 ));
1306 }
1307 status_code_option = Some((status_code, message.to_string()));
1308 }
1309 v6::ParsedDhcpOption::IaPd(ref iapd_data) => {
1310 if !identity_association_action.accept_option(&opt, exchange_type)? {
1311 continue;
1312 }
1313 let iaid = v6::IAID::new(iapd_data.iaid());
1314 let processed_ia_pd = match process_ia_pd(iapd_data) {
1315 Ok(o) => o,
1316 Err(IaPdOptionError::IaOptionError(IaOptionError::T1GreaterThanT2 {
1317 t1: _,
1318 t2: _,
1319 })) => {
1320 continue;
1328 }
1329 Err(
1330 e @ IaPdOptionError::IaOptionError(IaOptionError::StatusCode(_))
1331 | e @ IaPdOptionError::IaOptionError(IaOptionError::InvalidOption(_))
1332 | e @ IaPdOptionError::IaOptionError(IaOptionError::DuplicateIaValue {
1333 value: _,
1334 first_lifetimes: _,
1335 second_lifetimes: _,
1336 })
1337 | e @ IaPdOptionError::InvalidSubnet,
1338 ) => {
1339 return Err(OptionsError::IaPdError(e));
1340 }
1341 };
1342 if !iapd_checker.was_ia_requested(&iaid) {
1343 return Err(OptionsError::UnexpectedIaPd(iaid, processed_ia_pd));
1349 }
1350 match processed_ia_pd {
1351 IaPdOption::Failure(_) => {}
1352 IaPdOption::Success { status_message: _, t1, t2, ref ia_values } => {
1353 let mut update_t1_t2 = false;
1354 for (_value, lifetimes) in ia_values {
1355 match lifetimes {
1356 Err(_) => {}
1357 Ok(Lifetimes { preferred_lifetime, valid_lifetime }) => {
1358 update_min_preferred_valid_lifetimes(
1359 *preferred_lifetime,
1360 *valid_lifetime,
1361 );
1362 update_t1_t2 = true;
1363 }
1364 }
1365 }
1366 if update_t1_t2 {
1367 update_min_t1_t2(t1, t2);
1368 }
1369 }
1370 }
1371 match delegated_prefixes.entry(iaid) {
1376 Entry::Occupied(entry) => {
1377 return Err(OptionsError::DuplicateIaPdId(
1378 iaid,
1379 entry.remove(),
1380 processed_ia_pd,
1381 ));
1382 }
1383 Entry::Vacant(entry) => {
1384 let _: &mut IaPdOption = entry.insert(processed_ia_pd);
1385 }
1386 };
1387 }
1388 v6::ParsedDhcpOption::InformationRefreshTime(information_refresh_time) => {
1389 if !information_refresh_time_action.accept_option(&opt, exchange_type)? {
1390 continue;
1391 }
1392 if let Some(existing) = refresh_time_option {
1393 return Err(OptionsError::DuplicateOption(
1394 v6::OptionCode::InformationRefreshTime,
1395 format!("{:?}", existing),
1396 format!("{:?}", information_refresh_time),
1397 ));
1398 }
1399 refresh_time_option = Some(information_refresh_time);
1400 }
1401 v6::ParsedDhcpOption::IaAddr(_)
1402 | v6::ParsedDhcpOption::IaPrefix(_)
1403 | v6::ParsedDhcpOption::Oro(_)
1404 | v6::ParsedDhcpOption::ElapsedTime(_) => {
1405 return Err(OptionsError::InvalidOption(format!("{:?}", opt)));
1406 }
1407 v6::ParsedDhcpOption::DnsServers(server_addrs) => {
1408 if let Some(existing) = dns_servers {
1409 return Err(OptionsError::DuplicateOption(
1410 v6::OptionCode::DnsServers,
1411 format!("{:?}", existing),
1412 format!("{:?}", server_addrs),
1413 ));
1414 }
1415 dns_servers = Some(server_addrs);
1416 }
1417 v6::ParsedDhcpOption::DomainList(_domains) => {
1418 }
1420 }
1421 }
1422 let server_id = server_id_option.ok_or(OptionsError::MissingServerId)?;
1431 match (client_id_option, want_client_id) {
1450 (None, None) => {}
1451 (Some(got), None) => return Err(OptionsError::UnexpectedClientId(got)),
1452 (None, Some::<&[u8]>(_)) => return Err(OptionsError::MissingClientId),
1453 (Some(got), Some(want)) => {
1454 if got != want {
1455 return Err(OptionsError::MismatchedClientId {
1456 want: want.to_vec(),
1457 got: got.to_vec(),
1458 });
1459 }
1460 }
1461 }
1462 let success_status_message = match status_code_option {
1463 Some((status_code, message)) => match status_code.into_result() {
1464 Ok(()) => Some(message),
1465 Err(error_code) => {
1466 return Ok(ProcessedOptions {
1467 server_id,
1468 solicit_max_rt_opt: solicit_max_rt_option,
1469 result: Err(ErrorStatusCode(error_code, message)),
1470 });
1471 }
1472 },
1473 None => None,
1479 };
1480 let next_contact_time = match exchange_type {
1481 ExchangeType::ReplyToInformationRequest => {
1482 NextContactTime::InformationRefreshTime(refresh_time_option)
1483 }
1484 ExchangeType::AdvertiseToSolicit
1485 | ExchangeType::ReplyWithLeases(
1486 RequestLeasesMessageType::Request
1487 | RequestLeasesMessageType::Renew
1488 | RequestLeasesMessageType::Rebind,
1489 ) => {
1490 let t1 = match min_t1 {
1519 v6::TimeValue::Zero => {
1520 let min = match min_preferred_lifetime {
1521 v6::TimeValue::Zero => min_valid_lifetime,
1522 v6::TimeValue::NonZero(t) => t,
1523 };
1524 compute_t(min, T1_MIN_LIFETIME_RATIO)
1525 }
1526 v6::TimeValue::NonZero(t) => t,
1527 };
1528 let t2 = match min_t2 {
1530 v6::TimeValue::Zero => compute_t(t1, T2_T1_RATIO),
1531 v6::TimeValue::NonZero(t2_val) => {
1532 if t2_val < t1 {
1533 compute_t(t1, T2_T1_RATIO)
1534 } else {
1535 t2_val
1536 }
1537 }
1538 };
1539
1540 NextContactTime::RenewRebind { t1, t2 }
1541 }
1542 };
1543 Ok(ProcessedOptions {
1544 server_id,
1545 solicit_max_rt_opt: solicit_max_rt_option,
1546 result: Ok(Options {
1547 success_status_message,
1548 next_contact_time,
1549 preference,
1550 non_temporary_addresses,
1551 delegated_prefixes,
1552 dns_servers,
1553 }),
1554 })
1555}
1556
1557struct StatefulMessageBuilder<'a, AddrIter, PrefixIter, IaNaIter, IaPdIter> {
1558 transaction_id: [u8; 3],
1559 message_type: v6::MessageType,
1560 client_id: &'a [u8],
1561 server_id: Option<&'a [u8]>,
1562 elapsed_time_in_centisecs: u16,
1563 options_to_request: &'a [v6::OptionCode],
1564 ia_nas: IaNaIter,
1565 ia_pds: IaPdIter,
1566 _marker: std::marker::PhantomData<(AddrIter, PrefixIter)>,
1567}
1568
1569impl<
1570 'a,
1571 AddrIter: Iterator<Item = Ipv6Addr>,
1572 PrefixIter: Iterator<Item = Subnet<Ipv6Addr>>,
1573 IaNaIter: Iterator<Item = (v6::IAID, AddrIter)>,
1574 IaPdIter: Iterator<Item = (v6::IAID, PrefixIter)>,
1575> StatefulMessageBuilder<'a, AddrIter, PrefixIter, IaNaIter, IaPdIter>
1576{
1577 fn build(self) -> Vec<u8> {
1578 let StatefulMessageBuilder {
1579 transaction_id,
1580 message_type,
1581 client_id,
1582 server_id,
1583 elapsed_time_in_centisecs,
1584 options_to_request,
1585 ia_nas,
1586 ia_pds,
1587 _marker,
1588 } = self;
1589
1590 debug_assert!(!options_to_request.contains(&v6::OptionCode::SolMaxRt));
1591 let oro = [v6::OptionCode::SolMaxRt]
1592 .into_iter()
1593 .chain(options_to_request.iter().cloned())
1594 .collect::<Vec<_>>();
1595
1596 let iaaddr_options = ia_nas
1615 .map(|(iaid, inner)| {
1616 (
1617 iaid,
1618 inner
1619 .map(|addr| {
1620 v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(addr, 0, 0, &[]))
1621 })
1622 .collect::<Vec<_>>(),
1623 )
1624 })
1625 .collect::<HashMap<_, _>>();
1626 let iaprefix_options = ia_pds
1627 .map(|(iaid, inner)| {
1628 (
1629 iaid,
1630 inner
1631 .map(|prefix| {
1632 v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(0, 0, prefix, &[]))
1633 })
1634 .collect::<Vec<_>>(),
1635 )
1636 })
1637 .collect::<HashMap<_, _>>();
1638
1639 let options = server_id
1640 .into_iter()
1641 .map(v6::DhcpOption::ServerId)
1642 .chain([
1643 v6::DhcpOption::ClientId(client_id),
1644 v6::DhcpOption::ElapsedTime(elapsed_time_in_centisecs),
1645 v6::DhcpOption::Oro(&oro),
1646 ])
1647 .chain(iaaddr_options.iter().map(|(iaid, iaddr_opt)| {
1648 v6::DhcpOption::Iana(v6::IanaSerializer::new(*iaid, 0, 0, iaddr_opt.as_slice()))
1649 }))
1650 .chain(iaprefix_options.iter().map(|(iaid, iaprefix_opt)| {
1651 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(*iaid, 0, 0, iaprefix_opt.as_slice()))
1652 }))
1653 .collect::<Vec<_>>();
1654
1655 let builder = v6::MessageBuilder::new(message_type, transaction_id, &options);
1656 let mut buf = vec![0; builder.bytes_len()];
1657 builder.serialize(&mut buf);
1658 buf
1659 }
1660}
1661
1662#[derive(Debug)]
1665struct ServerDiscovery<I> {
1666 client_id: ClientDuid,
1671 configured_non_temporary_addresses: HashMap<v6::IAID, HashSet<Ipv6Addr>>,
1673 configured_delegated_prefixes: HashMap<v6::IAID, HashSet<Subnet<Ipv6Addr>>>,
1675 first_solicit_time: I,
1679 retrans_timeout: Duration,
1681 solicit_max_rt: Duration,
1685 collected_advertise: BinaryHeap<AdvertiseMessage<I>>,
1690 collected_sol_max_rt: Vec<u32>,
1692}
1693
1694impl<I: Instant> ServerDiscovery<I> {
1695 fn start<R: Rng>(
1700 client_id: ClientDuid,
1701 configured_non_temporary_addresses: HashMap<v6::IAID, HashSet<Ipv6Addr>>,
1702 configured_delegated_prefixes: HashMap<v6::IAID, HashSet<Subnet<Ipv6Addr>>>,
1703 options_to_request: &[v6::OptionCode],
1704 solicit_max_rt: Duration,
1705 rng: &mut R,
1706 now: I,
1707 initial_actions: impl Iterator<Item = Action<I>>,
1708 ) -> Transition<I> {
1709 let transaction_id = transaction_id(rng);
1710 Self {
1711 client_id,
1712 configured_non_temporary_addresses,
1713 configured_delegated_prefixes,
1714 first_solicit_time: now,
1715 retrans_timeout: Duration::default(),
1716 solicit_max_rt,
1717 collected_advertise: BinaryHeap::new(),
1718 collected_sol_max_rt: Vec::new(),
1719 }
1720 .send_and_schedule_retransmission(
1721 transaction_id,
1722 options_to_request,
1723 rng,
1724 now,
1725 initial_actions,
1726 )
1727 }
1728
1729 fn retransmission_timeout<R: Rng>(
1734 prev_retrans_timeout: Duration,
1735 max_retrans_timeout: Duration,
1736 rng: &mut R,
1737 ) -> Duration {
1738 retransmission_timeout(
1739 prev_retrans_timeout,
1740 INITIAL_SOLICIT_TIMEOUT,
1741 max_retrans_timeout,
1742 rng,
1743 )
1744 }
1745
1746 fn send_and_schedule_retransmission<R: Rng>(
1749 self,
1750 transaction_id: [u8; 3],
1751 options_to_request: &[v6::OptionCode],
1752 rng: &mut R,
1753 now: I,
1754 initial_actions: impl Iterator<Item = Action<I>>,
1755 ) -> Transition<I> {
1756 let Self {
1757 client_id,
1758 configured_non_temporary_addresses,
1759 configured_delegated_prefixes,
1760 first_solicit_time,
1761 retrans_timeout,
1762 solicit_max_rt,
1763 collected_advertise,
1764 collected_sol_max_rt,
1765 } = self;
1766
1767 let elapsed_time = elapsed_time_in_centisecs(first_solicit_time, now);
1768
1769 let buf = StatefulMessageBuilder {
1815 transaction_id,
1816 message_type: v6::MessageType::Solicit,
1817 server_id: None,
1818 client_id: &client_id,
1819 elapsed_time_in_centisecs: elapsed_time,
1820 options_to_request,
1821 ia_nas: configured_non_temporary_addresses
1822 .iter()
1823 .map(|(iaid, ia)| (*iaid, ia.iter().cloned())),
1824 ia_pds: configured_delegated_prefixes
1825 .iter()
1826 .map(|(iaid, ia)| (*iaid, ia.iter().cloned())),
1827 _marker: Default::default(),
1828 }
1829 .build();
1830
1831 let retrans_timeout = Self::retransmission_timeout(retrans_timeout, solicit_max_rt, rng);
1832
1833 Transition {
1834 state: ClientState::ServerDiscovery(ServerDiscovery {
1835 client_id,
1836 configured_non_temporary_addresses,
1837 configured_delegated_prefixes,
1838 first_solicit_time,
1839 retrans_timeout,
1840 solicit_max_rt,
1841 collected_advertise,
1842 collected_sol_max_rt,
1843 }),
1844 actions: initial_actions
1845 .chain([
1846 Action::SendMessage(buf),
1847 Action::ScheduleTimer(
1848 ClientTimerType::Retransmission,
1849 now.add(retrans_timeout),
1850 ),
1851 ])
1852 .collect(),
1853 transaction_id: Some(transaction_id),
1854 }
1855 }
1856
1857 fn retransmission_timer_expired<R: Rng>(
1860 self,
1861 transaction_id: [u8; 3],
1862 options_to_request: &[v6::OptionCode],
1863 rng: &mut R,
1864 now: I,
1865 ) -> Transition<I> {
1866 let Self {
1867 client_id,
1868 configured_non_temporary_addresses,
1869 configured_delegated_prefixes,
1870 first_solicit_time,
1871 retrans_timeout,
1872 solicit_max_rt,
1873 mut collected_advertise,
1874 collected_sol_max_rt,
1875 } = self;
1876 let solicit_max_rt = get_common_value(&collected_sol_max_rt).unwrap_or(solicit_max_rt);
1877
1878 if let Some(advertise) = collected_advertise.pop() {
1884 let AdvertiseMessage {
1885 server_id,
1886 non_temporary_addresses: advertised_non_temporary_addresses,
1887 delegated_prefixes: advertised_delegated_prefixes,
1888 dns_servers: _,
1889 preference: _,
1890 receive_time: _,
1891 preferred_non_temporary_addresses_count: _,
1892 preferred_delegated_prefixes_count: _,
1893 } = advertise;
1894 return Requesting::start(
1895 client_id,
1896 server_id,
1897 advertise_to_ia_entries(
1898 advertised_non_temporary_addresses,
1899 configured_non_temporary_addresses,
1900 ),
1901 advertise_to_ia_entries(
1902 advertised_delegated_prefixes,
1903 configured_delegated_prefixes,
1904 ),
1905 &options_to_request,
1906 collected_advertise,
1907 solicit_max_rt,
1908 rng,
1909 now,
1910 );
1911 }
1912
1913 ServerDiscovery {
1914 client_id,
1915 configured_non_temporary_addresses,
1916 configured_delegated_prefixes,
1917 first_solicit_time,
1918 retrans_timeout,
1919 solicit_max_rt,
1920 collected_advertise,
1921 collected_sol_max_rt,
1922 }
1923 .send_and_schedule_retransmission(
1924 transaction_id,
1925 options_to_request,
1926 rng,
1927 now,
1928 std::iter::empty(),
1929 )
1930 }
1931
1932 fn advertise_message_received<R: Rng, B: SplitByteSlice>(
1933 self,
1934 options_to_request: &[v6::OptionCode],
1935 rng: &mut R,
1936 msg: v6::Message<'_, B>,
1937 now: I,
1938 ) -> Transition<I> {
1939 let Self {
1940 client_id,
1941 configured_non_temporary_addresses,
1942 configured_delegated_prefixes,
1943 first_solicit_time,
1944 retrans_timeout,
1945 solicit_max_rt,
1946 collected_advertise,
1947 collected_sol_max_rt,
1948 } = self;
1949
1950 let ProcessedOptions { server_id, solicit_max_rt_opt, result } = match process_options(
1951 &msg,
1952 ExchangeType::AdvertiseToSolicit,
1953 Some(&client_id),
1954 &configured_non_temporary_addresses,
1955 &configured_delegated_prefixes,
1956 ) {
1957 Ok(processed_options) => processed_options,
1958 Err(e) => {
1959 warn!("ignoring Advertise: {}", e);
1960 return Transition {
1961 state: ClientState::ServerDiscovery(ServerDiscovery {
1962 client_id,
1963 configured_non_temporary_addresses,
1964 configured_delegated_prefixes,
1965 first_solicit_time,
1966 retrans_timeout,
1967 solicit_max_rt,
1968 collected_advertise,
1969 collected_sol_max_rt,
1970 }),
1971 actions: Vec::new(),
1972 transaction_id: None,
1973 };
1974 }
1975 };
1976
1977 let mut collected_sol_max_rt = collected_sol_max_rt;
1995 if let Some(solicit_max_rt) = solicit_max_rt_opt {
1996 collected_sol_max_rt.push(solicit_max_rt);
1997 }
1998 let Options {
1999 success_status_message,
2000 next_contact_time: _,
2001 preference,
2002 non_temporary_addresses,
2003 delegated_prefixes,
2004 dns_servers,
2005 } = match result {
2006 Ok(options) => options,
2007 Err(e) => {
2008 warn!("Advertise from server {:?} error status code: {}", server_id, e);
2009 return Transition {
2010 state: ClientState::ServerDiscovery(ServerDiscovery {
2011 client_id,
2012 configured_non_temporary_addresses,
2013 configured_delegated_prefixes,
2014 first_solicit_time,
2015 retrans_timeout,
2016 solicit_max_rt,
2017 collected_advertise,
2018 collected_sol_max_rt,
2019 }),
2020 actions: Vec::new(),
2021 transaction_id: None,
2022 };
2023 }
2024 };
2025 match success_status_message {
2026 Some(success_status_message) if !success_status_message.is_empty() => {
2027 info!(
2028 "Advertise from server {:?} contains success status code message: {}",
2029 server_id, success_status_message,
2030 );
2031 }
2032 _ => {
2033 info!("processing Advertise from server {:?}", server_id);
2034 }
2035 }
2036 let non_temporary_addresses = non_temporary_addresses
2037 .into_iter()
2038 .filter_map(|(iaid, ia_na)| {
2039 let (success_status_message, ia_addrs) = match ia_na {
2040 IaNaOption::Success { status_message, t1: _, t2: _, ia_values } => {
2041 (status_message, ia_values)
2042 }
2043 IaNaOption::Failure(e) => {
2044 warn!(
2045 "Advertise from server {:?} contains IA_NA with error status code: {}",
2046 server_id, e
2047 );
2048 return None;
2049 }
2050 };
2051 if let Some(success_status_message) = success_status_message {
2052 if !success_status_message.is_empty() {
2053 info!(
2054 "Advertise from server {:?} IA_NA with IAID {:?} \
2055 success status code message: {}",
2056 server_id, iaid, success_status_message,
2057 );
2058 }
2059 }
2060
2061 let ia_addrs = ia_addrs
2062 .into_iter()
2063 .filter_map(|(value, lifetimes)| match lifetimes {
2064 Ok(Lifetimes { preferred_lifetime: _, valid_lifetime: _ }) => Some(value),
2065 e @ Err(
2066 LifetimesError::ValidLifetimeZero
2067 | LifetimesError::PreferredLifetimeGreaterThanValidLifetime(_),
2068 ) => {
2069 warn!(
2070 "Advertise from server {:?}: ignoring IA Address in \
2071 IA_NA with IAID {:?} because of invalid lifetimes: {:?}",
2072 server_id, iaid, e
2073 );
2074
2075 None
2081 }
2082 })
2083 .collect::<HashSet<_>>();
2084
2085 (!ia_addrs.is_empty()).then_some((iaid, ia_addrs))
2086 })
2087 .collect::<HashMap<_, _>>();
2088 let delegated_prefixes = delegated_prefixes
2089 .into_iter()
2090 .filter_map(|(iaid, ia_pd)| {
2091 let (success_status_message, ia_prefixes) = match ia_pd {
2092 IaPdOption::Success { status_message, t1: _, t2: _, ia_values } => {
2093 (status_message, ia_values)
2094 }
2095 IaPdOption::Failure(e) => {
2096 warn!(
2097 "Advertise from server {:?} contains IA_PD with error status code: {}",
2098 server_id, e
2099 );
2100 return None;
2101 }
2102 };
2103 if let Some(success_status_message) = success_status_message {
2104 if !success_status_message.is_empty() {
2105 info!(
2106 "Advertise from server {:?} IA_PD with IAID {:?} \
2107 success status code message: {}",
2108 server_id, iaid, success_status_message,
2109 );
2110 }
2111 }
2112 let ia_prefixes = ia_prefixes
2113 .into_iter()
2114 .filter_map(|(value, lifetimes)| match lifetimes {
2115 Ok(Lifetimes { preferred_lifetime: _, valid_lifetime: _ }) => Some(value),
2116 e @ Err(
2117 LifetimesError::ValidLifetimeZero
2118 | LifetimesError::PreferredLifetimeGreaterThanValidLifetime(_),
2119 ) => {
2120 warn!(
2121 "Advertise from server {:?}: ignoring IA Prefix in \
2122 IA_PD with IAID {:?} because of invalid lifetimes: {:?}",
2123 server_id, iaid, e
2124 );
2125
2126 None
2132 }
2133 })
2134 .collect::<HashSet<_>>();
2135
2136 (!ia_prefixes.is_empty()).then_some((iaid, ia_prefixes))
2137 })
2138 .collect::<HashMap<_, _>>();
2139 let advertise = AdvertiseMessage {
2140 preferred_non_temporary_addresses_count: compute_preferred_ia_count(
2141 &non_temporary_addresses,
2142 &configured_non_temporary_addresses,
2143 ),
2144 preferred_delegated_prefixes_count: compute_preferred_ia_count(
2145 &delegated_prefixes,
2146 &configured_delegated_prefixes,
2147 ),
2148 server_id,
2149 non_temporary_addresses,
2150 delegated_prefixes,
2151 dns_servers: dns_servers.unwrap_or(Vec::new()),
2152 preference: preference.unwrap_or(0),
2157 receive_time: now,
2158 };
2159 if !advertise.has_ias() {
2160 return Transition {
2161 state: ClientState::ServerDiscovery(ServerDiscovery {
2162 client_id,
2163 configured_non_temporary_addresses,
2164 configured_delegated_prefixes,
2165 first_solicit_time,
2166 retrans_timeout,
2167 solicit_max_rt,
2168 collected_advertise,
2169 collected_sol_max_rt,
2170 }),
2171 actions: Vec::new(),
2172 transaction_id: None,
2173 };
2174 }
2175
2176 let solicit_timeout = INITIAL_SOLICIT_TIMEOUT.as_secs_f64();
2177 let is_retransmitting = retrans_timeout.as_secs_f64()
2178 >= solicit_timeout + solicit_timeout * RANDOMIZATION_FACTOR_MAX;
2179
2180 if (advertise.preference == ADVERTISE_MAX_PREFERENCE) || is_retransmitting {
2205 let solicit_max_rt = get_common_value(&collected_sol_max_rt).unwrap_or(solicit_max_rt);
2206 let AdvertiseMessage {
2207 server_id,
2208 non_temporary_addresses: advertised_non_temporary_addresses,
2209 delegated_prefixes: advertised_delegated_prefixes,
2210 dns_servers: _,
2211 preference: _,
2212 receive_time: _,
2213 preferred_non_temporary_addresses_count: _,
2214 preferred_delegated_prefixes_count: _,
2215 } = advertise;
2216 return Requesting::start(
2217 client_id,
2218 server_id,
2219 advertise_to_ia_entries(
2220 advertised_non_temporary_addresses,
2221 configured_non_temporary_addresses,
2222 ),
2223 advertise_to_ia_entries(
2224 advertised_delegated_prefixes,
2225 configured_delegated_prefixes,
2226 ),
2227 &options_to_request,
2228 collected_advertise,
2229 solicit_max_rt,
2230 rng,
2231 now,
2232 );
2233 }
2234
2235 let mut collected_advertise = collected_advertise;
2236 collected_advertise.push(advertise);
2237 Transition {
2238 state: ClientState::ServerDiscovery(ServerDiscovery {
2239 client_id,
2240 configured_non_temporary_addresses,
2241 configured_delegated_prefixes,
2242 first_solicit_time,
2243 retrans_timeout,
2244 solicit_max_rt,
2245 collected_advertise,
2246 collected_sol_max_rt,
2247 }),
2248 actions: Vec::new(),
2249 transaction_id: None,
2250 }
2251 }
2252}
2253
2254fn maybe_get_nonzero_min(old_value: v6::TimeValue, new_value: v6::TimeValue) -> v6::TimeValue {
2266 match old_value {
2267 v6::TimeValue::Zero => new_value,
2268 v6::TimeValue::NonZero(old_t) => v6::TimeValue::NonZero(get_nonzero_min(old_t, new_value)),
2269 }
2270}
2271
2272fn get_nonzero_min(
2274 old_value: v6::NonZeroTimeValue,
2275 new_value: v6::TimeValue,
2276) -> v6::NonZeroTimeValue {
2277 match new_value {
2278 v6::TimeValue::Zero => old_value,
2279 v6::TimeValue::NonZero(new_val) => std::cmp::min(old_value, new_val),
2280 }
2281}
2282
2283#[derive(Debug)]
2285struct Requesting<I> {
2286 client_id: ClientDuid,
2292 non_temporary_addresses: HashMap<v6::IAID, AddressEntry<I>>,
2294 delegated_prefixes: HashMap<v6::IAID, PrefixEntry<I>>,
2296 server_id: Vec<u8>,
2302 collected_advertise: BinaryHeap<AdvertiseMessage<I>>,
2307 first_request_time: I,
2311 retrans_timeout: Duration,
2313 transmission_count: u8,
2315 solicit_max_rt: Duration,
2320}
2321
2322fn compute_t(min: v6::NonZeroTimeValue, ratio: Ratio<u32>) -> v6::NonZeroTimeValue {
2323 match min {
2324 v6::NonZeroTimeValue::Finite(t) => {
2325 ratio.checked_mul(&Ratio::new_raw(t.get(), 1)).map_or(
2326 v6::NonZeroTimeValue::Infinity,
2327 |t| {
2328 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(t.to_integer()).expect(
2329 "non-zero ratio of NonZeroOrMaxU32 value should be NonZeroOrMaxU32",
2330 ))
2331 },
2332 )
2333 }
2334 v6::NonZeroTimeValue::Infinity => v6::NonZeroTimeValue::Infinity,
2335 }
2336}
2337
2338#[derive(Debug, thiserror::Error)]
2339enum ReplyWithLeasesError {
2340 #[error("option processing error")]
2341 OptionsError(#[from] OptionsError),
2342 #[error("mismatched Server ID, got {got:?} want {want:?}")]
2343 MismatchedServerId { got: Vec<u8>, want: Vec<u8> },
2344 #[error("status code error")]
2345 ErrorStatusCode(#[from] ErrorStatusCode),
2346}
2347
2348#[derive(Debug, Copy, Clone)]
2349enum IaStatusError {
2350 Retry { without_hints: bool },
2351 Invalid,
2352 Rerequest,
2353}
2354
2355fn process_ia_error_status(
2356 request_type: RequestLeasesMessageType,
2357 error_status: v6::ErrorStatusCode,
2358 ia_kind: IaKind,
2359) -> IaStatusError {
2360 match (request_type, error_status, ia_kind) {
2361 (RequestLeasesMessageType::Request, v6::ErrorStatusCode::NotOnLink, IaKind::Address) => {
2371 IaStatusError::Retry { without_hints: true }
2372 }
2373 (RequestLeasesMessageType::Request, v6::ErrorStatusCode::NotOnLink, IaKind::Prefix) => {
2383 IaStatusError::Invalid
2384 }
2385 (
2409 RequestLeasesMessageType::Renew | RequestLeasesMessageType::Rebind,
2410 v6::ErrorStatusCode::NotOnLink,
2411 IaKind::Address | IaKind::Prefix,
2412 ) => IaStatusError::Invalid,
2413
2414 (
2424 RequestLeasesMessageType::Request
2425 | RequestLeasesMessageType::Renew
2426 | RequestLeasesMessageType::Rebind,
2427 v6::ErrorStatusCode::UnspecFail,
2428 IaKind::Address | IaKind::Prefix,
2429 ) => IaStatusError::Retry { without_hints: false },
2430
2431 (
2457 RequestLeasesMessageType::Request
2458 | RequestLeasesMessageType::Renew
2459 | RequestLeasesMessageType::Rebind,
2460 v6::ErrorStatusCode::NoAddrsAvail,
2461 IaKind::Address,
2462 ) => IaStatusError::Retry { without_hints: false },
2463 (
2466 RequestLeasesMessageType::Request
2467 | RequestLeasesMessageType::Renew
2468 | RequestLeasesMessageType::Rebind,
2469 v6::ErrorStatusCode::NoAddrsAvail,
2470 IaKind::Prefix,
2471 ) => IaStatusError::Invalid,
2472
2473 (
2501 RequestLeasesMessageType::Request
2502 | RequestLeasesMessageType::Renew
2503 | RequestLeasesMessageType::Rebind,
2504 v6::ErrorStatusCode::NoPrefixAvail,
2505 IaKind::Prefix,
2506 ) => IaStatusError::Retry { without_hints: false },
2507 (
2508 RequestLeasesMessageType::Request
2509 | RequestLeasesMessageType::Renew
2510 | RequestLeasesMessageType::Rebind,
2511 v6::ErrorStatusCode::NoPrefixAvail,
2512 IaKind::Address,
2513 ) => IaStatusError::Invalid,
2514
2515 (
2529 RequestLeasesMessageType::Renew | RequestLeasesMessageType::Rebind,
2530 v6::ErrorStatusCode::NoBinding,
2531 IaKind::Address | IaKind::Prefix,
2532 ) => IaStatusError::Rerequest,
2533 (
2537 RequestLeasesMessageType::Request,
2538 v6::ErrorStatusCode::NoBinding,
2539 IaKind::Address | IaKind::Prefix,
2540 ) => IaStatusError::Invalid,
2541
2542 (
2556 RequestLeasesMessageType::Request | RequestLeasesMessageType::Renew,
2557 v6::ErrorStatusCode::UseMulticast,
2558 IaKind::Address | IaKind::Prefix,
2559 ) => IaStatusError::Invalid,
2560 (
2569 RequestLeasesMessageType::Rebind,
2570 v6::ErrorStatusCode::UseMulticast,
2571 IaKind::Address | IaKind::Prefix,
2572 ) => IaStatusError::Invalid,
2573 }
2574}
2575
2576#[derive(Debug)]
2578enum StateAfterReplyWithLeases {
2579 RequestNextServer,
2580 Assigned,
2581 StayRenewingRebinding,
2582 Requesting,
2583}
2584
2585#[derive(Debug)]
2586struct ProcessedReplyWithLeases<I> {
2587 server_id: Vec<u8>,
2588 non_temporary_addresses: HashMap<v6::IAID, AddressEntry<I>>,
2589 delegated_prefixes: HashMap<v6::IAID, PrefixEntry<I>>,
2590 dns_servers: Option<Vec<Ipv6Addr>>,
2591 actions: Vec<Action<I>>,
2592 next_state: StateAfterReplyWithLeases,
2593}
2594
2595fn has_no_assigned_ias<V: IaValue, I>(entries: &HashMap<v6::IAID, IaEntry<V, I>>) -> bool {
2596 entries.iter().all(|(_iaid, entry)| match entry {
2597 IaEntry::ToRequest(_) => true,
2598 IaEntry::Assigned(_) => false,
2599 })
2600}
2601
2602struct ComputeNewEntriesWithCurrentIasAndReplyResult<V: IaValue, I> {
2603 new_entries: HashMap<v6::IAID, IaEntry<V, I>>,
2604 go_to_requesting: bool,
2605 missing_ias_in_reply: bool,
2606 updates: HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>>,
2607 all_ias_invalidates_at: Option<AllIasInvalidatesAt<I>>,
2608}
2609
2610#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
2611enum AllIasInvalidatesAt<I> {
2612 At(I),
2613 Never,
2614}
2615
2616fn compute_new_entries_with_current_ias_and_reply<V: IaValue, I: Instant>(
2617 ia_name: &str,
2618 request_type: RequestLeasesMessageType,
2619 ias_in_reply: HashMap<v6::IAID, IaOption<V>>,
2620 current_entries: &HashMap<v6::IAID, IaEntry<V, I>>,
2621 now: I,
2622) -> ComputeNewEntriesWithCurrentIasAndReplyResult<V, I> {
2623 let mut go_to_requesting = false;
2624 let mut all_ias_invalidates_at = None;
2625
2626 let mut update_all_ias_invalidates_at = |LifetimesInfo::<I> {
2627 lifetimes:
2628 Lifetimes { valid_lifetime, preferred_lifetime: _ },
2629 updated_at,
2630 }| {
2631 all_ias_invalidates_at = core::cmp::max(
2632 all_ias_invalidates_at,
2633 Some(match valid_lifetime {
2634 v6::NonZeroTimeValue::Finite(lifetime) => AllIasInvalidatesAt::At(
2635 updated_at.add(Duration::from_secs(lifetime.get().into())),
2636 ),
2637 v6::NonZeroTimeValue::Infinity => AllIasInvalidatesAt::Never,
2638 }),
2639 );
2640 };
2641
2642 let mut updates = HashMap::new();
2665
2666 let mut new_entries = ias_in_reply
2667 .into_iter()
2668 .map(|(iaid, ia)| {
2669 let current_entry = current_entries
2670 .get(&iaid)
2671 .expect("process_options should have caught unrequested IAs");
2672
2673 let (success_status_message, ia_values) = match ia {
2674 IaOption::Success { status_message, t1: _, t2: _, ia_values } => {
2675 (status_message, ia_values)
2676 }
2677 IaOption::Failure(ErrorStatusCode(error_code, msg)) => {
2678 if !msg.is_empty() {
2679 warn!(
2680 "Reply to {}: {} with IAID {:?} status code {:?} message: {}",
2681 request_type, ia_name, iaid, error_code, msg
2682 );
2683 }
2684 let error = process_ia_error_status(request_type, error_code, V::KIND);
2685 let without_hints = match error {
2686 IaStatusError::Retry { without_hints } => without_hints,
2687 IaStatusError::Invalid => {
2688 warn!(
2689 "Reply to {}: received unexpected status code {:?} in {} option with IAID {:?}",
2690 request_type, error_code, ia_name, iaid,
2691 );
2692 false
2693 }
2694 IaStatusError::Rerequest => {
2695 go_to_requesting = true;
2696 false
2697 }
2698 };
2699
2700 match current_entry {
2703 IaEntry::Assigned(values) => {
2704 assert_matches!(
2705 updates.insert(
2706 iaid,
2707 values
2708 .keys()
2709 .cloned()
2710 .map(|value| (value, IaValueUpdateKind::Removed))
2711 .collect()
2712 ),
2713 None
2714 );
2715 },
2716 IaEntry::ToRequest(_) => {},
2717 }
2718
2719 return (iaid, current_entry.to_request(without_hints));
2720 }
2721 };
2722
2723 if let Some(success_status_message) = success_status_message {
2724 if !success_status_message.is_empty() {
2725 info!(
2726 "Reply to {}: {} with IAID {:?} success status code message: {}",
2727 request_type, ia_name, iaid, success_status_message,
2728 );
2729 }
2730 }
2731
2732 if ia_values.is_empty() {
2743 return (iaid, current_entry.clone());
2744 }
2745
2746 let mut inner_updates = HashMap::new();
2747 let mut ia_values = ia_values
2748 .into_iter()
2749 .filter_map(|(value, lifetimes)| {
2750 match lifetimes {
2751 Ok(lifetimes) => {
2754 assert_matches!(
2755 inner_updates.insert(
2756 value,
2757 match current_entry {
2758 IaEntry::Assigned(values) => {
2759 if values.contains_key(&value) {
2760 IaValueUpdateKind::UpdatedLifetimes(lifetimes)
2761 } else {
2762 IaValueUpdateKind::Added(lifetimes)
2763 }
2764 },
2765 IaEntry::ToRequest(_) => IaValueUpdateKind::Added(lifetimes),
2766 },
2767 ),
2768 None
2769 );
2770
2771 let lifetimes_info = LifetimesInfo { lifetimes, updated_at: now };
2772 update_all_ias_invalidates_at(lifetimes_info);
2773
2774 Some((
2775 value,
2776 lifetimes_info,
2777 ))
2778 },
2779 Err(LifetimesError::PreferredLifetimeGreaterThanValidLifetime(Lifetimes {
2780 preferred_lifetime,
2781 valid_lifetime,
2782 })) => {
2783 warn!(
2795 "Reply to {}: {} with IAID {:?}: ignoring value={:?} because \
2796 preferred lifetime={:?} greater than valid lifetime={:?}",
2797 request_type, ia_name, iaid, value, preferred_lifetime, valid_lifetime
2798 );
2799
2800 None
2801 },
2802 Err(LifetimesError::ValidLifetimeZero) => {
2803 info!(
2804 "Reply to {}: {} with IAID {:?}: invalidating value={:?} \
2805 with zero lifetime",
2806 request_type, ia_name, iaid, value
2807 );
2808
2809 match current_entry {
2813 IaEntry::Assigned(values) => {
2814 if values.contains_key(&value) {
2815 assert_matches!(
2816 inner_updates.insert(
2817 value,
2818 IaValueUpdateKind::Removed,
2819 ),
2820 None
2821 );
2822 }
2823 }
2824 IaEntry::ToRequest(_) => {},
2825 }
2826
2827 None
2828 }
2829 }
2830 })
2831 .collect::<HashMap<_, _>>();
2832
2833 match current_entry {
2835 IaEntry::Assigned(values) => {
2836 for (value, lifetimes) in values {
2837 match ia_values.entry(*value) {
2838 Entry::Occupied(_) => {},
2841
2842 Entry::Vacant(e) => match inner_updates.get(value) {
2847 Some(update) => assert_eq!(update, &IaValueUpdateKind::Removed),
2852 None => {
2855 let lifetimes = lifetimes.clone();
2856 update_all_ias_invalidates_at(lifetimes);
2857 let _: &mut LifetimesInfo<_> = e.insert(lifetimes);
2858 }
2859 }
2860
2861 }
2862 }
2863 },
2864 IaEntry::ToRequest(_) => {},
2865 }
2866
2867 assert_matches!(updates.insert(iaid, inner_updates), None);
2868
2869 if ia_values.is_empty() {
2870 (iaid, IaEntry::ToRequest(current_entry.value().collect()))
2871 } else {
2872 (iaid, IaEntry::Assigned(ia_values))
2881 }
2882 })
2883 .collect::<HashMap<_, _>>();
2884
2885 let mut missing_ias_in_reply = false;
2887 for (iaid, entry) in current_entries {
2888 match new_entries.entry(*iaid) {
2889 Entry::Occupied(_) => {
2890 }
2893 Entry::Vacant(e) => {
2894 missing_ias_in_reply = true;
2896
2897 let _: &mut IaEntry<_, _> = e.insert(match entry {
2898 IaEntry::ToRequest(address_to_request) => {
2899 IaEntry::ToRequest(address_to_request.clone())
2900 }
2901 IaEntry::Assigned(ia) => IaEntry::Assigned(ia.clone()),
2902 });
2903 }
2904 }
2905 }
2906
2907 ComputeNewEntriesWithCurrentIasAndReplyResult {
2908 new_entries,
2909 go_to_requesting,
2910 missing_ias_in_reply,
2911 updates,
2912 all_ias_invalidates_at,
2913 }
2914}
2915
2916#[derive(Debug, PartialEq, Clone)]
2918pub struct IaValueUpdate<V> {
2919 pub value: V,
2920 pub kind: IaValueUpdateKind,
2921}
2922
2923#[derive(Debug, PartialEq, Clone)]
2925pub enum IaValueUpdateKind {
2926 Added(Lifetimes),
2927 UpdatedLifetimes(Lifetimes),
2928 Removed,
2929}
2930
2931#[derive(Debug, PartialEq, Clone)]
2933pub struct IaUpdate<V> {
2934 pub iaid: v6::IAID,
2935 pub values: Vec<IaValueUpdate<V>>,
2936}
2937
2938#[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
2942fn process_reply_with_leases<B: SplitByteSlice, I: Instant>(
2943 client_id: &[u8],
2944 server_id: &[u8],
2945 current_non_temporary_addresses: &HashMap<v6::IAID, AddressEntry<I>>,
2946 current_delegated_prefixes: &HashMap<v6::IAID, PrefixEntry<I>>,
2947 solicit_max_rt: &mut Duration,
2948 msg: &v6::Message<'_, B>,
2949 request_type: RequestLeasesMessageType,
2950 now: I,
2951) -> Result<ProcessedReplyWithLeases<I>, ReplyWithLeasesError> {
2952 let ProcessedOptions { server_id: got_server_id, solicit_max_rt_opt, result } =
2953 process_options(
2954 &msg,
2955 ExchangeType::ReplyWithLeases(request_type),
2956 Some(client_id),
2957 current_non_temporary_addresses,
2958 current_delegated_prefixes,
2959 )?;
2960
2961 match request_type {
2962 RequestLeasesMessageType::Request | RequestLeasesMessageType::Renew => {
2963 if got_server_id != server_id {
2964 return Err(ReplyWithLeasesError::MismatchedServerId {
2965 got: got_server_id,
2966 want: server_id.to_vec(),
2967 });
2968 }
2969 }
2970 RequestLeasesMessageType::Rebind => {}
2973 }
2974
2975 *solicit_max_rt = solicit_max_rt_opt
2982 .map_or(*solicit_max_rt, |solicit_max_rt| Duration::from_secs(solicit_max_rt.into()));
2983
2984 let Options {
2985 success_status_message,
2986 next_contact_time,
2987 preference: _,
2988 non_temporary_addresses,
2989 delegated_prefixes,
2990 dns_servers,
2991 } = result?;
2992
2993 let (t1, t2) = assert_matches!(
2994 next_contact_time,
2995 NextContactTime::RenewRebind { t1, t2 } => (t1, t2)
2996 );
2997
2998 if let Some(success_status_message) = success_status_message {
2999 if !success_status_message.is_empty() {
3000 info!(
3001 "Reply to {} success status code message: {}",
3002 request_type, success_status_message
3003 );
3004 }
3005 }
3006
3007 let (
3008 non_temporary_addresses,
3009 ia_na_updates,
3010 delegated_prefixes,
3011 ia_pd_updates,
3012 go_to_requesting,
3013 missing_ias_in_reply,
3014 all_ias_invalidates_at,
3015 ) = {
3016 let ComputeNewEntriesWithCurrentIasAndReplyResult {
3017 new_entries: non_temporary_addresses,
3018 go_to_requesting: go_to_requesting_iana,
3019 missing_ias_in_reply: missing_ias_in_reply_iana,
3020 updates: ia_na_updates,
3021 all_ias_invalidates_at: all_ia_nas_invalidates_at,
3022 } = compute_new_entries_with_current_ias_and_reply(
3023 IA_NA_NAME,
3024 request_type,
3025 non_temporary_addresses,
3026 current_non_temporary_addresses,
3027 now,
3028 );
3029 let ComputeNewEntriesWithCurrentIasAndReplyResult {
3030 new_entries: delegated_prefixes,
3031 go_to_requesting: go_to_requesting_iapd,
3032 missing_ias_in_reply: missing_ias_in_reply_iapd,
3033 updates: ia_pd_updates,
3034 all_ias_invalidates_at: all_ia_pds_invalidates_at,
3035 } = compute_new_entries_with_current_ias_and_reply(
3036 IA_PD_NAME,
3037 request_type,
3038 delegated_prefixes,
3039 current_delegated_prefixes,
3040 now,
3041 );
3042 (
3043 non_temporary_addresses,
3044 ia_na_updates,
3045 delegated_prefixes,
3046 ia_pd_updates,
3047 go_to_requesting_iana || go_to_requesting_iapd,
3048 missing_ias_in_reply_iana || missing_ias_in_reply_iapd,
3049 core::cmp::max(all_ia_nas_invalidates_at, all_ia_pds_invalidates_at),
3050 )
3051 };
3052
3053 let next_state = if has_no_assigned_ias(&non_temporary_addresses)
3067 && has_no_assigned_ias(&delegated_prefixes)
3068 {
3069 warn!("Reply to {}: no usable lease returned", request_type);
3070 StateAfterReplyWithLeases::RequestNextServer
3071 } else if go_to_requesting {
3072 StateAfterReplyWithLeases::Requesting
3073 } else {
3074 match request_type {
3075 RequestLeasesMessageType::Request => StateAfterReplyWithLeases::Assigned,
3076 RequestLeasesMessageType::Renew | RequestLeasesMessageType::Rebind => {
3077 if missing_ias_in_reply {
3078 warn!(
3094 "Reply to {}: allowing retransmit timeout to retry due to missing IA",
3095 request_type
3096 );
3097 StateAfterReplyWithLeases::StayRenewingRebinding
3098 } else {
3099 StateAfterReplyWithLeases::Assigned
3100 }
3101 }
3102 }
3103 };
3104 let actions = match next_state {
3105 StateAfterReplyWithLeases::Assigned => Some(
3106 [
3107 Action::CancelTimer(ClientTimerType::Retransmission),
3108 if t1 == t2 {
3149 Action::CancelTimer(ClientTimerType::Renew)
3150 } else if t1 < t2 {
3151 assert_matches!(
3152 t1,
3153 v6::NonZeroTimeValue::Finite(t1_val) => Action::ScheduleTimer(
3154 ClientTimerType::Renew,
3155 now.add(Duration::from_secs(t1_val.get().into())),
3156 ),
3157 "must be Finite since Infinity is the largest possible value so if T1 \
3158 is Infinity, T2 must also be Infinity as T1 must always be less than \
3159 or equal to T2 in which case we would have not reached this point"
3160 )
3161 } else {
3162 unreachable!("should have rejected T1={:?} > T2={:?}", t1, t2);
3163 },
3164 match t2 {
3178 v6::NonZeroTimeValue::Finite(t2_val) => Action::ScheduleTimer(
3179 ClientTimerType::Rebind,
3180 now.add(Duration::from_secs(t2_val.get().into())),
3181 ),
3182 v6::NonZeroTimeValue::Infinity => Action::CancelTimer(ClientTimerType::Rebind),
3183 },
3184 ]
3185 .into_iter()
3186 .chain(dns_servers.clone().map(Action::UpdateDnsServers)),
3187 ),
3188 StateAfterReplyWithLeases::RequestNextServer
3189 | StateAfterReplyWithLeases::StayRenewingRebinding
3190 | StateAfterReplyWithLeases::Requesting => None,
3191 }
3192 .into_iter()
3193 .flatten()
3194 .chain((!ia_na_updates.is_empty()).then_some(Action::IaNaUpdates(ia_na_updates)))
3195 .chain((!ia_pd_updates.is_empty()).then_some(Action::IaPdUpdates(ia_pd_updates)))
3196 .chain(all_ias_invalidates_at.into_iter().map(|all_ias_invalidates_at| {
3197 match all_ias_invalidates_at {
3198 AllIasInvalidatesAt::At(instant) => {
3199 Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, instant)
3200 }
3201 AllIasInvalidatesAt::Never => {
3202 Action::CancelTimer(ClientTimerType::RestartServerDiscovery)
3203 }
3204 }
3205 }))
3206 .collect();
3207
3208 Ok(ProcessedReplyWithLeases {
3209 server_id: got_server_id,
3210 non_temporary_addresses,
3211 delegated_prefixes,
3212 dns_servers,
3213 actions,
3214 next_state,
3215 })
3216}
3217
3218fn advertise_to_ia_entries<V: IaValue, I>(
3221 mut advertised: HashMap<v6::IAID, HashSet<V>>,
3222 configured: HashMap<v6::IAID, HashSet<V>>,
3223) -> HashMap<v6::IAID, IaEntry<V, I>> {
3224 configured
3225 .into_iter()
3226 .map(|(iaid, configured)| {
3227 let addresses_to_request = match advertised.remove(&iaid) {
3228 Some(ias) => {
3229 ias
3232 }
3233 None => configured,
3241 };
3242 (iaid, IaEntry::ToRequest(addresses_to_request))
3243 })
3244 .collect()
3245}
3246
3247impl<I: Instant> Requesting<I> {
3248 fn start<R: Rng>(
3252 client_id: ClientDuid,
3253 server_id: Vec<u8>,
3254 non_temporary_addresses: HashMap<v6::IAID, AddressEntry<I>>,
3255 delegated_prefixes: HashMap<v6::IAID, PrefixEntry<I>>,
3256 options_to_request: &[v6::OptionCode],
3257 collected_advertise: BinaryHeap<AdvertiseMessage<I>>,
3258 solicit_max_rt: Duration,
3259 rng: &mut R,
3260 now: I,
3261 ) -> Transition<I> {
3262 Self {
3263 client_id,
3264 non_temporary_addresses,
3265 delegated_prefixes,
3266 server_id,
3267 collected_advertise,
3268 first_request_time: now,
3269 retrans_timeout: Duration::default(),
3270 transmission_count: 0,
3271 solicit_max_rt,
3272 }
3273 .send_and_reschedule_retransmission(
3274 transaction_id(rng),
3275 options_to_request,
3276 rng,
3277 now,
3278 std::iter::empty(),
3279 )
3280 }
3281
3282 fn retransmission_timeout<R: Rng>(prev_retrans_timeout: Duration, rng: &mut R) -> Duration {
3287 retransmission_timeout(
3288 prev_retrans_timeout,
3289 INITIAL_REQUEST_TIMEOUT,
3290 MAX_REQUEST_TIMEOUT,
3291 rng,
3292 )
3293 }
3294
3295 fn send_and_reschedule_retransmission<R: Rng>(
3299 self,
3300 transaction_id: [u8; 3],
3301 options_to_request: &[v6::OptionCode],
3302 rng: &mut R,
3303 now: I,
3304 initial_actions: impl Iterator<Item = Action<I>>,
3305 ) -> Transition<I> {
3306 let Transition { state, actions: request_actions, transaction_id } = self
3307 .send_and_schedule_retransmission(
3308 transaction_id,
3309 options_to_request,
3310 rng,
3311 now,
3312 initial_actions,
3313 );
3314 let actions = std::iter::once(Action::CancelTimer(ClientTimerType::Retransmission))
3315 .chain(request_actions.into_iter())
3316 .collect();
3317 Transition { state, actions, transaction_id }
3318 }
3319
3320 fn send_and_schedule_retransmission<R: Rng>(
3327 self,
3328 transaction_id: [u8; 3],
3329 options_to_request: &[v6::OptionCode],
3330 rng: &mut R,
3331 now: I,
3332 initial_actions: impl Iterator<Item = Action<I>>,
3333 ) -> Transition<I> {
3334 let Self {
3335 client_id,
3336 server_id,
3337 non_temporary_addresses,
3338 delegated_prefixes,
3339 collected_advertise,
3340 first_request_time,
3341 retrans_timeout: prev_retrans_timeout,
3342 transmission_count,
3343 solicit_max_rt,
3344 } = self;
3345 let retrans_timeout = Self::retransmission_timeout(prev_retrans_timeout, rng);
3346 let elapsed_time = elapsed_time_in_centisecs(first_request_time, now);
3347
3348 let buf = StatefulMessageBuilder {
3379 transaction_id,
3380 message_type: v6::MessageType::Request,
3381 server_id: Some(&server_id),
3382 client_id: &client_id,
3383 elapsed_time_in_centisecs: elapsed_time,
3384 options_to_request,
3385 ia_nas: non_temporary_addresses.iter().map(|(iaid, ia)| (*iaid, ia.value())),
3386 ia_pds: delegated_prefixes.iter().map(|(iaid, ia)| (*iaid, ia.value())),
3387 _marker: Default::default(),
3388 }
3389 .build();
3390
3391 Transition {
3392 state: ClientState::Requesting(Requesting {
3393 client_id,
3394 non_temporary_addresses,
3395 delegated_prefixes,
3396 server_id,
3397 collected_advertise,
3398 first_request_time,
3399 retrans_timeout,
3400 transmission_count: transmission_count + 1,
3401 solicit_max_rt,
3402 }),
3403 actions: initial_actions
3404 .chain([
3405 Action::SendMessage(buf),
3406 Action::ScheduleTimer(
3407 ClientTimerType::Retransmission,
3408 now.add(retrans_timeout),
3409 ),
3410 ])
3411 .collect(),
3412 transaction_id: Some(transaction_id),
3413 }
3414 }
3415
3416 fn retransmission_timer_expired<R: Rng>(
3447 self,
3448 request_transaction_id: [u8; 3],
3449 options_to_request: &[v6::OptionCode],
3450 rng: &mut R,
3451 now: I,
3452 ) -> Transition<I> {
3453 let Self {
3454 client_id: _,
3455 non_temporary_addresses: _,
3456 delegated_prefixes: _,
3457 server_id: _,
3458 collected_advertise: _,
3459 first_request_time: _,
3460 retrans_timeout: _,
3461 transmission_count,
3462 solicit_max_rt: _,
3463 } = &self;
3464 if *transmission_count > REQUEST_MAX_RC {
3465 self.request_from_alternate_server_or_restart_server_discovery(
3466 options_to_request,
3467 rng,
3468 now,
3469 )
3470 } else {
3471 self.send_and_schedule_retransmission(
3472 request_transaction_id,
3473 options_to_request,
3474 rng,
3475 now,
3476 std::iter::empty(),
3477 )
3478 }
3479 }
3480
3481 fn reply_message_received<R: Rng, B: SplitByteSlice>(
3482 self,
3483 options_to_request: &[v6::OptionCode],
3484 rng: &mut R,
3485 msg: v6::Message<'_, B>,
3486 now: I,
3487 ) -> Transition<I> {
3488 let Self {
3489 client_id,
3490 non_temporary_addresses: mut current_non_temporary_addresses,
3491 delegated_prefixes: mut current_delegated_prefixes,
3492 server_id,
3493 collected_advertise,
3494 first_request_time,
3495 retrans_timeout,
3496 transmission_count,
3497 mut solicit_max_rt,
3498 } = self;
3499 let ProcessedReplyWithLeases {
3500 server_id: got_server_id,
3501 non_temporary_addresses,
3502 delegated_prefixes,
3503 dns_servers,
3504 actions,
3505 next_state,
3506 } = match process_reply_with_leases(
3507 &client_id,
3508 &server_id,
3509 ¤t_non_temporary_addresses,
3510 ¤t_delegated_prefixes,
3511 &mut solicit_max_rt,
3512 &msg,
3513 RequestLeasesMessageType::Request,
3514 now,
3515 ) {
3516 Ok(processed) => processed,
3517 Err(e) => {
3518 match e {
3519 ReplyWithLeasesError::ErrorStatusCode(ErrorStatusCode(error_code, message)) => {
3520 match error_code {
3521 v6::ErrorStatusCode::NotOnLink => {
3522 fn get_updates_and_reset_to_empty_request<V: IaValue, I>(
3535 current: &mut HashMap<v6::IAID, IaEntry<V, I>>,
3536 ) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>>
3537 {
3538 let mut updates = HashMap::new();
3539 current.iter_mut().for_each(|(iaid, entry)| {
3540 match entry {
3542 IaEntry::Assigned(values) => {
3543 assert_matches!(
3544 updates.insert(
3545 *iaid,
3546 values
3547 .keys()
3548 .cloned()
3549 .map(|value| (
3550 value,
3551 IaValueUpdateKind::Removed
3552 ),)
3553 .collect()
3554 ),
3555 None
3556 );
3557 }
3558 IaEntry::ToRequest(_) => {}
3559 };
3560
3561 *entry = IaEntry::ToRequest(Default::default());
3562 });
3563
3564 updates
3565 }
3566
3567 let ia_na_updates = get_updates_and_reset_to_empty_request(
3568 &mut current_non_temporary_addresses,
3569 );
3570 let ia_pd_updates = get_updates_and_reset_to_empty_request(
3571 &mut current_delegated_prefixes,
3572 );
3573
3574 let initial_actions = (!ia_na_updates.is_empty())
3575 .then_some(Action::IaNaUpdates(ia_na_updates))
3576 .into_iter()
3577 .chain(
3578 (!ia_pd_updates.is_empty())
3579 .then_some(Action::IaPdUpdates(ia_pd_updates)),
3580 );
3581
3582 warn!(
3583 "Reply to Request: retrying Request without hints due to \
3584 NotOnLink error status code with message '{}'",
3585 message,
3586 );
3587 return Requesting {
3588 client_id,
3589 non_temporary_addresses: current_non_temporary_addresses,
3590 delegated_prefixes: current_delegated_prefixes,
3591 server_id,
3592 collected_advertise,
3593 first_request_time,
3594 retrans_timeout,
3595 transmission_count,
3596 solicit_max_rt,
3597 }
3598 .send_and_reschedule_retransmission(
3599 *msg.transaction_id(),
3600 options_to_request,
3601 rng,
3602 now,
3603 initial_actions,
3604 );
3605 }
3606 v6::ErrorStatusCode::UnspecFail => {
3620 warn!(
3621 "ignoring Reply to Request: ignoring due to UnspecFail error
3622 status code with message '{}'",
3623 message,
3624 );
3625 }
3626 v6::ErrorStatusCode::UseMulticast => {
3629 warn!(
3630 "ignoring Reply to Request: ignoring due to UseMulticast \
3631 with message '{}', but Request was already using multicast",
3632 message,
3633 );
3634 }
3635 v6::ErrorStatusCode::NoAddrsAvail
3637 | v6::ErrorStatusCode::NoPrefixAvail
3638 | v6::ErrorStatusCode::NoBinding => {
3639 warn!(
3640 "ignoring Reply to Request due to unexpected top level error
3641 {:?} with message '{}'",
3642 error_code, message,
3643 );
3644 }
3645 }
3646 return Transition {
3647 state: ClientState::Requesting(Self {
3648 client_id,
3649 non_temporary_addresses: current_non_temporary_addresses,
3650 delegated_prefixes: current_delegated_prefixes,
3651 server_id,
3652 collected_advertise,
3653 first_request_time,
3654 retrans_timeout,
3655 transmission_count,
3656 solicit_max_rt,
3657 }),
3658 actions: Vec::new(),
3659 transaction_id: None,
3660 };
3661 }
3662 _ => {}
3663 }
3664 warn!("ignoring Reply to Request: {:?}", e);
3665 return Transition {
3666 state: ClientState::Requesting(Self {
3667 client_id,
3668 non_temporary_addresses: current_non_temporary_addresses,
3669 delegated_prefixes: current_delegated_prefixes,
3670 server_id,
3671 collected_advertise,
3672 first_request_time,
3673 retrans_timeout,
3674 transmission_count,
3675 solicit_max_rt,
3676 }),
3677 actions: Vec::new(),
3678 transaction_id: None,
3679 };
3680 }
3681 };
3682 assert_eq!(
3683 server_id, got_server_id,
3684 "should be invalid to accept a reply to Request with mismatched server ID"
3685 );
3686
3687 match next_state {
3688 StateAfterReplyWithLeases::StayRenewingRebinding => {
3689 unreachable!("cannot stay in Renewing/Rebinding state while in Requesting state");
3690 }
3691 StateAfterReplyWithLeases::Requesting => {
3692 unreachable!(
3693 "cannot go back to Requesting from Requesting \
3694 (only possible from Renewing/Rebinding)"
3695 );
3696 }
3697 StateAfterReplyWithLeases::RequestNextServer => {
3698 warn!("Reply to Request: trying next server");
3699 Self {
3700 client_id,
3701 non_temporary_addresses: current_non_temporary_addresses,
3702 delegated_prefixes: current_delegated_prefixes,
3703 server_id,
3704 collected_advertise,
3705 first_request_time,
3706 retrans_timeout,
3707 transmission_count,
3708 solicit_max_rt,
3709 }
3710 .request_from_alternate_server_or_restart_server_discovery(
3711 options_to_request,
3712 rng,
3713 now,
3714 )
3715 }
3716 StateAfterReplyWithLeases::Assigned => {
3717 Transition {
3732 state: ClientState::Assigned(Assigned {
3733 client_id,
3734 non_temporary_addresses,
3735 delegated_prefixes,
3736 server_id,
3737 dns_servers: dns_servers.unwrap_or(Vec::new()),
3738 solicit_max_rt,
3739 _marker: Default::default(),
3740 }),
3741 actions,
3742 transaction_id: None,
3743 }
3744 }
3745 }
3746 }
3747
3748 fn restart_server_discovery<R: Rng>(
3749 self,
3750 options_to_request: &[v6::OptionCode],
3751 rng: &mut R,
3752 now: I,
3753 ) -> Transition<I> {
3754 let Self {
3755 client_id,
3756 non_temporary_addresses,
3757 delegated_prefixes,
3758 server_id: _,
3759 collected_advertise: _,
3760 first_request_time: _,
3761 retrans_timeout: _,
3762 transmission_count: _,
3763 solicit_max_rt: _,
3764 } = self;
3765
3766 restart_server_discovery(
3767 client_id,
3768 non_temporary_addresses,
3769 delegated_prefixes,
3770 Vec::new(),
3771 options_to_request,
3772 rng,
3773 now,
3774 )
3775 }
3776
3777 fn request_from_alternate_server_or_restart_server_discovery<R: Rng>(
3789 self,
3790 options_to_request: &[v6::OptionCode],
3791 rng: &mut R,
3792 now: I,
3793 ) -> Transition<I> {
3794 let Self {
3795 client_id,
3796 server_id: _,
3797 non_temporary_addresses,
3798 delegated_prefixes,
3799 mut collected_advertise,
3800 first_request_time: _,
3801 retrans_timeout: _,
3802 transmission_count: _,
3803 solicit_max_rt,
3804 } = self;
3805
3806 if let Some(advertise) = collected_advertise.pop() {
3807 fn to_configured_values<V: IaValue, I: Instant>(
3808 entries: HashMap<v6::IAID, IaEntry<V, I>>,
3809 ) -> HashMap<v6::IAID, HashSet<V>> {
3810 entries
3811 .into_iter()
3812 .map(|(iaid, entry)| {
3813 (
3814 iaid,
3815 match entry {
3816 IaEntry::Assigned(values) => unreachable!(
3817 "should not have advertisements after an IA was assigned; \
3818 iaid={:?}, values={:?}",
3819 iaid, values,
3820 ),
3821 IaEntry::ToRequest(values) => values,
3822 },
3823 )
3824 })
3825 .collect()
3826 }
3827
3828 let configured_non_temporary_addresses = to_configured_values(non_temporary_addresses);
3829 let configured_delegated_prefixes = to_configured_values(delegated_prefixes);
3830
3831 let AdvertiseMessage {
3834 server_id,
3835 non_temporary_addresses: advertised_non_temporary_addresses,
3836 delegated_prefixes: advertised_delegated_prefixes,
3837 dns_servers: _,
3838 preference: _,
3839 receive_time: _,
3840 preferred_non_temporary_addresses_count: _,
3841 preferred_delegated_prefixes_count: _,
3842 } = advertise;
3843 Requesting::start(
3844 client_id,
3845 server_id,
3846 advertise_to_ia_entries(
3847 advertised_non_temporary_addresses,
3848 configured_non_temporary_addresses,
3849 ),
3850 advertise_to_ia_entries(
3851 advertised_delegated_prefixes,
3852 configured_delegated_prefixes,
3853 ),
3854 options_to_request,
3855 collected_advertise,
3856 solicit_max_rt,
3857 rng,
3858 now,
3859 )
3860 } else {
3861 restart_server_discovery(
3862 client_id,
3863 non_temporary_addresses,
3864 delegated_prefixes,
3865 Vec::new(), options_to_request,
3867 rng,
3868 now,
3869 )
3870 }
3871 }
3872}
3873
3874#[derive(Copy, Clone, Debug, PartialEq)]
3875struct LifetimesInfo<I> {
3876 lifetimes: Lifetimes,
3877 updated_at: I,
3878}
3879
3880#[derive(Debug, PartialEq, Clone)]
3881enum IaEntry<V: IaValue, I> {
3882 Assigned(HashMap<V, LifetimesInfo<I>>),
3884 ToRequest(HashSet<V>),
3887}
3888
3889impl<V: IaValue, I> IaEntry<V, I> {
3890 fn value(&self) -> impl Iterator<Item = V> + '_ {
3891 match self {
3892 Self::Assigned(ias) => either::Either::Left(ias.keys().copied()),
3893 Self::ToRequest(values) => either::Either::Right(values.iter().copied()),
3894 }
3895 }
3896
3897 fn to_request(&self, without_hints: bool) -> Self {
3898 Self::ToRequest(if without_hints { Default::default() } else { self.value().collect() })
3899 }
3900}
3901
3902type AddressEntry<I> = IaEntry<Ipv6Addr, I>;
3903type PrefixEntry<I> = IaEntry<Subnet<Ipv6Addr>, I>;
3904
3905#[derive(Debug)]
3907struct Assigned<I> {
3908 client_id: ClientDuid,
3913 non_temporary_addresses: HashMap<v6::IAID, AddressEntry<I>>,
3915 delegated_prefixes: HashMap<v6::IAID, PrefixEntry<I>>,
3917 server_id: Vec<u8>,
3922 dns_servers: Vec<Ipv6Addr>,
3924 solicit_max_rt: Duration,
3927 _marker: PhantomData<I>,
3928}
3929
3930fn restart_server_discovery<R: Rng, I: Instant>(
3931 client_id: ClientDuid,
3932 non_temporary_addresses: HashMap<v6::IAID, AddressEntry<I>>,
3933 delegated_prefixes: HashMap<v6::IAID, PrefixEntry<I>>,
3934 dns_servers: Vec<Ipv6Addr>,
3935 options_to_request: &[v6::OptionCode],
3936 rng: &mut R,
3937 now: I,
3938) -> Transition<I> {
3939 #[derive(Derivative)]
3940 #[derivative(Default(bound = ""))]
3941 struct ClearValuesResult<V: IaValue> {
3942 updates: HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>>,
3943 entries: HashMap<v6::IAID, HashSet<V>>,
3944 }
3945
3946 fn clear_values<V: IaValue, I: Instant>(
3947 values: HashMap<v6::IAID, IaEntry<V, I>>,
3948 ) -> ClearValuesResult<V> {
3949 values.into_iter().fold(
3950 ClearValuesResult::default(),
3951 |ClearValuesResult { mut updates, mut entries }, (iaid, entry)| {
3952 match entry {
3953 IaEntry::Assigned(values) => {
3954 assert_matches!(
3955 updates.insert(
3956 iaid,
3957 values
3958 .keys()
3959 .copied()
3960 .map(|value| (value, IaValueUpdateKind::Removed))
3961 .collect()
3962 ),
3963 None
3964 );
3965
3966 assert_matches!(entries.insert(iaid, values.into_keys().collect()), None);
3967 }
3968 IaEntry::ToRequest(values) => {
3969 assert_matches!(entries.insert(iaid, values), None);
3970 }
3971 }
3972
3973 ClearValuesResult { updates, entries }
3974 },
3975 )
3976 }
3977
3978 let ClearValuesResult {
3979 updates: non_temporary_address_updates,
3980 entries: non_temporary_address_entries,
3981 } = clear_values(non_temporary_addresses);
3982
3983 let ClearValuesResult { updates: delegated_prefix_updates, entries: delegated_prefix_entries } =
3984 clear_values(delegated_prefixes);
3985
3986 ServerDiscovery::start(
3987 client_id,
3988 non_temporary_address_entries,
3989 delegated_prefix_entries,
3990 &options_to_request,
3991 MAX_SOLICIT_TIMEOUT,
3992 rng,
3993 now,
3994 [
3995 Action::CancelTimer(ClientTimerType::Retransmission),
3996 Action::CancelTimer(ClientTimerType::Refresh),
3997 Action::CancelTimer(ClientTimerType::Renew),
3998 Action::CancelTimer(ClientTimerType::Rebind),
3999 Action::CancelTimer(ClientTimerType::RestartServerDiscovery),
4000 ]
4001 .into_iter()
4002 .chain((!dns_servers.is_empty()).then(|| Action::UpdateDnsServers(Vec::new())))
4003 .chain(
4004 (!non_temporary_address_updates.is_empty())
4005 .then_some(Action::IaNaUpdates(non_temporary_address_updates)),
4006 )
4007 .chain(
4008 (!delegated_prefix_updates.is_empty())
4009 .then_some(Action::IaPdUpdates(delegated_prefix_updates)),
4010 ),
4011 )
4012}
4013
4014impl<I: Instant> Assigned<I> {
4015 fn renew_timer_expired<R: Rng>(
4019 self,
4020 options_to_request: &[v6::OptionCode],
4021 rng: &mut R,
4022 now: I,
4023 ) -> Transition<I> {
4024 let Self {
4025 client_id,
4026 non_temporary_addresses,
4027 delegated_prefixes,
4028 server_id,
4029 dns_servers,
4030 solicit_max_rt,
4031 _marker,
4032 } = self;
4033 Renewing::start(
4038 client_id,
4039 non_temporary_addresses,
4040 delegated_prefixes,
4041 server_id,
4042 options_to_request,
4043 dns_servers,
4044 solicit_max_rt,
4045 rng,
4046 now,
4047 )
4048 }
4049
4050 fn rebind_timer_expired<R: Rng>(
4054 self,
4055 options_to_request: &[v6::OptionCode],
4056 rng: &mut R,
4057 now: I,
4058 ) -> Transition<I> {
4059 let Self {
4060 client_id,
4061 non_temporary_addresses,
4062 delegated_prefixes,
4063 server_id,
4064 dns_servers,
4065 solicit_max_rt,
4066 _marker,
4067 } = self;
4068 Rebinding::start(
4075 client_id,
4076 non_temporary_addresses,
4077 delegated_prefixes,
4078 server_id,
4079 options_to_request,
4080 dns_servers,
4081 solicit_max_rt,
4082 rng,
4083 now,
4084 )
4085 }
4086
4087 fn restart_server_discovery<R: Rng>(
4088 self,
4089 options_to_request: &[v6::OptionCode],
4090 rng: &mut R,
4091 now: I,
4092 ) -> Transition<I> {
4093 let Self {
4094 client_id,
4095 non_temporary_addresses,
4096 delegated_prefixes,
4097 server_id: _,
4098 dns_servers,
4099 solicit_max_rt: _,
4100 _marker,
4101 } = self;
4102
4103 restart_server_discovery(
4104 client_id,
4105 non_temporary_addresses,
4106 delegated_prefixes,
4107 dns_servers,
4108 options_to_request,
4109 rng,
4110 now,
4111 )
4112 }
4113}
4114
4115type Renewing<I> = RenewingOrRebinding<I, false >;
4116type Rebinding<I> = RenewingOrRebinding<I, true >;
4117
4118impl<I: Instant> Renewing<I> {
4119 fn rebind_timer_expired<R: Rng>(
4123 self,
4124 options_to_request: &[v6::OptionCode],
4125 rng: &mut R,
4126 now: I,
4127 ) -> Transition<I> {
4128 let Self(RenewingOrRebindingInner {
4129 client_id,
4130 non_temporary_addresses,
4131 delegated_prefixes,
4132 server_id,
4133 dns_servers,
4134 start_time: _,
4135 retrans_timeout: _,
4136 solicit_max_rt,
4137 }) = self;
4138
4139 Rebinding::start(
4146 client_id,
4147 non_temporary_addresses,
4148 delegated_prefixes,
4149 server_id,
4150 options_to_request,
4151 dns_servers,
4152 solicit_max_rt,
4153 rng,
4154 now,
4155 )
4156 }
4157}
4158
4159#[derive(Debug)]
4160#[cfg_attr(test, derive(Clone))]
4161struct RenewingOrRebindingInner<I> {
4162 client_id: ClientDuid,
4165 non_temporary_addresses: HashMap<v6::IAID, AddressEntry<I>>,
4167 delegated_prefixes: HashMap<v6::IAID, PrefixEntry<I>>,
4169 server_id: Vec<u8>,
4172 dns_servers: Vec<Ipv6Addr>,
4174 start_time: I,
4179 retrans_timeout: Duration,
4181 solicit_max_rt: Duration,
4184}
4185
4186impl<I, const IS_REBINDING: bool> From<RenewingOrRebindingInner<I>>
4187 for RenewingOrRebinding<I, IS_REBINDING>
4188{
4189 fn from(inner: RenewingOrRebindingInner<I>) -> Self {
4190 Self(inner)
4191 }
4192}
4193
4194#[derive(Debug)]
4197struct RenewingOrRebinding<I, const IS_REBINDING: bool>(RenewingOrRebindingInner<I>);
4198
4199impl<I: Instant, const IS_REBINDING: bool> RenewingOrRebinding<I, IS_REBINDING> {
4200 fn start<R: Rng>(
4206 client_id: ClientDuid,
4207 non_temporary_addresses: HashMap<v6::IAID, AddressEntry<I>>,
4208 delegated_prefixes: HashMap<v6::IAID, PrefixEntry<I>>,
4209 server_id: Vec<u8>,
4210 options_to_request: &[v6::OptionCode],
4211 dns_servers: Vec<Ipv6Addr>,
4212 solicit_max_rt: Duration,
4213 rng: &mut R,
4214 now: I,
4215 ) -> Transition<I> {
4216 Self(RenewingOrRebindingInner {
4217 client_id,
4218 non_temporary_addresses,
4219 delegated_prefixes,
4220 server_id,
4221 dns_servers,
4222 start_time: now,
4223 retrans_timeout: Duration::default(),
4224 solicit_max_rt,
4225 })
4226 .send_and_schedule_retransmission(
4227 transaction_id(rng),
4228 options_to_request,
4229 rng,
4230 now,
4231 )
4232 }
4233
4234 fn retransmission_timeout<R: Rng>(prev_retrans_timeout: Duration, rng: &mut R) -> Duration {
4240 let (initial, max) = if IS_REBINDING {
4241 (INITIAL_REBIND_TIMEOUT, MAX_REBIND_TIMEOUT)
4242 } else {
4243 (INITIAL_RENEW_TIMEOUT, MAX_RENEW_TIMEOUT)
4244 };
4245
4246 retransmission_timeout(prev_retrans_timeout, initial, max, rng)
4247 }
4248
4249 fn send_and_schedule_retransmission<R: Rng>(
4252 self,
4253 transaction_id: [u8; 3],
4254 options_to_request: &[v6::OptionCode],
4255 rng: &mut R,
4256 now: I,
4257 ) -> Transition<I> {
4258 let Self(RenewingOrRebindingInner {
4259 client_id,
4260 non_temporary_addresses,
4261 delegated_prefixes,
4262 server_id,
4263 dns_servers,
4264 start_time,
4265 retrans_timeout: prev_retrans_timeout,
4266 solicit_max_rt,
4267 }) = self;
4268 let elapsed_time = elapsed_time_in_centisecs(start_time, now);
4269
4270 let (message_type, maybe_server_id) = if IS_REBINDING {
4322 (v6::MessageType::Rebind, None)
4323 } else {
4324 (v6::MessageType::Renew, Some(server_id.as_slice()))
4325 };
4326
4327 let buf = StatefulMessageBuilder {
4328 transaction_id,
4329 message_type,
4330 client_id: &client_id,
4331 server_id: maybe_server_id,
4332 elapsed_time_in_centisecs: elapsed_time,
4333 options_to_request,
4334 ia_nas: non_temporary_addresses.iter().map(|(iaid, ia)| (*iaid, ia.value())),
4335 ia_pds: delegated_prefixes.iter().map(|(iaid, ia)| (*iaid, ia.value())),
4336 _marker: Default::default(),
4337 }
4338 .build();
4339
4340 let retrans_timeout = Self::retransmission_timeout(prev_retrans_timeout, rng);
4341
4342 Transition {
4343 state: {
4344 let inner = RenewingOrRebindingInner {
4345 client_id,
4346 non_temporary_addresses,
4347 delegated_prefixes,
4348 server_id,
4349 dns_servers,
4350 start_time,
4351 retrans_timeout,
4352 solicit_max_rt,
4353 };
4354
4355 if IS_REBINDING {
4356 ClientState::Rebinding(inner.into())
4357 } else {
4358 ClientState::Renewing(inner.into())
4359 }
4360 },
4361 actions: vec![
4362 Action::SendMessage(buf),
4363 Action::ScheduleTimer(ClientTimerType::Retransmission, now.add(retrans_timeout)),
4364 ],
4365 transaction_id: Some(transaction_id),
4366 }
4367 }
4368
4369 fn retransmission_timer_expired<R: Rng>(
4371 self,
4372 transaction_id: [u8; 3],
4373 options_to_request: &[v6::OptionCode],
4374 rng: &mut R,
4375 now: I,
4376 ) -> Transition<I> {
4377 self.send_and_schedule_retransmission(transaction_id, options_to_request, rng, now)
4378 }
4379
4380 fn reply_message_received<R: Rng, B: SplitByteSlice>(
4381 self,
4382 options_to_request: &[v6::OptionCode],
4383 rng: &mut R,
4384 msg: v6::Message<'_, B>,
4385 now: I,
4386 ) -> Transition<I> {
4387 let Self(RenewingOrRebindingInner {
4388 client_id,
4389 non_temporary_addresses: current_non_temporary_addresses,
4390 delegated_prefixes: current_delegated_prefixes,
4391 server_id,
4392 dns_servers: current_dns_servers,
4393 start_time,
4394 retrans_timeout,
4395 mut solicit_max_rt,
4396 }) = self;
4397 let ProcessedReplyWithLeases {
4398 server_id: got_server_id,
4399 non_temporary_addresses,
4400 delegated_prefixes,
4401 dns_servers,
4402 actions,
4403 next_state,
4404 } = match process_reply_with_leases(
4405 &client_id,
4406 &server_id,
4407 ¤t_non_temporary_addresses,
4408 ¤t_delegated_prefixes,
4409 &mut solicit_max_rt,
4410 &msg,
4411 if IS_REBINDING {
4412 RequestLeasesMessageType::Rebind
4413 } else {
4414 RequestLeasesMessageType::Renew
4415 },
4416 now,
4417 ) {
4418 Ok(processed) => processed,
4419 Err(e) => {
4420 match e {
4421 ReplyWithLeasesError::ErrorStatusCode(ErrorStatusCode(
4440 v6::ErrorStatusCode::UnspecFail,
4441 message,
4442 )) => {
4443 warn!(
4444 "ignoring Reply to Renew with status code UnspecFail \
4445 and message '{}'",
4446 message
4447 );
4448 }
4449 ReplyWithLeasesError::ErrorStatusCode(ErrorStatusCode(
4450 v6::ErrorStatusCode::UseMulticast,
4451 message,
4452 )) => {
4453 warn!(
4455 "ignoring Reply to Renew with status code UseMulticast \
4456 and message '{}' as Reply was already sent as multicast",
4457 message
4458 );
4459 }
4460 ReplyWithLeasesError::ErrorStatusCode(ErrorStatusCode(
4461 error_code @ (v6::ErrorStatusCode::NoAddrsAvail
4462 | v6::ErrorStatusCode::NoBinding
4463 | v6::ErrorStatusCode::NotOnLink
4464 | v6::ErrorStatusCode::NoPrefixAvail),
4465 message,
4466 )) => {
4467 warn!(
4468 "ignoring Reply to Renew with unexpected status code {:?} \
4469 and message '{}'",
4470 error_code, message
4471 );
4472 }
4473 e @ (ReplyWithLeasesError::OptionsError(_)
4474 | ReplyWithLeasesError::MismatchedServerId { got: _, want: _ }) => {
4475 warn!("ignoring Reply to Renew: {}", e);
4476 }
4477 }
4478
4479 return Transition {
4480 state: {
4481 let inner = RenewingOrRebindingInner {
4482 client_id,
4483 non_temporary_addresses: current_non_temporary_addresses,
4484 delegated_prefixes: current_delegated_prefixes,
4485 server_id,
4486 dns_servers: current_dns_servers,
4487 start_time,
4488 retrans_timeout,
4489 solicit_max_rt,
4490 };
4491
4492 if IS_REBINDING {
4493 ClientState::Rebinding(inner.into())
4494 } else {
4495 ClientState::Renewing(inner.into())
4496 }
4497 },
4498 actions: Vec::new(),
4499 transaction_id: None,
4500 };
4501 }
4502 };
4503 if !IS_REBINDING {
4504 assert_eq!(
4505 server_id, got_server_id,
4506 "should be invalid to accept a reply to Renew with mismatched server ID"
4507 );
4508 } else if server_id != got_server_id {
4509 warn!(
4510 "using Reply to Rebind message from different server; current={:?}, new={:?}",
4511 server_id, got_server_id
4512 );
4513 }
4514 let server_id = got_server_id;
4515
4516 match next_state {
4517 StateAfterReplyWithLeases::RequestNextServer => restart_server_discovery(
4524 client_id,
4525 current_non_temporary_addresses,
4526 current_delegated_prefixes,
4527 current_dns_servers,
4528 &options_to_request,
4529 rng,
4530 now,
4531 ),
4532 StateAfterReplyWithLeases::StayRenewingRebinding => Transition {
4533 state: {
4534 let inner = RenewingOrRebindingInner {
4535 client_id,
4536 non_temporary_addresses,
4537 delegated_prefixes,
4538 server_id,
4539 dns_servers: dns_servers.unwrap_or_else(|| Vec::new()),
4540 start_time,
4541 retrans_timeout,
4542 solicit_max_rt,
4543 };
4544
4545 if IS_REBINDING {
4546 ClientState::Rebinding(inner.into())
4547 } else {
4548 ClientState::Renewing(inner.into())
4549 }
4550 },
4551 actions: Vec::new(),
4552 transaction_id: None,
4553 },
4554 StateAfterReplyWithLeases::Assigned => {
4555 Transition {
4558 state: ClientState::Assigned(Assigned {
4559 client_id,
4560 non_temporary_addresses,
4561 delegated_prefixes,
4562 server_id,
4563 dns_servers: dns_servers.unwrap_or_else(|| Vec::new()),
4564 solicit_max_rt,
4565 _marker: Default::default(),
4566 }),
4567 actions,
4568 transaction_id: None,
4569 }
4570 }
4571 StateAfterReplyWithLeases::Requesting => Requesting::start(
4572 client_id,
4573 server_id,
4574 non_temporary_addresses,
4575 delegated_prefixes,
4576 &options_to_request,
4577 Default::default(), solicit_max_rt,
4579 rng,
4580 now,
4581 ),
4582 }
4583 }
4584
4585 fn restart_server_discovery<R: Rng>(
4586 self,
4587 options_to_request: &[v6::OptionCode],
4588 rng: &mut R,
4589 now: I,
4590 ) -> Transition<I> {
4591 let Self(RenewingOrRebindingInner {
4592 client_id,
4593 non_temporary_addresses,
4594 delegated_prefixes,
4595 server_id: _,
4596 dns_servers,
4597 start_time: _,
4598 retrans_timeout: _,
4599 solicit_max_rt: _,
4600 }) = self;
4601
4602 restart_server_discovery(
4603 client_id,
4604 non_temporary_addresses,
4605 delegated_prefixes,
4606 dns_servers,
4607 options_to_request,
4608 rng,
4609 now,
4610 )
4611 }
4612}
4613
4614#[derive(Debug)]
4618enum ClientState<I> {
4619 InformationRequesting(InformationRequesting<I>),
4622 InformationReceived(InformationReceived<I>),
4625 ServerDiscovery(ServerDiscovery<I>),
4629 Requesting(Requesting<I>),
4632 Assigned(Assigned<I>),
4634 Renewing(Renewing<I>),
4636 Rebinding(Rebinding<I>),
4638}
4639
4640struct Transition<I> {
4644 state: ClientState<I>,
4645 actions: Actions<I>,
4646 transaction_id: Option<[u8; 3]>,
4647}
4648
4649impl<I: Instant> ClientState<I> {
4650 fn advertise_message_received<R: Rng, B: SplitByteSlice>(
4652 self,
4653 options_to_request: &[v6::OptionCode],
4654 rng: &mut R,
4655 msg: v6::Message<'_, B>,
4656 now: I,
4657 ) -> Transition<I> {
4658 match self {
4659 ClientState::ServerDiscovery(s) => {
4660 s.advertise_message_received(options_to_request, rng, msg, now)
4661 }
4662 ClientState::InformationRequesting(_)
4663 | ClientState::InformationReceived(_)
4664 | ClientState::Requesting(_)
4665 | ClientState::Assigned(_)
4666 | ClientState::Renewing(_)
4667 | ClientState::Rebinding(_) => {
4668 Transition { state: self, actions: vec![], transaction_id: None }
4669 }
4670 }
4671 }
4672
4673 fn reply_message_received<R: Rng, B: SplitByteSlice>(
4675 self,
4676 options_to_request: &[v6::OptionCode],
4677 rng: &mut R,
4678 msg: v6::Message<'_, B>,
4679 now: I,
4680 ) -> Transition<I> {
4681 match self {
4682 ClientState::InformationRequesting(s) => s.reply_message_received(msg, now),
4683 ClientState::Requesting(s) => {
4684 s.reply_message_received(options_to_request, rng, msg, now)
4685 }
4686 ClientState::Renewing(s) => s.reply_message_received(options_to_request, rng, msg, now),
4687 ClientState::Rebinding(s) => {
4688 s.reply_message_received(options_to_request, rng, msg, now)
4689 }
4690 ClientState::InformationReceived(_)
4691 | ClientState::ServerDiscovery(_)
4692 | ClientState::Assigned(_) => {
4693 Transition { state: self, actions: vec![], transaction_id: None }
4694 }
4695 }
4696 }
4697
4698 fn retransmission_timer_expired<R: Rng>(
4700 self,
4701 transaction_id: [u8; 3],
4702 options_to_request: &[v6::OptionCode],
4703 rng: &mut R,
4704 now: I,
4705 ) -> Transition<I> {
4706 match self {
4707 ClientState::InformationRequesting(s) => {
4708 s.retransmission_timer_expired(transaction_id, options_to_request, rng, now)
4709 }
4710 ClientState::ServerDiscovery(s) => {
4711 s.retransmission_timer_expired(transaction_id, options_to_request, rng, now)
4712 }
4713 ClientState::Requesting(s) => {
4714 s.retransmission_timer_expired(transaction_id, options_to_request, rng, now)
4715 }
4716 ClientState::Renewing(s) => {
4717 s.retransmission_timer_expired(transaction_id, options_to_request, rng, now)
4718 }
4719 ClientState::Rebinding(s) => {
4720 s.retransmission_timer_expired(transaction_id, options_to_request, rng, now)
4721 }
4722 ClientState::InformationReceived(_) | ClientState::Assigned(_) => {
4723 unreachable!("received unexpected retransmission timeout in state {:?}.", self);
4724 }
4725 }
4726 }
4727
4728 fn refresh_timer_expired<R: Rng>(
4730 self,
4731 options_to_request: &[v6::OptionCode],
4732 rng: &mut R,
4733 now: I,
4734 ) -> Transition<I> {
4735 match self {
4736 ClientState::InformationReceived(s) => {
4737 s.refresh_timer_expired(options_to_request, rng, now)
4738 }
4739 ClientState::InformationRequesting(_)
4740 | ClientState::ServerDiscovery(_)
4741 | ClientState::Requesting(_)
4742 | ClientState::Assigned(_)
4743 | ClientState::Renewing(_)
4744 | ClientState::Rebinding(_) => {
4745 unreachable!("received unexpected refresh timeout in state {:?}.", self);
4746 }
4747 }
4748 }
4749
4750 fn renew_timer_expired<R: Rng>(
4752 self,
4753 options_to_request: &[v6::OptionCode],
4754 rng: &mut R,
4755 now: I,
4756 ) -> Transition<I> {
4757 match self {
4758 ClientState::Assigned(s) => s.renew_timer_expired(options_to_request, rng, now),
4759 ClientState::InformationRequesting(_)
4760 | ClientState::InformationReceived(_)
4761 | ClientState::ServerDiscovery(_)
4762 | ClientState::Requesting(_)
4763 | ClientState::Renewing(_)
4764 | ClientState::Rebinding(_) => {
4765 unreachable!("received unexpected renew timeout in state {:?}.", self);
4766 }
4767 }
4768 }
4769
4770 fn rebind_timer_expired<R: Rng>(
4772 self,
4773 options_to_request: &[v6::OptionCode],
4774 rng: &mut R,
4775 now: I,
4776 ) -> Transition<I> {
4777 match self {
4778 ClientState::Assigned(s) => s.rebind_timer_expired(options_to_request, rng, now),
4779 ClientState::Renewing(s) => s.rebind_timer_expired(options_to_request, rng, now),
4780 ClientState::InformationRequesting(_)
4781 | ClientState::InformationReceived(_)
4782 | ClientState::ServerDiscovery(_)
4783 | ClientState::Requesting(_)
4784 | ClientState::Rebinding(_) => {
4785 unreachable!("received unexpected rebind timeout in state {:?}.", self);
4786 }
4787 }
4788 }
4789
4790 fn restart_server_discovery<R: Rng>(
4791 self,
4792 options_to_request: &[v6::OptionCode],
4793 rng: &mut R,
4794 now: I,
4795 ) -> Transition<I> {
4796 match self {
4797 ClientState::Requesting(s) => s.restart_server_discovery(options_to_request, rng, now),
4798 ClientState::Assigned(s) => s.restart_server_discovery(options_to_request, rng, now),
4799 ClientState::Renewing(s) => s.restart_server_discovery(options_to_request, rng, now),
4800 ClientState::Rebinding(s) => s.restart_server_discovery(options_to_request, rng, now),
4801 ClientState::InformationRequesting(_)
4802 | ClientState::InformationReceived(_)
4803 | ClientState::ServerDiscovery(_) => {
4804 unreachable!("received unexpected rebind timeout in state {:?}.", self);
4805 }
4806 }
4807 }
4808
4809 fn get_dns_servers(&self) -> Vec<Ipv6Addr> {
4811 match self {
4812 ClientState::InformationReceived(InformationReceived { dns_servers, _marker }) => {
4813 dns_servers.clone()
4814 }
4815 ClientState::Assigned(Assigned {
4816 client_id: _,
4817 non_temporary_addresses: _,
4818 delegated_prefixes: _,
4819 server_id: _,
4820 dns_servers,
4821 solicit_max_rt: _,
4822 _marker: _,
4823 })
4824 | ClientState::Renewing(RenewingOrRebinding(RenewingOrRebindingInner {
4825 client_id: _,
4826 non_temporary_addresses: _,
4827 delegated_prefixes: _,
4828 server_id: _,
4829 dns_servers,
4830 start_time: _,
4831 retrans_timeout: _,
4832 solicit_max_rt: _,
4833 }))
4834 | ClientState::Rebinding(RenewingOrRebinding(RenewingOrRebindingInner {
4835 client_id: _,
4836 non_temporary_addresses: _,
4837 delegated_prefixes: _,
4838 server_id: _,
4839 dns_servers,
4840 start_time: _,
4841 retrans_timeout: _,
4842 solicit_max_rt: _,
4843 })) => dns_servers.clone(),
4844 ClientState::InformationRequesting(InformationRequesting {
4845 retrans_timeout: _,
4846 _marker: _,
4847 })
4848 | ClientState::ServerDiscovery(ServerDiscovery {
4849 client_id: _,
4850 configured_non_temporary_addresses: _,
4851 configured_delegated_prefixes: _,
4852 first_solicit_time: _,
4853 retrans_timeout: _,
4854 solicit_max_rt: _,
4855 collected_advertise: _,
4856 collected_sol_max_rt: _,
4857 })
4858 | ClientState::Requesting(Requesting {
4859 client_id: _,
4860 non_temporary_addresses: _,
4861 delegated_prefixes: _,
4862 server_id: _,
4863 collected_advertise: _,
4864 first_request_time: _,
4865 retrans_timeout: _,
4866 transmission_count: _,
4867 solicit_max_rt: _,
4868 }) => Vec::new(),
4869 }
4870 }
4871}
4872
4873#[derive(Debug)]
4881pub struct ClientStateMachine<I, R: Rng> {
4882 transaction_id: [u8; 3],
4886 options_to_request: Vec<v6::OptionCode>,
4889 state: Option<ClientState<I>>,
4894 rng: R,
4896}
4897
4898impl<I: Instant, R: Rng> ClientStateMachine<I, R> {
4899 pub fn start_stateless(
4905 options_to_request: Vec<v6::OptionCode>,
4906 mut rng: R,
4907 now: I,
4908 ) -> (Self, Actions<I>) {
4909 let Transition { state, actions, transaction_id } =
4910 InformationRequesting::start(&options_to_request, &mut rng, now);
4911 (
4912 Self {
4913 state: Some(state),
4914 transaction_id: transaction_id.expect("transaction ID should be generated"),
4915 options_to_request,
4916 rng,
4917 },
4918 actions,
4919 )
4920 }
4921
4922 pub fn start_stateful(
4933 client_id: ClientDuid,
4934 configured_non_temporary_addresses: HashMap<v6::IAID, HashSet<Ipv6Addr>>,
4935 configured_delegated_prefixes: HashMap<v6::IAID, HashSet<Subnet<Ipv6Addr>>>,
4936 options_to_request: Vec<v6::OptionCode>,
4937 mut rng: R,
4938 now: I,
4939 ) -> (Self, Actions<I>) {
4940 let Transition { state, actions, transaction_id } = ServerDiscovery::start(
4941 client_id,
4942 configured_non_temporary_addresses,
4943 configured_delegated_prefixes,
4944 &options_to_request,
4945 MAX_SOLICIT_TIMEOUT,
4946 &mut rng,
4947 now,
4948 std::iter::empty(),
4949 );
4950 (
4951 Self {
4952 state: Some(state),
4953 transaction_id: transaction_id.expect("transaction ID should be generated"),
4954 options_to_request,
4955 rng,
4956 },
4957 actions,
4958 )
4959 }
4960
4961 pub fn get_dns_servers(&self) -> Vec<Ipv6Addr> {
4962 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } = self;
4963 state.as_ref().expect("state should not be empty").get_dns_servers()
4964 }
4965
4966 pub fn handle_timeout(&mut self, timeout_type: ClientTimerType, now: I) -> Actions<I> {
4972 let ClientStateMachine { transaction_id, options_to_request, state, rng } = self;
4973 let old_state = state.take().expect("state should not be empty");
4974 debug!("handling timeout {:?}", timeout_type);
4975 let Transition { state: new_state, actions, transaction_id: new_transaction_id } =
4976 match timeout_type {
4977 ClientTimerType::Retransmission => old_state.retransmission_timer_expired(
4978 *transaction_id,
4979 &options_to_request,
4980 rng,
4981 now,
4982 ),
4983 ClientTimerType::Refresh => {
4984 old_state.refresh_timer_expired(&options_to_request, rng, now)
4985 }
4986 ClientTimerType::Renew => {
4987 old_state.renew_timer_expired(&options_to_request, rng, now)
4988 }
4989 ClientTimerType::Rebind => {
4990 old_state.rebind_timer_expired(&options_to_request, rng, now)
4991 }
4992 ClientTimerType::RestartServerDiscovery => {
4993 old_state.restart_server_discovery(&options_to_request, rng, now)
4994 }
4995 };
4996 *state = Some(new_state);
4997 *transaction_id = new_transaction_id.unwrap_or(*transaction_id);
4998 actions
4999 }
5000
5001 pub fn handle_message_receive<B: SplitByteSlice>(
5007 &mut self,
5008 msg: v6::Message<'_, B>,
5009 now: I,
5010 ) -> Actions<I> {
5011 let ClientStateMachine { transaction_id, options_to_request, state, rng } = self;
5012 if msg.transaction_id() != transaction_id {
5013 Vec::new() } else {
5015 debug!("handling received message of type: {:?}", msg.msg_type());
5016 match msg.msg_type() {
5017 v6::MessageType::Reply => {
5018 let Transition {
5019 state: new_state,
5020 actions,
5021 transaction_id: new_transaction_id,
5022 } = state.take().expect("state should not be empty").reply_message_received(
5023 &options_to_request,
5024 rng,
5025 msg,
5026 now,
5027 );
5028 *state = Some(new_state);
5029 *transaction_id = new_transaction_id.unwrap_or(*transaction_id);
5030 actions
5031 }
5032 v6::MessageType::Advertise => {
5033 let Transition {
5034 state: new_state,
5035 actions,
5036 transaction_id: new_transaction_id,
5037 } = state
5038 .take()
5039 .expect("state should not be empty")
5040 .advertise_message_received(&options_to_request, rng, msg, now);
5041 *state = Some(new_state);
5042 *transaction_id = new_transaction_id.unwrap_or(*transaction_id);
5043 actions
5044 }
5045 v6::MessageType::Reconfigure => {
5046 Vec::new()
5049 }
5050 v6::MessageType::Solicit
5051 | v6::MessageType::Request
5052 | v6::MessageType::Confirm
5053 | v6::MessageType::Renew
5054 | v6::MessageType::Rebind
5055 | v6::MessageType::Release
5056 | v6::MessageType::Decline
5057 | v6::MessageType::InformationRequest
5058 | v6::MessageType::RelayForw
5059 | v6::MessageType::RelayRepl => {
5060 Vec::new()
5062 }
5063 }
5064 }
5065 }
5066}
5067
5068#[cfg(test)]
5069pub(crate) mod testconsts {
5070 use super::*;
5071 use net_declare::{net_ip_v6, net_subnet_v6};
5072
5073 pub(super) trait IaValueTestExt: IaValue {
5074 const CONFIGURED: [Self; 3];
5075 }
5076
5077 impl IaValueTestExt for Ipv6Addr {
5078 const CONFIGURED: [Self; 3] = CONFIGURED_NON_TEMPORARY_ADDRESSES;
5079 }
5080
5081 impl IaValueTestExt for Subnet<Ipv6Addr> {
5082 const CONFIGURED: [Self; 3] = CONFIGURED_DELEGATED_PREFIXES;
5083 }
5084
5085 pub(crate) const INFINITY: u32 = u32::MAX;
5086 pub(crate) const DNS_SERVERS: [Ipv6Addr; 2] =
5087 [net_ip_v6!("ff01::0102"), net_ip_v6!("ff01::0304")];
5088 pub(crate) const CLIENT_ID: [u8; 18] =
5089 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17];
5090 pub(crate) const MISMATCHED_CLIENT_ID: [u8; 18] =
5091 [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37];
5092 pub(crate) const TEST_SERVER_ID_LEN: usize = 3;
5093 pub(crate) const SERVER_ID: [[u8; TEST_SERVER_ID_LEN]; 3] =
5094 [[100, 101, 102], [110, 111, 112], [120, 121, 122]];
5095
5096 pub(crate) const RENEW_NON_TEMPORARY_ADDRESSES: [Ipv6Addr; 3] = [
5097 net_ip_v6!("::ffff:4e45:123"),
5098 net_ip_v6!("::ffff:4e45:456"),
5099 net_ip_v6!("::ffff:4e45:789"),
5100 ];
5101 pub(crate) const REPLY_NON_TEMPORARY_ADDRESSES: [Ipv6Addr; 3] = [
5102 net_ip_v6!("::ffff:5447:123"),
5103 net_ip_v6!("::ffff:5447:456"),
5104 net_ip_v6!("::ffff:5447:789"),
5105 ];
5106 pub(crate) const CONFIGURED_NON_TEMPORARY_ADDRESSES: [Ipv6Addr; 3] = [
5107 net_ip_v6!("::ffff:c00a:123"),
5108 net_ip_v6!("::ffff:c00a:456"),
5109 net_ip_v6!("::ffff:c00a:789"),
5110 ];
5111 pub(crate) const RENEW_DELEGATED_PREFIXES: [Subnet<Ipv6Addr>; 3] =
5112 [net_subnet_v6!("1::/64"), net_subnet_v6!("2::/60"), net_subnet_v6!("3::/56")];
5113 pub(crate) const REPLY_DELEGATED_PREFIXES: [Subnet<Ipv6Addr>; 3] =
5114 [net_subnet_v6!("d::/64"), net_subnet_v6!("e::/60"), net_subnet_v6!("f::/56")];
5115 pub(crate) const CONFIGURED_DELEGATED_PREFIXES: [Subnet<Ipv6Addr>; 3] =
5116 [net_subnet_v6!("a::/64"), net_subnet_v6!("b::/60"), net_subnet_v6!("c::/56")];
5117
5118 pub(crate) const T1: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(30).unwrap();
5119 pub(crate) const T2: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(70).unwrap();
5120 pub(crate) const PREFERRED_LIFETIME: v6::NonZeroOrMaxU32 =
5121 v6::NonZeroOrMaxU32::new(40).unwrap();
5122 pub(crate) const VALID_LIFETIME: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(80).unwrap();
5123
5124 pub(crate) const RENEWED_T1: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(130).unwrap();
5125 pub(crate) const RENEWED_T2: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(170).unwrap();
5126 pub(crate) const RENEWED_PREFERRED_LIFETIME: v6::NonZeroOrMaxU32 =
5127 v6::NonZeroOrMaxU32::new(140).unwrap();
5128 pub(crate) const RENEWED_VALID_LIFETIME: v6::NonZeroOrMaxU32 =
5129 v6::NonZeroOrMaxU32::new(180).unwrap();
5130}
5131
5132#[cfg(test)]
5133pub(crate) mod testutil {
5134 use std::time::Instant;
5135
5136 use super::*;
5137 use packet::ParsablePacket;
5138 use testconsts::*;
5139
5140 pub(crate) fn to_configured_addresses(
5141 address_count: usize,
5142 preferred_addresses: impl IntoIterator<Item = HashSet<Ipv6Addr>>,
5143 ) -> HashMap<v6::IAID, HashSet<Ipv6Addr>> {
5144 let addresses = preferred_addresses
5145 .into_iter()
5146 .chain(std::iter::repeat_with(HashSet::new))
5147 .take(address_count);
5148
5149 (0..).map(v6::IAID::new).zip(addresses).collect()
5150 }
5151
5152 pub(crate) fn to_configured_prefixes(
5153 prefix_count: usize,
5154 preferred_prefixes: impl IntoIterator<Item = HashSet<Subnet<Ipv6Addr>>>,
5155 ) -> HashMap<v6::IAID, HashSet<Subnet<Ipv6Addr>>> {
5156 let prefixes = preferred_prefixes
5157 .into_iter()
5158 .chain(std::iter::repeat_with(HashSet::new))
5159 .take(prefix_count);
5160
5161 (0..).map(v6::IAID::new).zip(prefixes).collect()
5162 }
5163
5164 pub(super) fn to_default_ias_map<A: IaValue>(addresses: &[A]) -> HashMap<v6::IAID, HashSet<A>> {
5165 (0..)
5166 .map(v6::IAID::new)
5167 .zip(addresses.iter().map(|value| HashSet::from([*value])))
5168 .collect()
5169 }
5170
5171 pub(super) fn assert_server_discovery(
5172 state: &Option<ClientState<Instant>>,
5173 client_id: &[u8],
5174 configured_non_temporary_addresses: HashMap<v6::IAID, HashSet<Ipv6Addr>>,
5175 configured_delegated_prefixes: HashMap<v6::IAID, HashSet<Subnet<Ipv6Addr>>>,
5176 first_solicit_time: Instant,
5177 buf: &[u8],
5178 options_to_request: &[v6::OptionCode],
5179 ) {
5180 assert_matches!(
5181 state,
5182 Some(ClientState::ServerDiscovery(ServerDiscovery {
5183 client_id: got_client_id,
5184 configured_non_temporary_addresses: got_configured_non_temporary_addresses,
5185 configured_delegated_prefixes: got_configured_delegated_prefixes,
5186 first_solicit_time: got_first_solicit_time,
5187 retrans_timeout: INITIAL_SOLICIT_TIMEOUT,
5188 solicit_max_rt: MAX_SOLICIT_TIMEOUT,
5189 collected_advertise,
5190 collected_sol_max_rt,
5191 })) => {
5192 assert_eq!(got_client_id, client_id);
5193 assert_eq!(
5194 got_configured_non_temporary_addresses,
5195 &configured_non_temporary_addresses,
5196 );
5197 assert_eq!(
5198 got_configured_delegated_prefixes,
5199 &configured_delegated_prefixes,
5200 );
5201 assert!(
5202 collected_advertise.is_empty(),
5203 "collected_advertise={:?}",
5204 collected_advertise,
5205 );
5206 assert_eq!(collected_sol_max_rt, &[]);
5207 assert_eq!(*got_first_solicit_time, first_solicit_time);
5208 }
5209 );
5210
5211 assert_outgoing_stateful_message(
5212 buf,
5213 v6::MessageType::Solicit,
5214 client_id,
5215 None,
5216 &options_to_request,
5217 &configured_non_temporary_addresses,
5218 &configured_delegated_prefixes,
5219 );
5220 }
5221
5222 pub(crate) fn start_and_assert_server_discovery<R: Rng + std::fmt::Debug>(
5230 client_id: &ClientDuid,
5231 configured_non_temporary_addresses: HashMap<v6::IAID, HashSet<Ipv6Addr>>,
5232 configured_delegated_prefixes: HashMap<v6::IAID, HashSet<Subnet<Ipv6Addr>>>,
5233 options_to_request: Vec<v6::OptionCode>,
5234 rng: R,
5235 now: Instant,
5236 ) -> ClientStateMachine<Instant, R> {
5237 let (client, actions) = ClientStateMachine::start_stateful(
5238 client_id.clone(),
5239 configured_non_temporary_addresses.clone(),
5240 configured_delegated_prefixes.clone(),
5241 options_to_request.clone(),
5242 rng,
5243 now,
5244 );
5245
5246 let ClientStateMachine {
5247 transaction_id: _,
5248 options_to_request: got_options_to_request,
5249 state,
5250 rng: _,
5251 } = &client;
5252 assert_eq!(got_options_to_request, &options_to_request);
5253
5254 let buf = assert_matches!( &actions[..],
5257 [
5258 Action::SendMessage(buf),
5259 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
5260 ] => {
5261 assert_eq!(*instant, now.add(INITIAL_SOLICIT_TIMEOUT));
5262 buf
5263 }
5264 );
5265
5266 assert_server_discovery(
5267 state,
5268 client_id,
5269 configured_non_temporary_addresses,
5270 configured_delegated_prefixes,
5271 now,
5272 buf,
5273 &options_to_request,
5274 );
5275
5276 client
5277 }
5278
5279 impl Lifetimes {
5280 pub(crate) const fn new_default() -> Self {
5281 Lifetimes::new_finite(PREFERRED_LIFETIME, VALID_LIFETIME)
5282 }
5283
5284 pub(crate) fn new(preferred_lifetime: u32, non_zero_valid_lifetime: u32) -> Self {
5285 Lifetimes {
5286 preferred_lifetime: v6::TimeValue::new(preferred_lifetime),
5287 valid_lifetime: assert_matches!(
5288 v6::TimeValue::new(non_zero_valid_lifetime),
5289 v6::TimeValue::NonZero(v) => v
5290 ),
5291 }
5292 }
5293
5294 pub(crate) const fn new_finite(
5295 preferred_lifetime: v6::NonZeroOrMaxU32,
5296 valid_lifetime: v6::NonZeroOrMaxU32,
5297 ) -> Lifetimes {
5298 Lifetimes {
5299 preferred_lifetime: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
5300 preferred_lifetime,
5301 )),
5302 valid_lifetime: v6::NonZeroTimeValue::Finite(valid_lifetime),
5303 }
5304 }
5305
5306 pub(crate) const fn new_renewed() -> Lifetimes {
5307 Lifetimes {
5308 preferred_lifetime: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
5309 RENEWED_PREFERRED_LIFETIME,
5310 )),
5311 valid_lifetime: v6::NonZeroTimeValue::Finite(RENEWED_VALID_LIFETIME),
5312 }
5313 }
5314 }
5315
5316 impl<V: IaValue> IaEntry<V, Instant> {
5317 pub(crate) fn new_assigned(
5318 value: V,
5319 preferred_lifetime: v6::NonZeroOrMaxU32,
5320 valid_lifetime: v6::NonZeroOrMaxU32,
5321 updated_at: Instant,
5322 ) -> Self {
5323 Self::Assigned(HashMap::from([(
5324 value,
5325 LifetimesInfo {
5326 lifetimes: Lifetimes {
5327 preferred_lifetime: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
5328 preferred_lifetime,
5329 )),
5330 valid_lifetime: v6::NonZeroTimeValue::Finite(valid_lifetime),
5331 },
5332 updated_at,
5333 },
5334 )]))
5335 }
5336 }
5337
5338 impl AdvertiseMessage<Instant> {
5339 pub(crate) fn new_default(
5340 server_id: [u8; TEST_SERVER_ID_LEN],
5341 non_temporary_addresses: &[Ipv6Addr],
5342 delegated_prefixes: &[Subnet<Ipv6Addr>],
5343 dns_servers: &[Ipv6Addr],
5344 configured_non_temporary_addresses: &HashMap<v6::IAID, HashSet<Ipv6Addr>>,
5345 configured_delegated_prefixes: &HashMap<v6::IAID, HashSet<Subnet<Ipv6Addr>>>,
5346 ) -> AdvertiseMessage<Instant> {
5347 let non_temporary_addresses = (0..)
5348 .map(v6::IAID::new)
5349 .zip(non_temporary_addresses.iter().map(|address| HashSet::from([*address])))
5350 .collect();
5351 let delegated_prefixes = (0..)
5352 .map(v6::IAID::new)
5353 .zip(delegated_prefixes.iter().map(|prefix| HashSet::from([*prefix])))
5354 .collect();
5355 let preferred_non_temporary_addresses_count = compute_preferred_ia_count(
5356 &non_temporary_addresses,
5357 &configured_non_temporary_addresses,
5358 );
5359 let preferred_delegated_prefixes_count =
5360 compute_preferred_ia_count(&delegated_prefixes, &configured_delegated_prefixes);
5361 AdvertiseMessage {
5362 server_id: server_id.to_vec(),
5363 non_temporary_addresses,
5364 delegated_prefixes,
5365 dns_servers: dns_servers.to_vec(),
5366 preference: 0,
5367 receive_time: Instant::now(),
5368 preferred_non_temporary_addresses_count,
5369 preferred_delegated_prefixes_count,
5370 }
5371 }
5372 }
5373
5374 pub(crate) fn msg_type(mut buf: &[u8]) -> v6::MessageType {
5380 let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
5381 msg.msg_type()
5382 }
5383
5384 #[derive(Clone)]
5387 pub(super) struct TestIa<V: IaValue> {
5388 pub(crate) values: HashMap<V, Lifetimes>,
5389 pub(crate) t1: v6::TimeValue,
5390 pub(crate) t2: v6::TimeValue,
5391 }
5392
5393 impl<V: IaValue> TestIa<V> {
5394 pub(crate) fn new_default(value: V) -> TestIa<V> {
5397 TestIa::new_default_with_values(HashMap::from([(value, Lifetimes::new_default())]))
5398 }
5399
5400 pub(crate) fn new_default_with_values(values: HashMap<V, Lifetimes>) -> TestIa<V> {
5401 TestIa {
5402 values,
5403 t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
5404 t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
5405 }
5406 }
5407
5408 pub(crate) fn new_renewed_default(value: V) -> TestIa<V> {
5411 TestIa::new_renewed_default_with_values([value].into_iter())
5412 }
5413
5414 pub(crate) fn new_renewed_default_with_values(
5415 values: impl Iterator<Item = V>,
5416 ) -> TestIa<V> {
5417 TestIa {
5418 values: values.map(|v| (v, Lifetimes::new_renewed())).collect(),
5419 t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(RENEWED_T1)),
5420 t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(RENEWED_T2)),
5421 }
5422 }
5423 }
5424
5425 pub(super) struct TestMessageBuilder<'a, IaNaIter, IaPdIter> {
5426 pub(super) transaction_id: [u8; 3],
5427 pub(super) message_type: v6::MessageType,
5428 pub(super) client_id: &'a [u8],
5429 pub(super) server_id: &'a [u8],
5430 pub(super) preference: Option<u8>,
5431 pub(super) dns_servers: Option<&'a [Ipv6Addr]>,
5432 pub(super) ia_nas: IaNaIter,
5433 pub(super) ia_pds: IaPdIter,
5434 }
5435
5436 impl<
5437 'a,
5438 IaNaIter: Iterator<Item = (v6::IAID, TestIa<Ipv6Addr>)>,
5439 IaPdIter: Iterator<Item = (v6::IAID, TestIa<Subnet<Ipv6Addr>>)>,
5440 > TestMessageBuilder<'a, IaNaIter, IaPdIter>
5441 {
5442 pub(super) fn build(self) -> Vec<u8> {
5443 let TestMessageBuilder {
5444 transaction_id,
5445 message_type,
5446 client_id,
5447 server_id,
5448 preference,
5449 dns_servers,
5450 ia_nas,
5451 ia_pds,
5452 } = self;
5453
5454 struct Inner<'a> {
5455 opt: Vec<v6::DhcpOption<'a>>,
5456 t1: v6::TimeValue,
5457 t2: v6::TimeValue,
5458 }
5459
5460 let iaaddr_options = ia_nas
5461 .map(|(iaid, TestIa { values, t1, t2 })| {
5462 (
5463 iaid,
5464 Inner {
5465 opt: values
5466 .into_iter()
5467 .map(|(value, Lifetimes { preferred_lifetime, valid_lifetime })| {
5468 v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
5469 value,
5470 get_value(preferred_lifetime),
5471 get_value(valid_lifetime.into()),
5472 &[],
5473 ))
5474 })
5475 .collect(),
5476 t1,
5477 t2,
5478 },
5479 )
5480 })
5481 .collect::<HashMap<_, _>>();
5482 let iaprefix_options = ia_pds
5483 .map(|(iaid, TestIa { values, t1, t2 })| {
5484 (
5485 iaid,
5486 Inner {
5487 opt: values
5488 .into_iter()
5489 .map(|(value, Lifetimes { preferred_lifetime, valid_lifetime })| {
5490 v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
5491 get_value(preferred_lifetime),
5492 get_value(valid_lifetime.into()),
5493 value,
5494 &[],
5495 ))
5496 })
5497 .collect(),
5498 t1,
5499 t2,
5500 },
5501 )
5502 })
5503 .collect::<HashMap<_, _>>();
5504
5505 let options =
5506 [v6::DhcpOption::ServerId(&server_id), v6::DhcpOption::ClientId(client_id)]
5507 .into_iter()
5508 .chain(preference.into_iter().map(v6::DhcpOption::Preference))
5509 .chain(dns_servers.into_iter().map(v6::DhcpOption::DnsServers))
5510 .chain(iaaddr_options.iter().map(|(iaid, Inner { opt, t1, t2 })| {
5511 v6::DhcpOption::Iana(v6::IanaSerializer::new(
5512 *iaid,
5513 get_value(*t1),
5514 get_value(*t2),
5515 opt.as_ref(),
5516 ))
5517 }))
5518 .chain(iaprefix_options.iter().map(|(iaid, Inner { opt, t1, t2 })| {
5519 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
5520 *iaid,
5521 get_value(*t1),
5522 get_value(*t2),
5523 opt.as_ref(),
5524 ))
5525 }))
5526 .collect::<Vec<_>>();
5527
5528 let builder = v6::MessageBuilder::new(message_type, transaction_id, &options);
5529 let mut buf = vec![0; builder.bytes_len()];
5530 builder.serialize(&mut buf);
5531 buf
5532 }
5533 }
5534
5535 pub(super) type TestIaNa = TestIa<Ipv6Addr>;
5536 pub(super) type TestIaPd = TestIa<Subnet<Ipv6Addr>>;
5537
5538 pub(super) fn request_and_assert<R: Rng + std::fmt::Debug>(
5549 client_id: &ClientDuid,
5550 server_id: [u8; TEST_SERVER_ID_LEN],
5551 non_temporary_addresses_to_assign: Vec<TestIaNa>,
5552 delegated_prefixes_to_assign: Vec<TestIaPd>,
5553 expected_dns_servers: &[Ipv6Addr],
5554 rng: R,
5555 now: Instant,
5556 ) -> (ClientStateMachine<Instant, R>, [u8; 3]) {
5557 let configured_non_temporary_addresses = to_configured_addresses(
5558 non_temporary_addresses_to_assign.len(),
5559 non_temporary_addresses_to_assign
5560 .iter()
5561 .map(|TestIaNa { values, t1: _, t2: _ }| values.keys().cloned().collect()),
5562 );
5563 let configured_delegated_prefixes = to_configured_prefixes(
5564 delegated_prefixes_to_assign.len(),
5565 delegated_prefixes_to_assign
5566 .iter()
5567 .map(|TestIaPd { values, t1: _, t2: _ }| values.keys().cloned().collect()),
5568 );
5569 let options_to_request = if expected_dns_servers.is_empty() {
5570 Vec::new()
5571 } else {
5572 vec![v6::OptionCode::DnsServers]
5573 };
5574 let mut client = testutil::start_and_assert_server_discovery(
5575 client_id,
5576 configured_non_temporary_addresses.clone(),
5577 configured_delegated_prefixes.clone(),
5578 options_to_request.clone(),
5579 rng,
5580 now,
5581 );
5582 let transaction_id = client.transaction_id;
5583
5584 let buf = TestMessageBuilder {
5585 transaction_id,
5586 message_type: v6::MessageType::Advertise,
5587 client_id: &CLIENT_ID,
5588 server_id: &server_id,
5589 preference: Some(ADVERTISE_MAX_PREFERENCE),
5590 dns_servers: (!expected_dns_servers.is_empty()).then(|| expected_dns_servers),
5591 ia_nas: (0..).map(v6::IAID::new).zip(non_temporary_addresses_to_assign),
5592 ia_pds: (0..).map(v6::IAID::new).zip(delegated_prefixes_to_assign),
5593 }
5594 .build();
5595 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
5597 let actions = client.handle_message_receive(msg, now);
5600 let buf = assert_matches!(
5601 &actions[..],
5602 [
5603 Action::CancelTimer(ClientTimerType::Retransmission),
5604 Action::SendMessage(buf),
5605 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
5606 ] => {
5607 assert_eq!(*instant, now.add(INITIAL_REQUEST_TIMEOUT));
5608 buf
5609 }
5610 );
5611 testutil::assert_outgoing_stateful_message(
5612 &buf,
5613 v6::MessageType::Request,
5614 &client_id,
5615 Some(&server_id),
5616 &options_to_request,
5617 &configured_non_temporary_addresses,
5618 &configured_delegated_prefixes,
5619 );
5620 let ClientStateMachine { transaction_id, options_to_request: _, state, rng: _ } = &client;
5621 let request_transaction_id = *transaction_id;
5622 {
5623 let Requesting {
5624 client_id: got_client_id,
5625 server_id: got_server_id,
5626 collected_advertise,
5627 retrans_timeout,
5628 transmission_count,
5629 solicit_max_rt,
5630 non_temporary_addresses: _,
5631 delegated_prefixes: _,
5632 first_request_time: _,
5633 } = assert_matches!(&state, Some(ClientState::Requesting(requesting)) => requesting);
5634 assert_eq!(got_client_id, client_id);
5635 assert_eq!(*got_server_id, server_id);
5636 assert!(
5637 collected_advertise.is_empty(),
5638 "collected_advertise = {:?}",
5639 collected_advertise
5640 );
5641 assert_eq!(*retrans_timeout, INITIAL_REQUEST_TIMEOUT);
5642 assert_eq!(*transmission_count, 1);
5643 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
5644 }
5645 (client, request_transaction_id)
5646 }
5647
5648 pub(super) fn assign_and_assert<R: Rng + std::fmt::Debug>(
5657 client_id: &ClientDuid,
5658 server_id: [u8; TEST_SERVER_ID_LEN],
5659 non_temporary_addresses_to_assign: Vec<TestIaNa>,
5660 delegated_prefixes_to_assign: Vec<TestIaPd>,
5661 expected_dns_servers: &[Ipv6Addr],
5662 rng: R,
5663 now: Instant,
5664 ) -> (ClientStateMachine<Instant, R>, Actions<Instant>) {
5665 let (mut client, transaction_id) = testutil::request_and_assert(
5666 client_id,
5667 server_id.clone(),
5668 non_temporary_addresses_to_assign.clone(),
5669 delegated_prefixes_to_assign.clone(),
5670 expected_dns_servers,
5671 rng,
5672 now,
5673 );
5674
5675 let non_temporary_addresses_to_assign = (0..)
5676 .map(v6::IAID::new)
5677 .zip(non_temporary_addresses_to_assign)
5678 .collect::<HashMap<_, _>>();
5679 let delegated_prefixes_to_assign =
5680 (0..).map(v6::IAID::new).zip(delegated_prefixes_to_assign).collect::<HashMap<_, _>>();
5681
5682 let buf = TestMessageBuilder {
5683 transaction_id,
5684 message_type: v6::MessageType::Reply,
5685 client_id: &CLIENT_ID,
5686 server_id: &SERVER_ID[0],
5687 preference: None,
5688 dns_servers: (!expected_dns_servers.is_empty()).then(|| expected_dns_servers),
5689 ia_nas: non_temporary_addresses_to_assign.iter().map(|(k, v)| (*k, v.clone())),
5690 ia_pds: delegated_prefixes_to_assign.iter().map(|(k, v)| (*k, v.clone())),
5691 }
5692 .build();
5693 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
5695 let actions = client.handle_message_receive(msg, now);
5696 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
5697 &client;
5698 let expected_non_temporary_addresses = non_temporary_addresses_to_assign
5699 .iter()
5700 .map(|(iaid, TestIaNa { values, t1: _, t2: _ })| {
5701 (
5702 *iaid,
5703 AddressEntry::Assigned(
5704 values
5705 .iter()
5706 .map(|(v, lifetimes)| {
5707 (*v, LifetimesInfo { lifetimes: *lifetimes, updated_at: now })
5708 })
5709 .collect(),
5710 ),
5711 )
5712 })
5713 .collect::<HashMap<_, _>>();
5714 let expected_delegated_prefixes = delegated_prefixes_to_assign
5715 .iter()
5716 .map(|(iaid, TestIaPd { values, t1: _, t2: _ })| {
5717 (
5718 *iaid,
5719 PrefixEntry::Assigned(
5720 values
5721 .iter()
5722 .map(|(v, lifetimes)| {
5723 (*v, LifetimesInfo { lifetimes: *lifetimes, updated_at: now })
5724 })
5725 .collect(),
5726 ),
5727 )
5728 })
5729 .collect::<HashMap<_, _>>();
5730 let Assigned {
5731 client_id: got_client_id,
5732 non_temporary_addresses,
5733 delegated_prefixes,
5734 server_id: got_server_id,
5735 dns_servers,
5736 solicit_max_rt,
5737 _marker,
5738 } = assert_matches!(
5739 &state,
5740 Some(ClientState::Assigned(assigned)) => assigned
5741 );
5742 assert_eq!(got_client_id, client_id);
5743 assert_eq!(non_temporary_addresses, &expected_non_temporary_addresses);
5744 assert_eq!(delegated_prefixes, &expected_delegated_prefixes);
5745 assert_eq!(*got_server_id, server_id);
5746 assert_eq!(dns_servers, expected_dns_servers);
5747 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
5748 (client, actions)
5749 }
5750
5751 pub(crate) fn get_value(t: v6::TimeValue) -> u32 {
5753 const INFINITY: u32 = u32::MAX;
5754 match t {
5755 v6::TimeValue::Zero => 0,
5756 v6::TimeValue::NonZero(non_zero_tv) => match non_zero_tv {
5757 v6::NonZeroTimeValue::Finite(t) => t.get(),
5758 v6::NonZeroTimeValue::Infinity => INFINITY,
5759 },
5760 }
5761 }
5762
5763 pub(crate) fn assert_outgoing_stateful_message(
5771 mut buf: &[u8],
5772 expected_msg_type: v6::MessageType,
5773 expected_client_id: &[u8],
5774 expected_server_id: Option<&[u8; TEST_SERVER_ID_LEN]>,
5775 expected_oro: &[v6::OptionCode],
5776 expected_non_temporary_addresses: &HashMap<v6::IAID, HashSet<Ipv6Addr>>,
5777 expected_delegated_prefixes: &HashMap<v6::IAID, HashSet<Subnet<Ipv6Addr>>>,
5778 ) {
5779 let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
5780 assert_eq!(msg.msg_type(), expected_msg_type);
5781
5782 let (mut non_ia_opts, iana_opts, iapd_opts, other) = msg.options().fold(
5783 (Vec::new(), Vec::new(), Vec::new(), Vec::new()),
5784 |(mut non_ia_opts, mut iana_opts, mut iapd_opts, mut other), opt| {
5785 match opt {
5786 v6::ParsedDhcpOption::ClientId(_)
5787 | v6::ParsedDhcpOption::ElapsedTime(_)
5788 | v6::ParsedDhcpOption::Oro(_) => non_ia_opts.push(opt),
5789 v6::ParsedDhcpOption::ServerId(_) if expected_server_id.is_some() => {
5790 non_ia_opts.push(opt)
5791 }
5792 v6::ParsedDhcpOption::Iana(iana_data) => iana_opts.push(iana_data),
5793 v6::ParsedDhcpOption::IaPd(iapd_data) => iapd_opts.push(iapd_data),
5794 opt => other.push(opt),
5795 }
5796 (non_ia_opts, iana_opts, iapd_opts, other)
5797 },
5798 );
5799 let option_sorter: fn(
5800 &v6::ParsedDhcpOption<'_>,
5801 &v6::ParsedDhcpOption<'_>,
5802 ) -> std::cmp::Ordering =
5803 |opt1, opt2| (u16::from(opt1.code())).cmp(&(u16::from(opt2.code())));
5804
5805 non_ia_opts.sort_by(option_sorter);
5807 let expected_non_ia_opts = {
5808 let oro = std::iter::once(v6::OptionCode::SolMaxRt)
5809 .chain(expected_oro.iter().copied())
5810 .collect();
5811 let mut expected_non_ia_opts = vec![
5812 v6::ParsedDhcpOption::ClientId(expected_client_id),
5813 v6::ParsedDhcpOption::ElapsedTime(0),
5814 v6::ParsedDhcpOption::Oro(oro),
5815 ];
5816 if let Some(server_id) = expected_server_id {
5817 expected_non_ia_opts.push(v6::ParsedDhcpOption::ServerId(server_id));
5818 }
5819 expected_non_ia_opts.sort_by(option_sorter);
5820 expected_non_ia_opts
5821 };
5822 assert_eq!(non_ia_opts, expected_non_ia_opts);
5823
5824 let sent_non_temporary_addresses = {
5826 let mut sent_non_temporary_addresses: HashMap<v6::IAID, HashSet<Ipv6Addr>> =
5827 HashMap::new();
5828 for iana_data in iana_opts.iter() {
5829 let mut opts = HashSet::new();
5830
5831 for iana_option in iana_data.iter_options() {
5832 match iana_option {
5833 v6::ParsedDhcpOption::IaAddr(iaaddr_data) => {
5834 assert!(opts.insert(iaaddr_data.addr()));
5835 }
5836 option => panic!("unexpected option {:?}", option),
5837 }
5838 }
5839
5840 assert_eq!(
5841 sent_non_temporary_addresses.insert(v6::IAID::new(iana_data.iaid()), opts),
5842 None
5843 );
5844 }
5845 sent_non_temporary_addresses
5846 };
5847 assert_eq!(&sent_non_temporary_addresses, expected_non_temporary_addresses);
5848
5849 let sent_prefixes = {
5850 let mut sent_prefixes: HashMap<v6::IAID, HashSet<Subnet<Ipv6Addr>>> = HashMap::new();
5851 for iapd_data in iapd_opts.iter() {
5852 let mut opts = HashSet::new();
5853
5854 for iapd_option in iapd_data.iter_options() {
5855 match iapd_option {
5856 v6::ParsedDhcpOption::IaPrefix(iaprefix_data) => {
5857 assert!(opts.insert(iaprefix_data.prefix().unwrap()));
5858 }
5859 option => panic!("unexpected option {:?}", option),
5860 }
5861 }
5862
5863 assert_eq!(sent_prefixes.insert(v6::IAID::new(iapd_data.iaid()), opts), None);
5864 }
5865 sent_prefixes
5866 };
5867 assert_eq!(&sent_prefixes, expected_delegated_prefixes);
5868
5869 assert_eq!(&other, &[]);
5872 }
5873
5874 pub(super) fn send_renew_and_assert<R: Rng + std::fmt::Debug>(
5884 client_id: &ClientDuid,
5885 server_id: [u8; TEST_SERVER_ID_LEN],
5886 non_temporary_addresses_to_assign: Vec<TestIaNa>,
5887 delegated_prefixes_to_assign: Vec<TestIaPd>,
5888 expected_dns_servers: Option<&[Ipv6Addr]>,
5889 expected_t1_secs: v6::NonZeroOrMaxU32,
5890 expected_t2_secs: v6::NonZeroOrMaxU32,
5891 max_valid_lifetime: v6::NonZeroTimeValue,
5892 rng: R,
5893 now: Instant,
5894 ) -> ClientStateMachine<Instant, R> {
5895 let expected_dns_servers_as_slice = expected_dns_servers.unwrap_or(&[]);
5896 let (client, actions) = testutil::assign_and_assert(
5897 client_id,
5898 server_id.clone(),
5899 non_temporary_addresses_to_assign.clone(),
5900 delegated_prefixes_to_assign.clone(),
5901 expected_dns_servers_as_slice,
5902 rng,
5903 now,
5904 );
5905 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
5906 &client;
5907 {
5908 let Assigned {
5909 client_id: _,
5910 non_temporary_addresses: _,
5911 delegated_prefixes: _,
5912 server_id: _,
5913 dns_servers: _,
5914 solicit_max_rt: _,
5915 _marker,
5916 } = assert_matches!(
5917 state,
5918 Some(ClientState::Assigned(assigned)) => assigned
5919 );
5920 }
5921 let (expected_oro, maybe_dns_server_action) =
5922 if let Some(expected_dns_servers) = expected_dns_servers {
5923 (
5924 Some([v6::OptionCode::DnsServers]),
5925 Some(Action::UpdateDnsServers(expected_dns_servers.to_vec())),
5926 )
5927 } else {
5928 (None, None)
5929 };
5930 let iana_updates = (0..)
5931 .map(v6::IAID::new)
5932 .zip(non_temporary_addresses_to_assign.iter())
5933 .map(|(iaid, TestIa { values, t1: _, t2: _ })| {
5934 (
5935 iaid,
5936 values
5937 .iter()
5938 .map(|(value, lifetimes)| (*value, IaValueUpdateKind::Added(*lifetimes)))
5939 .collect(),
5940 )
5941 })
5942 .collect::<HashMap<_, _>>();
5943 let iapd_updates = (0..)
5944 .map(v6::IAID::new)
5945 .zip(delegated_prefixes_to_assign.iter())
5946 .map(|(iaid, TestIa { values, t1: _, t2: _ })| {
5947 (
5948 iaid,
5949 values
5950 .iter()
5951 .map(|(value, lifetimes)| (*value, IaValueUpdateKind::Added(*lifetimes)))
5952 .collect(),
5953 )
5954 })
5955 .collect::<HashMap<_, _>>();
5956 let expected_actions = [
5957 Action::CancelTimer(ClientTimerType::Retransmission),
5958 Action::ScheduleTimer(
5959 ClientTimerType::Renew,
5960 now.add(Duration::from_secs(expected_t1_secs.get().into())),
5961 ),
5962 Action::ScheduleTimer(
5963 ClientTimerType::Rebind,
5964 now.add(Duration::from_secs(expected_t2_secs.get().into())),
5965 ),
5966 ]
5967 .into_iter()
5968 .chain(maybe_dns_server_action)
5969 .chain((!iana_updates.is_empty()).then(|| Action::IaNaUpdates(iana_updates)))
5970 .chain((!iapd_updates.is_empty()).then(|| Action::IaPdUpdates(iapd_updates)))
5971 .chain([match max_valid_lifetime {
5972 v6::NonZeroTimeValue::Finite(max_valid_lifetime) => Action::ScheduleTimer(
5973 ClientTimerType::RestartServerDiscovery,
5974 now.add(Duration::from_secs(max_valid_lifetime.get().into())),
5975 ),
5976 v6::NonZeroTimeValue::Infinity => {
5977 Action::CancelTimer(ClientTimerType::RestartServerDiscovery)
5978 }
5979 }])
5980 .collect::<Vec<_>>();
5981 assert_eq!(actions, expected_actions);
5982
5983 handle_renew_or_rebind_timer(
5984 client,
5985 &client_id,
5986 server_id,
5987 non_temporary_addresses_to_assign,
5988 delegated_prefixes_to_assign,
5989 expected_dns_servers_as_slice,
5990 expected_oro.as_ref().map_or(&[], |oro| &oro[..]),
5991 now,
5992 RENEW_TEST_STATE,
5993 )
5994 }
5995
5996 pub(super) struct RenewRebindTestState {
5997 initial_timeout: Duration,
5998 timer_type: ClientTimerType,
5999 message_type: v6::MessageType,
6000 expect_server_id: bool,
6001 with_state: fn(&Option<ClientState<Instant>>) -> &RenewingOrRebindingInner<Instant>,
6002 }
6003
6004 pub(super) const RENEW_TEST_STATE: RenewRebindTestState = RenewRebindTestState {
6005 initial_timeout: INITIAL_RENEW_TIMEOUT,
6006 timer_type: ClientTimerType::Renew,
6007 message_type: v6::MessageType::Renew,
6008 expect_server_id: true,
6009 with_state: |state| {
6010 assert_matches!(
6011 state,
6012 Some(ClientState::Renewing(RenewingOrRebinding(inner))) => inner
6013 )
6014 },
6015 };
6016
6017 pub(super) const REBIND_TEST_STATE: RenewRebindTestState = RenewRebindTestState {
6018 initial_timeout: INITIAL_REBIND_TIMEOUT,
6019 timer_type: ClientTimerType::Rebind,
6020 message_type: v6::MessageType::Rebind,
6021 expect_server_id: false,
6022 with_state: |state| {
6023 assert_matches!(
6024 state,
6025 Some(ClientState::Rebinding(RenewingOrRebinding(inner))) => inner
6026 )
6027 },
6028 };
6029
6030 pub(super) fn handle_renew_or_rebind_timer<R: Rng>(
6031 mut client: ClientStateMachine<Instant, R>,
6032 client_id: &[u8],
6033 server_id: [u8; TEST_SERVER_ID_LEN],
6034 non_temporary_addresses_to_assign: Vec<TestIaNa>,
6035 delegated_prefixes_to_assign: Vec<TestIaPd>,
6036 expected_dns_servers_as_slice: &[Ipv6Addr],
6037 expected_oro: &[v6::OptionCode],
6038 now: Instant,
6039 RenewRebindTestState {
6040 initial_timeout,
6041 timer_type,
6042 message_type,
6043 expect_server_id,
6044 with_state,
6045 }: RenewRebindTestState,
6046 ) -> ClientStateMachine<Instant, R> {
6047 let actions = client.handle_timeout(timer_type, now);
6048 let buf = assert_matches!(
6049 &actions[..],
6050 [
6051 Action::SendMessage(buf),
6052 Action::ScheduleTimer(ClientTimerType::Retransmission, got_time)
6053 ] => {
6054 assert_eq!(*got_time, now.add(initial_timeout));
6055 buf
6056 }
6057 );
6058 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
6059 &client;
6060 let RenewingOrRebindingInner {
6061 client_id: got_client_id,
6062 server_id: got_server_id,
6063 dns_servers,
6064 solicit_max_rt,
6065 non_temporary_addresses: _,
6066 delegated_prefixes: _,
6067 start_time: _,
6068 retrans_timeout: _,
6069 } = with_state(state);
6070 assert_eq!(got_client_id, client_id);
6071 assert_eq!(*got_server_id, server_id);
6072 assert_eq!(dns_servers, expected_dns_servers_as_slice);
6073 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
6074 let expected_addresses_to_renew: HashMap<v6::IAID, HashSet<Ipv6Addr>> = (0..)
6075 .map(v6::IAID::new)
6076 .zip(
6077 non_temporary_addresses_to_assign
6078 .iter()
6079 .map(|TestIaNa { values, t1: _, t2: _ }| values.keys().cloned().collect()),
6080 )
6081 .collect();
6082 let expected_prefixes_to_renew: HashMap<v6::IAID, HashSet<Subnet<Ipv6Addr>>> = (0..)
6083 .map(v6::IAID::new)
6084 .zip(
6085 delegated_prefixes_to_assign
6086 .iter()
6087 .map(|TestIaPd { values, t1: _, t2: _ }| values.keys().cloned().collect()),
6088 )
6089 .collect();
6090 testutil::assert_outgoing_stateful_message(
6091 &buf,
6092 message_type,
6093 client_id,
6094 expect_server_id.then(|| &server_id),
6095 expected_oro,
6096 &expected_addresses_to_renew,
6097 &expected_prefixes_to_renew,
6098 );
6099 client
6100 }
6101
6102 pub(super) fn send_rebind_and_assert<R: Rng + std::fmt::Debug>(
6112 client_id: &ClientDuid,
6113 server_id: [u8; TEST_SERVER_ID_LEN],
6114 non_temporary_addresses_to_assign: Vec<TestIaNa>,
6115 delegated_prefixes_to_assign: Vec<TestIaPd>,
6116 expected_dns_servers: Option<&[Ipv6Addr]>,
6117 expected_t1_secs: v6::NonZeroOrMaxU32,
6118 expected_t2_secs: v6::NonZeroOrMaxU32,
6119 max_valid_lifetime: v6::NonZeroTimeValue,
6120 rng: R,
6121 now: Instant,
6122 ) -> ClientStateMachine<Instant, R> {
6123 let client = testutil::send_renew_and_assert(
6124 client_id,
6125 server_id,
6126 non_temporary_addresses_to_assign.clone(),
6127 delegated_prefixes_to_assign.clone(),
6128 expected_dns_servers,
6129 expected_t1_secs,
6130 expected_t2_secs,
6131 max_valid_lifetime,
6132 rng,
6133 now,
6134 );
6135 let (expected_oro, expected_dns_servers) =
6136 if let Some(expected_dns_servers) = expected_dns_servers {
6137 (Some([v6::OptionCode::DnsServers]), expected_dns_servers)
6138 } else {
6139 (None, &[][..])
6140 };
6141
6142 handle_renew_or_rebind_timer(
6143 client,
6144 &client_id,
6145 server_id,
6146 non_temporary_addresses_to_assign,
6147 delegated_prefixes_to_assign,
6148 expected_dns_servers,
6149 expected_oro.as_ref().map_or(&[], |oro| &oro[..]),
6150 now,
6151 REBIND_TEST_STATE,
6152 )
6153 }
6154}
6155
6156#[cfg(test)]
6157mod tests {
6158 use std::cmp::Ordering;
6159 use std::time::Instant;
6160
6161 use super::*;
6162 use packet::ParsablePacket;
6163 use rand::RngCore;
6164 use test_case::test_case;
6165 use testconsts::*;
6166 use testutil::{
6167 REBIND_TEST_STATE, RENEW_TEST_STATE, RenewRebindTestState, TestIa, TestIaNa, TestIaPd,
6168 TestMessageBuilder, handle_renew_or_rebind_timer,
6169 };
6170
6171 #[derive(Debug)]
6172 struct StepRng {
6173 state: u64,
6174 increment: u64,
6175 }
6176
6177 impl StepRng {
6178 pub fn new(initial: u64, increment: u64) -> Self {
6179 Self { state: initial, increment: increment }
6180 }
6181 }
6182
6183 impl RngCore for StepRng {
6184 fn next_u32(&mut self) -> u32 {
6185 self.next_u64() as u32
6186 }
6187
6188 fn next_u64(&mut self) -> u64 {
6189 let r = self.state;
6190 self.state = self.state.wrapping_add(self.increment);
6191 r
6192 }
6193
6194 fn fill_bytes(&mut self, dst: &mut [u8]) {
6195 for byte in dst {
6196 *byte = self.next_u64() as u8;
6197 }
6198 }
6199 }
6200
6201 #[test]
6202 fn send_information_request_and_receive_reply() {
6203 for options in [
6205 Vec::new(),
6206 vec![v6::OptionCode::DnsServers],
6207 vec![v6::OptionCode::DnsServers, v6::OptionCode::DomainList],
6208 ] {
6209 let now = Instant::now();
6210 let (mut client, actions) = ClientStateMachine::start_stateless(
6211 options.clone(),
6212 StepRng::new(u64::MAX / 2, 0),
6213 now,
6214 );
6215
6216 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
6217 &client;
6218 assert_matches!(
6219 *state,
6220 Some(ClientState::InformationRequesting(InformationRequesting {
6221 retrans_timeout: INITIAL_INFO_REQ_TIMEOUT,
6222 _marker,
6223 }))
6224 );
6225
6226 let want_options_array = [v6::DhcpOption::Oro(&options)];
6229 let want_options = if options.is_empty() { &[][..] } else { &want_options_array[..] };
6230 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
6231 &client;
6232 let builder = v6::MessageBuilder::new(
6233 v6::MessageType::InformationRequest,
6234 *transaction_id,
6235 want_options,
6236 );
6237 let mut want_buf = vec![0; builder.bytes_len()];
6238 builder.serialize(&mut want_buf);
6239 assert_eq!(
6240 actions[..],
6241 [
6242 Action::SendMessage(want_buf),
6243 Action::ScheduleTimer(
6244 ClientTimerType::Retransmission,
6245 now.add(INITIAL_INFO_REQ_TIMEOUT),
6246 )
6247 ]
6248 );
6249
6250 let test_dhcp_refresh_time = 42u32;
6251 let options = [
6252 v6::DhcpOption::ServerId(&SERVER_ID[0]),
6253 v6::DhcpOption::InformationRefreshTime(test_dhcp_refresh_time),
6254 v6::DhcpOption::DnsServers(&DNS_SERVERS),
6255 ];
6256 let builder =
6257 v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
6258 let mut buf = vec![0; builder.bytes_len()];
6259 builder.serialize(&mut buf);
6260 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6262
6263 let now = Instant::now();
6264 let actions = client.handle_message_receive(msg, now);
6265 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
6266 client;
6267
6268 {
6269 assert_matches!(
6270 state,
6271 Some(ClientState::InformationReceived(InformationReceived { dns_servers, _marker }))
6272 if dns_servers == DNS_SERVERS.to_vec()
6273 );
6274 }
6275 assert_eq!(
6277 actions[..],
6278 [
6279 Action::CancelTimer(ClientTimerType::Retransmission),
6280 Action::ScheduleTimer(
6281 ClientTimerType::Refresh,
6282 now.add(Duration::from_secs(u64::from(test_dhcp_refresh_time))),
6283 ),
6284 Action::UpdateDnsServers(DNS_SERVERS.to_vec()),
6285 ]
6286 );
6287 }
6288 }
6289
6290 #[test]
6291 fn send_information_request_on_retransmission_timeout() {
6292 let now = Instant::now();
6293 let (mut client, actions) =
6294 ClientStateMachine::start_stateless(Vec::new(), StepRng::new(u64::MAX / 2, 0), now);
6295 assert_matches!(
6296 actions[..],
6297 [_, Action::ScheduleTimer(ClientTimerType::Retransmission, instant)] => {
6298 assert_eq!(instant, now.add(INITIAL_INFO_REQ_TIMEOUT));
6299 }
6300 );
6301
6302 let actions = client.handle_timeout(ClientTimerType::Retransmission, now);
6303 assert_matches!(
6305 actions[..],
6306 [
6307 _,
6308 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
6309 ] => assert_eq!(instant, now.add(2 * INITIAL_INFO_REQ_TIMEOUT))
6310 );
6311 }
6312
6313 #[test]
6314 fn send_information_request_on_refresh_timeout() {
6315 let (mut client, _) = ClientStateMachine::start_stateless(
6316 Vec::new(),
6317 StepRng::new(u64::MAX / 2, 1),
6321 Instant::now(),
6322 );
6323
6324 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
6325 &client;
6326 let options = [v6::DhcpOption::ServerId(&SERVER_ID[0])];
6327 let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
6328 let mut buf = vec![0; builder.bytes_len()];
6329 builder.serialize(&mut buf);
6330 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6332
6333 let time = Instant::now();
6335 assert_eq!(
6336 client.handle_message_receive(msg, time)[..],
6337 [
6338 Action::CancelTimer(ClientTimerType::Retransmission),
6339 Action::ScheduleTimer(ClientTimerType::Refresh, time.add(IRT_DEFAULT))
6340 ]
6341 );
6342
6343 let old_transaction_id = client.transaction_id;
6344
6345 let actions = client.handle_timeout(ClientTimerType::Refresh, time);
6347 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
6348 &client;
6349
6350 assert_ne!(old_transaction_id, *transaction_id);
6353
6354 let builder =
6355 v6::MessageBuilder::new(v6::MessageType::InformationRequest, *transaction_id, &[]);
6356 let mut want_buf = vec![0; builder.bytes_len()];
6357 builder.serialize(&mut want_buf);
6358 assert_eq!(
6359 actions[..],
6360 [
6361 Action::SendMessage(want_buf),
6362 Action::ScheduleTimer(
6363 ClientTimerType::Retransmission,
6364 time.add(INITIAL_INFO_REQ_TIMEOUT)
6365 )
6366 ]
6367 );
6368 }
6369
6370 #[test_case(
6373 0, std::iter::empty(),
6374 2, (&CONFIGURED_DELEGATED_PREFIXES[0..2]).iter().copied(),
6375 Vec::new()
6376 )]
6377 #[test_case(
6378 2, (&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]).iter().copied(),
6379 0, std::iter::empty(),
6380 vec![v6::OptionCode::DnsServers]
6381 )]
6382 #[test_case(
6383 1, std::iter::empty(),
6384 2, (&CONFIGURED_DELEGATED_PREFIXES[0..2]).iter().copied(),
6385 Vec::new()
6386 )]
6387 #[test_case(
6388 2, std::iter::once(CONFIGURED_NON_TEMPORARY_ADDRESSES[0]),
6389 1, std::iter::empty(),
6390 vec![v6::OptionCode::DnsServers]
6391 )]
6392 #[test_case(
6393 2, (&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]).iter().copied(),
6394 2, std::iter::once(CONFIGURED_DELEGATED_PREFIXES[0]),
6395 vec![v6::OptionCode::DnsServers]
6396 )]
6397 fn send_solicit(
6398 address_count: usize,
6399 preferred_non_temporary_addresses: impl IntoIterator<Item = Ipv6Addr>,
6400 prefix_count: usize,
6401 preferred_delegated_prefixes: impl IntoIterator<Item = Subnet<Ipv6Addr>>,
6402 options_to_request: Vec<v6::OptionCode>,
6403 ) {
6404 let _client = testutil::start_and_assert_server_discovery(
6406 &(CLIENT_ID.into()),
6407 testutil::to_configured_addresses(
6408 address_count,
6409 preferred_non_temporary_addresses.into_iter().map(|a| HashSet::from([a])),
6410 ),
6411 testutil::to_configured_prefixes(
6412 prefix_count,
6413 preferred_delegated_prefixes.into_iter().map(|a| HashSet::from([a])),
6414 ),
6415 options_to_request,
6416 StepRng::new(u64::MAX / 2, 0),
6417 Instant::now(),
6418 );
6419 }
6420
6421 #[test_case(
6422 1, std::iter::empty(), std::iter::once(CONFIGURED_NON_TEMPORARY_ADDRESSES[0]), 0;
6423 "zero"
6424 )]
6425 #[test_case(
6426 2, CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2].iter().copied(), CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2].iter().copied(), 2;
6427 "two"
6428 )]
6429 #[test_case(
6430 4,
6431 CONFIGURED_NON_TEMPORARY_ADDRESSES.iter().copied(),
6432 std::iter::once(CONFIGURED_NON_TEMPORARY_ADDRESSES[0]).chain(REPLY_NON_TEMPORARY_ADDRESSES.iter().copied()),
6433 1;
6434 "one"
6435 )]
6436 fn compute_preferred_address_count(
6437 configure_count: usize,
6438 hints: impl IntoIterator<Item = Ipv6Addr>,
6439 got_addresses: impl IntoIterator<Item = Ipv6Addr>,
6440 want: usize,
6441 ) {
6442 let got_addresses: HashMap<_, _> = (0..)
6444 .map(v6::IAID::new)
6445 .zip(got_addresses.into_iter().map(|a| HashSet::from([a])))
6446 .collect();
6447 let configured_non_temporary_addresses = testutil::to_configured_addresses(
6448 configure_count,
6449 hints.into_iter().map(|a| HashSet::from([a])),
6450 );
6451 assert_eq!(
6452 super::compute_preferred_ia_count(&got_addresses, &configured_non_temporary_addresses),
6453 want,
6454 );
6455 }
6456
6457 #[test_case(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2], &CONFIGURED_DELEGATED_PREFIXES[0..2], true)]
6458 #[test_case(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..1], &CONFIGURED_DELEGATED_PREFIXES[0..1], true)]
6459 #[test_case(&REPLY_NON_TEMPORARY_ADDRESSES[0..2], &REPLY_DELEGATED_PREFIXES[0..2], true)]
6460 #[test_case(&[], &[], false)]
6461 fn advertise_message_has_ias(
6462 non_temporary_addresses: &[Ipv6Addr],
6463 delegated_prefixes: &[Subnet<Ipv6Addr>],
6464 expected: bool,
6465 ) {
6466 let configured_non_temporary_addresses = testutil::to_configured_addresses(
6467 2,
6468 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
6469 );
6470
6471 let configured_delegated_prefixes = testutil::to_configured_prefixes(
6472 2,
6473 std::iter::once(HashSet::from([CONFIGURED_DELEGATED_PREFIXES[0]])),
6474 );
6475
6476 let advertise = AdvertiseMessage::new_default(
6479 SERVER_ID[0],
6480 non_temporary_addresses,
6481 delegated_prefixes,
6482 &[],
6483 &configured_non_temporary_addresses,
6484 &configured_delegated_prefixes,
6485 );
6486 assert_eq!(advertise.has_ias(), expected);
6487 }
6488
6489 struct AdvertiseMessageOrdTestCase<'a> {
6490 adv1_non_temporary_addresses: &'a [Ipv6Addr],
6491 adv1_delegated_prefixes: &'a [Subnet<Ipv6Addr>],
6492 adv2_non_temporary_addresses: &'a [Ipv6Addr],
6493 adv2_delegated_prefixes: &'a [Subnet<Ipv6Addr>],
6494 expected: Ordering,
6495 }
6496
6497 #[test_case(AdvertiseMessageOrdTestCase{
6498 adv1_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2],
6499 adv1_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..2],
6500 adv2_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..3],
6501 adv2_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..3],
6502 expected: Ordering::Less,
6503 }; "adv1 has less IAs")]
6504 #[test_case(AdvertiseMessageOrdTestCase{
6505 adv1_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2],
6506 adv1_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..2],
6507 adv2_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[1..3],
6508 adv2_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[1..3],
6509 expected: Ordering::Greater,
6510 }; "adv1 has IAs matching hint")]
6511 #[test_case(AdvertiseMessageOrdTestCase{
6512 adv1_non_temporary_addresses: &[],
6513 adv1_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..3],
6514 adv2_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..1],
6515 adv2_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..1],
6516 expected: Ordering::Less,
6517 }; "adv1 missing IA_NA")]
6518 #[test_case(AdvertiseMessageOrdTestCase{
6519 adv1_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..3],
6520 adv1_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..1],
6521 adv2_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..3],
6522 adv2_delegated_prefixes: &[],
6523 expected: Ordering::Greater,
6524 }; "adv2 missing IA_PD")]
6525 fn advertise_message_ord(
6526 AdvertiseMessageOrdTestCase {
6527 adv1_non_temporary_addresses,
6528 adv1_delegated_prefixes,
6529 adv2_non_temporary_addresses,
6530 adv2_delegated_prefixes,
6531 expected,
6532 }: AdvertiseMessageOrdTestCase<'_>,
6533 ) {
6534 let configured_non_temporary_addresses = testutil::to_configured_addresses(
6535 3,
6536 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
6537 );
6538
6539 let configured_delegated_prefixes = testutil::to_configured_prefixes(
6540 3,
6541 std::iter::once(HashSet::from([CONFIGURED_DELEGATED_PREFIXES[0]])),
6542 );
6543
6544 let advertise1 = AdvertiseMessage::new_default(
6545 SERVER_ID[0],
6546 adv1_non_temporary_addresses,
6547 adv1_delegated_prefixes,
6548 &[],
6549 &configured_non_temporary_addresses,
6550 &configured_delegated_prefixes,
6551 );
6552 let advertise2 = AdvertiseMessage::new_default(
6553 SERVER_ID[1],
6554 adv2_non_temporary_addresses,
6555 adv2_delegated_prefixes,
6556 &[],
6557 &configured_non_temporary_addresses,
6558 &configured_delegated_prefixes,
6559 );
6560 assert_eq!(advertise1.cmp(&advertise2), expected);
6561 }
6562
6563 #[test_case(v6::DhcpOption::StatusCode(v6::StatusCode::Success.into(), ""); "status_code")]
6564 #[test_case(v6::DhcpOption::ClientId(&CLIENT_ID); "client_id")]
6565 #[test_case(v6::DhcpOption::ServerId(&SERVER_ID[0]); "server_id")]
6566 #[test_case(v6::DhcpOption::Preference(ADVERTISE_MAX_PREFERENCE); "preference")]
6567 #[test_case(v6::DhcpOption::SolMaxRt(*VALID_MAX_SOLICIT_TIMEOUT_RANGE.end()); "sol_max_rt")]
6568 #[test_case(v6::DhcpOption::DnsServers(&DNS_SERVERS); "dns_servers")]
6569 fn process_options_duplicates<'a>(opt: v6::DhcpOption<'a>) {
6570 let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6571 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
6572 60,
6573 60,
6574 &[],
6575 ))];
6576 let iaid = v6::IAID::new(0);
6577 let options = [
6578 v6::DhcpOption::StatusCode(v6::StatusCode::Success.into(), ""),
6579 v6::DhcpOption::ClientId(&CLIENT_ID),
6580 v6::DhcpOption::ServerId(&SERVER_ID[0]),
6581 v6::DhcpOption::Preference(ADVERTISE_MAX_PREFERENCE),
6582 v6::DhcpOption::SolMaxRt(*VALID_MAX_SOLICIT_TIMEOUT_RANGE.end()),
6583 v6::DhcpOption::Iana(v6::IanaSerializer::new(iaid, T1.get(), T2.get(), &iana_options)),
6584 v6::DhcpOption::DnsServers(&DNS_SERVERS),
6585 opt,
6586 ];
6587 let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6588 let mut buf = vec![0; builder.bytes_len()];
6589 builder.serialize(&mut buf);
6590 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6592 let requested_ia_nas = HashMap::from([(iaid, None::<Ipv6Addr>)]);
6593 assert_matches!(
6594 process_options(
6595 &msg,
6596 ExchangeType::AdvertiseToSolicit,
6597 Some(&CLIENT_ID),
6598 &requested_ia_nas,
6599 &NoIaRequested
6600 ),
6601 Err(OptionsError::DuplicateOption(_, _, _))
6602 );
6603 }
6604
6605 #[derive(Copy, Clone)]
6606 enum DupIaValue {
6607 Address,
6608 Prefix,
6609 }
6610
6611 impl DupIaValue {
6612 fn second_address(self) -> Ipv6Addr {
6613 match self {
6614 DupIaValue::Address => CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
6615 DupIaValue::Prefix => CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
6616 }
6617 }
6618
6619 fn second_prefix(self) -> Subnet<Ipv6Addr> {
6620 match self {
6621 DupIaValue::Address => CONFIGURED_DELEGATED_PREFIXES[1],
6622 DupIaValue::Prefix => CONFIGURED_DELEGATED_PREFIXES[0],
6623 }
6624 }
6625 }
6626
6627 #[test_case(
6628 DupIaValue::Address,
6629 |res| {
6630 assert_matches!(
6631 res,
6632 Err(OptionsError::IaNaError(IaOptionError::DuplicateIaValue {
6633 value,
6634 first_lifetimes,
6635 second_lifetimes,
6636 })) => {
6637 assert_eq!(value, CONFIGURED_NON_TEMPORARY_ADDRESSES[0]);
6638 (first_lifetimes, second_lifetimes)
6639 }
6640 )
6641 }; "duplicate address")]
6642 #[test_case(
6643 DupIaValue::Prefix,
6644 |res| {
6645 assert_matches!(
6646 res,
6647 Err(OptionsError::IaPdError(IaPdOptionError::IaOptionError(
6648 IaOptionError::DuplicateIaValue {
6649 value,
6650 first_lifetimes,
6651 second_lifetimes,
6652 }
6653 ))) => {
6654 assert_eq!(value, CONFIGURED_DELEGATED_PREFIXES[0]);
6655 (first_lifetimes, second_lifetimes)
6656 }
6657 )
6658 }; "duplicate prefix")]
6659 fn process_options_duplicate_ia_value(
6660 dup_ia_value: DupIaValue,
6661 check: fn(
6662 Result<ProcessedOptions, OptionsError>,
6663 )
6664 -> (Result<Lifetimes, LifetimesError>, Result<Lifetimes, LifetimesError>),
6665 ) {
6666 const IA_VALUE1_LIFETIME: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(60).unwrap();
6667 const IA_VALUE2_LIFETIME: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(100).unwrap();
6668 let iana_options = [
6669 v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6670 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
6671 IA_VALUE1_LIFETIME.get(),
6672 IA_VALUE1_LIFETIME.get(),
6673 &[],
6674 )),
6675 v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6676 dup_ia_value.second_address(),
6677 IA_VALUE2_LIFETIME.get(),
6678 IA_VALUE2_LIFETIME.get(),
6679 &[],
6680 )),
6681 ];
6682 let iapd_options = [
6683 v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
6684 IA_VALUE1_LIFETIME.get(),
6685 IA_VALUE1_LIFETIME.get(),
6686 CONFIGURED_DELEGATED_PREFIXES[0],
6687 &[],
6688 )),
6689 v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
6690 IA_VALUE2_LIFETIME.get(),
6691 IA_VALUE2_LIFETIME.get(),
6692 dup_ia_value.second_prefix(),
6693 &[],
6694 )),
6695 ];
6696 let iaid = v6::IAID::new(0);
6697 let options = [
6698 v6::DhcpOption::ClientId(&CLIENT_ID),
6699 v6::DhcpOption::ServerId(&SERVER_ID[0]),
6700 v6::DhcpOption::Iana(v6::IanaSerializer::new(iaid, T1.get(), T2.get(), &iana_options)),
6701 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(iaid, T1.get(), T2.get(), &iapd_options)),
6702 ];
6703 let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6704 let mut buf = vec![0; builder.bytes_len()];
6705 builder.serialize(&mut buf);
6706 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6708 let requested_ia_nas = HashMap::from([(iaid, None::<Ipv6Addr>)]);
6709 let (first_lifetimes, second_lifetimes) = check(process_options(
6710 &msg,
6711 ExchangeType::AdvertiseToSolicit,
6712 Some(&CLIENT_ID),
6713 &requested_ia_nas,
6714 &NoIaRequested,
6715 ));
6716 assert_eq!(
6717 first_lifetimes,
6718 Ok(Lifetimes::new_finite(IA_VALUE1_LIFETIME, IA_VALUE1_LIFETIME))
6719 );
6720 assert_eq!(
6721 second_lifetimes,
6722 Ok(Lifetimes::new_finite(IA_VALUE2_LIFETIME, IA_VALUE2_LIFETIME))
6723 )
6724 }
6725
6726 #[test]
6727 fn process_options_t1_greather_than_t2() {
6728 let iana_options1 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6729 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
6730 MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6731 MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6732 &[],
6733 ))];
6734 let iana_options2 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6735 CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
6736 MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6737 MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6738 &[],
6739 ))];
6740 let iapd_options1 = [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
6741 LARGE_NON_ZERO_OR_MAX_U32.get(),
6742 LARGE_NON_ZERO_OR_MAX_U32.get(),
6743 CONFIGURED_DELEGATED_PREFIXES[0],
6744 &[],
6745 ))];
6746 let iapd_options2 = [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
6747 LARGE_NON_ZERO_OR_MAX_U32.get(),
6748 LARGE_NON_ZERO_OR_MAX_U32.get(),
6749 CONFIGURED_DELEGATED_PREFIXES[1],
6750 &[],
6751 ))];
6752
6753 let iaid1 = v6::IAID::new(1);
6754 let iaid2 = v6::IAID::new(2);
6755 let options = [
6756 v6::DhcpOption::ClientId(&CLIENT_ID),
6757 v6::DhcpOption::ServerId(&SERVER_ID[0]),
6758 v6::DhcpOption::Iana(v6::IanaSerializer::new(
6759 iaid1,
6760 MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6761 SMALL_NON_ZERO_OR_MAX_U32.get(),
6762 &iana_options1,
6763 )),
6764 v6::DhcpOption::Iana(v6::IanaSerializer::new(
6765 iaid2,
6766 SMALL_NON_ZERO_OR_MAX_U32.get(),
6767 MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6768 &iana_options2,
6769 )),
6770 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
6771 iaid1,
6772 LARGE_NON_ZERO_OR_MAX_U32.get(),
6773 TINY_NON_ZERO_OR_MAX_U32.get(),
6774 &iapd_options1,
6775 )),
6776 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
6777 iaid2,
6778 TINY_NON_ZERO_OR_MAX_U32.get(),
6779 LARGE_NON_ZERO_OR_MAX_U32.get(),
6780 &iapd_options2,
6781 )),
6782 ];
6783 let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6784 let mut buf = vec![0; builder.bytes_len()];
6785 builder.serialize(&mut buf);
6786 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6788 let requested_ia_nas = HashMap::from([(iaid1, None::<Ipv6Addr>), (iaid2, None)]);
6789 let requested_ia_pds = HashMap::from([(iaid1, None::<Subnet<Ipv6Addr>>), (iaid2, None)]);
6790 assert_matches!(
6791 process_options(&msg, ExchangeType::AdvertiseToSolicit, Some(&CLIENT_ID), &requested_ia_nas, &requested_ia_pds),
6792 Ok(ProcessedOptions {
6793 server_id: _,
6794 solicit_max_rt_opt: _,
6795 result: Ok(Options {
6796 success_status_message: _,
6797 next_contact_time: _,
6798 non_temporary_addresses,
6799 delegated_prefixes,
6800 dns_servers: _,
6801 preference: _,
6802 }),
6803 }) => {
6804 assert_eq!(non_temporary_addresses, HashMap::from([(iaid2, IaOption::Success {
6805 status_message: None,
6806 t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(SMALL_NON_ZERO_OR_MAX_U32)),
6807 t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(MEDIUM_NON_ZERO_OR_MAX_U32)),
6808 ia_values: HashMap::from([(CONFIGURED_NON_TEMPORARY_ADDRESSES[1], Ok(Lifetimes{
6809 preferred_lifetime: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(MEDIUM_NON_ZERO_OR_MAX_U32)),
6810 valid_lifetime: v6::NonZeroTimeValue::Finite(MEDIUM_NON_ZERO_OR_MAX_U32),
6811 }))]),
6812 })]));
6813 assert_eq!(delegated_prefixes, HashMap::from([(iaid2, IaOption::Success {
6814 status_message: None,
6815 t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(TINY_NON_ZERO_OR_MAX_U32)),
6816 t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(LARGE_NON_ZERO_OR_MAX_U32)),
6817 ia_values: HashMap::from([(CONFIGURED_DELEGATED_PREFIXES[1], Ok(Lifetimes{
6818 preferred_lifetime: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(LARGE_NON_ZERO_OR_MAX_U32)),
6819 valid_lifetime: v6::NonZeroTimeValue::Finite(LARGE_NON_ZERO_OR_MAX_U32),
6820 }))]),
6821 })]));
6822 }
6823 );
6824 }
6825
6826 #[test]
6827 fn process_options_duplicate_ia_na_id() {
6828 let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6829 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
6830 60,
6831 60,
6832 &[],
6833 ))];
6834 let iaid = v6::IAID::new(0);
6835 let options = [
6836 v6::DhcpOption::ClientId(&CLIENT_ID),
6837 v6::DhcpOption::ServerId(&SERVER_ID[0]),
6838 v6::DhcpOption::Iana(v6::IanaSerializer::new(iaid, T1.get(), T2.get(), &iana_options)),
6839 v6::DhcpOption::Iana(v6::IanaSerializer::new(iaid, T1.get(), T2.get(), &iana_options)),
6840 ];
6841 let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6842 let mut buf = vec![0; builder.bytes_len()];
6843 builder.serialize(&mut buf);
6844 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6846 let requested_ia_nas = HashMap::from([(iaid, None::<Ipv6Addr>)]);
6847 assert_matches!(
6848 process_options(&msg, ExchangeType::AdvertiseToSolicit, Some(&CLIENT_ID), &requested_ia_nas, &NoIaRequested),
6849 Err(OptionsError::DuplicateIaNaId(got_iaid, _, _)) if got_iaid == iaid
6850 );
6851 }
6852
6853 #[test]
6854 fn process_options_missing_server_id() {
6855 let options = [v6::DhcpOption::ClientId(&CLIENT_ID)];
6856 let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6857 let mut buf = vec![0; builder.bytes_len()];
6858 builder.serialize(&mut buf);
6859 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6861 assert_matches!(
6862 process_options(
6863 &msg,
6864 ExchangeType::AdvertiseToSolicit,
6865 Some(&CLIENT_ID),
6866 &NoIaRequested,
6867 &NoIaRequested
6868 ),
6869 Err(OptionsError::MissingServerId)
6870 );
6871 }
6872
6873 #[test]
6874 fn process_options_missing_client_id() {
6875 let options = [v6::DhcpOption::ServerId(&SERVER_ID[0])];
6876 let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6877 let mut buf = vec![0; builder.bytes_len()];
6878 builder.serialize(&mut buf);
6879 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6881 assert_matches!(
6882 process_options(
6883 &msg,
6884 ExchangeType::AdvertiseToSolicit,
6885 Some(&CLIENT_ID),
6886 &NoIaRequested,
6887 &NoIaRequested
6888 ),
6889 Err(OptionsError::MissingClientId)
6890 );
6891 }
6892
6893 #[test]
6894 fn process_options_mismatched_client_id() {
6895 let options = [
6896 v6::DhcpOption::ClientId(&MISMATCHED_CLIENT_ID),
6897 v6::DhcpOption::ServerId(&SERVER_ID[0]),
6898 ];
6899 let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6900 let mut buf = vec![0; builder.bytes_len()];
6901 builder.serialize(&mut buf);
6902 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6904 assert_matches!(
6905 process_options(&msg, ExchangeType::AdvertiseToSolicit, Some(&CLIENT_ID), &NoIaRequested, &NoIaRequested),
6906 Err(OptionsError::MismatchedClientId { got, want })
6907 if got[..] == MISMATCHED_CLIENT_ID && want == CLIENT_ID
6908 );
6909 }
6910
6911 #[test]
6912 fn process_options_unexpected_client_id() {
6913 let options =
6914 [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0])];
6915 let builder = v6::MessageBuilder::new(v6::MessageType::Reply, [0, 1, 2], &options);
6916 let mut buf = vec![0; builder.bytes_len()];
6917 builder.serialize(&mut buf);
6918 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6920 assert_matches!(
6921 process_options(&msg, ExchangeType::ReplyToInformationRequest, None, &NoIaRequested, &NoIaRequested),
6922 Err(OptionsError::UnexpectedClientId(got))
6923 if got[..] == CLIENT_ID
6924 );
6925 }
6926
6927 #[test_case(
6928 v6::MessageType::Reply,
6929 ExchangeType::ReplyToInformationRequest,
6930 v6::DhcpOption::Iana(v6::IanaSerializer::new(v6::IAID::new(0), T1.get(),T2.get(), &[]));
6931 "reply_to_information_request_ia_na"
6932 )]
6933 fn process_options_drop<'a>(
6934 message_type: v6::MessageType,
6935 exchange_type: ExchangeType,
6936 opt: v6::DhcpOption<'a>,
6937 ) {
6938 let options =
6939 [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0]), opt];
6940 let builder = v6::MessageBuilder::new(message_type, [0, 1, 2], &options);
6941 let mut buf = vec![0; builder.bytes_len()];
6942 builder.serialize(&mut buf);
6943 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6945 assert_matches!(
6946 process_options(&msg, exchange_type, Some(&CLIENT_ID), &NoIaRequested, &NoIaRequested),
6947 Err(OptionsError::InvalidOption(_))
6948 );
6949 }
6950
6951 #[test_case(
6952 v6::MessageType::Reply,
6953 ExchangeType::ReplyToInformationRequest;
6954 "reply_to_information_request"
6955 )]
6956 #[test_case(
6957 v6::MessageType::Reply,
6958 ExchangeType::ReplyWithLeases(RequestLeasesMessageType::Request);
6959 "reply_to_request"
6960 )]
6961 fn process_options_ignore_preference<'a>(
6962 message_type: v6::MessageType,
6963 exchange_type: ExchangeType,
6964 ) {
6965 let options = [
6966 v6::DhcpOption::ClientId(&CLIENT_ID),
6967 v6::DhcpOption::ServerId(&SERVER_ID[0]),
6968 v6::DhcpOption::Preference(ADVERTISE_MAX_PREFERENCE),
6969 ];
6970 let builder = v6::MessageBuilder::new(message_type, [0, 1, 2], &options);
6971 let mut buf = vec![0; builder.bytes_len()];
6972 builder.serialize(&mut buf);
6973 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6975 assert_matches!(
6976 process_options(&msg, exchange_type, Some(&CLIENT_ID), &NoIaRequested, &NoIaRequested),
6977 Ok(ProcessedOptions { result: Ok(Options { preference: None, .. }), .. })
6978 );
6979 }
6980
6981 #[test_case(
6982 v6::MessageType::Advertise,
6983 ExchangeType::AdvertiseToSolicit;
6984 "advertise_to_solicit"
6985 )]
6986 #[test_case(
6987 v6::MessageType::Reply,
6988 ExchangeType::ReplyWithLeases(RequestLeasesMessageType::Request);
6989 "reply_to_request"
6990 )]
6991 fn process_options_ignore_information_refresh_time<'a>(
6992 message_type: v6::MessageType,
6993 exchange_type: ExchangeType,
6994 ) {
6995 let options = [
6996 v6::DhcpOption::ClientId(&CLIENT_ID),
6997 v6::DhcpOption::ServerId(&SERVER_ID[0]),
6998 v6::DhcpOption::InformationRefreshTime(42u32),
6999 ];
7000 let builder = v6::MessageBuilder::new(message_type, [0, 1, 2], &options);
7001 let mut buf = vec![0; builder.bytes_len()];
7002 builder.serialize(&mut buf);
7003 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7005 assert_matches!(
7006 process_options(&msg, exchange_type, Some(&CLIENT_ID), &NoIaRequested, &NoIaRequested),
7007 Ok(ProcessedOptions {
7008 result: Ok(Options {
7009 next_contact_time: NextContactTime::RenewRebind { t1, t2 },
7010 ..
7011 }),
7012 ..
7013 }) => {
7014 assert_eq!(t1, v6::NonZeroTimeValue::Infinity);
7015 assert_eq!(t2, v6::NonZeroTimeValue::Infinity);
7016 }
7017 );
7018 }
7019
7020 mod process_reply_with_leases_unexpected_iaid {
7021 use super::*;
7022
7023 use test_case::test_case;
7024
7025 const EXPECTED_IAID: v6::IAID = v6::IAID::new(1);
7026 const UNEXPECTED_IAID: v6::IAID = v6::IAID::new(2);
7027
7028 struct TestCase {
7029 assigned_addresses: fn(Instant) -> HashMap<v6::IAID, AddressEntry<Instant>>,
7030 assigned_prefixes: fn(Instant) -> HashMap<v6::IAID, PrefixEntry<Instant>>,
7031 check_res: fn(Result<ProcessedReplyWithLeases<Instant>, ReplyWithLeasesError>),
7032 }
7033
7034 fn expected_iaids<V: IaValueTestExt>(
7035 time: Instant,
7036 ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
7037 HashMap::from([(
7038 EXPECTED_IAID,
7039 IaEntry::new_assigned(V::CONFIGURED[0], PREFERRED_LIFETIME, VALID_LIFETIME, time),
7040 )])
7041 }
7042
7043 fn unexpected_iaids<V: IaValueTestExt>(
7044 time: Instant,
7045 ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
7046 [EXPECTED_IAID, UNEXPECTED_IAID]
7047 .into_iter()
7048 .enumerate()
7049 .map(|(i, iaid)| {
7050 (
7051 iaid,
7052 IaEntry::new_assigned(
7053 V::CONFIGURED[i],
7054 PREFERRED_LIFETIME,
7055 VALID_LIFETIME,
7056 time,
7057 ),
7058 )
7059 })
7060 .collect()
7061 }
7062
7063 #[test_case(
7064 TestCase {
7065 assigned_addresses: expected_iaids::<Ipv6Addr>,
7066 assigned_prefixes: unexpected_iaids::<Subnet<Ipv6Addr>>,
7067 check_res: |res| {
7068 assert_matches!(
7069 res,
7070 Err(ReplyWithLeasesError::OptionsError(
7071 OptionsError::UnexpectedIaNa(iaid, _),
7072 )) => {
7073 assert_eq!(iaid, UNEXPECTED_IAID);
7074 }
7075 );
7076 },
7077 }
7078 ; "unknown IA_NA IAID")]
7079 #[test_case(
7080 TestCase {
7081 assigned_addresses: unexpected_iaids::<Ipv6Addr>,
7082 assigned_prefixes: expected_iaids::<Subnet<Ipv6Addr>>,
7083 check_res: |res| {
7084 assert_matches!(
7085 res,
7086 Err(ReplyWithLeasesError::OptionsError(
7087 OptionsError::UnexpectedIaPd(iaid, _),
7088 )) => {
7089 assert_eq!(iaid, UNEXPECTED_IAID);
7090 }
7091 );
7092 },
7093 }
7094 ; "unknown IA_PD IAID")]
7095 fn test(TestCase { assigned_addresses, assigned_prefixes, check_res }: TestCase) {
7096 let options =
7097 [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0])]
7098 .into_iter()
7099 .chain([EXPECTED_IAID, UNEXPECTED_IAID].into_iter().map(|iaid| {
7100 v6::DhcpOption::Iana(v6::IanaSerializer::new(iaid, T1.get(), T2.get(), &[]))
7101 }))
7102 .chain([EXPECTED_IAID, UNEXPECTED_IAID].into_iter().map(|iaid| {
7103 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(iaid, T1.get(), T2.get(), &[]))
7104 }))
7105 .collect::<Vec<_>>();
7106 let builder =
7107 v6::MessageBuilder::new(v6::MessageType::Reply, [0, 1, 2], options.as_slice());
7108 let mut buf = vec![0; builder.bytes_len()];
7109 builder.serialize(&mut buf);
7110 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7112
7113 let mut solicit_max_rt = MAX_SOLICIT_TIMEOUT;
7114 let time = Instant::now();
7115 check_res(process_reply_with_leases(
7116 &CLIENT_ID,
7117 &SERVER_ID[0],
7118 &assigned_addresses(time),
7119 &assigned_prefixes(time),
7120 &mut solicit_max_rt,
7121 &msg,
7122 RequestLeasesMessageType::Request,
7123 time,
7124 ))
7125 }
7126 }
7127
7128 #[test]
7129 fn ignore_advertise_with_unknown_ia() {
7130 let time = Instant::now();
7131 let mut client = testutil::start_and_assert_server_discovery(
7132 &(CLIENT_ID.into()),
7133 testutil::to_configured_addresses(
7134 1,
7135 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
7136 ),
7137 Default::default(),
7138 Vec::new(),
7139 StepRng::new(u64::MAX / 2, 0),
7140 time,
7141 );
7142
7143 let iana_options_0 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7144 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7145 60,
7146 60,
7147 &[],
7148 ))];
7149 let iana_options_99 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7150 CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
7151 60,
7152 60,
7153 &[],
7154 ))];
7155 let options = [
7156 v6::DhcpOption::ClientId(&CLIENT_ID),
7157 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7158 v6::DhcpOption::Preference(42),
7159 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7160 v6::IAID::new(0),
7161 T1.get(),
7162 T2.get(),
7163 &iana_options_0,
7164 )),
7165 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7168 v6::IAID::new(99),
7169 T1.get(),
7170 T2.get(),
7171 &iana_options_99,
7172 )),
7173 ];
7174
7175 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
7176 &client;
7177 let builder =
7178 v6::MessageBuilder::new(v6::MessageType::Advertise, *transaction_id, &options);
7179 let mut buf = vec![0; builder.bytes_len()];
7180 builder.serialize(&mut buf);
7181 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7183
7184 assert_eq!(client.handle_message_receive(msg, time), []);
7187 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
7188 &client;
7189 assert_matches!(
7190 state,
7191 Some(ClientState::ServerDiscovery(ServerDiscovery {
7192 client_id: _,
7193 configured_non_temporary_addresses: _,
7194 configured_delegated_prefixes: _,
7195 first_solicit_time: _,
7196 retrans_timeout: _,
7197 solicit_max_rt: _,
7198 collected_advertise,
7199 collected_sol_max_rt: _,
7200 })) => {
7201 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
7202 }
7203 );
7204 }
7205
7206 #[test]
7207 fn receive_advertise_with_max_preference() {
7208 let time = Instant::now();
7209 let mut client = testutil::start_and_assert_server_discovery(
7210 &(CLIENT_ID.into()),
7211 testutil::to_configured_addresses(
7212 2,
7213 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
7214 ),
7215 Default::default(),
7216 Vec::new(),
7217 StepRng::new(u64::MAX / 2, 0),
7218 time,
7219 );
7220
7221 let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7222 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7223 60,
7224 60,
7225 &[],
7226 ))];
7227
7228 for (preference, iana) in [
7233 (
7234 42,
7235 Some(v6::DhcpOption::Iana(v6::IanaSerializer::new(
7236 v6::IAID::new(0),
7237 T1.get(),
7238 T2.get(),
7239 &iana_options,
7240 ))),
7241 ),
7242 (255, None),
7243 ]
7244 .into_iter()
7245 {
7246 let options = [
7247 v6::DhcpOption::ClientId(&CLIENT_ID),
7248 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7249 v6::DhcpOption::Preference(preference),
7250 ]
7251 .into_iter()
7252 .chain(iana)
7253 .collect::<Vec<_>>();
7254 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
7255 &client;
7256 let builder =
7257 v6::MessageBuilder::new(v6::MessageType::Advertise, *transaction_id, &options);
7258 let mut buf = vec![0; builder.bytes_len()];
7259 builder.serialize(&mut buf);
7260 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7262 assert_eq!(client.handle_message_receive(msg, time), []);
7263 }
7264 let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7265 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7266 60,
7267 60,
7268 &[],
7269 ))];
7270 let options = [
7271 v6::DhcpOption::ClientId(&CLIENT_ID),
7272 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7273 v6::DhcpOption::Preference(255),
7274 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7275 v6::IAID::new(0),
7276 T1.get(),
7277 T2.get(),
7278 &iana_options,
7279 )),
7280 ];
7281 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
7282 &client;
7283 let builder =
7284 v6::MessageBuilder::new(v6::MessageType::Advertise, *transaction_id, &options);
7285 let mut buf = vec![0; builder.bytes_len()];
7286 builder.serialize(&mut buf);
7287 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7289
7290 let actions = client.handle_message_receive(msg, time);
7293 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } = client;
7294 let Requesting {
7295 client_id: _,
7296 non_temporary_addresses: _,
7297 delegated_prefixes: _,
7298 server_id: _,
7299 collected_advertise: _,
7300 first_request_time: _,
7301 retrans_timeout: _,
7302 transmission_count: _,
7303 solicit_max_rt: _,
7304 } = assert_matches!(
7305 state,
7306 Some(ClientState::Requesting(requesting)) => requesting
7307 );
7308 let buf = assert_matches!(
7309 &actions[..],
7310 [
7311 Action::CancelTimer(ClientTimerType::Retransmission),
7312 Action::SendMessage(buf),
7313 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
7314 ] => {
7315 assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
7316 buf
7317 }
7318 );
7319 assert_eq!(testutil::msg_type(buf), v6::MessageType::Request);
7320 }
7321
7322 #[test_case(T2.get() + 1, T2.get(), true)]
7324 #[test_case(INFINITY, T2.get(), true)]
7325 #[test_case(T1.get(), 0, false)]
7327 #[test_case(0, T2.get(), false)]
7329 #[test_case(T1.get(), T2.get(), false)]
7331 #[test_case(T1.get(), INFINITY, false)]
7332 #[test_case(INFINITY, INFINITY, false)]
7333 fn receive_advertise_with_invalid_iana(t1: u32, t2: u32, ignore_iana: bool) {
7334 let time = Instant::now();
7335 let mut client = testutil::start_and_assert_server_discovery(
7336 &(CLIENT_ID.into()),
7337 testutil::to_configured_addresses(
7338 1,
7339 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
7340 ),
7341 Default::default(),
7342 Vec::new(),
7343 StepRng::new(u64::MAX / 2, 0),
7344 time,
7345 );
7346 let transaction_id = client.transaction_id;
7347
7348 let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7349 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7350 PREFERRED_LIFETIME.get(),
7351 VALID_LIFETIME.get(),
7352 &[],
7353 ))];
7354 let options = [
7355 v6::DhcpOption::ClientId(&CLIENT_ID),
7356 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7357 v6::DhcpOption::Iana(v6::IanaSerializer::new(v6::IAID::new(0), t1, t2, &iana_options)),
7358 ];
7359 let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, transaction_id, &options);
7360 let mut buf = vec![0; builder.bytes_len()];
7361 builder.serialize(&mut buf);
7362 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7364
7365 assert_matches!(client.handle_message_receive(msg, time)[..], []);
7366 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
7367 &client;
7368 let collected_advertise = assert_matches!(
7369 state,
7370 Some(ClientState::ServerDiscovery(ServerDiscovery {
7371 client_id: _,
7372 configured_non_temporary_addresses: _,
7373 configured_delegated_prefixes: _,
7374 first_solicit_time: _,
7375 retrans_timeout: _,
7376 solicit_max_rt: _,
7377 collected_advertise,
7378 collected_sol_max_rt: _,
7379 })) => collected_advertise
7380 );
7381 match ignore_iana {
7382 true => assert!(collected_advertise.is_empty(), "{:?}", collected_advertise),
7383 false => {
7384 assert_matches!(
7385 collected_advertise.peek(),
7386 Some(AdvertiseMessage {
7387 server_id: _,
7388 non_temporary_addresses,
7389 delegated_prefixes: _,
7390 dns_servers: _,
7391 preference: _,
7392 receive_time: _,
7393 preferred_non_temporary_addresses_count: _,
7394 preferred_delegated_prefixes_count: _,
7395 }) => {
7396 assert_eq!(
7397 non_temporary_addresses,
7398 &HashMap::from([(
7399 v6::IAID::new(0),
7400 HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])
7401 )])
7402 );
7403 }
7404 )
7405 }
7406 }
7407 }
7408
7409 #[test]
7410 fn select_first_server_while_retransmitting() {
7411 let time = Instant::now();
7412 let mut client = testutil::start_and_assert_server_discovery(
7413 &(CLIENT_ID.into()),
7414 testutil::to_configured_addresses(
7415 1,
7416 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
7417 ),
7418 Default::default(),
7419 Vec::new(),
7420 StepRng::new(u64::MAX / 2, 0),
7421 time,
7422 );
7423
7424 let actions = client.handle_timeout(ClientTimerType::Retransmission, time);
7427 assert_matches!(
7428 &actions[..],
7429 [
7430 Action::SendMessage(buf),
7431 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
7432 ] => {
7433 assert_eq!(testutil::msg_type(buf), v6::MessageType::Solicit);
7434 assert_eq!(*instant, time.add(2 * INITIAL_SOLICIT_TIMEOUT));
7435 buf
7436 }
7437 );
7438 let ClientStateMachine { transaction_id, options_to_request: _, state, rng: _ } = &client;
7439 {
7440 let ServerDiscovery {
7441 client_id: _,
7442 configured_non_temporary_addresses: _,
7443 configured_delegated_prefixes: _,
7444 first_solicit_time: _,
7445 retrans_timeout: _,
7446 solicit_max_rt: _,
7447 collected_advertise,
7448 collected_sol_max_rt: _,
7449 } = assert_matches!(
7450 state,
7451 Some(ClientState::ServerDiscovery(server_discovery)) => server_discovery
7452 );
7453 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
7454 }
7455
7456 let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7457 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7458 60,
7459 60,
7460 &[],
7461 ))];
7462 let options = [
7463 v6::DhcpOption::ClientId(&CLIENT_ID),
7464 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7465 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7466 v6::IAID::new(0),
7467 T1.get(),
7468 T2.get(),
7469 &iana_options,
7470 )),
7471 ];
7472 let builder =
7473 v6::MessageBuilder::new(v6::MessageType::Advertise, *transaction_id, &options);
7474 let mut buf = vec![0; builder.bytes_len()];
7475 builder.serialize(&mut buf);
7476 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7478
7479 let actions = client.handle_message_receive(msg, time);
7482 assert_matches!(
7483 &actions[..],
7484 [
7485 Action::CancelTimer(ClientTimerType::Retransmission),
7486 Action::SendMessage(buf),
7487 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
7488 ] => {
7489 assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
7490 assert_eq!(testutil::msg_type(buf), v6::MessageType::Request);
7491 }
7492 );
7493 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } = client;
7494 let Requesting {
7495 client_id: _,
7496 non_temporary_addresses: _,
7497 delegated_prefixes: _,
7498 server_id: _,
7499 collected_advertise,
7500 first_request_time: _,
7501 retrans_timeout: _,
7502 transmission_count: _,
7503 solicit_max_rt: _,
7504 } = assert_matches!(
7505 state,
7506 Some(ClientState::Requesting(requesting )) => requesting
7507 );
7508 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
7509 }
7510
7511 #[test]
7512 fn send_request() {
7513 let (mut _client, _transaction_id) = testutil::request_and_assert(
7514 &(CLIENT_ID.into()),
7515 SERVER_ID[0],
7516 CONFIGURED_NON_TEMPORARY_ADDRESSES.into_iter().map(TestIaNa::new_default).collect(),
7517 CONFIGURED_DELEGATED_PREFIXES.into_iter().map(TestIaPd::new_default).collect(),
7518 &[],
7519 StepRng::new(u64::MAX / 2, 0),
7520 Instant::now(),
7521 );
7522 }
7523
7524 #[test]
7526 fn requesting_receive_reply_with_failure_status_code() {
7527 let options_to_request = vec![];
7528 let configured_non_temporary_addresses = testutil::to_configured_addresses(1, vec![]);
7529 let advertised_non_temporary_addresses = [CONFIGURED_NON_TEMPORARY_ADDRESSES[0]];
7530 let configured_delegated_prefixes = HashMap::new();
7531 let mut want_collected_advertise = [
7532 AdvertiseMessage::new_default(
7533 SERVER_ID[1],
7534 &CONFIGURED_NON_TEMPORARY_ADDRESSES[1..=1],
7535 &[],
7536 &[],
7537 &configured_non_temporary_addresses,
7538 &configured_delegated_prefixes,
7539 ),
7540 AdvertiseMessage::new_default(
7541 SERVER_ID[2],
7542 &CONFIGURED_NON_TEMPORARY_ADDRESSES[2..=2],
7543 &[],
7544 &[],
7545 &configured_non_temporary_addresses,
7546 &configured_delegated_prefixes,
7547 ),
7548 ]
7549 .into_iter()
7550 .collect::<BinaryHeap<_>>();
7551 let mut rng = StepRng::new(u64::MAX / 2, 0);
7552
7553 let time = Instant::now();
7554 let Transition { state, actions: _, transaction_id } = Requesting::start(
7555 CLIENT_ID.into(),
7556 SERVER_ID[0].to_vec(),
7557 advertise_to_ia_entries(
7558 testutil::to_default_ias_map(&advertised_non_temporary_addresses),
7559 configured_non_temporary_addresses.clone(),
7560 ),
7561 Default::default(), &options_to_request[..],
7563 want_collected_advertise.clone(),
7564 MAX_SOLICIT_TIMEOUT,
7565 &mut rng,
7566 time,
7567 );
7568
7569 let expected_non_temporary_addresses = (0..)
7570 .map(v6::IAID::new)
7571 .zip(
7572 advertised_non_temporary_addresses
7573 .iter()
7574 .map(|addr| AddressEntry::ToRequest(HashSet::from([*addr]))),
7575 )
7576 .collect::<HashMap<v6::IAID, AddressEntry<_>>>();
7577 {
7578 let Requesting {
7579 non_temporary_addresses: got_non_temporary_addresses,
7580 delegated_prefixes: _,
7581 server_id,
7582 collected_advertise,
7583 client_id: _,
7584 first_request_time: _,
7585 retrans_timeout: _,
7586 transmission_count: _,
7587 solicit_max_rt: _,
7588 } = assert_matches!(&state, ClientState::Requesting(requesting) => requesting);
7589 assert_eq!(server_id[..], SERVER_ID[0]);
7590 assert_eq!(*got_non_temporary_addresses, expected_non_temporary_addresses);
7591 assert_eq!(
7592 collected_advertise.clone().into_sorted_vec(),
7593 want_collected_advertise.clone().into_sorted_vec()
7594 );
7595 }
7596
7597 let options = [
7600 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7601 v6::DhcpOption::ClientId(&CLIENT_ID),
7602 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7603 v6::IAID::new(0),
7604 T1.get(),
7605 T2.get(),
7606 &[],
7607 )),
7608 v6::DhcpOption::StatusCode(v6::ErrorStatusCode::UnspecFail.into(), ""),
7609 ];
7610 let request_transaction_id = transaction_id.unwrap();
7611 let builder =
7612 v6::MessageBuilder::new(v6::MessageType::Reply, request_transaction_id, &options);
7613 let mut buf = vec![0; builder.bytes_len()];
7614 builder.serialize(&mut buf);
7615 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7617 let Transition { state, actions, transaction_id: got_transaction_id } =
7618 state.reply_message_received(&options_to_request, &mut rng, msg, time);
7619 {
7620 let Requesting {
7621 client_id: _,
7622 non_temporary_addresses: got_non_temporary_addresses,
7623 delegated_prefixes: _,
7624 server_id,
7625 collected_advertise,
7626 first_request_time: _,
7627 retrans_timeout: _,
7628 transmission_count: _,
7629 solicit_max_rt: _,
7630 } = assert_matches!(&state, ClientState::Requesting(requesting) => requesting);
7631 assert_eq!(server_id[..], SERVER_ID[0]);
7632 assert_eq!(
7633 collected_advertise.clone().into_sorted_vec(),
7634 want_collected_advertise.clone().into_sorted_vec()
7635 );
7636 assert_eq!(*got_non_temporary_addresses, expected_non_temporary_addresses);
7637 }
7638 assert_eq!(got_transaction_id, None);
7639 assert_eq!(actions[..], []);
7640
7641 let options = [
7644 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7645 v6::DhcpOption::ClientId(&CLIENT_ID),
7646 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7647 v6::IAID::new(0),
7648 T1.get(),
7649 T2.get(),
7650 &[],
7651 )),
7652 v6::DhcpOption::StatusCode(v6::ErrorStatusCode::NotOnLink.into(), ""),
7653 ];
7654 let request_transaction_id = transaction_id.unwrap();
7655 let builder =
7656 v6::MessageBuilder::new(v6::MessageType::Reply, request_transaction_id, &options);
7657 let mut buf = vec![0; builder.bytes_len()];
7658 builder.serialize(&mut buf);
7659 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7661 let Transition { state, actions: _, transaction_id } =
7662 state.reply_message_received(&options_to_request, &mut rng, msg, time);
7663
7664 let expected_non_temporary_addresses: HashMap<v6::IAID, AddressEntry<_>> =
7665 HashMap::from([(v6::IAID::new(0), AddressEntry::ToRequest(Default::default()))]);
7666 {
7667 let Requesting {
7668 client_id: _,
7669 non_temporary_addresses: got_non_temporary_addresses,
7670 delegated_prefixes: _,
7671 server_id,
7672 collected_advertise,
7673 first_request_time: _,
7674 retrans_timeout: _,
7675 transmission_count: _,
7676 solicit_max_rt: _,
7677 } = assert_matches!(
7678 &state,
7679 ClientState::Requesting(requesting) => requesting
7680 );
7681 assert_eq!(server_id[..], SERVER_ID[0]);
7682 assert_eq!(
7683 collected_advertise.clone().into_sorted_vec(),
7684 want_collected_advertise.clone().into_sorted_vec()
7685 );
7686 assert_eq!(*got_non_temporary_addresses, expected_non_temporary_addresses);
7687 }
7688 assert!(transaction_id.is_some());
7689
7690 let iana_options =
7693 [v6::DhcpOption::StatusCode(v6::ErrorStatusCode::NoAddrsAvail.into(), "")];
7694 let options = [
7695 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7696 v6::DhcpOption::ClientId(&CLIENT_ID),
7697 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7698 v6::IAID::new(0),
7699 T1.get(),
7700 T2.get(),
7701 &iana_options,
7702 )),
7703 ];
7704 let builder =
7705 v6::MessageBuilder::new(v6::MessageType::Reply, transaction_id.unwrap(), &options);
7706 let mut buf = vec![0; builder.bytes_len()];
7707 builder.serialize(&mut buf);
7708 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7710 let Transition { state, actions, transaction_id } =
7711 state.reply_message_received(&options_to_request, &mut rng, msg, time);
7712 {
7713 let Requesting {
7714 server_id,
7715 collected_advertise,
7716 client_id: _,
7717 non_temporary_addresses: _,
7718 delegated_prefixes: _,
7719 first_request_time: _,
7720 retrans_timeout: _,
7721 transmission_count: _,
7722 solicit_max_rt: _,
7723 } = assert_matches!(
7724 state,
7725 ClientState::Requesting(requesting) => requesting
7726 );
7727 assert_eq!(server_id[..], SERVER_ID[1]);
7728 let _: Option<AdvertiseMessage<_>> = want_collected_advertise.pop();
7729 assert_eq!(
7730 collected_advertise.clone().into_sorted_vec(),
7731 want_collected_advertise.clone().into_sorted_vec(),
7732 );
7733 }
7734 assert_matches!(
7735 &actions[..],
7736 [
7737 Action::CancelTimer(ClientTimerType::Retransmission),
7738 Action::SendMessage(_buf),
7739 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
7740 ] => {
7741 assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
7742 }
7743 );
7744 assert!(transaction_id.is_some());
7745 }
7746
7747 #[test]
7748 fn requesting_receive_reply_with_ia_not_on_link() {
7749 let options_to_request = vec![];
7750 let configured_non_temporary_addresses = testutil::to_configured_addresses(
7751 2,
7752 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
7753 );
7754 let mut rng = StepRng::new(u64::MAX / 2, 0);
7755
7756 let time = Instant::now();
7757 let Transition { state, actions: _, transaction_id } = Requesting::start(
7758 CLIENT_ID.into(),
7759 SERVER_ID[0].to_vec(),
7760 advertise_to_ia_entries(
7761 testutil::to_default_ias_map(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]),
7762 configured_non_temporary_addresses.clone(),
7763 ),
7764 Default::default(), &options_to_request[..],
7766 BinaryHeap::new(),
7767 MAX_SOLICIT_TIMEOUT,
7768 &mut rng,
7769 time,
7770 );
7771
7772 let iana_options1 = [v6::DhcpOption::StatusCode(v6::ErrorStatusCode::NotOnLink.into(), "")];
7776 let iana_options2 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7777 CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
7778 PREFERRED_LIFETIME.get(),
7779 VALID_LIFETIME.get(),
7780 &[],
7781 ))];
7782 let iaid1 = v6::IAID::new(0);
7783 let iaid2 = v6::IAID::new(1);
7784 let options = [
7785 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7786 v6::DhcpOption::ClientId(&CLIENT_ID),
7787 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7788 iaid1,
7789 T1.get(),
7790 T2.get(),
7791 &iana_options1,
7792 )),
7793 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7794 iaid2,
7795 T1.get(),
7796 T2.get(),
7797 &iana_options2,
7798 )),
7799 ];
7800 let builder =
7801 v6::MessageBuilder::new(v6::MessageType::Reply, transaction_id.unwrap(), &options);
7802 let mut buf = vec![0; builder.bytes_len()];
7803 builder.serialize(&mut buf);
7804 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7806 let Transition { state, actions, transaction_id } =
7807 state.reply_message_received(&options_to_request, &mut rng, msg, time);
7808 let expected_non_temporary_addresses = HashMap::from([
7809 (iaid1, AddressEntry::ToRequest(Default::default())),
7810 (
7811 iaid2,
7812 AddressEntry::Assigned(HashMap::from([(
7813 CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
7814 LifetimesInfo {
7815 lifetimes: Lifetimes {
7816 preferred_lifetime: v6::TimeValue::NonZero(
7817 v6::NonZeroTimeValue::Finite(PREFERRED_LIFETIME),
7818 ),
7819 valid_lifetime: v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
7820 },
7821 updated_at: time,
7822 },
7823 )])),
7824 ),
7825 ]);
7826 {
7827 let Assigned {
7828 client_id: _,
7829 non_temporary_addresses,
7830 delegated_prefixes,
7831 server_id,
7832 dns_servers: _,
7833 solicit_max_rt: _,
7834 _marker,
7835 } = assert_matches!(
7836 state,
7837 ClientState::Assigned(assigned) => assigned
7838 );
7839 assert_eq!(server_id[..], SERVER_ID[0]);
7840 assert_eq!(non_temporary_addresses, expected_non_temporary_addresses);
7841 assert_eq!(delegated_prefixes, HashMap::new());
7842 }
7843 assert_matches!(
7844 &actions[..],
7845 [
7846 Action::CancelTimer(ClientTimerType::Retransmission),
7847 Action::ScheduleTimer(ClientTimerType::Renew, t1),
7848 Action::ScheduleTimer(ClientTimerType::Rebind, t2),
7849 Action::IaNaUpdates(iana_updates),
7850 Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
7851 ] => {
7852 assert_eq!(*t1, time.add(Duration::from_secs(T1.get().into())));
7853 assert_eq!(*t2, time.add(Duration::from_secs(T2.get().into())));
7854 assert_eq!(
7855 *restart_time,
7856 time.add(Duration::from_secs(VALID_LIFETIME.get().into())),
7857 );
7858 assert_eq!(
7859 iana_updates,
7860 &HashMap::from([(
7861 iaid2,
7862 HashMap::from([(
7863 CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
7864 IaValueUpdateKind::Added(Lifetimes::new_default()),
7865 )]),
7866 )]),
7867 );
7868 }
7869 );
7870 assert!(transaction_id.is_none());
7871 }
7872
7873 #[test_case(0, VALID_LIFETIME.get(), true)]
7874 #[test_case(PREFERRED_LIFETIME.get(), 0, false)]
7875 #[test_case(VALID_LIFETIME.get() + 1, VALID_LIFETIME.get(), false)]
7876 #[test_case(0, 0, false)]
7877 #[test_case(PREFERRED_LIFETIME.get(), VALID_LIFETIME.get(), true)]
7878 fn requesting_receive_reply_with_invalid_ia_lifetimes(
7879 preferred_lifetime: u32,
7880 valid_lifetime: u32,
7881 valid_ia: bool,
7882 ) {
7883 let options_to_request = vec![];
7884 let configured_non_temporary_addresses = testutil::to_configured_addresses(1, vec![]);
7885 let mut rng = StepRng::new(u64::MAX / 2, 0);
7886
7887 let time = Instant::now();
7888 let Transition { state, actions: _, transaction_id } = Requesting::start(
7889 CLIENT_ID.into(),
7890 SERVER_ID[0].to_vec(),
7891 advertise_to_ia_entries(
7892 testutil::to_default_ias_map(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..1]),
7893 configured_non_temporary_addresses.clone(),
7894 ),
7895 Default::default(), &options_to_request[..],
7897 BinaryHeap::new(),
7898 MAX_SOLICIT_TIMEOUT,
7899 &mut rng,
7900 time,
7901 );
7902
7903 let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7905 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7906 preferred_lifetime,
7907 valid_lifetime,
7908 &[],
7909 ))];
7910 let options = [
7911 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7912 v6::DhcpOption::ClientId(&CLIENT_ID),
7913 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7914 v6::IAID::new(0),
7915 T1.get(),
7916 T2.get(),
7917 &iana_options,
7918 )),
7919 ];
7920 let builder =
7921 v6::MessageBuilder::new(v6::MessageType::Reply, transaction_id.unwrap(), &options);
7922 let mut buf = vec![0; builder.bytes_len()];
7923 builder.serialize(&mut buf);
7924 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7926 let Transition { state, actions: _, transaction_id: _ } =
7927 state.reply_message_received(&options_to_request, &mut rng, msg, time);
7928 match valid_ia {
7929 true =>
7930 {
7933 let Assigned {
7934 client_id: _,
7935 non_temporary_addresses: _,
7936 delegated_prefixes: _,
7937 server_id: _,
7938 dns_servers: _,
7939 solicit_max_rt: _,
7940 _marker,
7941 } = assert_matches!(
7942 state,
7943 ClientState::Assigned(assigned) => assigned
7944 );
7945 }
7946 false =>
7947 {
7950 let ServerDiscovery {
7951 client_id: _,
7952 configured_non_temporary_addresses: _,
7953 configured_delegated_prefixes: _,
7954 first_solicit_time: _,
7955 retrans_timeout: _,
7956 solicit_max_rt: _,
7957 collected_advertise,
7958 collected_sol_max_rt: _,
7959 } = assert_matches!(
7960 state,
7961 ClientState::ServerDiscovery(server_discovery) => server_discovery
7962 );
7963 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
7964 }
7965 }
7966 }
7967
7968 #[test]
7970 fn compute_t1_t2_on_reply_to_request() {
7971 let mut rng = StepRng::new(u64::MAX / 2, 0);
7972
7973 for (
7974 (ia1_preferred_lifetime, ia1_valid_lifetime, ia1_t1, ia1_t2),
7975 (ia2_preferred_lifetime, ia2_valid_lifetime, ia2_t1, ia2_t2),
7976 expected_t1,
7977 expected_t2,
7978 ) in vec![
7979 (
7983 (100, 160, 0, 0),
7984 (120, 180, 0, 0),
7985 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(50).expect("should succeed")),
7986 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(80).expect("should succeed")),
7987 ),
7988 (
7989 (INFINITY, INFINITY, 0, 0),
7990 (120, 180, 0, 0),
7991 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(60).expect("should succeed")),
7992 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(96).expect("should succeed")),
7993 ),
7994 (
7997 (INFINITY, INFINITY, 0, 0),
7998 (INFINITY, INFINITY, 0, 0),
7999 v6::NonZeroTimeValue::Infinity,
8000 v6::NonZeroTimeValue::Infinity,
8001 ),
8002 (
8004 (INFINITY, INFINITY, 50, INFINITY),
8005 (INFINITY, INFINITY, 50, INFINITY),
8006 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(50).expect("should succeed")),
8007 v6::NonZeroTimeValue::Infinity,
8008 ),
8009 (
8014 (100, 160, 40, 70),
8015 (120, 180, 50, 80),
8016 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(40).expect("should succeed")),
8017 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(70).expect("should succeed")),
8018 ),
8019 ] {
8020 let time = Instant::now();
8021 let Transition { state, actions: _, transaction_id } = Requesting::start(
8022 CLIENT_ID.into(),
8023 SERVER_ID[0].to_vec(),
8024 advertise_to_ia_entries(
8025 testutil::to_default_ias_map(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]),
8026 testutil::to_configured_addresses(
8027 2,
8028 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
8029 ),
8030 ),
8031 Default::default(), &[],
8033 BinaryHeap::new(),
8034 MAX_SOLICIT_TIMEOUT,
8035 &mut rng,
8036 time,
8037 );
8038
8039 let iana_options1 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
8040 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
8041 ia1_preferred_lifetime,
8042 ia1_valid_lifetime,
8043 &[],
8044 ))];
8045 let iana_options2 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
8046 CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
8047 ia2_preferred_lifetime,
8048 ia2_valid_lifetime,
8049 &[],
8050 ))];
8051 let iaid1 = v6::IAID::new(0);
8052 let iaid2 = v6::IAID::new(1);
8053 let options = [
8054 v6::DhcpOption::ServerId(&SERVER_ID[0]),
8055 v6::DhcpOption::ClientId(&CLIENT_ID),
8056 v6::DhcpOption::Iana(v6::IanaSerializer::new(
8057 iaid1,
8058 ia1_t1,
8059 ia1_t2,
8060 &iana_options1,
8061 )),
8062 v6::DhcpOption::Iana(v6::IanaSerializer::new(
8063 iaid2,
8064 ia2_t1,
8065 ia2_t2,
8066 &iana_options2,
8067 )),
8068 ];
8069 let builder =
8070 v6::MessageBuilder::new(v6::MessageType::Reply, transaction_id.unwrap(), &options);
8071 let mut buf = vec![0; builder.bytes_len()];
8072 builder.serialize(&mut buf);
8073 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8075 let Transition { state, actions, transaction_id: _ } =
8076 state.reply_message_received(&[], &mut rng, msg, time);
8077 let Assigned {
8078 client_id: _,
8079 non_temporary_addresses: _,
8080 delegated_prefixes: _,
8081 server_id: _,
8082 dns_servers: _,
8083 solicit_max_rt: _,
8084 _marker,
8085 } = assert_matches!(
8086 state,
8087 ClientState::Assigned(assigned) => assigned
8088 );
8089
8090 let update_actions = [Action::IaNaUpdates(HashMap::from([
8091 (
8092 iaid1,
8093 HashMap::from([(
8094 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
8095 IaValueUpdateKind::Added(Lifetimes::new(
8096 ia1_preferred_lifetime,
8097 ia1_valid_lifetime,
8098 )),
8099 )]),
8100 ),
8101 (
8102 iaid2,
8103 HashMap::from([(
8104 CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
8105 IaValueUpdateKind::Added(Lifetimes::new(
8106 ia2_preferred_lifetime,
8107 ia2_valid_lifetime,
8108 )),
8109 )]),
8110 ),
8111 ]))];
8112
8113 let timer_action = |timer, tv| match tv {
8114 v6::NonZeroTimeValue::Finite(tv) => {
8115 Action::ScheduleTimer(timer, time.add(Duration::from_secs(tv.get().into())))
8116 }
8117 v6::NonZeroTimeValue::Infinity => Action::CancelTimer(timer),
8118 };
8119
8120 let non_zero_time_value = |v| {
8121 assert_matches!(
8122 v6::TimeValue::new(v),
8123 v6::TimeValue::NonZero(v) => v
8124 )
8125 };
8126
8127 assert!(expected_t1 <= expected_t2);
8128 assert_eq!(
8129 actions,
8130 [
8131 Action::CancelTimer(ClientTimerType::Retransmission),
8132 timer_action(ClientTimerType::Renew, expected_t1),
8133 timer_action(ClientTimerType::Rebind, expected_t2),
8134 ]
8135 .into_iter()
8136 .chain(update_actions)
8137 .chain([timer_action(
8138 ClientTimerType::RestartServerDiscovery,
8139 std::cmp::max(
8140 non_zero_time_value(ia1_valid_lifetime),
8141 non_zero_time_value(ia2_valid_lifetime),
8142 ),
8143 )])
8144 .collect::<Vec<_>>(),
8145 );
8146 }
8147 }
8148
8149 #[test]
8150 fn use_advertise_from_best_server() {
8151 let time = Instant::now();
8152 let mut client = testutil::start_and_assert_server_discovery(
8153 &(CLIENT_ID.into()),
8154 testutil::to_configured_addresses(
8155 CONFIGURED_NON_TEMPORARY_ADDRESSES.len(),
8156 CONFIGURED_NON_TEMPORARY_ADDRESSES.map(|a| HashSet::from([a])),
8157 ),
8158 testutil::to_configured_prefixes(
8159 CONFIGURED_DELEGATED_PREFIXES.len(),
8160 CONFIGURED_DELEGATED_PREFIXES.map(|a| HashSet::from([a])),
8161 ),
8162 Vec::new(),
8163 StepRng::new(u64::MAX / 2, 0),
8164 time,
8165 );
8166 let transaction_id = client.transaction_id;
8167
8168 let buf = TestMessageBuilder {
8170 transaction_id,
8171 message_type: v6::MessageType::Advertise,
8172 client_id: &CLIENT_ID,
8173 server_id: &SERVER_ID[0],
8174 preference: None,
8175 dns_servers: None,
8176 ia_nas: (0..)
8177 .map(v6::IAID::new)
8178 .zip(CONFIGURED_NON_TEMPORARY_ADDRESSES)
8179 .map(|(iaid, value)| (iaid, TestIa::new_default(value))),
8180 ia_pds: std::iter::empty(),
8181 }
8182 .build();
8183 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8185 assert_matches!(client.handle_message_receive(msg, time)[..], []);
8186
8187 let buf = TestMessageBuilder {
8189 transaction_id,
8190 message_type: v6::MessageType::Advertise,
8191 client_id: &CLIENT_ID,
8192 server_id: &SERVER_ID[1],
8193 preference: None,
8194 dns_servers: None,
8195 ia_nas: std::iter::empty(),
8196 ia_pds: (0..)
8197 .map(v6::IAID::new)
8198 .zip(CONFIGURED_DELEGATED_PREFIXES)
8199 .map(|(iaid, value)| (iaid, TestIa::new_default(value))),
8200 }
8201 .build();
8202 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8204 assert_matches!(client.handle_message_receive(msg, time)[..], []);
8205
8206 let buf = TestMessageBuilder {
8213 transaction_id,
8214 message_type: v6::MessageType::Advertise,
8215 client_id: &CLIENT_ID,
8216 server_id: &SERVER_ID[2],
8217 preference: None,
8218 dns_servers: None,
8219 ia_nas: std::iter::once((
8220 v6::IAID::new(0),
8221 TestIa {
8222 values: HashMap::from([(
8223 REPLY_NON_TEMPORARY_ADDRESSES[0],
8224 Lifetimes {
8225 preferred_lifetime: v6::TimeValue::NonZero(
8226 v6::NonZeroTimeValue::Finite(PREFERRED_LIFETIME),
8227 ),
8228 valid_lifetime: v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
8229 },
8230 )]),
8231 t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
8232 t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8233 },
8234 )),
8235 ia_pds: std::iter::once((
8236 v6::IAID::new(0),
8237 TestIa {
8238 values: HashMap::from([(
8239 REPLY_DELEGATED_PREFIXES[0],
8240 Lifetimes {
8241 preferred_lifetime: v6::TimeValue::NonZero(
8242 v6::NonZeroTimeValue::Finite(PREFERRED_LIFETIME),
8243 ),
8244 valid_lifetime: v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
8245 },
8246 )]),
8247 t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
8248 t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8249 },
8250 )),
8251 }
8252 .build();
8253 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8255 assert_matches!(client.handle_message_receive(msg, time)[..], []);
8256
8257 let actions = client.handle_timeout(ClientTimerType::Retransmission, time);
8266 assert_matches!(
8267 &actions[..],
8268 [
8269 Action::CancelTimer(ClientTimerType::Retransmission),
8270 Action::SendMessage(buf),
8271 Action::ScheduleTimer(ClientTimerType::Retransmission, instant),
8272 ] => {
8273 assert_eq!(testutil::msg_type(buf), v6::MessageType::Request);
8274 assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
8275 }
8276 );
8277 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } = client;
8278 assert_matches!(
8279 state,
8280 Some(ClientState::Requesting(Requesting {
8281 client_id: _,
8282 non_temporary_addresses,
8283 delegated_prefixes,
8284 server_id,
8285 collected_advertise: _,
8286 first_request_time: _,
8287 retrans_timeout: _,
8288 transmission_count: _,
8289 solicit_max_rt: _,
8290 })) => {
8291 assert_eq!(&server_id, &SERVER_ID[2]);
8292 assert_eq!(
8293 non_temporary_addresses,
8294 [REPLY_NON_TEMPORARY_ADDRESSES[0]]
8295 .iter()
8296 .chain(CONFIGURED_NON_TEMPORARY_ADDRESSES[1..3].iter())
8297 .enumerate().map(|(iaid, addr)| {
8298 (v6::IAID::new(iaid.try_into().unwrap()), AddressEntry::ToRequest(HashSet::from([*addr])))
8299 }).collect::<HashMap<_, _>>()
8300 );
8301 assert_eq!(
8302 delegated_prefixes,
8303 [REPLY_DELEGATED_PREFIXES[0]]
8304 .iter()
8305 .chain(CONFIGURED_DELEGATED_PREFIXES[1..3].iter())
8306 .enumerate().map(|(iaid, addr)| {
8307 (v6::IAID::new(iaid.try_into().unwrap()), PrefixEntry::ToRequest(HashSet::from([*addr])))
8308 }).collect::<HashMap<_, _>>()
8309 );
8310 }
8311 );
8312 }
8313
8314 #[test]
8316 fn requesting_retransmit_max_retrans_count() {
8317 let time = Instant::now();
8318 let mut client = testutil::start_and_assert_server_discovery(
8319 &(CLIENT_ID.into()),
8320 testutil::to_configured_addresses(
8321 1,
8322 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
8323 ),
8324 Default::default(),
8325 Vec::new(),
8326 StepRng::new(u64::MAX / 2, 0),
8327 time,
8328 );
8329 let transaction_id = client.transaction_id;
8330
8331 for i in 0..2 {
8332 let buf = TestMessageBuilder {
8333 transaction_id,
8334 message_type: v6::MessageType::Advertise,
8335 client_id: &CLIENT_ID,
8336 server_id: &SERVER_ID[i],
8337 preference: None,
8338 dns_servers: None,
8339 ia_nas: std::iter::once((
8340 v6::IAID::new(0),
8341 TestIa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[i]),
8342 )),
8343 ia_pds: std::iter::empty(),
8344 }
8345 .build();
8346 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8348 assert_matches!(client.handle_message_receive(msg, time)[..], []);
8349 }
8350 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8351 &client;
8352 let ServerDiscovery {
8353 client_id: _,
8354 configured_non_temporary_addresses: _,
8355 configured_delegated_prefixes: _,
8356 first_solicit_time: _,
8357 retrans_timeout: _,
8358 solicit_max_rt: _,
8359 collected_advertise: want_collected_advertise,
8360 collected_sol_max_rt: _,
8361 } = assert_matches!(
8362 state,
8363 Some(ClientState::ServerDiscovery(server_discovery)) => server_discovery
8364 );
8365 let mut want_collected_advertise = want_collected_advertise.clone();
8366 let _: Option<AdvertiseMessage<_>> = want_collected_advertise.pop();
8367
8368 assert_matches!(
8371 &client.handle_timeout(ClientTimerType::Retransmission, time)[..],
8372 [
8373 Action::CancelTimer(ClientTimerType::Retransmission),
8374 Action::SendMessage(buf),
8375 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
8376 ] => {
8377 assert_eq!(testutil::msg_type(buf), v6::MessageType::Request);
8378 assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
8379 }
8380 );
8381 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8382 &client;
8383 {
8384 let Requesting {
8385 client_id: _,
8386 non_temporary_addresses: _,
8387 delegated_prefixes: _,
8388 server_id,
8389 collected_advertise,
8390 first_request_time: _,
8391 retrans_timeout: _,
8392 transmission_count,
8393 solicit_max_rt: _,
8394 } = assert_matches!(state, Some(ClientState::Requesting(requesting)) => requesting);
8395 assert_eq!(
8396 collected_advertise.clone().into_sorted_vec(),
8397 want_collected_advertise.clone().into_sorted_vec()
8398 );
8399 assert_eq!(server_id[..], SERVER_ID[0]);
8400 assert_eq!(*transmission_count, 1);
8401 }
8402
8403 for count in 2..=(REQUEST_MAX_RC + 1) {
8404 assert_matches!(
8405 &client.handle_timeout(ClientTimerType::Retransmission, time)[..],
8406 [
8407 Action::SendMessage(buf),
8408 Action::ScheduleTimer(ClientTimerType::Retransmission, _timeout)
8411 ] if testutil::msg_type(buf) == v6::MessageType::Request
8412 );
8413 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8414 &client;
8415 let Requesting {
8416 client_id: _,
8417 non_temporary_addresses: _,
8418 delegated_prefixes: _,
8419 server_id,
8420 collected_advertise,
8421 first_request_time: _,
8422 retrans_timeout: _,
8423 transmission_count,
8424 solicit_max_rt: _,
8425 } = assert_matches!(state, Some(ClientState::Requesting(requesting)) => requesting);
8426 assert_eq!(
8427 collected_advertise.clone().into_sorted_vec(),
8428 want_collected_advertise.clone().into_sorted_vec()
8429 );
8430 assert_eq!(server_id[..], SERVER_ID[0]);
8431 assert_eq!(*transmission_count, count);
8432 }
8433
8434 assert_matches!(
8437 &client.handle_timeout(ClientTimerType::Retransmission, time)[..],
8438 [
8439 Action::CancelTimer(ClientTimerType::Retransmission),
8440 Action::SendMessage(buf),
8441 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
8442 ] => {
8443 assert_eq!(testutil::msg_type(buf), v6::MessageType::Request);
8444 assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
8445 }
8446 );
8447 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8448 &client;
8449 let Requesting {
8450 client_id: _,
8451 non_temporary_addresses: _,
8452 delegated_prefixes: _,
8453 server_id,
8454 collected_advertise,
8455 first_request_time: _,
8456 retrans_timeout: _,
8457 transmission_count,
8458 solicit_max_rt: _,
8459 } = assert_matches!(state, Some(ClientState::Requesting(requesting)) => requesting);
8460 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
8461 assert_eq!(server_id[..], SERVER_ID[1]);
8462 assert_eq!(*transmission_count, 1);
8463
8464 for count in 2..=(REQUEST_MAX_RC + 1) {
8465 assert_matches!(
8466 &client.handle_timeout(ClientTimerType::Retransmission, time)[..],
8467 [
8468 Action::SendMessage(buf),
8469 Action::ScheduleTimer(ClientTimerType::Retransmission, _timeout)
8472 ] if testutil::msg_type(buf) == v6::MessageType::Request
8473 );
8474 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8475 &client;
8476 let Requesting {
8477 client_id: _,
8478 non_temporary_addresses: _,
8479 delegated_prefixes: _,
8480 server_id,
8481 collected_advertise,
8482 first_request_time: _,
8483 retrans_timeout: _,
8484 transmission_count,
8485 solicit_max_rt: _,
8486 } = assert_matches!(state, Some(ClientState::Requesting(requesting)) => requesting);
8487 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
8488 assert_eq!(server_id[..], SERVER_ID[1]);
8489 assert_eq!(*transmission_count, count);
8490 }
8491
8492 assert_matches!(
8496 &client.handle_timeout(ClientTimerType::Retransmission, time)[..],
8497 [
8498 Action::CancelTimer(ClientTimerType::Retransmission),
8499 Action::CancelTimer(ClientTimerType::Refresh),
8500 Action::CancelTimer(ClientTimerType::Renew),
8501 Action::CancelTimer(ClientTimerType::Rebind),
8502 Action::CancelTimer(ClientTimerType::RestartServerDiscovery),
8503 Action::SendMessage(buf),
8504 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
8505 ] => {
8506 assert_eq!(testutil::msg_type(buf), v6::MessageType::Solicit);
8507 assert_eq!(*instant, time.add(INITIAL_SOLICIT_TIMEOUT));
8508 }
8509 );
8510 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } = client;
8511 assert_matches!(state,
8512 Some(ClientState::ServerDiscovery(ServerDiscovery {
8513 client_id: _,
8514 configured_non_temporary_addresses: _,
8515 configured_delegated_prefixes: _,
8516 first_solicit_time: _,
8517 retrans_timeout: _,
8518 solicit_max_rt: _,
8519 collected_advertise,
8520 collected_sol_max_rt: _,
8521 })) if collected_advertise.is_empty()
8522 );
8523 }
8524
8525 #[test]
8527 fn assignment() {
8528 let now = Instant::now();
8529 let (client, actions) = testutil::assign_and_assert(
8530 &(CLIENT_ID.into()),
8531 SERVER_ID[0],
8532 CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]
8533 .iter()
8534 .copied()
8535 .map(TestIaNa::new_default)
8536 .collect(),
8537 CONFIGURED_DELEGATED_PREFIXES[0..2]
8538 .iter()
8539 .copied()
8540 .map(TestIaPd::new_default)
8541 .collect(),
8542 &[],
8543 StepRng::new(u64::MAX / 2, 0),
8544 now,
8545 );
8546
8547 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8548 &client;
8549 let Assigned {
8550 client_id: _,
8551 non_temporary_addresses: _,
8552 delegated_prefixes: _,
8553 server_id: _,
8554 dns_servers: _,
8555 solicit_max_rt: _,
8556 _marker,
8557 } = assert_matches!(
8558 state,
8559 Some(ClientState::Assigned(assigned)) => assigned
8560 );
8561 assert_matches!(
8562 &actions[..],
8563 [
8564 Action::CancelTimer(ClientTimerType::Retransmission),
8565 Action::ScheduleTimer(ClientTimerType::Renew, t1),
8566 Action::ScheduleTimer(ClientTimerType::Rebind, t2),
8567 Action::IaNaUpdates(iana_updates),
8568 Action::IaPdUpdates(iapd_updates),
8569 Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
8570 ] => {
8571 assert_eq!(*t1, now.add(Duration::from_secs(T1.get().into())));
8572 assert_eq!(*t2, now.add(Duration::from_secs(T2.get().into())));
8573 assert_eq!(
8574 *restart_time,
8575 now.add(Duration::from_secs(VALID_LIFETIME.get().into())),
8576 );
8577 assert_eq!(
8578 iana_updates,
8579 &(0..).map(v6::IAID::new)
8580 .zip(CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2].iter().cloned())
8581 .map(|(iaid, value)| (
8582 iaid,
8583 HashMap::from([(value, IaValueUpdateKind::Added(Lifetimes::new_default()))])
8584 ))
8585 .collect::<HashMap<_, _>>(),
8586 );
8587 assert_eq!(
8588 iapd_updates,
8589 &(0..).map(v6::IAID::new)
8590 .zip(CONFIGURED_DELEGATED_PREFIXES[0..2].iter().cloned())
8591 .map(|(iaid, value)| (
8592 iaid,
8593 HashMap::from([(value, IaValueUpdateKind::Added(Lifetimes::new_default()))])
8594 ))
8595 .collect::<HashMap<_, _>>(),
8596 );
8597 }
8598 );
8599 }
8600
8601 #[test]
8602 fn assigned_get_dns_servers() {
8603 let now = Instant::now();
8604 let (client, actions) = testutil::assign_and_assert(
8605 &(CLIENT_ID.into()),
8606 SERVER_ID[0],
8607 vec![TestIaNa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[0])],
8608 Default::default(), &DNS_SERVERS,
8610 StepRng::new(u64::MAX / 2, 0),
8611 now,
8612 );
8613 assert_matches!(
8614 &actions[..],
8615 [
8616 Action::CancelTimer(ClientTimerType::Retransmission),
8617 Action::ScheduleTimer(ClientTimerType::Renew, t1),
8618 Action::ScheduleTimer(ClientTimerType::Rebind, t2),
8619 Action::UpdateDnsServers(dns_servers),
8620 Action::IaNaUpdates(iana_updates),
8621 Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
8622 ] => {
8623 assert_eq!(dns_servers[..], DNS_SERVERS);
8624 assert_eq!(*t1, now.add(Duration::from_secs(T1.get().into())));
8625 assert_eq!(*t2, now.add(Duration::from_secs(T2.get().into())));
8626 assert_eq!(
8627 *restart_time,
8628 now.add(Duration::from_secs(VALID_LIFETIME.get().into())),
8629 );
8630 assert_eq!(
8631 iana_updates,
8632 &HashMap::from([
8633 (
8634 v6::IAID::new(0),
8635 HashMap::from([(
8636 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
8637 IaValueUpdateKind::Added(Lifetimes::new_default()),
8638 )]),
8639 ),
8640 ]),
8641 );
8642 }
8643 );
8644 assert_eq!(client.get_dns_servers()[..], DNS_SERVERS);
8645 }
8646
8647 #[test]
8648 fn update_sol_max_rt_on_reply_to_request() {
8649 let options_to_request = vec![];
8650 let configured_non_temporary_addresses = testutil::to_configured_addresses(1, vec![]);
8651 let mut rng = StepRng::new(u64::MAX / 2, 0);
8652 let time = Instant::now();
8653 let Transition { state, actions: _, transaction_id } = Requesting::start(
8654 CLIENT_ID.into(),
8655 SERVER_ID[0].to_vec(),
8656 advertise_to_ia_entries(
8657 testutil::to_default_ias_map(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..1]),
8658 configured_non_temporary_addresses.clone(),
8659 ),
8660 Default::default(), &options_to_request[..],
8662 BinaryHeap::new(),
8663 MAX_SOLICIT_TIMEOUT,
8664 &mut rng,
8665 time,
8666 );
8667 {
8668 let Requesting {
8669 collected_advertise,
8670 solicit_max_rt,
8671 client_id: _,
8672 non_temporary_addresses: _,
8673 delegated_prefixes: _,
8674 server_id: _,
8675 first_request_time: _,
8676 retrans_timeout: _,
8677 transmission_count: _,
8678 } = assert_matches!(&state, ClientState::Requesting(requesting) => requesting);
8679 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
8680 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
8681 }
8682 let received_sol_max_rt = 4800;
8683
8684 let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
8687 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
8688 60,
8689 120,
8690 &[],
8691 ))];
8692 let options = [
8693 v6::DhcpOption::ClientId(&CLIENT_ID),
8694 v6::DhcpOption::Iana(v6::IanaSerializer::new(
8695 v6::IAID::new(0),
8696 T1.get(),
8697 T2.get(),
8698 &iana_options,
8699 )),
8700 v6::DhcpOption::SolMaxRt(received_sol_max_rt),
8701 ];
8702 let request_transaction_id = transaction_id.unwrap();
8703 let builder =
8704 v6::MessageBuilder::new(v6::MessageType::Reply, request_transaction_id, &options);
8705 let mut buf = vec![0; builder.bytes_len()];
8706 builder.serialize(&mut buf);
8707 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8709 let Transition { state, actions: _, transaction_id: _ } =
8710 state.reply_message_received(&options_to_request, &mut rng, msg, time);
8711 {
8712 let Requesting {
8713 collected_advertise,
8714 solicit_max_rt,
8715 client_id: _,
8716 non_temporary_addresses: _,
8717 delegated_prefixes: _,
8718 server_id: _,
8719 first_request_time: _,
8720 retrans_timeout: _,
8721 transmission_count: _,
8722 } = assert_matches!(&state, ClientState::Requesting(requesting) => requesting);
8723 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
8724 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
8725 }
8726
8727 let options = [
8730 v6::DhcpOption::ServerId(&SERVER_ID[0]),
8731 v6::DhcpOption::ClientId(&MISMATCHED_CLIENT_ID),
8732 v6::DhcpOption::Iana(v6::IanaSerializer::new(
8733 v6::IAID::new(0),
8734 T1.get(),
8735 T2.get(),
8736 &iana_options,
8737 )),
8738 v6::DhcpOption::SolMaxRt(received_sol_max_rt),
8739 ];
8740 let builder =
8741 v6::MessageBuilder::new(v6::MessageType::Reply, request_transaction_id, &options);
8742 let mut buf = vec![0; builder.bytes_len()];
8743 builder.serialize(&mut buf);
8744 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8746 let Transition { state, actions: _, transaction_id: _ } =
8747 state.reply_message_received(&options_to_request, &mut rng, msg, time);
8748 {
8749 let Requesting {
8750 collected_advertise,
8751 solicit_max_rt,
8752 client_id: _,
8753 non_temporary_addresses: _,
8754 delegated_prefixes: _,
8755 server_id: _,
8756 first_request_time: _,
8757 retrans_timeout: _,
8758 transmission_count: _,
8759 } = assert_matches!(&state, ClientState::Requesting(requesting) => requesting);
8760 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
8761 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
8762 }
8763
8764 let options = [
8767 v6::DhcpOption::ServerId(&SERVER_ID[0]),
8768 v6::DhcpOption::ClientId(&CLIENT_ID),
8769 v6::DhcpOption::Iana(v6::IanaSerializer::new(
8770 v6::IAID::new(0),
8771 T1.get(),
8772 T2.get(),
8773 &iana_options,
8774 )),
8775 v6::DhcpOption::SolMaxRt(received_sol_max_rt),
8776 ];
8777 let builder =
8778 v6::MessageBuilder::new(v6::MessageType::Reply, request_transaction_id, &options);
8779 let mut buf = vec![0; builder.bytes_len()];
8780 builder.serialize(&mut buf);
8781 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8783 let Transition { state, actions: _, transaction_id: _ } =
8784 state.reply_message_received(&options_to_request, &mut rng, msg, time);
8785 {
8786 let Assigned {
8787 solicit_max_rt,
8788 client_id: _,
8789 non_temporary_addresses: _,
8790 delegated_prefixes: _,
8791 server_id: _,
8792 dns_servers: _,
8793 _marker,
8794 } = assert_matches!(&state, ClientState::Assigned(assigned) => assigned);
8795 assert_eq!(*solicit_max_rt, Duration::from_secs(received_sol_max_rt.into()));
8796 }
8797 }
8798
8799 struct RenewRebindTest {
8800 send_and_assert: fn(
8801 &ClientDuid,
8802 [u8; TEST_SERVER_ID_LEN],
8803 Vec<TestIaNa>,
8804 Vec<TestIaPd>,
8805 Option<&[Ipv6Addr]>,
8806 v6::NonZeroOrMaxU32,
8807 v6::NonZeroOrMaxU32,
8808 v6::NonZeroTimeValue,
8809 StepRng,
8810 Instant,
8811 ) -> ClientStateMachine<Instant, StepRng>,
8812 message_type: v6::MessageType,
8813 expect_server_id: bool,
8814 with_state: fn(&Option<ClientState<Instant>>) -> &RenewingOrRebindingInner<Instant>,
8815 allow_response_from_any_server: bool,
8816 }
8817
8818 const RENEW_TEST: RenewRebindTest = RenewRebindTest {
8819 send_and_assert: testutil::send_renew_and_assert,
8820 message_type: v6::MessageType::Renew,
8821 expect_server_id: true,
8822 with_state: |state| {
8823 assert_matches!(
8824 state,
8825 Some(ClientState::Renewing(RenewingOrRebinding(inner))) => inner
8826 )
8827 },
8828 allow_response_from_any_server: false,
8829 };
8830
8831 const REBIND_TEST: RenewRebindTest = RenewRebindTest {
8832 send_and_assert: testutil::send_rebind_and_assert,
8833 message_type: v6::MessageType::Rebind,
8834 expect_server_id: false,
8835 with_state: |state| {
8836 assert_matches!(
8837 state,
8838 Some(ClientState::Rebinding(RenewingOrRebinding(inner))) => inner
8839 )
8840 },
8841 allow_response_from_any_server: true,
8842 };
8843
8844 struct RenewRebindSendTestCase {
8845 ia_nas: Vec<TestIaNa>,
8846 ia_pds: Vec<TestIaPd>,
8847 }
8848
8849 impl RenewRebindSendTestCase {
8850 fn single_value_per_ia() -> RenewRebindSendTestCase {
8851 RenewRebindSendTestCase {
8852 ia_nas: CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]
8853 .iter()
8854 .map(|&addr| TestIaNa::new_default(addr))
8855 .collect(),
8856 ia_pds: CONFIGURED_DELEGATED_PREFIXES[0..2]
8857 .iter()
8858 .map(|&addr| TestIaPd::new_default(addr))
8859 .collect(),
8860 }
8861 }
8862
8863 fn multiple_values_per_ia() -> RenewRebindSendTestCase {
8864 RenewRebindSendTestCase {
8865 ia_nas: vec![TestIaNa::new_default_with_values(
8866 CONFIGURED_NON_TEMPORARY_ADDRESSES
8867 .into_iter()
8868 .map(|a| (a, Lifetimes::new_default()))
8869 .collect(),
8870 )],
8871 ia_pds: vec![TestIaPd::new_default_with_values(
8872 CONFIGURED_DELEGATED_PREFIXES
8873 .into_iter()
8874 .map(|a| (a, Lifetimes::new_default()))
8875 .collect(),
8876 )],
8877 }
8878 }
8879 }
8880
8881 #[test_case(
8882 RENEW_TEST,
8883 RenewRebindSendTestCase::single_value_per_ia(); "renew single value per IA")]
8884 #[test_case(
8885 RENEW_TEST,
8886 RenewRebindSendTestCase::multiple_values_per_ia(); "renew multiple value per IA")]
8887 #[test_case(
8888 REBIND_TEST,
8889 RenewRebindSendTestCase::single_value_per_ia(); "rebind single value per IA")]
8890 #[test_case(
8891 REBIND_TEST,
8892 RenewRebindSendTestCase::multiple_values_per_ia(); "rebind multiple value per IA")]
8893 fn send(
8894 RenewRebindTest {
8895 send_and_assert,
8896 message_type: _,
8897 expect_server_id: _,
8898 with_state: _,
8899 allow_response_from_any_server: _,
8900 }: RenewRebindTest,
8901 RenewRebindSendTestCase { ia_nas, ia_pds }: RenewRebindSendTestCase,
8902 ) {
8903 let _client = send_and_assert(
8904 &(CLIENT_ID.into()),
8905 SERVER_ID[0],
8906 ia_nas,
8907 ia_pds,
8908 None,
8909 T1,
8910 T2,
8911 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
8912 StepRng::new(u64::MAX / 2, 0),
8913 Instant::now(),
8914 );
8915 }
8916
8917 #[test_case(RENEW_TEST)]
8918 #[test_case(REBIND_TEST)]
8919 fn get_dns_server(
8920 RenewRebindTest {
8921 send_and_assert,
8922 message_type: _,
8923 expect_server_id: _,
8924 with_state: _,
8925 allow_response_from_any_server: _,
8926 }: RenewRebindTest,
8927 ) {
8928 let client = send_and_assert(
8929 &(CLIENT_ID.into()),
8930 SERVER_ID[0],
8931 CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]
8932 .iter()
8933 .map(|&addr| TestIaNa::new_default(addr))
8934 .collect(),
8935 Default::default(), Some(&DNS_SERVERS),
8937 T1,
8938 T2,
8939 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
8940 StepRng::new(u64::MAX / 2, 0),
8941 Instant::now(),
8942 );
8943 assert_eq!(client.get_dns_servers()[..], DNS_SERVERS);
8944 }
8945
8946 struct ScheduleRenewAndRebindTimersAfterAssignmentTestCase {
8947 ia_na_t1: v6::TimeValue,
8948 ia_na_t2: v6::TimeValue,
8949 ia_pd_t1: v6::TimeValue,
8950 ia_pd_t2: v6::TimeValue,
8951 expected_timer_actions: fn(Instant) -> [Action<Instant>; 2],
8952 next_timer: Option<RenewRebindTestState>,
8953 }
8954
8955 #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
8958 ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
8959 ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
8960 ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
8961 ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
8962 expected_timer_actions: |_| [
8963 Action::CancelTimer(ClientTimerType::Renew),
8964 Action::CancelTimer(ClientTimerType::Rebind),
8965 ],
8966 next_timer: None,
8967 }; "all infinite time values")]
8968 #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
8969 ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
8970 ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8971 ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
8972 ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8973 expected_timer_actions: |time| [
8974 Action::ScheduleTimer(
8975 ClientTimerType::Renew,
8976 time.add(Duration::from_secs(T1.get().into())),
8977 ),
8978 Action::ScheduleTimer(
8979 ClientTimerType::Rebind,
8980 time.add(Duration::from_secs(T2.get().into())),
8981 ),
8982 ],
8983 next_timer: Some(RENEW_TEST_STATE),
8984 }; "all finite time values")]
8985 #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
8986 ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8987 ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8988 ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8989 ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8990 expected_timer_actions: |time| [
8991 Action::CancelTimer(ClientTimerType::Renew),
8993 Action::ScheduleTimer(
8994 ClientTimerType::Rebind,
8995 time.add(Duration::from_secs(T2.get().into())),
8996 ),
8997 ],
8998 next_timer: Some(REBIND_TEST_STATE),
8999 }; "finite T1 equals finite T2")]
9000 #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
9001 ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
9002 ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9003 ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9004 ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9005 expected_timer_actions: |time| [
9006 Action::ScheduleTimer(
9007 ClientTimerType::Renew,
9008 time.add(Duration::from_secs(T1.get().into())),
9009 ),
9010 Action::CancelTimer(ClientTimerType::Rebind),
9011 ],
9012 next_timer: Some(RENEW_TEST_STATE),
9013 }; "finite IA_NA T1")]
9014 #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
9015 ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
9016 ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
9017 ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9018 ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9019 expected_timer_actions: |time| [
9020 Action::ScheduleTimer(
9021 ClientTimerType::Renew,
9022 time.add(Duration::from_secs(T1.get().into())),
9023 ),
9024 Action::ScheduleTimer(
9025 ClientTimerType::Rebind,
9026 time.add(Duration::from_secs(T2.get().into())),
9027 ),
9028 ],
9029 next_timer: Some(RENEW_TEST_STATE),
9030 }; "finite IA_NA T1 and T2")]
9031 #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
9032 ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9033 ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9034 ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
9035 ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9036 expected_timer_actions: |time| [
9037 Action::ScheduleTimer(
9038 ClientTimerType::Renew,
9039 time.add(Duration::from_secs(T1.get().into())),
9040 ),
9041 Action::CancelTimer(ClientTimerType::Rebind),
9042 ],
9043 next_timer: Some(RENEW_TEST_STATE),
9044 }; "finite IA_PD t1")]
9045 #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
9046 ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9047 ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9048 ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
9049 ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
9050 expected_timer_actions: |time| [
9051 Action::ScheduleTimer(
9052 ClientTimerType::Renew,
9053 time.add(Duration::from_secs(T1.get().into())),
9054 ),
9055 Action::ScheduleTimer(
9056 ClientTimerType::Rebind,
9057 time.add(Duration::from_secs(T2.get().into())),
9058 ),
9059 ],
9060 next_timer: Some(RENEW_TEST_STATE),
9061 }; "finite IA_PD T1 and T2")]
9062 fn schedule_renew_and_rebind_timers_after_assignment(
9063 ScheduleRenewAndRebindTimersAfterAssignmentTestCase {
9064 ia_na_t1,
9065 ia_na_t2,
9066 ia_pd_t1,
9067 ia_pd_t2,
9068 expected_timer_actions,
9069 next_timer,
9070 }: ScheduleRenewAndRebindTimersAfterAssignmentTestCase,
9071 ) {
9072 fn get_ia_and_updates<V: IaValue>(
9073 t1: v6::TimeValue,
9074 t2: v6::TimeValue,
9075 value: V,
9076 ) -> (TestIa<V>, HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>>) {
9077 (
9078 TestIa { t1, t2, ..TestIa::new_default(value) },
9079 HashMap::from([(
9080 v6::IAID::new(0),
9081 HashMap::from([(value, IaValueUpdateKind::Added(Lifetimes::new_default()))]),
9082 )]),
9083 )
9084 }
9085
9086 let (iana, iana_updates) =
9087 get_ia_and_updates(ia_na_t1, ia_na_t2, CONFIGURED_NON_TEMPORARY_ADDRESSES[0]);
9088 let (iapd, iapd_updates) =
9089 get_ia_and_updates(ia_pd_t1, ia_pd_t2, CONFIGURED_DELEGATED_PREFIXES[0]);
9090 let iana = vec![iana];
9091 let iapd = vec![iapd];
9092 let now = Instant::now();
9093 let (client, actions) = testutil::assign_and_assert(
9094 &(CLIENT_ID.into()),
9095 SERVER_ID[0],
9096 iana.clone(),
9097 iapd.clone(),
9098 &[],
9099 StepRng::new(u64::MAX / 2, 0),
9100 now,
9101 );
9102 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
9103 &client;
9104 let Assigned {
9105 client_id: _,
9106 non_temporary_addresses: _,
9107 delegated_prefixes: _,
9108 server_id: _,
9109 dns_servers: _,
9110 solicit_max_rt: _,
9111 _marker,
9112 } = assert_matches!(
9113 state,
9114 Some(ClientState::Assigned(assigned)) => assigned
9115 );
9116
9117 assert_eq!(
9118 actions,
9119 [Action::CancelTimer(ClientTimerType::Retransmission)]
9120 .into_iter()
9121 .chain(expected_timer_actions(now))
9122 .chain((!iana_updates.is_empty()).then(|| Action::IaNaUpdates(iana_updates)))
9123 .chain((!iapd_updates.is_empty()).then(|| Action::IaPdUpdates(iapd_updates)))
9124 .chain([Action::ScheduleTimer(
9125 ClientTimerType::RestartServerDiscovery,
9126 now.add(Duration::from_secs(VALID_LIFETIME.get().into())),
9127 ),])
9128 .collect::<Vec<_>>()
9129 );
9130
9131 let _client = if let Some(next_timer) = next_timer {
9132 handle_renew_or_rebind_timer(
9133 client,
9134 &CLIENT_ID,
9135 SERVER_ID[0],
9136 iana,
9137 iapd,
9138 &[],
9139 &[],
9140 Instant::now(),
9141 next_timer,
9142 )
9143 } else {
9144 client
9145 };
9146 }
9147
9148 #[test_case(RENEW_TEST)]
9149 #[test_case(REBIND_TEST)]
9150 fn retransmit(
9151 RenewRebindTest {
9152 send_and_assert,
9153 message_type,
9154 expect_server_id,
9155 with_state,
9156 allow_response_from_any_server: _,
9157 }: RenewRebindTest,
9158 ) {
9159 let non_temporary_addresses_to_assign = CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]
9160 .iter()
9161 .map(|&addr| TestIaNa::new_default(addr))
9162 .collect::<Vec<_>>();
9163 let delegated_prefixes_to_assign = CONFIGURED_DELEGATED_PREFIXES[0..2]
9164 .iter()
9165 .map(|&addr| TestIaPd::new_default(addr))
9166 .collect::<Vec<_>>();
9167 let time = Instant::now();
9168 let mut client = send_and_assert(
9169 &(CLIENT_ID.into()),
9170 SERVER_ID[0],
9171 non_temporary_addresses_to_assign.clone(),
9172 delegated_prefixes_to_assign.clone(),
9173 None,
9174 T1,
9175 T2,
9176 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9177 StepRng::new(u64::MAX / 2, 0),
9178 time,
9179 );
9180 let ClientStateMachine { transaction_id, options_to_request: _, state, rng: _ } = &client;
9181 let expected_transaction_id = *transaction_id;
9182 let RenewingOrRebindingInner {
9183 client_id: _,
9184 non_temporary_addresses: _,
9185 delegated_prefixes: _,
9186 server_id: _,
9187 dns_servers: _,
9188 start_time: _,
9189 retrans_timeout: _,
9190 solicit_max_rt: _,
9191 } = with_state(state);
9192
9193 let actions = client.handle_timeout(ClientTimerType::Retransmission, time);
9195 let buf = assert_matches!(
9196 &actions[..],
9197 [
9198 Action::SendMessage(buf),
9199 Action::ScheduleTimer(ClientTimerType::Retransmission, timeout)
9200 ] => {
9201 assert_eq!(*timeout, time.add(2 * INITIAL_RENEW_TIMEOUT));
9202 buf
9203 }
9204 );
9205 let ClientStateMachine { transaction_id, options_to_request: _, state, rng: _ } = &client;
9206 assert_eq!(*transaction_id, expected_transaction_id);
9208 {
9209 let RenewingOrRebindingInner {
9210 client_id,
9211 server_id,
9212 dns_servers,
9213 solicit_max_rt,
9214 non_temporary_addresses: _,
9215 delegated_prefixes: _,
9216 start_time: _,
9217 retrans_timeout: _,
9218 } = with_state(state);
9219 assert_eq!(client_id.as_slice(), &CLIENT_ID);
9220 assert_eq!(server_id[..], SERVER_ID[0]);
9221 assert_eq!(dns_servers, &[] as &[Ipv6Addr]);
9222 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
9223 }
9224 let expected_non_temporary_addresses: HashMap<v6::IAID, HashSet<Ipv6Addr>> = (0..)
9225 .map(v6::IAID::new)
9226 .zip(
9227 non_temporary_addresses_to_assign
9228 .iter()
9229 .map(|TestIaNa { values, t1: _, t2: _ }| values.keys().cloned().collect()),
9230 )
9231 .collect();
9232 let expected_delegated_prefixes: HashMap<v6::IAID, HashSet<Subnet<Ipv6Addr>>> = (0..)
9233 .map(v6::IAID::new)
9234 .zip(
9235 delegated_prefixes_to_assign
9236 .iter()
9237 .map(|TestIaPd { values, t1: _, t2: _ }| values.keys().cloned().collect()),
9238 )
9239 .collect();
9240 testutil::assert_outgoing_stateful_message(
9241 &buf,
9242 message_type,
9243 &CLIENT_ID,
9244 expect_server_id.then(|| &SERVER_ID[0]),
9245 &[],
9246 &expected_non_temporary_addresses,
9247 &expected_delegated_prefixes,
9248 );
9249 }
9250
9251 #[test_case(
9252 RENEW_TEST,
9253 &SERVER_ID[0],
9254 &SERVER_ID[0],
9255 RenewRebindSendTestCase::single_value_per_ia()
9256 )]
9257 #[test_case(
9258 REBIND_TEST,
9259 &SERVER_ID[0],
9260 &SERVER_ID[0],
9261 RenewRebindSendTestCase::single_value_per_ia()
9262 )]
9263 #[test_case(
9264 RENEW_TEST,
9265 &SERVER_ID[0],
9266 &SERVER_ID[1],
9267 RenewRebindSendTestCase::single_value_per_ia()
9268 )]
9269 #[test_case(
9270 REBIND_TEST,
9271 &SERVER_ID[0],
9272 &SERVER_ID[1],
9273 RenewRebindSendTestCase::single_value_per_ia()
9274 )]
9275 #[test_case(
9276 RENEW_TEST,
9277 &SERVER_ID[0],
9278 &SERVER_ID[0],
9279 RenewRebindSendTestCase::multiple_values_per_ia()
9280 )]
9281 #[test_case(
9282 REBIND_TEST,
9283 &SERVER_ID[0],
9284 &SERVER_ID[0],
9285 RenewRebindSendTestCase::multiple_values_per_ia()
9286 )]
9287 #[test_case(
9288 RENEW_TEST,
9289 &SERVER_ID[0],
9290 &SERVER_ID[1],
9291 RenewRebindSendTestCase::multiple_values_per_ia()
9292 )]
9293 #[test_case(
9294 REBIND_TEST,
9295 &SERVER_ID[0],
9296 &SERVER_ID[1],
9297 RenewRebindSendTestCase::multiple_values_per_ia()
9298 )]
9299 fn receive_reply_extends_lifetime(
9300 RenewRebindTest {
9301 send_and_assert,
9302 message_type: _,
9303 expect_server_id: _,
9304 with_state,
9305 allow_response_from_any_server,
9306 }: RenewRebindTest,
9307 original_server_id: &[u8; TEST_SERVER_ID_LEN],
9308 reply_server_id: &[u8],
9309 RenewRebindSendTestCase { ia_nas, ia_pds }: RenewRebindSendTestCase,
9310 ) {
9311 let time = Instant::now();
9312 let mut client = send_and_assert(
9313 &(CLIENT_ID.into()),
9314 original_server_id.clone(),
9315 ia_nas.clone(),
9316 ia_pds.clone(),
9317 None,
9318 T1,
9319 T2,
9320 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9321 StepRng::new(u64::MAX / 2, 0),
9322 time,
9323 );
9324 let ClientStateMachine { transaction_id, options_to_request: _, state, rng: _ } = &client;
9325 let buf = TestMessageBuilder {
9326 transaction_id: *transaction_id,
9327 message_type: v6::MessageType::Reply,
9328 client_id: &CLIENT_ID,
9329 server_id: reply_server_id,
9330 preference: None,
9331 dns_servers: None,
9332 ia_nas: (0..).map(v6::IAID::new).zip(ia_nas.iter().map(
9333 |TestIa { values, t1: _, t2: _ }| {
9334 TestIa::new_renewed_default_with_values(values.keys().cloned())
9335 },
9336 )),
9337 ia_pds: (0..).map(v6::IAID::new).zip(ia_pds.iter().map(
9338 |TestIa { values, t1: _, t2: _ }| {
9339 TestIa::new_renewed_default_with_values(values.keys().cloned())
9340 },
9341 )),
9342 }
9343 .build();
9344 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
9346
9347 let original_state = with_state(state).clone();
9349
9350 let actions = client.handle_message_receive(msg, time);
9351 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
9352 &client;
9353
9354 if original_server_id.as_slice() != reply_server_id && !allow_response_from_any_server {
9355 let RenewingOrRebindingInner {
9360 client_id: original_client_id,
9361 non_temporary_addresses: original_non_temporary_addresses,
9362 delegated_prefixes: original_delegated_prefixes,
9363 server_id: original_server_id,
9364 dns_servers: original_dns_servers,
9365 start_time: original_start_time,
9366 retrans_timeout: original_retrans_timeout,
9367 solicit_max_rt: original_solicit_max_rt,
9368 } = original_state;
9369 let RenewingOrRebindingInner {
9370 client_id: new_client_id,
9371 non_temporary_addresses: new_non_temporary_addresses,
9372 delegated_prefixes: new_delegated_prefixes,
9373 server_id: new_server_id,
9374 dns_servers: new_dns_servers,
9375 start_time: new_start_time,
9376 retrans_timeout: new_retrans_timeout,
9377 solicit_max_rt: new_solicit_max_rt,
9378 } = with_state(state);
9379 assert_eq!(&original_client_id, new_client_id);
9380 assert_eq!(&original_non_temporary_addresses, new_non_temporary_addresses);
9381 assert_eq!(&original_delegated_prefixes, new_delegated_prefixes);
9382 assert_eq!(&original_server_id, new_server_id);
9383 assert_eq!(&original_dns_servers, new_dns_servers);
9384 assert_eq!(&original_start_time, new_start_time);
9385 assert_eq!(&original_retrans_timeout, new_retrans_timeout);
9386 assert_eq!(&original_solicit_max_rt, new_solicit_max_rt);
9387 assert_eq!(actions, []);
9388 return;
9389 }
9390
9391 let expected_non_temporary_addresses = (0..)
9392 .map(v6::IAID::new)
9393 .zip(ia_nas.iter().map(|TestIa { values, t1: _, t2: _ }| {
9394 AddressEntry::Assigned(
9395 values
9396 .keys()
9397 .cloned()
9398 .map(|value| {
9399 (
9400 value,
9401 LifetimesInfo {
9402 lifetimes: Lifetimes::new_renewed(),
9403 updated_at: time,
9404 },
9405 )
9406 })
9407 .collect(),
9408 )
9409 }))
9410 .collect();
9411 let expected_delegated_prefixes = (0..)
9412 .map(v6::IAID::new)
9413 .zip(ia_pds.iter().map(|TestIa { values, t1: _, t2: _ }| {
9414 PrefixEntry::Assigned(
9415 values
9416 .keys()
9417 .cloned()
9418 .map(|value| {
9419 (
9420 value,
9421 LifetimesInfo {
9422 lifetimes: Lifetimes::new_renewed(),
9423 updated_at: time,
9424 },
9425 )
9426 })
9427 .collect(),
9428 )
9429 }))
9430 .collect();
9431 assert_matches!(
9432 &state,
9433 Some(ClientState::Assigned(Assigned {
9434 client_id,
9435 non_temporary_addresses,
9436 delegated_prefixes,
9437 server_id,
9438 dns_servers,
9439 solicit_max_rt,
9440 _marker,
9441 })) => {
9442 assert_eq!(client_id.as_slice(), &CLIENT_ID);
9443 assert_eq!(non_temporary_addresses, &expected_non_temporary_addresses);
9444 assert_eq!(delegated_prefixes, &expected_delegated_prefixes);
9445 assert_eq!(server_id.as_slice(), reply_server_id);
9446 assert_eq!(dns_servers.as_slice(), &[] as &[Ipv6Addr]);
9447 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
9448 }
9449 );
9450 assert_matches!(
9451 &actions[..],
9452 [
9453 Action::CancelTimer(ClientTimerType::Retransmission),
9454 Action::ScheduleTimer(ClientTimerType::Renew, t1),
9455 Action::ScheduleTimer(ClientTimerType::Rebind, t2),
9456 Action::IaNaUpdates(iana_updates),
9457 Action::IaPdUpdates(iapd_updates),
9458 Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
9459 ] => {
9460 assert_eq!(*t1, time.add(Duration::from_secs(RENEWED_T1.get().into())));
9461 assert_eq!(*t2, time.add(Duration::from_secs(RENEWED_T2.get().into())));
9462 assert_eq!(
9463 *restart_time,
9464 time.add(Duration::from_secs(RENEWED_VALID_LIFETIME.get().into()))
9465 );
9466
9467 fn get_updates<V: IaValue>(ias: Vec<TestIa<V>>) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
9468 (0..).map(v6::IAID::new).zip(ias.into_iter().map(
9469 |TestIa { values, t1: _, t2: _ }| {
9470 values.into_keys()
9471 .map(|value| (
9472 value,
9473 IaValueUpdateKind::UpdatedLifetimes(Lifetimes::new_renewed())
9474 ))
9475 .collect()
9476 },
9477 )).collect()
9478 }
9479
9480 assert_eq!(iana_updates, &get_updates(ia_nas));
9481 assert_eq!(iapd_updates, &get_updates(ia_pds));
9482 }
9483 );
9484 }
9485
9486 #[test_case(RENEW_TEST, v6::ErrorStatusCode::UnspecFail)]
9490 #[test_case(RENEW_TEST, v6::ErrorStatusCode::NoBinding)]
9491 #[test_case(RENEW_TEST, v6::ErrorStatusCode::NotOnLink)]
9492 #[test_case(RENEW_TEST, v6::ErrorStatusCode::NoAddrsAvail)]
9493 #[test_case(RENEW_TEST, v6::ErrorStatusCode::NoPrefixAvail)]
9494 #[test_case(REBIND_TEST, v6::ErrorStatusCode::UnspecFail)]
9495 #[test_case(REBIND_TEST, v6::ErrorStatusCode::NoBinding)]
9496 #[test_case(REBIND_TEST, v6::ErrorStatusCode::NotOnLink)]
9497 #[test_case(REBIND_TEST, v6::ErrorStatusCode::NoAddrsAvail)]
9498 #[test_case(REBIND_TEST, v6::ErrorStatusCode::NoPrefixAvail)]
9499 fn renewing_receive_reply_with_error_status(
9500 RenewRebindTest {
9501 send_and_assert,
9502 message_type: _,
9503 expect_server_id: _,
9504 with_state,
9505 allow_response_from_any_server: _,
9506 }: RenewRebindTest,
9507 error_status_code: v6::ErrorStatusCode,
9508 ) {
9509 let time = Instant::now();
9510 let addr = CONFIGURED_NON_TEMPORARY_ADDRESSES[0];
9511 let prefix = CONFIGURED_DELEGATED_PREFIXES[0];
9512 let mut client = send_and_assert(
9513 &(CLIENT_ID.into()),
9514 SERVER_ID[0],
9515 vec![TestIaNa::new_default(addr)],
9516 vec![TestIaPd::new_default(prefix)],
9517 None,
9518 T1,
9519 T2,
9520 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9521 StepRng::new(u64::MAX / 2, 0),
9522 time,
9523 );
9524 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
9525 &client;
9526 let ia_na_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
9527 addr,
9528 RENEWED_PREFERRED_LIFETIME.get(),
9529 RENEWED_VALID_LIFETIME.get(),
9530 &[],
9531 ))];
9532 let sol_max_rt = *VALID_MAX_SOLICIT_TIMEOUT_RANGE.start();
9533 let options = vec![
9534 v6::DhcpOption::ClientId(&CLIENT_ID),
9535 v6::DhcpOption::ServerId(&SERVER_ID[0]),
9536 v6::DhcpOption::StatusCode(error_status_code.into(), ""),
9537 v6::DhcpOption::Iana(v6::IanaSerializer::new(
9538 v6::IAID::new(0),
9539 RENEWED_T1.get(),
9540 RENEWED_T2.get(),
9541 &ia_na_options,
9542 )),
9543 v6::DhcpOption::SolMaxRt(sol_max_rt),
9544 ];
9545 let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
9546 let mut buf = vec![0; builder.bytes_len()];
9547 builder.serialize(&mut buf);
9548 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
9550 let actions = client.handle_message_receive(msg, time);
9551 assert_eq!(actions, &[]);
9552 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
9553 &client;
9554
9555 let RenewingOrRebindingInner {
9556 client_id,
9557 non_temporary_addresses,
9558 delegated_prefixes,
9559 server_id,
9560 dns_servers,
9561 start_time: _,
9562 retrans_timeout: _,
9563 solicit_max_rt: got_sol_max_rt,
9564 } = with_state(state);
9565 assert_eq!(client_id.as_slice(), &CLIENT_ID);
9566 fn expected_values<V: IaValue>(
9567 value: V,
9568 time: Instant,
9569 ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
9570 std::iter::once((
9571 v6::IAID::new(0),
9572 IaEntry::new_assigned(value, PREFERRED_LIFETIME, VALID_LIFETIME, time),
9573 ))
9574 .collect()
9575 }
9576 assert_eq!(*non_temporary_addresses, expected_values(addr, time));
9577 assert_eq!(*delegated_prefixes, expected_values(prefix, time));
9578 assert_eq!(*server_id, SERVER_ID[0]);
9579 assert_eq!(dns_servers, &[] as &[Ipv6Addr]);
9580 assert_eq!(*got_sol_max_rt, Duration::from_secs(sol_max_rt.into()));
9581 assert_matches!(&actions[..], []);
9582 }
9583
9584 struct ReceiveReplyWithMissingIasTestCase {
9585 present_ia_na_iaids: Vec<v6::IAID>,
9586 present_ia_pd_iaids: Vec<v6::IAID>,
9587 }
9588
9589 #[test_case(
9590 REBIND_TEST,
9591 ReceiveReplyWithMissingIasTestCase {
9592 present_ia_na_iaids: Vec::new(),
9593 present_ia_pd_iaids: Vec::new(),
9594 }; "none presenet")]
9595 #[test_case(
9596 RENEW_TEST,
9597 ReceiveReplyWithMissingIasTestCase {
9598 present_ia_na_iaids: vec![v6::IAID::new(0)],
9599 present_ia_pd_iaids: Vec::new(),
9600 }; "only one IA_NA present")]
9601 #[test_case(
9602 RENEW_TEST,
9603 ReceiveReplyWithMissingIasTestCase {
9604 present_ia_na_iaids: Vec::new(),
9605 present_ia_pd_iaids: vec![v6::IAID::new(1)],
9606 }; "only one IA_PD present")]
9607 #[test_case(
9608 REBIND_TEST,
9609 ReceiveReplyWithMissingIasTestCase {
9610 present_ia_na_iaids: vec![v6::IAID::new(0), v6::IAID::new(1)],
9611 present_ia_pd_iaids: Vec::new(),
9612 }; "only both IA_NAs present")]
9613 #[test_case(
9614 REBIND_TEST,
9615 ReceiveReplyWithMissingIasTestCase {
9616 present_ia_na_iaids: Vec::new(),
9617 present_ia_pd_iaids: vec![v6::IAID::new(0), v6::IAID::new(1)],
9618 }; "only both IA_PDs present")]
9619 #[test_case(
9620 REBIND_TEST,
9621 ReceiveReplyWithMissingIasTestCase {
9622 present_ia_na_iaids: vec![v6::IAID::new(1)],
9623 present_ia_pd_iaids: vec![v6::IAID::new(0), v6::IAID::new(1)],
9624 }; "both IA_PDs and one IA_NA present")]
9625 #[test_case(
9626 REBIND_TEST,
9627 ReceiveReplyWithMissingIasTestCase {
9628 present_ia_na_iaids: vec![v6::IAID::new(0), v6::IAID::new(1)],
9629 present_ia_pd_iaids: vec![v6::IAID::new(0)],
9630 }; "both IA_NAs and one IA_PD present")]
9631 fn receive_reply_with_missing_ias(
9632 RenewRebindTest {
9633 send_and_assert,
9634 message_type: _,
9635 expect_server_id: _,
9636 with_state,
9637 allow_response_from_any_server: _,
9638 }: RenewRebindTest,
9639 ReceiveReplyWithMissingIasTestCase {
9640 present_ia_na_iaids,
9641 present_ia_pd_iaids,
9642 }: ReceiveReplyWithMissingIasTestCase,
9643 ) {
9644 let non_temporary_addresses = &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2];
9645 let delegated_prefixes = &CONFIGURED_DELEGATED_PREFIXES[0..2];
9646 let time = Instant::now();
9647 let mut client = send_and_assert(
9648 &(CLIENT_ID.into()),
9649 SERVER_ID[0],
9650 non_temporary_addresses.iter().copied().map(TestIaNa::new_default).collect(),
9651 delegated_prefixes.iter().copied().map(TestIaPd::new_default).collect(),
9652 None,
9653 T1,
9654 T2,
9655 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9656 StepRng::new(u64::MAX / 2, 0),
9657 time,
9658 );
9659 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
9660 &client;
9661 let buf = TestMessageBuilder {
9664 transaction_id: *transaction_id,
9665 message_type: v6::MessageType::Reply,
9666 client_id: &CLIENT_ID,
9667 server_id: &SERVER_ID[0],
9668 preference: None,
9669 dns_servers: None,
9670 ia_nas: present_ia_na_iaids.iter().map(|iaid| {
9671 (
9672 *iaid,
9673 TestIa::new_renewed_default(
9674 CONFIGURED_NON_TEMPORARY_ADDRESSES[iaid.get() as usize],
9675 ),
9676 )
9677 }),
9678 ia_pds: present_ia_pd_iaids.iter().map(|iaid| {
9679 (
9680 *iaid,
9681 TestIa::new_renewed_default(CONFIGURED_DELEGATED_PREFIXES[iaid.get() as usize]),
9682 )
9683 }),
9684 }
9685 .build();
9686 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
9688 let actions = client.handle_message_receive(msg, time);
9689 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
9690 &client;
9691 {
9693 let RenewingOrRebindingInner {
9694 client_id,
9695 non_temporary_addresses: got_non_temporary_addresses,
9696 delegated_prefixes: got_delegated_prefixes,
9697 server_id,
9698 dns_servers,
9699 start_time: _,
9700 retrans_timeout: _,
9701 solicit_max_rt,
9702 } = with_state(state);
9703 assert_eq!(client_id.as_slice(), &CLIENT_ID);
9704 fn expected_values<V: IaValue>(
9705 values: &[V],
9706 present_iaids: Vec<v6::IAID>,
9707 time: Instant,
9708 ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
9709 (0..)
9710 .map(v6::IAID::new)
9711 .zip(values)
9712 .map(|(iaid, &value)| {
9713 (
9714 iaid,
9715 if present_iaids.contains(&iaid) {
9716 IaEntry::new_assigned(
9717 value,
9718 RENEWED_PREFERRED_LIFETIME,
9719 RENEWED_VALID_LIFETIME,
9720 time,
9721 )
9722 } else {
9723 IaEntry::new_assigned(
9724 value,
9725 PREFERRED_LIFETIME,
9726 VALID_LIFETIME,
9727 time,
9728 )
9729 },
9730 )
9731 })
9732 .collect()
9733 }
9734 assert_eq!(
9735 *got_non_temporary_addresses,
9736 expected_values(non_temporary_addresses, present_ia_na_iaids, time)
9737 );
9738 assert_eq!(
9739 *got_delegated_prefixes,
9740 expected_values(delegated_prefixes, present_ia_pd_iaids, time)
9741 );
9742 assert_eq!(*server_id, SERVER_ID[0]);
9743 assert_eq!(dns_servers, &[] as &[Ipv6Addr]);
9744 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
9745 }
9746 assert_matches!(&actions[..], []);
9748 }
9749
9750 #[test_case(RENEW_TEST)]
9751 #[test_case(REBIND_TEST)]
9752 fn receive_reply_with_missing_ia_suboption_for_assigned_entry_does_not_extend_lifetime(
9753 RenewRebindTest {
9754 send_and_assert,
9755 message_type: _,
9756 expect_server_id: _,
9757 with_state: _,
9758 allow_response_from_any_server: _,
9759 }: RenewRebindTest,
9760 ) {
9761 const IA_NA_WITHOUT_ADDRESS_IAID: v6::IAID = v6::IAID::new(0);
9762 const IA_PD_WITHOUT_PREFIX_IAID: v6::IAID = v6::IAID::new(1);
9763
9764 let time = Instant::now();
9765 let mut client = send_and_assert(
9766 &(CLIENT_ID.into()),
9767 SERVER_ID[0],
9768 CONFIGURED_NON_TEMPORARY_ADDRESSES.iter().copied().map(TestIaNa::new_default).collect(),
9769 CONFIGURED_DELEGATED_PREFIXES.iter().copied().map(TestIaPd::new_default).collect(),
9770 None,
9771 T1,
9772 T2,
9773 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9774 StepRng::new(u64::MAX / 2, 0),
9775 time,
9776 );
9777 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
9778 &client;
9779 let iaaddr_opts = (0..)
9781 .map(v6::IAID::new)
9782 .zip(CONFIGURED_NON_TEMPORARY_ADDRESSES)
9783 .map(|(iaid, addr)| {
9784 (
9785 iaid,
9786 (iaid != IA_NA_WITHOUT_ADDRESS_IAID).then(|| {
9787 [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
9788 addr,
9789 RENEWED_PREFERRED_LIFETIME.get(),
9790 RENEWED_VALID_LIFETIME.get(),
9791 &[],
9792 ))]
9793 }),
9794 )
9795 })
9796 .collect::<HashMap<_, _>>();
9797 let iaprefix_opts = (0..)
9798 .map(v6::IAID::new)
9799 .zip(CONFIGURED_DELEGATED_PREFIXES)
9800 .map(|(iaid, prefix)| {
9801 (
9802 iaid,
9803 (iaid != IA_PD_WITHOUT_PREFIX_IAID).then(|| {
9804 [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
9805 RENEWED_PREFERRED_LIFETIME.get(),
9806 RENEWED_VALID_LIFETIME.get(),
9807 prefix,
9808 &[],
9809 ))]
9810 }),
9811 )
9812 })
9813 .collect::<HashMap<_, _>>();
9814 let options =
9815 [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0])]
9816 .into_iter()
9817 .chain(iaaddr_opts.iter().map(|(iaid, iaaddr_opts)| {
9818 v6::DhcpOption::Iana(v6::IanaSerializer::new(
9819 *iaid,
9820 RENEWED_T1.get(),
9821 RENEWED_T2.get(),
9822 iaaddr_opts.as_ref().map_or(&[], AsRef::as_ref),
9823 ))
9824 }))
9825 .chain(iaprefix_opts.iter().map(|(iaid, iaprefix_opts)| {
9826 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
9827 *iaid,
9828 RENEWED_T1.get(),
9829 RENEWED_T2.get(),
9830 iaprefix_opts.as_ref().map_or(&[], AsRef::as_ref),
9831 ))
9832 }))
9833 .collect::<Vec<_>>();
9834 let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
9835 let mut buf = vec![0; builder.bytes_len()];
9836 builder.serialize(&mut buf);
9837 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
9839 let actions = client.handle_message_receive(msg, time);
9840 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
9841 &client;
9842 assert_matches!(
9845 &state,
9846 Some(ClientState::Assigned(Assigned {
9847 client_id,
9848 non_temporary_addresses,
9849 delegated_prefixes,
9850 server_id,
9851 dns_servers,
9852 solicit_max_rt,
9853 _marker,
9854 })) => {
9855 assert_eq!(client_id.as_slice(), &CLIENT_ID);
9856 fn expected_values<V: IaValueTestExt>(
9857 without_value: v6::IAID,
9858 time: Instant,
9859 ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
9860 (0..)
9861 .map(v6::IAID::new)
9862 .zip(V::CONFIGURED)
9863 .map(|(iaid, value)| {
9864 let (preferred_lifetime, valid_lifetime) =
9865 if iaid == without_value {
9866 (PREFERRED_LIFETIME, VALID_LIFETIME)
9867 } else {
9868 (RENEWED_PREFERRED_LIFETIME, RENEWED_VALID_LIFETIME)
9869 };
9870
9871 (
9872 iaid,
9873 IaEntry::new_assigned(
9874 value,
9875 preferred_lifetime,
9876 valid_lifetime,
9877 time,
9878 ),
9879 )
9880 })
9881 .collect()
9882 }
9883 assert_eq!(
9884 non_temporary_addresses,
9885 &expected_values::<Ipv6Addr>(IA_NA_WITHOUT_ADDRESS_IAID, time)
9886 );
9887 assert_eq!(
9888 delegated_prefixes,
9889 &expected_values::<Subnet<Ipv6Addr>>(IA_PD_WITHOUT_PREFIX_IAID, time)
9890 );
9891 assert_eq!(server_id.as_slice(), &SERVER_ID[0]);
9892 assert_eq!(dns_servers.as_slice(), &[] as &[Ipv6Addr]);
9893 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
9894 }
9895 );
9896 assert_matches!(
9897 &actions[..],
9898 [
9899 Action::CancelTimer(ClientTimerType::Retransmission),
9900 Action::ScheduleTimer(ClientTimerType::Renew, t1),
9901 Action::ScheduleTimer(ClientTimerType::Rebind, t2),
9902 Action::IaNaUpdates(iana_updates),
9903 Action::IaPdUpdates(iapd_updates),
9904 Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
9905 ] => {
9906 assert_eq!(*t1, time.add(Duration::from_secs(RENEWED_T1.get().into())));
9907 assert_eq!(*t2, time.add(Duration::from_secs(RENEWED_T2.get().into())));
9908 assert_eq!(
9909 *restart_time,
9910 time.add(Duration::from_secs(std::cmp::max(
9911 VALID_LIFETIME,
9912 RENEWED_VALID_LIFETIME,
9913 ).get().into()))
9914 );
9915
9916 fn get_updates<V: IaValue>(
9917 values: &[V],
9918 omit_iaid: v6::IAID,
9919 ) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
9920 (0..).map(v6::IAID::new)
9921 .zip(values.iter().cloned())
9922 .filter_map(|(iaid, value)| {
9923 (iaid != omit_iaid).then(|| (
9924 iaid,
9925 HashMap::from([(
9926 value,
9927 IaValueUpdateKind::UpdatedLifetimes(Lifetimes::new_renewed()),
9928 )])
9929 ))
9930 })
9931 .collect()
9932 }
9933 assert_eq!(
9934 iana_updates,
9935 &get_updates(&CONFIGURED_NON_TEMPORARY_ADDRESSES, IA_NA_WITHOUT_ADDRESS_IAID),
9936 );
9937 assert_eq!(
9938 iapd_updates,
9939 &get_updates(&CONFIGURED_DELEGATED_PREFIXES, IA_PD_WITHOUT_PREFIX_IAID),
9940 );
9941 }
9942 );
9943 }
9944
9945 #[test_case(RENEW_TEST)]
9946 #[test_case(REBIND_TEST)]
9947 fn receive_reply_with_zero_lifetime(
9948 RenewRebindTest {
9949 send_and_assert,
9950 message_type: _,
9951 expect_server_id: _,
9952 with_state: _,
9953 allow_response_from_any_server: _,
9954 }: RenewRebindTest,
9955 ) {
9956 const IA_NA_ZERO_LIFETIMES_ADDRESS_IAID: v6::IAID = v6::IAID::new(0);
9957 const IA_PD_ZERO_LIFETIMES_PREFIX_IAID: v6::IAID = v6::IAID::new(1);
9958
9959 let time = Instant::now();
9960 let mut client = send_and_assert(
9961 &(CLIENT_ID.into()),
9962 SERVER_ID[0],
9963 CONFIGURED_NON_TEMPORARY_ADDRESSES.iter().copied().map(TestIaNa::new_default).collect(),
9964 CONFIGURED_DELEGATED_PREFIXES.iter().copied().map(TestIaPd::new_default).collect(),
9965 None,
9966 T1,
9967 T2,
9968 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9969 StepRng::new(u64::MAX / 2, 0),
9970 time,
9971 );
9972 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
9973 &client;
9974 let iaaddr_opts = (0..)
9976 .map(v6::IAID::new)
9977 .zip(CONFIGURED_NON_TEMPORARY_ADDRESSES)
9978 .map(|(iaid, addr)| {
9979 let (pl, vl) = if iaid == IA_NA_ZERO_LIFETIMES_ADDRESS_IAID {
9980 (0, 0)
9981 } else {
9982 (RENEWED_PREFERRED_LIFETIME.get(), RENEWED_VALID_LIFETIME.get())
9983 };
9984
9985 (iaid, [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(addr, pl, vl, &[]))])
9986 })
9987 .collect::<HashMap<_, _>>();
9988 let iaprefix_opts = (0..)
9989 .map(v6::IAID::new)
9990 .zip(CONFIGURED_DELEGATED_PREFIXES)
9991 .map(|(iaid, prefix)| {
9992 let (pl, vl) = if iaid == IA_PD_ZERO_LIFETIMES_PREFIX_IAID {
9993 (0, 0)
9994 } else {
9995 (RENEWED_PREFERRED_LIFETIME.get(), RENEWED_VALID_LIFETIME.get())
9996 };
9997
9998 (iaid, [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(pl, vl, prefix, &[]))])
9999 })
10000 .collect::<HashMap<_, _>>();
10001 let options =
10002 [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0])]
10003 .into_iter()
10004 .chain(iaaddr_opts.iter().map(|(iaid, iaaddr_opts)| {
10005 v6::DhcpOption::Iana(v6::IanaSerializer::new(
10006 *iaid,
10007 RENEWED_T1.get(),
10008 RENEWED_T2.get(),
10009 iaaddr_opts.as_ref(),
10010 ))
10011 }))
10012 .chain(iaprefix_opts.iter().map(|(iaid, iaprefix_opts)| {
10013 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
10014 *iaid,
10015 RENEWED_T1.get(),
10016 RENEWED_T2.get(),
10017 iaprefix_opts.as_ref(),
10018 ))
10019 }))
10020 .collect::<Vec<_>>();
10021 let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
10022 let mut buf = vec![0; builder.bytes_len()];
10023 builder.serialize(&mut buf);
10024 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10026 let actions = client.handle_message_receive(msg, time);
10027 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
10028 &client;
10029 assert_matches!(
10032 &state,
10033 Some(ClientState::Assigned(Assigned {
10034 client_id,
10035 non_temporary_addresses,
10036 delegated_prefixes,
10037 server_id,
10038 dns_servers,
10039 solicit_max_rt,
10040 _marker,
10041 })) => {
10042 assert_eq!(client_id.as_slice(), &CLIENT_ID);
10043 fn expected_values<V: IaValueTestExt>(
10044 zero_lifetime_iaid: v6::IAID,
10045 time: Instant,
10046 ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
10047 (0..)
10048 .map(v6::IAID::new)
10049 .zip(V::CONFIGURED)
10050 .map(|(iaid, value)| {
10051 (
10052 iaid,
10053 if iaid == zero_lifetime_iaid {
10054 IaEntry::ToRequest(HashSet::from([value]))
10055 } else {
10056 IaEntry::new_assigned(
10057 value,
10058 RENEWED_PREFERRED_LIFETIME,
10059 RENEWED_VALID_LIFETIME,
10060 time,
10061 )
10062 },
10063 )
10064 })
10065 .collect()
10066 }
10067 assert_eq!(
10068 non_temporary_addresses,
10069 &expected_values::<Ipv6Addr>(IA_NA_ZERO_LIFETIMES_ADDRESS_IAID, time)
10070 );
10071 assert_eq!(
10072 delegated_prefixes,
10073 &expected_values::<Subnet<Ipv6Addr>>(IA_PD_ZERO_LIFETIMES_PREFIX_IAID, time)
10074 );
10075 assert_eq!(server_id.as_slice(), &SERVER_ID[0]);
10076 assert_eq!(dns_servers.as_slice(), &[] as &[Ipv6Addr]);
10077 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
10078 }
10079 );
10080 assert_matches!(
10081 &actions[..],
10082 [
10083 Action::CancelTimer(ClientTimerType::Retransmission),
10084 Action::ScheduleTimer(ClientTimerType::Renew, t1),
10085 Action::ScheduleTimer(ClientTimerType::Rebind, t2),
10086 Action::IaNaUpdates(iana_updates),
10087 Action::IaPdUpdates(iapd_updates),
10088 Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
10089 ] => {
10090 assert_eq!(*t1, time.add(Duration::from_secs(RENEWED_T1.get().into())));
10091 assert_eq!(*t2, time.add(Duration::from_secs(RENEWED_T2.get().into())));
10092 assert_eq!(
10093 *restart_time,
10094 time.add(Duration::from_secs(RENEWED_VALID_LIFETIME.get().into()))
10095 );
10096
10097 fn get_updates<V: IaValue>(
10098 values: &[V],
10099 omit_iaid: v6::IAID,
10100 ) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
10101 (0..).map(v6::IAID::new)
10102 .zip(values.iter().cloned())
10103 .map(|(iaid, value)| (
10104 iaid,
10105 HashMap::from([(
10106 value,
10107 if iaid == omit_iaid {
10108 IaValueUpdateKind::Removed
10109 } else {
10110 IaValueUpdateKind::UpdatedLifetimes(Lifetimes::new_renewed())
10111 }
10112 )]),
10113 ))
10114 .collect()
10115 }
10116 assert_eq!(
10117 iana_updates,
10118 &get_updates(
10119 &CONFIGURED_NON_TEMPORARY_ADDRESSES,
10120 IA_NA_ZERO_LIFETIMES_ADDRESS_IAID,
10121 ),
10122 );
10123 assert_eq!(
10124 iapd_updates,
10125 &get_updates(
10126 &CONFIGURED_DELEGATED_PREFIXES,
10127 IA_PD_ZERO_LIFETIMES_PREFIX_IAID,
10128 ),
10129 );
10130 }
10131 );
10132 }
10133
10134 #[test_case(RENEW_TEST)]
10135 #[test_case(REBIND_TEST)]
10136 fn receive_reply_with_original_ia_value_omitted(
10137 RenewRebindTest {
10138 send_and_assert,
10139 message_type: _,
10140 expect_server_id: _,
10141 with_state: _,
10142 allow_response_from_any_server: _,
10143 }: RenewRebindTest,
10144 ) {
10145 let time = Instant::now();
10146 let mut client = send_and_assert(
10147 &(CLIENT_ID.into()),
10148 SERVER_ID[0],
10149 vec![TestIaNa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[0])],
10150 vec![TestIaPd::new_default(CONFIGURED_DELEGATED_PREFIXES[0])],
10151 None,
10152 T1,
10153 T2,
10154 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
10155 StepRng::new(u64::MAX / 2, 0),
10156 time,
10157 );
10158 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
10159 &client;
10160 let iaid = v6::IAID::new(0);
10163 let buf = TestMessageBuilder {
10164 transaction_id: *transaction_id,
10165 message_type: v6::MessageType::Reply,
10166 client_id: &CLIENT_ID,
10167 server_id: &SERVER_ID[0],
10168 preference: None,
10169 dns_servers: None,
10170 ia_nas: std::iter::once((
10171 iaid,
10172 TestIa::new_renewed_default(RENEW_NON_TEMPORARY_ADDRESSES[0]),
10173 )),
10174 ia_pds: std::iter::once((
10175 iaid,
10176 TestIa::new_renewed_default(RENEW_DELEGATED_PREFIXES[0]),
10177 )),
10178 }
10179 .build();
10180 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10182 let actions = client.handle_message_receive(msg, time);
10183 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
10184 &client;
10185
10186 assert_matches!(
10192 &state,
10193 Some(ClientState::Assigned(Assigned {
10194 client_id,
10195 non_temporary_addresses,
10196 delegated_prefixes,
10197 server_id,
10198 dns_servers,
10199 solicit_max_rt,
10200 _marker,
10201 })) => {
10202 assert_eq!(client_id.as_slice(), &CLIENT_ID);
10203 fn calc_expected<V: IaValue>(
10204 iaid: v6::IAID,
10205 time: Instant,
10206 initial: V,
10207 in_renew: V,
10208 ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
10209 HashMap::from([(
10210 iaid,
10211 IaEntry::Assigned(HashMap::from([
10212 (
10213 initial,
10214 LifetimesInfo {
10215 lifetimes: Lifetimes::new_finite(
10216 PREFERRED_LIFETIME,
10217 VALID_LIFETIME,
10218 ),
10219 updated_at: time,
10220 }
10221 ),
10222 (
10223 in_renew,
10224 LifetimesInfo {
10225 lifetimes: Lifetimes::new_finite(
10226 RENEWED_PREFERRED_LIFETIME,
10227 RENEWED_VALID_LIFETIME,
10228 ),
10229 updated_at: time,
10230 },
10231 ),
10232 ])),
10233 )])
10234 }
10235 assert_eq!(
10236 non_temporary_addresses,
10237 &calc_expected(
10238 iaid,
10239 time,
10240 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
10241 RENEW_NON_TEMPORARY_ADDRESSES[0],
10242 )
10243 );
10244 assert_eq!(
10245 delegated_prefixes,
10246 &calc_expected(
10247 iaid,
10248 time,
10249 CONFIGURED_DELEGATED_PREFIXES[0],
10250 RENEW_DELEGATED_PREFIXES[0],
10251 )
10252 );
10253 assert_eq!(server_id.as_slice(), &SERVER_ID[0]);
10254 assert_eq!(dns_servers.as_slice(), &[] as &[Ipv6Addr]);
10255 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
10256 }
10257 );
10258 assert_matches!(
10259 &actions[..],
10260 [
10261 Action::CancelTimer(ClientTimerType::Retransmission),
10262 Action::ScheduleTimer(ClientTimerType::Renew, t1),
10263 Action::ScheduleTimer(ClientTimerType::Rebind, t2),
10264 Action::IaNaUpdates(iana_updates),
10265 Action::IaPdUpdates(iapd_updates),
10266 Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
10267 ] => {
10268 assert_eq!(*t1, time.add(Duration::from_secs(RENEWED_T1.get().into())));
10269 assert_eq!(*t2, time.add(Duration::from_secs(RENEWED_T2.get().into())));
10270 assert_eq!(
10271 *restart_time,
10272 time.add(Duration::from_secs(std::cmp::max(
10273 VALID_LIFETIME,
10274 RENEWED_VALID_LIFETIME,
10275 ).get().into()))
10276 );
10277
10278 fn get_updates<V: IaValue>(
10279 iaid: v6::IAID,
10280 new_value: V,
10281 ) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
10282 HashMap::from([
10283 (
10284 iaid,
10285 HashMap::from([
10286 (
10287 new_value,
10288 IaValueUpdateKind::Added(Lifetimes::new_renewed()),
10289 ),
10290 ])
10291 ),
10292 ])
10293 }
10294
10295 assert_eq!(
10296 iana_updates,
10297 &get_updates(
10298 iaid,
10299 RENEW_NON_TEMPORARY_ADDRESSES[0],
10300 ),
10301 );
10302 assert_eq!(
10303 iapd_updates,
10304 &get_updates(
10305 iaid,
10306 RENEW_DELEGATED_PREFIXES[0],
10307 ),
10308 );
10309 }
10310 );
10311 }
10312
10313 struct NoBindingTestCase {
10314 ia_na_no_binding: bool,
10315 ia_pd_no_binding: bool,
10316 }
10317
10318 #[test_case(
10319 RENEW_TEST,
10320 NoBindingTestCase {
10321 ia_na_no_binding: true,
10322 ia_pd_no_binding: false,
10323 }
10324 )]
10325 #[test_case(
10326 REBIND_TEST,
10327 NoBindingTestCase {
10328 ia_na_no_binding: true,
10329 ia_pd_no_binding: false,
10330 }
10331 )]
10332 #[test_case(
10333 RENEW_TEST,
10334 NoBindingTestCase {
10335 ia_na_no_binding: false,
10336 ia_pd_no_binding: true,
10337 }
10338 )]
10339 #[test_case(
10340 REBIND_TEST,
10341 NoBindingTestCase {
10342 ia_na_no_binding: false,
10343 ia_pd_no_binding: true,
10344 }
10345 )]
10346 #[test_case(
10347 RENEW_TEST,
10348 NoBindingTestCase {
10349 ia_na_no_binding: true,
10350 ia_pd_no_binding: true,
10351 }
10352 )]
10353 #[test_case(
10354 REBIND_TEST,
10355 NoBindingTestCase {
10356 ia_na_no_binding: true,
10357 ia_pd_no_binding: true,
10358 }
10359 )]
10360 fn no_binding(
10361 RenewRebindTest {
10362 send_and_assert,
10363 message_type: _,
10364 expect_server_id: _,
10365 with_state: _,
10366 allow_response_from_any_server: _,
10367 }: RenewRebindTest,
10368 NoBindingTestCase { ia_na_no_binding, ia_pd_no_binding }: NoBindingTestCase,
10369 ) {
10370 const NUM_IAS: u32 = 2;
10371 const NO_BINDING_IA_IDX: usize = (NUM_IAS - 1) as usize;
10372
10373 fn to_assign<V: IaValueTestExt>() -> Vec<TestIa<V>> {
10374 V::CONFIGURED[0..usize::try_from(NUM_IAS).unwrap()]
10375 .iter()
10376 .copied()
10377 .map(TestIa::new_default)
10378 .collect()
10379 }
10380 let time = Instant::now();
10381 let non_temporary_addresses_to_assign = to_assign::<Ipv6Addr>();
10382 let delegated_prefixes_to_assign = to_assign::<Subnet<Ipv6Addr>>();
10383 let mut client = send_and_assert(
10384 &(CLIENT_ID.into()),
10385 SERVER_ID[0],
10386 non_temporary_addresses_to_assign.clone(),
10387 delegated_prefixes_to_assign.clone(),
10388 None,
10389 T1,
10390 T2,
10391 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
10392 StepRng::new(u64::MAX / 2, 0),
10393 time,
10394 );
10395 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
10396 &client;
10397
10398 let iaaddr_opts = (0..usize::try_from(NUM_IAS).unwrap())
10400 .map(|i| {
10401 if i == NO_BINDING_IA_IDX && ia_na_no_binding {
10402 [v6::DhcpOption::StatusCode(
10403 v6::ErrorStatusCode::NoBinding.into(),
10404 "Binding not found.",
10405 )]
10406 } else {
10407 [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
10408 CONFIGURED_NON_TEMPORARY_ADDRESSES[i],
10409 RENEWED_PREFERRED_LIFETIME.get(),
10410 RENEWED_VALID_LIFETIME.get(),
10411 &[],
10412 ))]
10413 }
10414 })
10415 .collect::<Vec<_>>();
10416 let iaprefix_opts = (0..usize::try_from(NUM_IAS).unwrap())
10417 .map(|i| {
10418 if i == NO_BINDING_IA_IDX && ia_pd_no_binding {
10419 [v6::DhcpOption::StatusCode(
10420 v6::ErrorStatusCode::NoBinding.into(),
10421 "Binding not found.",
10422 )]
10423 } else {
10424 [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
10425 RENEWED_PREFERRED_LIFETIME.get(),
10426 RENEWED_VALID_LIFETIME.get(),
10427 CONFIGURED_DELEGATED_PREFIXES[i],
10428 &[],
10429 ))]
10430 }
10431 })
10432 .collect::<Vec<_>>();
10433 let options =
10434 [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0])]
10435 .into_iter()
10436 .chain((0..NUM_IAS).map(|id| {
10437 v6::DhcpOption::Iana(v6::IanaSerializer::new(
10438 v6::IAID::new(id),
10439 RENEWED_T1.get(),
10440 RENEWED_T2.get(),
10441 &iaaddr_opts[id as usize],
10442 ))
10443 }))
10444 .chain((0..NUM_IAS).map(|id| {
10445 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
10446 v6::IAID::new(id),
10447 RENEWED_T1.get(),
10448 RENEWED_T2.get(),
10449 &iaprefix_opts[id as usize],
10450 ))
10451 }))
10452 .collect::<Vec<_>>();
10453
10454 let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
10455 let mut buf = vec![0; builder.bytes_len()];
10456 builder.serialize(&mut buf);
10457 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10459 let actions = client.handle_message_receive(msg, time);
10460 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
10461 &client;
10462 {
10464 let Requesting {
10465 client_id,
10466 non_temporary_addresses,
10467 delegated_prefixes,
10468 server_id,
10469 collected_advertise: _,
10470 first_request_time: _,
10471 retrans_timeout: _,
10472 transmission_count: _,
10473 solicit_max_rt,
10474 } = assert_matches!(
10475 &state,
10476 Some(ClientState::Requesting(requesting)) => requesting
10477 );
10478 assert_eq!(client_id.as_slice(), &CLIENT_ID);
10479 fn expected_values<V: IaValueTestExt>(
10480 no_binding: bool,
10481 time: Instant,
10482 ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
10483 (0..NUM_IAS)
10484 .map(|i| {
10485 (v6::IAID::new(i), {
10486 let i = usize::try_from(i).unwrap();
10487 if i == NO_BINDING_IA_IDX && no_binding {
10488 IaEntry::ToRequest(HashSet::from([V::CONFIGURED[i]]))
10489 } else {
10490 IaEntry::new_assigned(
10491 V::CONFIGURED[i],
10492 RENEWED_PREFERRED_LIFETIME,
10493 RENEWED_VALID_LIFETIME,
10494 time,
10495 )
10496 }
10497 })
10498 })
10499 .collect()
10500 }
10501 assert_eq!(
10502 *non_temporary_addresses,
10503 expected_values::<Ipv6Addr>(ia_na_no_binding, time)
10504 );
10505 assert_eq!(
10506 *delegated_prefixes,
10507 expected_values::<Subnet<Ipv6Addr>>(ia_pd_no_binding, time)
10508 );
10509 assert_eq!(*server_id, SERVER_ID[0]);
10510 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
10511 }
10512 let buf = assert_matches!(
10513 &actions[..],
10514 [
10515 Action::CancelTimer(ClientTimerType::Retransmission),
10518 Action::SendMessage(buf),
10519 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
10520 ] => {
10521 assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
10522 buf
10523 }
10524 );
10525 testutil::assert_outgoing_stateful_message(
10528 &buf,
10529 v6::MessageType::Request,
10530 &CLIENT_ID,
10531 Some(&SERVER_ID[0]),
10532 &[],
10533 &(0..NUM_IAS)
10534 .map(v6::IAID::new)
10535 .zip(CONFIGURED_NON_TEMPORARY_ADDRESSES.into_iter().map(|a| HashSet::from([a])))
10536 .collect(),
10537 &(0..NUM_IAS)
10538 .map(v6::IAID::new)
10539 .zip(CONFIGURED_DELEGATED_PREFIXES.into_iter().map(|p| HashSet::from([p])))
10540 .collect(),
10541 );
10542
10543 handle_all_leases_invalidated(
10546 client,
10547 &CLIENT_ID,
10548 non_temporary_addresses_to_assign,
10549 delegated_prefixes_to_assign,
10550 ia_na_no_binding.then_some(NO_BINDING_IA_IDX),
10551 ia_pd_no_binding.then_some(NO_BINDING_IA_IDX),
10552 &[],
10553 )
10554 }
10555
10556 struct ReceiveReplyCalculateT1T2 {
10557 ia_na_success_t1: v6::NonZeroOrMaxU32,
10558 ia_na_success_t2: v6::NonZeroOrMaxU32,
10559 ia_pd_success_t1: v6::NonZeroOrMaxU32,
10560 ia_pd_success_t2: v6::NonZeroOrMaxU32,
10561 }
10562
10563 const TINY_NON_ZERO_OR_MAX_U32: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(10).unwrap();
10564 const SMALL_NON_ZERO_OR_MAX_U32: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(100).unwrap();
10565 const MEDIUM_NON_ZERO_OR_MAX_U32: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(1000).unwrap();
10566 const LARGE_NON_ZERO_OR_MAX_U32: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(10000).unwrap();
10567
10568 #[test_case(
10569 RENEW_TEST,
10570 ReceiveReplyCalculateT1T2 {
10571 ia_na_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10572 ia_na_success_t2: TINY_NON_ZERO_OR_MAX_U32,
10573 ia_pd_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10574 ia_pd_success_t2: TINY_NON_ZERO_OR_MAX_U32,
10575 }; "renew lifetimes matching erroneous IAs")]
10576 #[test_case(
10577 RENEW_TEST,
10578 ReceiveReplyCalculateT1T2 {
10579 ia_na_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10580 ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10581 ia_pd_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10582 ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10583 }; "renew same lifetimes")]
10584 #[test_case(
10585 RENEW_TEST,
10586 ReceiveReplyCalculateT1T2 {
10587 ia_na_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10588 ia_na_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10589 ia_pd_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10590 ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10591 }; "renew IA_NA smaller lifetimes")]
10592 #[test_case(
10593 RENEW_TEST,
10594 ReceiveReplyCalculateT1T2 {
10595 ia_na_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10596 ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10597 ia_pd_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10598 ia_pd_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10599 }; "renew IA_PD smaller lifetimes")]
10600 #[test_case(
10601 RENEW_TEST,
10602 ReceiveReplyCalculateT1T2 {
10603 ia_na_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10604 ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10605 ia_pd_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10606 ia_pd_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10607 }; "renew IA_NA smaller T1 but IA_PD smaller t2")]
10608 #[test_case(
10609 RENEW_TEST,
10610 ReceiveReplyCalculateT1T2 {
10611 ia_na_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10612 ia_na_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10613 ia_pd_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10614 ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10615 }; "renew IA_PD smaller T1 but IA_NA smaller t2")]
10616 #[test_case(
10617 REBIND_TEST,
10618 ReceiveReplyCalculateT1T2 {
10619 ia_na_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10620 ia_na_success_t2: TINY_NON_ZERO_OR_MAX_U32,
10621 ia_pd_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10622 ia_pd_success_t2: TINY_NON_ZERO_OR_MAX_U32,
10623 }; "rebind lifetimes matching erroneous IAs")]
10624 #[test_case(
10625 REBIND_TEST,
10626 ReceiveReplyCalculateT1T2 {
10627 ia_na_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10628 ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10629 ia_pd_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10630 ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10631 }; "rebind same lifetimes")]
10632 #[test_case(
10633 REBIND_TEST,
10634 ReceiveReplyCalculateT1T2 {
10635 ia_na_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10636 ia_na_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10637 ia_pd_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10638 ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10639 }; "rebind IA_NA smaller lifetimes")]
10640 #[test_case(
10641 REBIND_TEST,
10642 ReceiveReplyCalculateT1T2 {
10643 ia_na_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10644 ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10645 ia_pd_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10646 ia_pd_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10647 }; "rebind IA_PD smaller lifetimes")]
10648 #[test_case(
10649 REBIND_TEST,
10650 ReceiveReplyCalculateT1T2 {
10651 ia_na_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10652 ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10653 ia_pd_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10654 ia_pd_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10655 }; "rebind IA_NA smaller T1 but IA_PD smaller t2")]
10656 #[test_case(
10657 REBIND_TEST,
10658 ReceiveReplyCalculateT1T2 {
10659 ia_na_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10660 ia_na_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10661 ia_pd_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10662 ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10663 }; "rebind IA_PD smaller T1 but IA_NA smaller t2")]
10664 fn receive_reply_calculate_t1_t2(
10666 RenewRebindTest {
10667 send_and_assert,
10668 message_type: _,
10669 expect_server_id: _,
10670 with_state: _,
10671 allow_response_from_any_server: _,
10672 }: RenewRebindTest,
10673 ReceiveReplyCalculateT1T2 {
10674 ia_na_success_t1,
10675 ia_na_success_t2,
10676 ia_pd_success_t1,
10677 ia_pd_success_t2,
10678 }: ReceiveReplyCalculateT1T2,
10679 ) {
10680 let time = Instant::now();
10681 let mut client = send_and_assert(
10682 &(CLIENT_ID.into()),
10683 SERVER_ID[0],
10684 CONFIGURED_NON_TEMPORARY_ADDRESSES.into_iter().map(TestIaNa::new_default).collect(),
10685 CONFIGURED_DELEGATED_PREFIXES.into_iter().map(TestIaPd::new_default).collect(),
10686 None,
10687 T1,
10688 T2,
10689 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
10690 StepRng::new(u64::MAX / 2, 0),
10691 time,
10692 );
10693 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
10694 &client;
10695 let ia_addr = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
10696 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
10697 RENEWED_PREFERRED_LIFETIME.get(),
10698 RENEWED_VALID_LIFETIME.get(),
10699 &[],
10700 ))];
10701 let ia_no_addrs_avail = [v6::DhcpOption::StatusCode(
10702 v6::ErrorStatusCode::NoAddrsAvail.into(),
10703 "No address available.",
10704 )];
10705 let ia_prefix = [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
10706 RENEWED_PREFERRED_LIFETIME.get(),
10707 RENEWED_VALID_LIFETIME.get(),
10708 CONFIGURED_DELEGATED_PREFIXES[0],
10709 &[],
10710 ))];
10711 let ia_no_prefixes_avail = [v6::DhcpOption::StatusCode(
10712 v6::ErrorStatusCode::NoPrefixAvail.into(),
10713 "No prefixes available.",
10714 )];
10715 let ok_iaid = v6::IAID::new(0);
10716 let no_value_avail_iaid = v6::IAID::new(1);
10717 let empty_values_iaid = v6::IAID::new(2);
10718 let options = vec![
10719 v6::DhcpOption::ClientId(&CLIENT_ID),
10720 v6::DhcpOption::ServerId(&SERVER_ID[0]),
10721 v6::DhcpOption::Iana(v6::IanaSerializer::new(
10722 ok_iaid,
10723 ia_na_success_t1.get(),
10724 ia_na_success_t2.get(),
10725 &ia_addr,
10726 )),
10727 v6::DhcpOption::Iana(v6::IanaSerializer::new(
10728 no_value_avail_iaid,
10729 TINY_NON_ZERO_OR_MAX_U32.get(),
10733 TINY_NON_ZERO_OR_MAX_U32.get(),
10734 &ia_no_addrs_avail,
10735 )),
10736 v6::DhcpOption::Iana(v6::IanaSerializer::new(
10737 empty_values_iaid,
10738 TINY_NON_ZERO_OR_MAX_U32.get(),
10742 TINY_NON_ZERO_OR_MAX_U32.get(),
10743 &[],
10744 )),
10745 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
10746 ok_iaid,
10747 ia_pd_success_t1.get(),
10748 ia_pd_success_t2.get(),
10749 &ia_prefix,
10750 )),
10751 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
10752 no_value_avail_iaid,
10753 TINY_NON_ZERO_OR_MAX_U32.get(),
10757 TINY_NON_ZERO_OR_MAX_U32.get(),
10758 &ia_no_prefixes_avail,
10759 )),
10760 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
10761 empty_values_iaid,
10762 TINY_NON_ZERO_OR_MAX_U32.get(),
10766 TINY_NON_ZERO_OR_MAX_U32.get(),
10767 &[],
10768 )),
10769 ];
10770
10771 let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
10772 let mut buf = vec![0; builder.bytes_len()];
10773 builder.serialize(&mut buf);
10774 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10776
10777 fn get_updates<V: IaValue>(
10778 ok_iaid: v6::IAID,
10779 ok_value: V,
10780 no_value_avail_iaid: v6::IAID,
10781 no_value_avail_value: V,
10782 ) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
10783 HashMap::from([
10784 (
10785 ok_iaid,
10786 HashMap::from([(
10787 ok_value,
10788 IaValueUpdateKind::UpdatedLifetimes(Lifetimes::new_renewed()),
10789 )]),
10790 ),
10791 (
10792 no_value_avail_iaid,
10793 HashMap::from([(no_value_avail_value, IaValueUpdateKind::Removed)]),
10794 ),
10795 ])
10796 }
10797 let expected_t1 = std::cmp::min(ia_na_success_t1, ia_pd_success_t1);
10798 let expected_t2 = std::cmp::min(ia_na_success_t2, ia_pd_success_t2);
10799 assert_eq!(
10800 client.handle_message_receive(msg, time),
10801 [
10802 Action::CancelTimer(ClientTimerType::Retransmission),
10803 if expected_t1 == expected_t2 {
10804 Action::CancelTimer(ClientTimerType::Renew)
10806 } else {
10807 Action::ScheduleTimer(
10808 ClientTimerType::Renew,
10809 time.add(Duration::from_secs(expected_t1.get().into())),
10810 )
10811 },
10812 Action::ScheduleTimer(
10813 ClientTimerType::Rebind,
10814 time.add(Duration::from_secs(expected_t2.get().into())),
10815 ),
10816 Action::IaNaUpdates(get_updates(
10817 ok_iaid,
10818 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
10819 no_value_avail_iaid,
10820 CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
10821 )),
10822 Action::IaPdUpdates(get_updates(
10823 ok_iaid,
10824 CONFIGURED_DELEGATED_PREFIXES[0],
10825 no_value_avail_iaid,
10826 CONFIGURED_DELEGATED_PREFIXES[1],
10827 )),
10828 Action::ScheduleTimer(
10829 ClientTimerType::RestartServerDiscovery,
10830 time.add(Duration::from_secs(
10831 std::cmp::max(VALID_LIFETIME, RENEWED_VALID_LIFETIME,).get().into()
10832 )),
10833 ),
10834 ],
10835 );
10836 }
10837
10838 #[test]
10839 fn unexpected_messages_are_ignored() {
10840 let (mut client, _) = ClientStateMachine::start_stateless(
10841 Vec::new(),
10842 StepRng::new(u64::MAX / 2, 0),
10843 Instant::now(),
10844 );
10845
10846 let builder = v6::MessageBuilder::new(
10847 v6::MessageType::Reply,
10848 [4, 5, 6],
10850 &[],
10851 );
10852 let mut buf = vec![0; builder.bytes_len()];
10853 builder.serialize(&mut buf);
10854 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10856
10857 assert!(client.handle_message_receive(msg, Instant::now()).is_empty());
10858
10859 for msg_type in [
10861 v6::MessageType::Solicit,
10862 v6::MessageType::Advertise,
10863 v6::MessageType::Request,
10864 v6::MessageType::Confirm,
10865 v6::MessageType::Renew,
10866 v6::MessageType::Rebind,
10867 v6::MessageType::Release,
10868 v6::MessageType::Decline,
10869 v6::MessageType::Reconfigure,
10870 v6::MessageType::InformationRequest,
10871 v6::MessageType::RelayForw,
10872 v6::MessageType::RelayRepl,
10873 ] {
10874 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
10875 &client;
10876 let builder = v6::MessageBuilder::new(msg_type, *transaction_id, &[]);
10877 let mut buf = vec![0; builder.bytes_len()];
10878 builder.serialize(&mut buf);
10879 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10881
10882 assert!(client.handle_message_receive(msg, Instant::now()).is_empty());
10883 }
10884 }
10885
10886 #[test]
10887 #[should_panic(expected = "received unexpected refresh timeout")]
10888 fn information_requesting_refresh_timeout_is_unreachable() {
10889 let (mut client, _) = ClientStateMachine::start_stateless(
10890 Vec::new(),
10891 StepRng::new(u64::MAX / 2, 0),
10892 Instant::now(),
10893 );
10894
10895 let _actions = client.handle_timeout(ClientTimerType::Refresh, Instant::now());
10898 }
10899
10900 #[test]
10901 #[should_panic(expected = "received unexpected retransmission timeout")]
10902 fn information_received_retransmission_timeout_is_unreachable() {
10903 let (mut client, _) = ClientStateMachine::start_stateless(
10904 Vec::new(),
10905 StepRng::new(u64::MAX / 2, 0),
10906 Instant::now(),
10907 );
10908 let ClientStateMachine { transaction_id, options_to_request: _, state, rng: _ } = &client;
10909 assert_matches!(
10910 *state,
10911 Some(ClientState::InformationRequesting(InformationRequesting {
10912 retrans_timeout: INITIAL_INFO_REQ_TIMEOUT,
10913 _marker,
10914 }))
10915 );
10916
10917 let options = [v6::DhcpOption::ServerId(&SERVER_ID[0])];
10918 let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
10919 let mut buf = vec![0; builder.bytes_len()];
10920 builder.serialize(&mut buf);
10921 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10923 let time = Instant::now();
10925 let actions = client.handle_message_receive(msg, time);
10926 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
10927 &client;
10928 assert_matches!(
10929 state,
10930 Some(ClientState::InformationReceived(InformationReceived { dns_servers, _marker }))
10931 if dns_servers.is_empty()
10932 );
10933 assert_eq!(
10934 actions[..],
10935 [
10936 Action::CancelTimer(ClientTimerType::Retransmission),
10937 Action::ScheduleTimer(ClientTimerType::Refresh, time.add(IRT_DEFAULT)),
10938 ]
10939 );
10940
10941 let _actions = client.handle_timeout(ClientTimerType::Retransmission, time);
10944 }
10945
10946 #[test]
10947 #[should_panic(expected = "received unexpected refresh timeout")]
10948 fn server_discovery_refresh_timeout_is_unreachable() {
10949 let time = Instant::now();
10950 let mut client = testutil::start_and_assert_server_discovery(
10951 &(CLIENT_ID.into()),
10952 testutil::to_configured_addresses(
10953 1,
10954 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
10955 ),
10956 Default::default(),
10957 Vec::new(),
10958 StepRng::new(u64::MAX / 2, 0),
10959 time,
10960 );
10961
10962 let _actions = client.handle_timeout(ClientTimerType::Refresh, time);
10964 }
10965
10966 #[test]
10967 #[should_panic(expected = "received unexpected refresh timeout")]
10968 fn requesting_refresh_timeout_is_unreachable() {
10969 let time = Instant::now();
10970 let (mut client, _transaction_id) = testutil::request_and_assert(
10971 &(CLIENT_ID.into()),
10972 SERVER_ID[0],
10973 vec![TestIaNa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[0])],
10974 Default::default(),
10975 &[],
10976 StepRng::new(u64::MAX / 2, 0),
10977 time,
10978 );
10979
10980 let _actions = client.handle_timeout(ClientTimerType::Refresh, time);
10982 }
10983
10984 #[test_case(ClientTimerType::Refresh)]
10985 #[test_case(ClientTimerType::Retransmission)]
10986 #[should_panic(expected = "received unexpected")]
10987 fn address_assiged_unexpected_timeout_is_unreachable(timeout: ClientTimerType) {
10988 let time = Instant::now();
10989 let (mut client, _actions) = testutil::assign_and_assert(
10990 &(CLIENT_ID.into()),
10991 SERVER_ID[0],
10992 vec![TestIaNa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[0])],
10993 Default::default(), &[],
10995 StepRng::new(u64::MAX / 2, 0),
10996 time,
10997 );
10998
10999 let _actions = client.handle_timeout(timeout, time);
11002 }
11003
11004 #[test_case(RENEW_TEST)]
11005 #[test_case(REBIND_TEST)]
11006 #[should_panic(expected = "received unexpected refresh timeout")]
11007 fn refresh_timeout_is_unreachable(
11008 RenewRebindTest {
11009 send_and_assert,
11010 message_type: _,
11011 expect_server_id: _,
11012 with_state: _,
11013 allow_response_from_any_server: _,
11014 }: RenewRebindTest,
11015 ) {
11016 let time = Instant::now();
11017 let mut client = send_and_assert(
11018 &(CLIENT_ID.into()),
11019 SERVER_ID[0],
11020 vec![TestIaNa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[0])],
11021 Default::default(), None,
11023 T1,
11024 T2,
11025 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
11026 StepRng::new(u64::MAX / 2, 0),
11027 time,
11028 );
11029
11030 let _actions = client.handle_timeout(ClientTimerType::Refresh, time);
11032 }
11033
11034 fn handle_all_leases_invalidated<R: Rng>(
11035 mut client: ClientStateMachine<Instant, R>,
11036 client_id: &[u8],
11037 non_temporary_addresses_to_assign: Vec<TestIaNa>,
11038 delegated_prefixes_to_assign: Vec<TestIaPd>,
11039 skip_removed_event_for_test_iana_idx: Option<usize>,
11040 skip_removed_event_for_test_iapd_idx: Option<usize>,
11041 options_to_request: &[v6::OptionCode],
11042 ) {
11043 let time = Instant::now();
11044 let actions = client.handle_timeout(ClientTimerType::RestartServerDiscovery, time);
11045 let buf = assert_matches!(
11046 &actions[..],
11047 [
11048 Action::CancelTimer(ClientTimerType::Retransmission),
11049 Action::CancelTimer(ClientTimerType::Refresh),
11050 Action::CancelTimer(ClientTimerType::Renew),
11051 Action::CancelTimer(ClientTimerType::Rebind),
11052 Action::CancelTimer(ClientTimerType::RestartServerDiscovery),
11053 Action::IaNaUpdates(ia_na_updates),
11054 Action::IaPdUpdates(ia_pd_updates),
11055 Action::SendMessage(buf),
11056 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
11057 ] => {
11058 fn get_updates<V: IaValue>(
11059 to_assign: &Vec<TestIa<V>>,
11060 skip_idx: Option<usize>,
11061 ) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
11062 (0..).zip(to_assign.iter())
11063 .filter_map(|(iaid, TestIa { values, t1: _, t2: _})| {
11064 skip_idx
11065 .map_or(true, |skip_idx| skip_idx != iaid)
11066 .then(|| (
11067 v6::IAID::new(iaid.try_into().unwrap()),
11068 values.keys().copied().map(|value| (
11069 value,
11070 IaValueUpdateKind::Removed,
11071 )).collect(),
11072 ))
11073 })
11074 .collect()
11075 }
11076 assert_eq!(
11077 ia_na_updates,
11078 &get_updates(
11079 &non_temporary_addresses_to_assign,
11080 skip_removed_event_for_test_iana_idx
11081 ),
11082 );
11083 assert_eq!(
11084 ia_pd_updates,
11085 &get_updates(
11086 &delegated_prefixes_to_assign,
11087 skip_removed_event_for_test_iapd_idx,
11088 ),
11089 );
11090 assert_eq!(*instant, time.add(INITIAL_SOLICIT_TIMEOUT));
11091 buf
11092 }
11093 );
11094
11095 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
11096 &client;
11097 testutil::assert_server_discovery(
11098 state,
11099 client_id,
11100 testutil::to_configured_addresses(
11101 non_temporary_addresses_to_assign.len(),
11102 non_temporary_addresses_to_assign
11103 .iter()
11104 .map(|TestIaNa { values, t1: _, t2: _ }| values.keys().cloned().collect()),
11105 ),
11106 testutil::to_configured_prefixes(
11107 delegated_prefixes_to_assign.len(),
11108 delegated_prefixes_to_assign
11109 .iter()
11110 .map(|TestIaPd { values, t1: _, t2: _ }| values.keys().cloned().collect()),
11111 ),
11112 time,
11113 buf,
11114 options_to_request,
11115 )
11116 }
11117
11118 #[test]
11119 fn assigned_handle_all_leases_invalidated() {
11120 let non_temporary_addresses_to_assign = CONFIGURED_NON_TEMPORARY_ADDRESSES
11121 .iter()
11122 .copied()
11123 .map(TestIaNa::new_default)
11124 .collect::<Vec<_>>();
11125 let delegated_prefixes_to_assign = CONFIGURED_DELEGATED_PREFIXES
11126 .iter()
11127 .copied()
11128 .map(TestIaPd::new_default)
11129 .collect::<Vec<_>>();
11130 let (client, _actions) = testutil::assign_and_assert(
11131 &(CLIENT_ID.into()),
11132 SERVER_ID[0],
11133 non_temporary_addresses_to_assign.clone(),
11134 delegated_prefixes_to_assign.clone(),
11135 &[],
11136 StepRng::new(u64::MAX / 2, 0),
11137 Instant::now(),
11138 );
11139
11140 handle_all_leases_invalidated(
11141 client,
11142 &CLIENT_ID,
11143 non_temporary_addresses_to_assign,
11144 delegated_prefixes_to_assign,
11145 None,
11146 None,
11147 &[],
11148 )
11149 }
11150
11151 #[test_case(RENEW_TEST)]
11152 #[test_case(REBIND_TEST)]
11153 fn renew_rebind_handle_all_leases_invalidated(
11154 RenewRebindTest {
11155 send_and_assert,
11156 message_type: _,
11157 expect_server_id: _,
11158 with_state: _,
11159 allow_response_from_any_server: _,
11160 }: RenewRebindTest,
11161 ) {
11162 let non_temporary_addresses_to_assign = CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]
11163 .iter()
11164 .map(|&addr| TestIaNa::new_default(addr))
11165 .collect::<Vec<_>>();
11166 let delegated_prefixes_to_assign = CONFIGURED_DELEGATED_PREFIXES[0..2]
11167 .iter()
11168 .map(|&addr| TestIaPd::new_default(addr))
11169 .collect::<Vec<_>>();
11170 let client = send_and_assert(
11171 &(CLIENT_ID.into()),
11172 SERVER_ID[0],
11173 non_temporary_addresses_to_assign.clone(),
11174 delegated_prefixes_to_assign.clone(),
11175 None,
11176 T1,
11177 T2,
11178 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
11179 StepRng::new(u64::MAX / 2, 0),
11180 Instant::now(),
11181 );
11182
11183 handle_all_leases_invalidated(
11184 client,
11185 &CLIENT_ID,
11186 non_temporary_addresses_to_assign,
11187 delegated_prefixes_to_assign,
11188 None,
11189 None,
11190 &[],
11191 )
11192 }
11193
11194 #[test]
11197 fn retransmission_timeout() {
11198 let mut rng = StepRng::new(u64::MAX / 2, 0);
11199
11200 let initial_rt = Duration::from_secs(1);
11201 let max_rt = Duration::from_secs(100);
11202
11203 let t =
11205 super::retransmission_timeout(Duration::from_nanos(0), initial_rt, max_rt, &mut rng);
11206 assert_eq!(t.as_millis(), initial_rt.as_millis());
11207
11208 let t =
11210 super::retransmission_timeout(Duration::from_secs(10), initial_rt, max_rt, &mut rng);
11211 assert_eq!(t, Duration::from_secs(20));
11212
11213 let t = super::retransmission_timeout(100 * max_rt, initial_rt, max_rt, &mut rng);
11215 assert_eq!(t.as_millis(), max_rt.as_millis());
11216 let t = super::retransmission_timeout(MAX_DURATION, initial_rt, max_rt, &mut rng);
11217 assert_eq!(t.as_millis(), max_rt.as_millis());
11218 let t = super::retransmission_timeout(
11220 100 * max_rt,
11221 initial_rt,
11222 Duration::from_nanos(0),
11223 &mut rng,
11224 );
11225 assert_eq!(t.as_millis(), (200 * max_rt).as_millis());
11226 let t = super::retransmission_timeout(
11228 MAX_DURATION,
11229 initial_rt,
11230 Duration::from_nanos(0),
11231 &mut rng,
11232 );
11233 assert_eq!(t.as_millis(), MAX_DURATION.as_millis());
11234
11235 let mut rng = StepRng::new(0, u64::MAX / 5);
11237 [
11238 (Duration::from_millis(10000), 19000),
11239 (Duration::from_millis(10000), 19400),
11240 (Duration::from_millis(10000), 19800),
11241 (Duration::from_millis(10000), 20200),
11242 (Duration::from_millis(10000), 20600),
11243 (Duration::from_millis(10000), 21000),
11244 (Duration::from_millis(10000), 19400),
11245 (100 * max_rt, 98000),
11247 (100 * max_rt, 102000),
11248 (100 * max_rt, 106000),
11249 (100 * max_rt, 110000),
11250 (100 * max_rt, 94000),
11251 (100 * max_rt, 98000),
11252 ]
11253 .iter()
11254 .for_each(|(rt, want_ms)| {
11255 let t = super::retransmission_timeout(*rt, initial_rt, max_rt, &mut rng);
11256 assert_eq!(t.as_millis(), *want_ms);
11257 });
11258 }
11259
11260 #[test_case(v6::TimeValue::Zero, v6::TimeValue::Zero, v6::TimeValue::Zero)]
11261 #[test_case(
11262 v6::TimeValue::Zero,
11263 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11264 v6::NonZeroOrMaxU32::new(120)
11265 .expect("should succeed for non-zero or u32::MAX values")
11266 )),
11267 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11268 v6::NonZeroOrMaxU32::new(120)
11269 .expect("should succeed for non-zero or u32::MAX values")
11270 ))
11271 )]
11272 #[test_case(
11273 v6::TimeValue::Zero,
11274 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11275 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity)
11276 )]
11277 #[test_case(
11278 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11279 v6::NonZeroOrMaxU32::new(120)
11280 .expect("should succeed for non-zero or u32::MAX values")
11281 )),
11282 v6::TimeValue::Zero,
11283 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11284 v6::NonZeroOrMaxU32::new(120)
11285 .expect("should succeed for non-zero or u32::MAX values")
11286 ))
11287 )]
11288 #[test_case(
11289 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11290 v6::NonZeroOrMaxU32::new(120)
11291 .expect("should succeed for non-zero or u32::MAX values")
11292 )),
11293 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11294 v6::NonZeroOrMaxU32::new(60)
11295 .expect("should succeed for non-zero or u32::MAX values")
11296 )),
11297 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11298 v6::NonZeroOrMaxU32::new(60)
11299 .expect("should succeed for non-zero or u32::MAX values")
11300 ))
11301 )]
11302 #[test_case(
11303 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11304 v6::NonZeroOrMaxU32::new(120)
11305 .expect("should succeed for non-zero or u32::MAX values")
11306 )),
11307 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11308 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11309 v6::NonZeroOrMaxU32::new(120)
11310 .expect("should succeed for non-zero or u32::MAX values")
11311 ))
11312 )]
11313 #[test_case(
11314 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11315 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11316 v6::NonZeroOrMaxU32::new(120)
11317 .expect("should succeed for non-zero or u32::MAX values")
11318 )),
11319 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11320 v6::NonZeroOrMaxU32::new(120)
11321 .expect("should succeed for non-zero or u32::MAX values")
11322 ))
11323 )]
11324 #[test_case(
11325 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11326 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11327 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity)
11328 )]
11329 fn maybe_get_nonzero_min(
11330 old_value: v6::TimeValue,
11331 new_value: v6::TimeValue,
11332 expected_value: v6::TimeValue,
11333 ) {
11334 assert_eq!(super::maybe_get_nonzero_min(old_value, new_value), expected_value);
11335 }
11336
11337 #[test_case(
11338 v6::NonZeroTimeValue::Finite(
11339 v6::NonZeroOrMaxU32::new(120)
11340 .expect("should succeed for non-zero or u32::MAX values")
11341 ),
11342 v6::TimeValue::Zero,
11343 v6::NonZeroTimeValue::Finite(
11344 v6::NonZeroOrMaxU32::new(120)
11345 .expect("should succeed for non-zero or u32::MAX values")
11346 )
11347 )]
11348 #[test_case(
11349 v6::NonZeroTimeValue::Finite(
11350 v6::NonZeroOrMaxU32::new(120)
11351 .expect("should succeed for non-zero or u32::MAX values")
11352 ),
11353 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11354 v6::NonZeroOrMaxU32::new(60)
11355 .expect("should succeed for non-zero or u32::MAX values")
11356 )),
11357 v6::NonZeroTimeValue::Finite(
11358 v6::NonZeroOrMaxU32::new(60)
11359 .expect("should succeed for non-zero or u32::MAX values")
11360 )
11361 )]
11362 #[test_case(
11363 v6::NonZeroTimeValue::Finite(
11364 v6::NonZeroOrMaxU32::new(120)
11365 .expect("should succeed for non-zero or u32::MAX values")
11366 ),
11367 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11368 v6::NonZeroTimeValue::Finite(
11369 v6::NonZeroOrMaxU32::new(120)
11370 .expect("should succeed for non-zero or u32::MAX values")
11371 )
11372 )]
11373 #[test_case(
11374 v6::NonZeroTimeValue::Infinity,
11375 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11376 v6::NonZeroOrMaxU32::new(120)
11377 .expect("should succeed for non-zero or u32::MAX values"))
11378 ),
11379 v6::NonZeroTimeValue::Finite(
11380 v6::NonZeroOrMaxU32::new(120)
11381 .expect("should succeed for non-zero or u32::MAX values")
11382 )
11383 )]
11384 #[test_case(
11385 v6::NonZeroTimeValue::Infinity,
11386 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11387 v6::NonZeroTimeValue::Infinity
11388 )]
11389 #[test_case(
11390 v6::NonZeroTimeValue::Infinity,
11391 v6::TimeValue::Zero,
11392 v6::NonZeroTimeValue::Infinity
11393 )]
11394 fn get_nonzero_min(
11395 old_value: v6::NonZeroTimeValue,
11396 new_value: v6::TimeValue,
11397 expected_value: v6::NonZeroTimeValue,
11398 ) {
11399 assert_eq!(super::get_nonzero_min(old_value, new_value), expected_value);
11400 }
11401
11402 #[test_case(
11403 v6::NonZeroTimeValue::Infinity,
11404 T1_MIN_LIFETIME_RATIO,
11405 v6::NonZeroTimeValue::Infinity
11406 )]
11407 #[test_case(
11408 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(100).expect("should succeed")),
11409 T1_MIN_LIFETIME_RATIO,
11410 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(50).expect("should succeed"))
11411 )]
11412 #[test_case(v6::NonZeroTimeValue::Infinity, T2_T1_RATIO, v6::NonZeroTimeValue::Infinity)]
11413 #[test_case(
11414 v6::NonZeroTimeValue::Finite(
11415 v6::NonZeroOrMaxU32::new(INFINITY - 1)
11416 .expect("should succeed")
11417 ),
11418 T2_T1_RATIO,
11419 v6::NonZeroTimeValue::Infinity
11420 )]
11421 fn compute_t(min: v6::NonZeroTimeValue, ratio: Ratio<u32>, expected_t: v6::NonZeroTimeValue) {
11422 assert_eq!(super::compute_t(min, ratio), expected_t);
11423 }
11424}