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, RngExt as _};
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 = 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::TryRng;
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 TryRng for StepRng {
6184 type Error = core::convert::Infallible;
6185
6186 fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
6187 Ok(self.try_next_u64()? as u32)
6188 }
6189
6190 fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
6191 let r = self.state;
6192 self.state = self.state.wrapping_add(self.increment);
6193 Ok(r)
6194 }
6195
6196 fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
6197 for byte in dst {
6198 *byte = self.try_next_u64()? as u8;
6199 }
6200 Ok(())
6201 }
6202 }
6203
6204 #[test]
6205 fn send_information_request_and_receive_reply() {
6206 for options in [
6208 Vec::new(),
6209 vec![v6::OptionCode::DnsServers],
6210 vec![v6::OptionCode::DnsServers, v6::OptionCode::DomainList],
6211 ] {
6212 let now = Instant::now();
6213 let (mut client, actions) = ClientStateMachine::start_stateless(
6214 options.clone(),
6215 StepRng::new(u64::MAX / 2, 0),
6216 now,
6217 );
6218
6219 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
6220 &client;
6221 assert_matches!(
6222 *state,
6223 Some(ClientState::InformationRequesting(InformationRequesting {
6224 retrans_timeout: INITIAL_INFO_REQ_TIMEOUT,
6225 _marker,
6226 }))
6227 );
6228
6229 let want_options_array = [v6::DhcpOption::Oro(&options)];
6232 let want_options = if options.is_empty() { &[][..] } else { &want_options_array[..] };
6233 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
6234 &client;
6235 let builder = v6::MessageBuilder::new(
6236 v6::MessageType::InformationRequest,
6237 *transaction_id,
6238 want_options,
6239 );
6240 let mut want_buf = vec![0; builder.bytes_len()];
6241 builder.serialize(&mut want_buf);
6242 assert_eq!(
6243 actions[..],
6244 [
6245 Action::SendMessage(want_buf),
6246 Action::ScheduleTimer(
6247 ClientTimerType::Retransmission,
6248 now.add(INITIAL_INFO_REQ_TIMEOUT),
6249 )
6250 ]
6251 );
6252
6253 let test_dhcp_refresh_time = 42u32;
6254 let options = [
6255 v6::DhcpOption::ServerId(&SERVER_ID[0]),
6256 v6::DhcpOption::InformationRefreshTime(test_dhcp_refresh_time),
6257 v6::DhcpOption::DnsServers(&DNS_SERVERS),
6258 ];
6259 let builder =
6260 v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
6261 let mut buf = vec![0; builder.bytes_len()];
6262 builder.serialize(&mut buf);
6263 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6265
6266 let now = Instant::now();
6267 let actions = client.handle_message_receive(msg, now);
6268 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
6269 client;
6270
6271 {
6272 assert_matches!(
6273 state,
6274 Some(ClientState::InformationReceived(InformationReceived { dns_servers, _marker }))
6275 if dns_servers == DNS_SERVERS.to_vec()
6276 );
6277 }
6278 assert_eq!(
6280 actions[..],
6281 [
6282 Action::CancelTimer(ClientTimerType::Retransmission),
6283 Action::ScheduleTimer(
6284 ClientTimerType::Refresh,
6285 now.add(Duration::from_secs(u64::from(test_dhcp_refresh_time))),
6286 ),
6287 Action::UpdateDnsServers(DNS_SERVERS.to_vec()),
6288 ]
6289 );
6290 }
6291 }
6292
6293 #[test]
6294 fn send_information_request_on_retransmission_timeout() {
6295 let now = Instant::now();
6296 let (mut client, actions) =
6297 ClientStateMachine::start_stateless(Vec::new(), StepRng::new(u64::MAX / 2, 0), now);
6298 assert_matches!(
6299 actions[..],
6300 [_, Action::ScheduleTimer(ClientTimerType::Retransmission, instant)] => {
6301 assert_eq!(instant, now.add(INITIAL_INFO_REQ_TIMEOUT));
6302 }
6303 );
6304
6305 let actions = client.handle_timeout(ClientTimerType::Retransmission, now);
6306 assert_matches!(
6308 actions[..],
6309 [
6310 _,
6311 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
6312 ] => assert_eq!(instant, now.add(2 * INITIAL_INFO_REQ_TIMEOUT))
6313 );
6314 }
6315
6316 #[test]
6317 fn send_information_request_on_refresh_timeout() {
6318 let (mut client, _) = ClientStateMachine::start_stateless(
6319 Vec::new(),
6320 StepRng::new(u64::MAX / 2, 1),
6324 Instant::now(),
6325 );
6326
6327 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
6328 &client;
6329 let options = [v6::DhcpOption::ServerId(&SERVER_ID[0])];
6330 let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
6331 let mut buf = vec![0; builder.bytes_len()];
6332 builder.serialize(&mut buf);
6333 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6335
6336 let time = Instant::now();
6338 assert_eq!(
6339 client.handle_message_receive(msg, time)[..],
6340 [
6341 Action::CancelTimer(ClientTimerType::Retransmission),
6342 Action::ScheduleTimer(ClientTimerType::Refresh, time.add(IRT_DEFAULT))
6343 ]
6344 );
6345
6346 let old_transaction_id = client.transaction_id;
6347
6348 let actions = client.handle_timeout(ClientTimerType::Refresh, time);
6350 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
6351 &client;
6352
6353 assert_ne!(old_transaction_id, *transaction_id);
6356
6357 let builder =
6358 v6::MessageBuilder::new(v6::MessageType::InformationRequest, *transaction_id, &[]);
6359 let mut want_buf = vec![0; builder.bytes_len()];
6360 builder.serialize(&mut want_buf);
6361 assert_eq!(
6362 actions[..],
6363 [
6364 Action::SendMessage(want_buf),
6365 Action::ScheduleTimer(
6366 ClientTimerType::Retransmission,
6367 time.add(INITIAL_INFO_REQ_TIMEOUT)
6368 )
6369 ]
6370 );
6371 }
6372
6373 #[test_case(
6376 0, std::iter::empty(),
6377 2, (&CONFIGURED_DELEGATED_PREFIXES[0..2]).iter().copied(),
6378 Vec::new()
6379 )]
6380 #[test_case(
6381 2, (&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]).iter().copied(),
6382 0, std::iter::empty(),
6383 vec![v6::OptionCode::DnsServers]
6384 )]
6385 #[test_case(
6386 1, std::iter::empty(),
6387 2, (&CONFIGURED_DELEGATED_PREFIXES[0..2]).iter().copied(),
6388 Vec::new()
6389 )]
6390 #[test_case(
6391 2, std::iter::once(CONFIGURED_NON_TEMPORARY_ADDRESSES[0]),
6392 1, std::iter::empty(),
6393 vec![v6::OptionCode::DnsServers]
6394 )]
6395 #[test_case(
6396 2, (&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]).iter().copied(),
6397 2, std::iter::once(CONFIGURED_DELEGATED_PREFIXES[0]),
6398 vec![v6::OptionCode::DnsServers]
6399 )]
6400 fn send_solicit(
6401 address_count: usize,
6402 preferred_non_temporary_addresses: impl IntoIterator<Item = Ipv6Addr>,
6403 prefix_count: usize,
6404 preferred_delegated_prefixes: impl IntoIterator<Item = Subnet<Ipv6Addr>>,
6405 options_to_request: Vec<v6::OptionCode>,
6406 ) {
6407 let _client = testutil::start_and_assert_server_discovery(
6409 &(CLIENT_ID.into()),
6410 testutil::to_configured_addresses(
6411 address_count,
6412 preferred_non_temporary_addresses.into_iter().map(|a| HashSet::from([a])),
6413 ),
6414 testutil::to_configured_prefixes(
6415 prefix_count,
6416 preferred_delegated_prefixes.into_iter().map(|a| HashSet::from([a])),
6417 ),
6418 options_to_request,
6419 StepRng::new(u64::MAX / 2, 0),
6420 Instant::now(),
6421 );
6422 }
6423
6424 #[test_case(
6425 1, std::iter::empty(), std::iter::once(CONFIGURED_NON_TEMPORARY_ADDRESSES[0]), 0;
6426 "zero"
6427 )]
6428 #[test_case(
6429 2, CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2].iter().copied(), CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2].iter().copied(), 2;
6430 "two"
6431 )]
6432 #[test_case(
6433 4,
6434 CONFIGURED_NON_TEMPORARY_ADDRESSES.iter().copied(),
6435 std::iter::once(CONFIGURED_NON_TEMPORARY_ADDRESSES[0]).chain(REPLY_NON_TEMPORARY_ADDRESSES.iter().copied()),
6436 1;
6437 "one"
6438 )]
6439 fn compute_preferred_address_count(
6440 configure_count: usize,
6441 hints: impl IntoIterator<Item = Ipv6Addr>,
6442 got_addresses: impl IntoIterator<Item = Ipv6Addr>,
6443 want: usize,
6444 ) {
6445 let got_addresses: HashMap<_, _> = (0..)
6447 .map(v6::IAID::new)
6448 .zip(got_addresses.into_iter().map(|a| HashSet::from([a])))
6449 .collect();
6450 let configured_non_temporary_addresses = testutil::to_configured_addresses(
6451 configure_count,
6452 hints.into_iter().map(|a| HashSet::from([a])),
6453 );
6454 assert_eq!(
6455 super::compute_preferred_ia_count(&got_addresses, &configured_non_temporary_addresses),
6456 want,
6457 );
6458 }
6459
6460 #[test_case(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2], &CONFIGURED_DELEGATED_PREFIXES[0..2], true)]
6461 #[test_case(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..1], &CONFIGURED_DELEGATED_PREFIXES[0..1], true)]
6462 #[test_case(&REPLY_NON_TEMPORARY_ADDRESSES[0..2], &REPLY_DELEGATED_PREFIXES[0..2], true)]
6463 #[test_case(&[], &[], false)]
6464 fn advertise_message_has_ias(
6465 non_temporary_addresses: &[Ipv6Addr],
6466 delegated_prefixes: &[Subnet<Ipv6Addr>],
6467 expected: bool,
6468 ) {
6469 let configured_non_temporary_addresses = testutil::to_configured_addresses(
6470 2,
6471 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
6472 );
6473
6474 let configured_delegated_prefixes = testutil::to_configured_prefixes(
6475 2,
6476 std::iter::once(HashSet::from([CONFIGURED_DELEGATED_PREFIXES[0]])),
6477 );
6478
6479 let advertise = AdvertiseMessage::new_default(
6482 SERVER_ID[0],
6483 non_temporary_addresses,
6484 delegated_prefixes,
6485 &[],
6486 &configured_non_temporary_addresses,
6487 &configured_delegated_prefixes,
6488 );
6489 assert_eq!(advertise.has_ias(), expected);
6490 }
6491
6492 struct AdvertiseMessageOrdTestCase<'a> {
6493 adv1_non_temporary_addresses: &'a [Ipv6Addr],
6494 adv1_delegated_prefixes: &'a [Subnet<Ipv6Addr>],
6495 adv2_non_temporary_addresses: &'a [Ipv6Addr],
6496 adv2_delegated_prefixes: &'a [Subnet<Ipv6Addr>],
6497 expected: Ordering,
6498 }
6499
6500 #[test_case(AdvertiseMessageOrdTestCase{
6501 adv1_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2],
6502 adv1_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..2],
6503 adv2_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..3],
6504 adv2_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..3],
6505 expected: Ordering::Less,
6506 }; "adv1 has less IAs")]
6507 #[test_case(AdvertiseMessageOrdTestCase{
6508 adv1_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2],
6509 adv1_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..2],
6510 adv2_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[1..3],
6511 adv2_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[1..3],
6512 expected: Ordering::Greater,
6513 }; "adv1 has IAs matching hint")]
6514 #[test_case(AdvertiseMessageOrdTestCase{
6515 adv1_non_temporary_addresses: &[],
6516 adv1_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..3],
6517 adv2_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..1],
6518 adv2_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..1],
6519 expected: Ordering::Less,
6520 }; "adv1 missing IA_NA")]
6521 #[test_case(AdvertiseMessageOrdTestCase{
6522 adv1_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..3],
6523 adv1_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..1],
6524 adv2_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..3],
6525 adv2_delegated_prefixes: &[],
6526 expected: Ordering::Greater,
6527 }; "adv2 missing IA_PD")]
6528 fn advertise_message_ord(
6529 AdvertiseMessageOrdTestCase {
6530 adv1_non_temporary_addresses,
6531 adv1_delegated_prefixes,
6532 adv2_non_temporary_addresses,
6533 adv2_delegated_prefixes,
6534 expected,
6535 }: AdvertiseMessageOrdTestCase<'_>,
6536 ) {
6537 let configured_non_temporary_addresses = testutil::to_configured_addresses(
6538 3,
6539 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
6540 );
6541
6542 let configured_delegated_prefixes = testutil::to_configured_prefixes(
6543 3,
6544 std::iter::once(HashSet::from([CONFIGURED_DELEGATED_PREFIXES[0]])),
6545 );
6546
6547 let advertise1 = AdvertiseMessage::new_default(
6548 SERVER_ID[0],
6549 adv1_non_temporary_addresses,
6550 adv1_delegated_prefixes,
6551 &[],
6552 &configured_non_temporary_addresses,
6553 &configured_delegated_prefixes,
6554 );
6555 let advertise2 = AdvertiseMessage::new_default(
6556 SERVER_ID[1],
6557 adv2_non_temporary_addresses,
6558 adv2_delegated_prefixes,
6559 &[],
6560 &configured_non_temporary_addresses,
6561 &configured_delegated_prefixes,
6562 );
6563 assert_eq!(advertise1.cmp(&advertise2), expected);
6564 }
6565
6566 #[test_case(v6::DhcpOption::StatusCode(v6::StatusCode::Success.into(), ""); "status_code")]
6567 #[test_case(v6::DhcpOption::ClientId(&CLIENT_ID); "client_id")]
6568 #[test_case(v6::DhcpOption::ServerId(&SERVER_ID[0]); "server_id")]
6569 #[test_case(v6::DhcpOption::Preference(ADVERTISE_MAX_PREFERENCE); "preference")]
6570 #[test_case(v6::DhcpOption::SolMaxRt(*VALID_MAX_SOLICIT_TIMEOUT_RANGE.end()); "sol_max_rt")]
6571 #[test_case(v6::DhcpOption::DnsServers(&DNS_SERVERS); "dns_servers")]
6572 fn process_options_duplicates<'a>(opt: v6::DhcpOption<'a>) {
6573 let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6574 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
6575 60,
6576 60,
6577 &[],
6578 ))];
6579 let iaid = v6::IAID::new(0);
6580 let options = [
6581 v6::DhcpOption::StatusCode(v6::StatusCode::Success.into(), ""),
6582 v6::DhcpOption::ClientId(&CLIENT_ID),
6583 v6::DhcpOption::ServerId(&SERVER_ID[0]),
6584 v6::DhcpOption::Preference(ADVERTISE_MAX_PREFERENCE),
6585 v6::DhcpOption::SolMaxRt(*VALID_MAX_SOLICIT_TIMEOUT_RANGE.end()),
6586 v6::DhcpOption::Iana(v6::IanaSerializer::new(iaid, T1.get(), T2.get(), &iana_options)),
6587 v6::DhcpOption::DnsServers(&DNS_SERVERS),
6588 opt,
6589 ];
6590 let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6591 let mut buf = vec![0; builder.bytes_len()];
6592 builder.serialize(&mut buf);
6593 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6595 let requested_ia_nas = HashMap::from([(iaid, None::<Ipv6Addr>)]);
6596 assert_matches!(
6597 process_options(
6598 &msg,
6599 ExchangeType::AdvertiseToSolicit,
6600 Some(&CLIENT_ID),
6601 &requested_ia_nas,
6602 &NoIaRequested
6603 ),
6604 Err(OptionsError::DuplicateOption(_, _, _))
6605 );
6606 }
6607
6608 #[derive(Copy, Clone)]
6609 enum DupIaValue {
6610 Address,
6611 Prefix,
6612 }
6613
6614 impl DupIaValue {
6615 fn second_address(self) -> Ipv6Addr {
6616 match self {
6617 DupIaValue::Address => CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
6618 DupIaValue::Prefix => CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
6619 }
6620 }
6621
6622 fn second_prefix(self) -> Subnet<Ipv6Addr> {
6623 match self {
6624 DupIaValue::Address => CONFIGURED_DELEGATED_PREFIXES[1],
6625 DupIaValue::Prefix => CONFIGURED_DELEGATED_PREFIXES[0],
6626 }
6627 }
6628 }
6629
6630 #[test_case(
6631 DupIaValue::Address,
6632 |res| {
6633 assert_matches!(
6634 res,
6635 Err(OptionsError::IaNaError(IaOptionError::DuplicateIaValue {
6636 value,
6637 first_lifetimes,
6638 second_lifetimes,
6639 })) => {
6640 assert_eq!(value, CONFIGURED_NON_TEMPORARY_ADDRESSES[0]);
6641 (first_lifetimes, second_lifetimes)
6642 }
6643 )
6644 }; "duplicate address")]
6645 #[test_case(
6646 DupIaValue::Prefix,
6647 |res| {
6648 assert_matches!(
6649 res,
6650 Err(OptionsError::IaPdError(IaPdOptionError::IaOptionError(
6651 IaOptionError::DuplicateIaValue {
6652 value,
6653 first_lifetimes,
6654 second_lifetimes,
6655 }
6656 ))) => {
6657 assert_eq!(value, CONFIGURED_DELEGATED_PREFIXES[0]);
6658 (first_lifetimes, second_lifetimes)
6659 }
6660 )
6661 }; "duplicate prefix")]
6662 fn process_options_duplicate_ia_value(
6663 dup_ia_value: DupIaValue,
6664 check: fn(
6665 Result<ProcessedOptions, OptionsError>,
6666 )
6667 -> (Result<Lifetimes, LifetimesError>, Result<Lifetimes, LifetimesError>),
6668 ) {
6669 const IA_VALUE1_LIFETIME: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(60).unwrap();
6670 const IA_VALUE2_LIFETIME: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(100).unwrap();
6671 let iana_options = [
6672 v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6673 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
6674 IA_VALUE1_LIFETIME.get(),
6675 IA_VALUE1_LIFETIME.get(),
6676 &[],
6677 )),
6678 v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6679 dup_ia_value.second_address(),
6680 IA_VALUE2_LIFETIME.get(),
6681 IA_VALUE2_LIFETIME.get(),
6682 &[],
6683 )),
6684 ];
6685 let iapd_options = [
6686 v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
6687 IA_VALUE1_LIFETIME.get(),
6688 IA_VALUE1_LIFETIME.get(),
6689 CONFIGURED_DELEGATED_PREFIXES[0],
6690 &[],
6691 )),
6692 v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
6693 IA_VALUE2_LIFETIME.get(),
6694 IA_VALUE2_LIFETIME.get(),
6695 dup_ia_value.second_prefix(),
6696 &[],
6697 )),
6698 ];
6699 let iaid = v6::IAID::new(0);
6700 let options = [
6701 v6::DhcpOption::ClientId(&CLIENT_ID),
6702 v6::DhcpOption::ServerId(&SERVER_ID[0]),
6703 v6::DhcpOption::Iana(v6::IanaSerializer::new(iaid, T1.get(), T2.get(), &iana_options)),
6704 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(iaid, T1.get(), T2.get(), &iapd_options)),
6705 ];
6706 let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6707 let mut buf = vec![0; builder.bytes_len()];
6708 builder.serialize(&mut buf);
6709 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6711 let requested_ia_nas = HashMap::from([(iaid, None::<Ipv6Addr>)]);
6712 let (first_lifetimes, second_lifetimes) = check(process_options(
6713 &msg,
6714 ExchangeType::AdvertiseToSolicit,
6715 Some(&CLIENT_ID),
6716 &requested_ia_nas,
6717 &NoIaRequested,
6718 ));
6719 assert_eq!(
6720 first_lifetimes,
6721 Ok(Lifetimes::new_finite(IA_VALUE1_LIFETIME, IA_VALUE1_LIFETIME))
6722 );
6723 assert_eq!(
6724 second_lifetimes,
6725 Ok(Lifetimes::new_finite(IA_VALUE2_LIFETIME, IA_VALUE2_LIFETIME))
6726 )
6727 }
6728
6729 #[test]
6730 fn process_options_t1_greather_than_t2() {
6731 let iana_options1 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6732 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
6733 MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6734 MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6735 &[],
6736 ))];
6737 let iana_options2 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6738 CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
6739 MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6740 MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6741 &[],
6742 ))];
6743 let iapd_options1 = [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
6744 LARGE_NON_ZERO_OR_MAX_U32.get(),
6745 LARGE_NON_ZERO_OR_MAX_U32.get(),
6746 CONFIGURED_DELEGATED_PREFIXES[0],
6747 &[],
6748 ))];
6749 let iapd_options2 = [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
6750 LARGE_NON_ZERO_OR_MAX_U32.get(),
6751 LARGE_NON_ZERO_OR_MAX_U32.get(),
6752 CONFIGURED_DELEGATED_PREFIXES[1],
6753 &[],
6754 ))];
6755
6756 let iaid1 = v6::IAID::new(1);
6757 let iaid2 = v6::IAID::new(2);
6758 let options = [
6759 v6::DhcpOption::ClientId(&CLIENT_ID),
6760 v6::DhcpOption::ServerId(&SERVER_ID[0]),
6761 v6::DhcpOption::Iana(v6::IanaSerializer::new(
6762 iaid1,
6763 MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6764 SMALL_NON_ZERO_OR_MAX_U32.get(),
6765 &iana_options1,
6766 )),
6767 v6::DhcpOption::Iana(v6::IanaSerializer::new(
6768 iaid2,
6769 SMALL_NON_ZERO_OR_MAX_U32.get(),
6770 MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6771 &iana_options2,
6772 )),
6773 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
6774 iaid1,
6775 LARGE_NON_ZERO_OR_MAX_U32.get(),
6776 TINY_NON_ZERO_OR_MAX_U32.get(),
6777 &iapd_options1,
6778 )),
6779 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
6780 iaid2,
6781 TINY_NON_ZERO_OR_MAX_U32.get(),
6782 LARGE_NON_ZERO_OR_MAX_U32.get(),
6783 &iapd_options2,
6784 )),
6785 ];
6786 let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6787 let mut buf = vec![0; builder.bytes_len()];
6788 builder.serialize(&mut buf);
6789 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6791 let requested_ia_nas = HashMap::from([(iaid1, None::<Ipv6Addr>), (iaid2, None)]);
6792 let requested_ia_pds = HashMap::from([(iaid1, None::<Subnet<Ipv6Addr>>), (iaid2, None)]);
6793 assert_matches!(
6794 process_options(&msg, ExchangeType::AdvertiseToSolicit, Some(&CLIENT_ID), &requested_ia_nas, &requested_ia_pds),
6795 Ok(ProcessedOptions {
6796 server_id: _,
6797 solicit_max_rt_opt: _,
6798 result: Ok(Options {
6799 success_status_message: _,
6800 next_contact_time: _,
6801 non_temporary_addresses,
6802 delegated_prefixes,
6803 dns_servers: _,
6804 preference: _,
6805 }),
6806 }) => {
6807 assert_eq!(non_temporary_addresses, HashMap::from([(iaid2, IaOption::Success {
6808 status_message: None,
6809 t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(SMALL_NON_ZERO_OR_MAX_U32)),
6810 t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(MEDIUM_NON_ZERO_OR_MAX_U32)),
6811 ia_values: HashMap::from([(CONFIGURED_NON_TEMPORARY_ADDRESSES[1], Ok(Lifetimes{
6812 preferred_lifetime: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(MEDIUM_NON_ZERO_OR_MAX_U32)),
6813 valid_lifetime: v6::NonZeroTimeValue::Finite(MEDIUM_NON_ZERO_OR_MAX_U32),
6814 }))]),
6815 })]));
6816 assert_eq!(delegated_prefixes, HashMap::from([(iaid2, IaOption::Success {
6817 status_message: None,
6818 t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(TINY_NON_ZERO_OR_MAX_U32)),
6819 t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(LARGE_NON_ZERO_OR_MAX_U32)),
6820 ia_values: HashMap::from([(CONFIGURED_DELEGATED_PREFIXES[1], Ok(Lifetimes{
6821 preferred_lifetime: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(LARGE_NON_ZERO_OR_MAX_U32)),
6822 valid_lifetime: v6::NonZeroTimeValue::Finite(LARGE_NON_ZERO_OR_MAX_U32),
6823 }))]),
6824 })]));
6825 }
6826 );
6827 }
6828
6829 #[test]
6830 fn process_options_duplicate_ia_na_id() {
6831 let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6832 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
6833 60,
6834 60,
6835 &[],
6836 ))];
6837 let iaid = v6::IAID::new(0);
6838 let options = [
6839 v6::DhcpOption::ClientId(&CLIENT_ID),
6840 v6::DhcpOption::ServerId(&SERVER_ID[0]),
6841 v6::DhcpOption::Iana(v6::IanaSerializer::new(iaid, T1.get(), T2.get(), &iana_options)),
6842 v6::DhcpOption::Iana(v6::IanaSerializer::new(iaid, T1.get(), T2.get(), &iana_options)),
6843 ];
6844 let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6845 let mut buf = vec![0; builder.bytes_len()];
6846 builder.serialize(&mut buf);
6847 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6849 let requested_ia_nas = HashMap::from([(iaid, None::<Ipv6Addr>)]);
6850 assert_matches!(
6851 process_options(&msg, ExchangeType::AdvertiseToSolicit, Some(&CLIENT_ID), &requested_ia_nas, &NoIaRequested),
6852 Err(OptionsError::DuplicateIaNaId(got_iaid, _, _)) if got_iaid == iaid
6853 );
6854 }
6855
6856 #[test]
6857 fn process_options_missing_server_id() {
6858 let options = [v6::DhcpOption::ClientId(&CLIENT_ID)];
6859 let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6860 let mut buf = vec![0; builder.bytes_len()];
6861 builder.serialize(&mut buf);
6862 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6864 assert_matches!(
6865 process_options(
6866 &msg,
6867 ExchangeType::AdvertiseToSolicit,
6868 Some(&CLIENT_ID),
6869 &NoIaRequested,
6870 &NoIaRequested
6871 ),
6872 Err(OptionsError::MissingServerId)
6873 );
6874 }
6875
6876 #[test]
6877 fn process_options_missing_client_id() {
6878 let options = [v6::DhcpOption::ServerId(&SERVER_ID[0])];
6879 let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6880 let mut buf = vec![0; builder.bytes_len()];
6881 builder.serialize(&mut buf);
6882 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6884 assert_matches!(
6885 process_options(
6886 &msg,
6887 ExchangeType::AdvertiseToSolicit,
6888 Some(&CLIENT_ID),
6889 &NoIaRequested,
6890 &NoIaRequested
6891 ),
6892 Err(OptionsError::MissingClientId)
6893 );
6894 }
6895
6896 #[test]
6897 fn process_options_mismatched_client_id() {
6898 let options = [
6899 v6::DhcpOption::ClientId(&MISMATCHED_CLIENT_ID),
6900 v6::DhcpOption::ServerId(&SERVER_ID[0]),
6901 ];
6902 let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6903 let mut buf = vec![0; builder.bytes_len()];
6904 builder.serialize(&mut buf);
6905 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6907 assert_matches!(
6908 process_options(&msg, ExchangeType::AdvertiseToSolicit, Some(&CLIENT_ID), &NoIaRequested, &NoIaRequested),
6909 Err(OptionsError::MismatchedClientId { got, want })
6910 if got[..] == MISMATCHED_CLIENT_ID && want == CLIENT_ID
6911 );
6912 }
6913
6914 #[test]
6915 fn process_options_unexpected_client_id() {
6916 let options =
6917 [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0])];
6918 let builder = v6::MessageBuilder::new(v6::MessageType::Reply, [0, 1, 2], &options);
6919 let mut buf = vec![0; builder.bytes_len()];
6920 builder.serialize(&mut buf);
6921 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6923 assert_matches!(
6924 process_options(&msg, ExchangeType::ReplyToInformationRequest, None, &NoIaRequested, &NoIaRequested),
6925 Err(OptionsError::UnexpectedClientId(got))
6926 if got[..] == CLIENT_ID
6927 );
6928 }
6929
6930 #[test_case(
6931 v6::MessageType::Reply,
6932 ExchangeType::ReplyToInformationRequest,
6933 v6::DhcpOption::Iana(v6::IanaSerializer::new(v6::IAID::new(0), T1.get(),T2.get(), &[]));
6934 "reply_to_information_request_ia_na"
6935 )]
6936 fn process_options_drop<'a>(
6937 message_type: v6::MessageType,
6938 exchange_type: ExchangeType,
6939 opt: v6::DhcpOption<'a>,
6940 ) {
6941 let options =
6942 [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0]), opt];
6943 let builder = v6::MessageBuilder::new(message_type, [0, 1, 2], &options);
6944 let mut buf = vec![0; builder.bytes_len()];
6945 builder.serialize(&mut buf);
6946 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6948 assert_matches!(
6949 process_options(&msg, exchange_type, Some(&CLIENT_ID), &NoIaRequested, &NoIaRequested),
6950 Err(OptionsError::InvalidOption(_))
6951 );
6952 }
6953
6954 #[test_case(
6955 v6::MessageType::Reply,
6956 ExchangeType::ReplyToInformationRequest;
6957 "reply_to_information_request"
6958 )]
6959 #[test_case(
6960 v6::MessageType::Reply,
6961 ExchangeType::ReplyWithLeases(RequestLeasesMessageType::Request);
6962 "reply_to_request"
6963 )]
6964 fn process_options_ignore_preference<'a>(
6965 message_type: v6::MessageType,
6966 exchange_type: ExchangeType,
6967 ) {
6968 let options = [
6969 v6::DhcpOption::ClientId(&CLIENT_ID),
6970 v6::DhcpOption::ServerId(&SERVER_ID[0]),
6971 v6::DhcpOption::Preference(ADVERTISE_MAX_PREFERENCE),
6972 ];
6973 let builder = v6::MessageBuilder::new(message_type, [0, 1, 2], &options);
6974 let mut buf = vec![0; builder.bytes_len()];
6975 builder.serialize(&mut buf);
6976 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6978 assert_matches!(
6979 process_options(&msg, exchange_type, Some(&CLIENT_ID), &NoIaRequested, &NoIaRequested),
6980 Ok(ProcessedOptions { result: Ok(Options { preference: None, .. }), .. })
6981 );
6982 }
6983
6984 #[test_case(
6985 v6::MessageType::Advertise,
6986 ExchangeType::AdvertiseToSolicit;
6987 "advertise_to_solicit"
6988 )]
6989 #[test_case(
6990 v6::MessageType::Reply,
6991 ExchangeType::ReplyWithLeases(RequestLeasesMessageType::Request);
6992 "reply_to_request"
6993 )]
6994 fn process_options_ignore_information_refresh_time<'a>(
6995 message_type: v6::MessageType,
6996 exchange_type: ExchangeType,
6997 ) {
6998 let options = [
6999 v6::DhcpOption::ClientId(&CLIENT_ID),
7000 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7001 v6::DhcpOption::InformationRefreshTime(42u32),
7002 ];
7003 let builder = v6::MessageBuilder::new(message_type, [0, 1, 2], &options);
7004 let mut buf = vec![0; builder.bytes_len()];
7005 builder.serialize(&mut buf);
7006 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7008 assert_matches!(
7009 process_options(&msg, exchange_type, Some(&CLIENT_ID), &NoIaRequested, &NoIaRequested),
7010 Ok(ProcessedOptions {
7011 result: Ok(Options {
7012 next_contact_time: NextContactTime::RenewRebind { t1, t2 },
7013 ..
7014 }),
7015 ..
7016 }) => {
7017 assert_eq!(t1, v6::NonZeroTimeValue::Infinity);
7018 assert_eq!(t2, v6::NonZeroTimeValue::Infinity);
7019 }
7020 );
7021 }
7022
7023 mod process_reply_with_leases_unexpected_iaid {
7024 use super::*;
7025
7026 use test_case::test_case;
7027
7028 const EXPECTED_IAID: v6::IAID = v6::IAID::new(1);
7029 const UNEXPECTED_IAID: v6::IAID = v6::IAID::new(2);
7030
7031 struct TestCase {
7032 assigned_addresses: fn(Instant) -> HashMap<v6::IAID, AddressEntry<Instant>>,
7033 assigned_prefixes: fn(Instant) -> HashMap<v6::IAID, PrefixEntry<Instant>>,
7034 check_res: fn(Result<ProcessedReplyWithLeases<Instant>, ReplyWithLeasesError>),
7035 }
7036
7037 fn expected_iaids<V: IaValueTestExt>(
7038 time: Instant,
7039 ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
7040 HashMap::from([(
7041 EXPECTED_IAID,
7042 IaEntry::new_assigned(V::CONFIGURED[0], PREFERRED_LIFETIME, VALID_LIFETIME, time),
7043 )])
7044 }
7045
7046 fn unexpected_iaids<V: IaValueTestExt>(
7047 time: Instant,
7048 ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
7049 [EXPECTED_IAID, UNEXPECTED_IAID]
7050 .into_iter()
7051 .enumerate()
7052 .map(|(i, iaid)| {
7053 (
7054 iaid,
7055 IaEntry::new_assigned(
7056 V::CONFIGURED[i],
7057 PREFERRED_LIFETIME,
7058 VALID_LIFETIME,
7059 time,
7060 ),
7061 )
7062 })
7063 .collect()
7064 }
7065
7066 #[test_case(
7067 TestCase {
7068 assigned_addresses: expected_iaids::<Ipv6Addr>,
7069 assigned_prefixes: unexpected_iaids::<Subnet<Ipv6Addr>>,
7070 check_res: |res| {
7071 assert_matches!(
7072 res,
7073 Err(ReplyWithLeasesError::OptionsError(
7074 OptionsError::UnexpectedIaNa(iaid, _),
7075 )) => {
7076 assert_eq!(iaid, UNEXPECTED_IAID);
7077 }
7078 );
7079 },
7080 }
7081 ; "unknown IA_NA IAID")]
7082 #[test_case(
7083 TestCase {
7084 assigned_addresses: unexpected_iaids::<Ipv6Addr>,
7085 assigned_prefixes: expected_iaids::<Subnet<Ipv6Addr>>,
7086 check_res: |res| {
7087 assert_matches!(
7088 res,
7089 Err(ReplyWithLeasesError::OptionsError(
7090 OptionsError::UnexpectedIaPd(iaid, _),
7091 )) => {
7092 assert_eq!(iaid, UNEXPECTED_IAID);
7093 }
7094 );
7095 },
7096 }
7097 ; "unknown IA_PD IAID")]
7098 fn test(TestCase { assigned_addresses, assigned_prefixes, check_res }: TestCase) {
7099 let options =
7100 [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0])]
7101 .into_iter()
7102 .chain([EXPECTED_IAID, UNEXPECTED_IAID].into_iter().map(|iaid| {
7103 v6::DhcpOption::Iana(v6::IanaSerializer::new(iaid, T1.get(), T2.get(), &[]))
7104 }))
7105 .chain([EXPECTED_IAID, UNEXPECTED_IAID].into_iter().map(|iaid| {
7106 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(iaid, T1.get(), T2.get(), &[]))
7107 }))
7108 .collect::<Vec<_>>();
7109 let builder =
7110 v6::MessageBuilder::new(v6::MessageType::Reply, [0, 1, 2], options.as_slice());
7111 let mut buf = vec![0; builder.bytes_len()];
7112 builder.serialize(&mut buf);
7113 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7115
7116 let mut solicit_max_rt = MAX_SOLICIT_TIMEOUT;
7117 let time = Instant::now();
7118 check_res(process_reply_with_leases(
7119 &CLIENT_ID,
7120 &SERVER_ID[0],
7121 &assigned_addresses(time),
7122 &assigned_prefixes(time),
7123 &mut solicit_max_rt,
7124 &msg,
7125 RequestLeasesMessageType::Request,
7126 time,
7127 ))
7128 }
7129 }
7130
7131 #[test]
7132 fn ignore_advertise_with_unknown_ia() {
7133 let time = Instant::now();
7134 let mut client = testutil::start_and_assert_server_discovery(
7135 &(CLIENT_ID.into()),
7136 testutil::to_configured_addresses(
7137 1,
7138 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
7139 ),
7140 Default::default(),
7141 Vec::new(),
7142 StepRng::new(u64::MAX / 2, 0),
7143 time,
7144 );
7145
7146 let iana_options_0 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7147 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7148 60,
7149 60,
7150 &[],
7151 ))];
7152 let iana_options_99 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7153 CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
7154 60,
7155 60,
7156 &[],
7157 ))];
7158 let options = [
7159 v6::DhcpOption::ClientId(&CLIENT_ID),
7160 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7161 v6::DhcpOption::Preference(42),
7162 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7163 v6::IAID::new(0),
7164 T1.get(),
7165 T2.get(),
7166 &iana_options_0,
7167 )),
7168 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7171 v6::IAID::new(99),
7172 T1.get(),
7173 T2.get(),
7174 &iana_options_99,
7175 )),
7176 ];
7177
7178 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
7179 &client;
7180 let builder =
7181 v6::MessageBuilder::new(v6::MessageType::Advertise, *transaction_id, &options);
7182 let mut buf = vec![0; builder.bytes_len()];
7183 builder.serialize(&mut buf);
7184 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7186
7187 assert_eq!(client.handle_message_receive(msg, time), []);
7190 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
7191 &client;
7192 assert_matches!(
7193 state,
7194 Some(ClientState::ServerDiscovery(ServerDiscovery {
7195 client_id: _,
7196 configured_non_temporary_addresses: _,
7197 configured_delegated_prefixes: _,
7198 first_solicit_time: _,
7199 retrans_timeout: _,
7200 solicit_max_rt: _,
7201 collected_advertise,
7202 collected_sol_max_rt: _,
7203 })) => {
7204 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
7205 }
7206 );
7207 }
7208
7209 #[test]
7210 fn receive_advertise_with_max_preference() {
7211 let time = Instant::now();
7212 let mut client = testutil::start_and_assert_server_discovery(
7213 &(CLIENT_ID.into()),
7214 testutil::to_configured_addresses(
7215 2,
7216 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
7217 ),
7218 Default::default(),
7219 Vec::new(),
7220 StepRng::new(u64::MAX / 2, 0),
7221 time,
7222 );
7223
7224 let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7225 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7226 60,
7227 60,
7228 &[],
7229 ))];
7230
7231 for (preference, iana) in [
7236 (
7237 42,
7238 Some(v6::DhcpOption::Iana(v6::IanaSerializer::new(
7239 v6::IAID::new(0),
7240 T1.get(),
7241 T2.get(),
7242 &iana_options,
7243 ))),
7244 ),
7245 (255, None),
7246 ]
7247 .into_iter()
7248 {
7249 let options = [
7250 v6::DhcpOption::ClientId(&CLIENT_ID),
7251 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7252 v6::DhcpOption::Preference(preference),
7253 ]
7254 .into_iter()
7255 .chain(iana)
7256 .collect::<Vec<_>>();
7257 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
7258 &client;
7259 let builder =
7260 v6::MessageBuilder::new(v6::MessageType::Advertise, *transaction_id, &options);
7261 let mut buf = vec![0; builder.bytes_len()];
7262 builder.serialize(&mut buf);
7263 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7265 assert_eq!(client.handle_message_receive(msg, time), []);
7266 }
7267 let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7268 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7269 60,
7270 60,
7271 &[],
7272 ))];
7273 let options = [
7274 v6::DhcpOption::ClientId(&CLIENT_ID),
7275 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7276 v6::DhcpOption::Preference(255),
7277 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7278 v6::IAID::new(0),
7279 T1.get(),
7280 T2.get(),
7281 &iana_options,
7282 )),
7283 ];
7284 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
7285 &client;
7286 let builder =
7287 v6::MessageBuilder::new(v6::MessageType::Advertise, *transaction_id, &options);
7288 let mut buf = vec![0; builder.bytes_len()];
7289 builder.serialize(&mut buf);
7290 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7292
7293 let actions = client.handle_message_receive(msg, time);
7296 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } = client;
7297 let Requesting {
7298 client_id: _,
7299 non_temporary_addresses: _,
7300 delegated_prefixes: _,
7301 server_id: _,
7302 collected_advertise: _,
7303 first_request_time: _,
7304 retrans_timeout: _,
7305 transmission_count: _,
7306 solicit_max_rt: _,
7307 } = assert_matches!(
7308 state,
7309 Some(ClientState::Requesting(requesting)) => requesting
7310 );
7311 let buf = assert_matches!(
7312 &actions[..],
7313 [
7314 Action::CancelTimer(ClientTimerType::Retransmission),
7315 Action::SendMessage(buf),
7316 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
7317 ] => {
7318 assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
7319 buf
7320 }
7321 );
7322 assert_eq!(testutil::msg_type(buf), v6::MessageType::Request);
7323 }
7324
7325 #[test_case(T2.get() + 1, T2.get(), true)]
7327 #[test_case(INFINITY, T2.get(), true)]
7328 #[test_case(T1.get(), 0, false)]
7330 #[test_case(0, T2.get(), false)]
7332 #[test_case(T1.get(), T2.get(), false)]
7334 #[test_case(T1.get(), INFINITY, false)]
7335 #[test_case(INFINITY, INFINITY, false)]
7336 fn receive_advertise_with_invalid_iana(t1: u32, t2: u32, ignore_iana: bool) {
7337 let time = Instant::now();
7338 let mut client = testutil::start_and_assert_server_discovery(
7339 &(CLIENT_ID.into()),
7340 testutil::to_configured_addresses(
7341 1,
7342 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
7343 ),
7344 Default::default(),
7345 Vec::new(),
7346 StepRng::new(u64::MAX / 2, 0),
7347 time,
7348 );
7349 let transaction_id = client.transaction_id;
7350
7351 let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7352 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7353 PREFERRED_LIFETIME.get(),
7354 VALID_LIFETIME.get(),
7355 &[],
7356 ))];
7357 let options = [
7358 v6::DhcpOption::ClientId(&CLIENT_ID),
7359 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7360 v6::DhcpOption::Iana(v6::IanaSerializer::new(v6::IAID::new(0), t1, t2, &iana_options)),
7361 ];
7362 let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, transaction_id, &options);
7363 let mut buf = vec![0; builder.bytes_len()];
7364 builder.serialize(&mut buf);
7365 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7367
7368 assert_matches!(client.handle_message_receive(msg, time)[..], []);
7369 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
7370 &client;
7371 let collected_advertise = assert_matches!(
7372 state,
7373 Some(ClientState::ServerDiscovery(ServerDiscovery {
7374 client_id: _,
7375 configured_non_temporary_addresses: _,
7376 configured_delegated_prefixes: _,
7377 first_solicit_time: _,
7378 retrans_timeout: _,
7379 solicit_max_rt: _,
7380 collected_advertise,
7381 collected_sol_max_rt: _,
7382 })) => collected_advertise
7383 );
7384 match ignore_iana {
7385 true => assert!(collected_advertise.is_empty(), "{:?}", collected_advertise),
7386 false => {
7387 assert_matches!(
7388 collected_advertise.peek(),
7389 Some(AdvertiseMessage {
7390 server_id: _,
7391 non_temporary_addresses,
7392 delegated_prefixes: _,
7393 dns_servers: _,
7394 preference: _,
7395 receive_time: _,
7396 preferred_non_temporary_addresses_count: _,
7397 preferred_delegated_prefixes_count: _,
7398 }) => {
7399 assert_eq!(
7400 non_temporary_addresses,
7401 &HashMap::from([(
7402 v6::IAID::new(0),
7403 HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])
7404 )])
7405 );
7406 }
7407 )
7408 }
7409 }
7410 }
7411
7412 #[test]
7413 fn select_first_server_while_retransmitting() {
7414 let time = Instant::now();
7415 let mut client = testutil::start_and_assert_server_discovery(
7416 &(CLIENT_ID.into()),
7417 testutil::to_configured_addresses(
7418 1,
7419 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
7420 ),
7421 Default::default(),
7422 Vec::new(),
7423 StepRng::new(u64::MAX / 2, 0),
7424 time,
7425 );
7426
7427 let actions = client.handle_timeout(ClientTimerType::Retransmission, time);
7430 assert_matches!(
7431 &actions[..],
7432 [
7433 Action::SendMessage(buf),
7434 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
7435 ] => {
7436 assert_eq!(testutil::msg_type(buf), v6::MessageType::Solicit);
7437 assert_eq!(*instant, time.add(2 * INITIAL_SOLICIT_TIMEOUT));
7438 buf
7439 }
7440 );
7441 let ClientStateMachine { transaction_id, options_to_request: _, state, rng: _ } = &client;
7442 {
7443 let ServerDiscovery {
7444 client_id: _,
7445 configured_non_temporary_addresses: _,
7446 configured_delegated_prefixes: _,
7447 first_solicit_time: _,
7448 retrans_timeout: _,
7449 solicit_max_rt: _,
7450 collected_advertise,
7451 collected_sol_max_rt: _,
7452 } = assert_matches!(
7453 state,
7454 Some(ClientState::ServerDiscovery(server_discovery)) => server_discovery
7455 );
7456 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
7457 }
7458
7459 let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7460 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7461 60,
7462 60,
7463 &[],
7464 ))];
7465 let options = [
7466 v6::DhcpOption::ClientId(&CLIENT_ID),
7467 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7468 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7469 v6::IAID::new(0),
7470 T1.get(),
7471 T2.get(),
7472 &iana_options,
7473 )),
7474 ];
7475 let builder =
7476 v6::MessageBuilder::new(v6::MessageType::Advertise, *transaction_id, &options);
7477 let mut buf = vec![0; builder.bytes_len()];
7478 builder.serialize(&mut buf);
7479 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7481
7482 let actions = client.handle_message_receive(msg, time);
7485 assert_matches!(
7486 &actions[..],
7487 [
7488 Action::CancelTimer(ClientTimerType::Retransmission),
7489 Action::SendMessage(buf),
7490 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
7491 ] => {
7492 assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
7493 assert_eq!(testutil::msg_type(buf), v6::MessageType::Request);
7494 }
7495 );
7496 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } = client;
7497 let Requesting {
7498 client_id: _,
7499 non_temporary_addresses: _,
7500 delegated_prefixes: _,
7501 server_id: _,
7502 collected_advertise,
7503 first_request_time: _,
7504 retrans_timeout: _,
7505 transmission_count: _,
7506 solicit_max_rt: _,
7507 } = assert_matches!(
7508 state,
7509 Some(ClientState::Requesting(requesting )) => requesting
7510 );
7511 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
7512 }
7513
7514 #[test]
7515 fn send_request() {
7516 let (mut _client, _transaction_id) = testutil::request_and_assert(
7517 &(CLIENT_ID.into()),
7518 SERVER_ID[0],
7519 CONFIGURED_NON_TEMPORARY_ADDRESSES.into_iter().map(TestIaNa::new_default).collect(),
7520 CONFIGURED_DELEGATED_PREFIXES.into_iter().map(TestIaPd::new_default).collect(),
7521 &[],
7522 StepRng::new(u64::MAX / 2, 0),
7523 Instant::now(),
7524 );
7525 }
7526
7527 #[test]
7529 fn requesting_receive_reply_with_failure_status_code() {
7530 let options_to_request = vec![];
7531 let configured_non_temporary_addresses = testutil::to_configured_addresses(1, vec![]);
7532 let advertised_non_temporary_addresses = [CONFIGURED_NON_TEMPORARY_ADDRESSES[0]];
7533 let configured_delegated_prefixes = HashMap::new();
7534 let mut want_collected_advertise = [
7535 AdvertiseMessage::new_default(
7536 SERVER_ID[1],
7537 &CONFIGURED_NON_TEMPORARY_ADDRESSES[1..=1],
7538 &[],
7539 &[],
7540 &configured_non_temporary_addresses,
7541 &configured_delegated_prefixes,
7542 ),
7543 AdvertiseMessage::new_default(
7544 SERVER_ID[2],
7545 &CONFIGURED_NON_TEMPORARY_ADDRESSES[2..=2],
7546 &[],
7547 &[],
7548 &configured_non_temporary_addresses,
7549 &configured_delegated_prefixes,
7550 ),
7551 ]
7552 .into_iter()
7553 .collect::<BinaryHeap<_>>();
7554 let mut rng = StepRng::new(u64::MAX / 2, 0);
7555
7556 let time = Instant::now();
7557 let Transition { state, actions: _, transaction_id } = Requesting::start(
7558 CLIENT_ID.into(),
7559 SERVER_ID[0].to_vec(),
7560 advertise_to_ia_entries(
7561 testutil::to_default_ias_map(&advertised_non_temporary_addresses),
7562 configured_non_temporary_addresses.clone(),
7563 ),
7564 Default::default(), &options_to_request[..],
7566 want_collected_advertise.clone(),
7567 MAX_SOLICIT_TIMEOUT,
7568 &mut rng,
7569 time,
7570 );
7571
7572 let expected_non_temporary_addresses = (0..)
7573 .map(v6::IAID::new)
7574 .zip(
7575 advertised_non_temporary_addresses
7576 .iter()
7577 .map(|addr| AddressEntry::ToRequest(HashSet::from([*addr]))),
7578 )
7579 .collect::<HashMap<v6::IAID, AddressEntry<_>>>();
7580 {
7581 let Requesting {
7582 non_temporary_addresses: got_non_temporary_addresses,
7583 delegated_prefixes: _,
7584 server_id,
7585 collected_advertise,
7586 client_id: _,
7587 first_request_time: _,
7588 retrans_timeout: _,
7589 transmission_count: _,
7590 solicit_max_rt: _,
7591 } = assert_matches!(&state, ClientState::Requesting(requesting) => requesting);
7592 assert_eq!(server_id[..], SERVER_ID[0]);
7593 assert_eq!(*got_non_temporary_addresses, expected_non_temporary_addresses);
7594 assert_eq!(
7595 collected_advertise.clone().into_sorted_vec(),
7596 want_collected_advertise.clone().into_sorted_vec()
7597 );
7598 }
7599
7600 let options = [
7603 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7604 v6::DhcpOption::ClientId(&CLIENT_ID),
7605 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7606 v6::IAID::new(0),
7607 T1.get(),
7608 T2.get(),
7609 &[],
7610 )),
7611 v6::DhcpOption::StatusCode(v6::ErrorStatusCode::UnspecFail.into(), ""),
7612 ];
7613 let request_transaction_id = transaction_id.unwrap();
7614 let builder =
7615 v6::MessageBuilder::new(v6::MessageType::Reply, request_transaction_id, &options);
7616 let mut buf = vec![0; builder.bytes_len()];
7617 builder.serialize(&mut buf);
7618 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7620 let Transition { state, actions, transaction_id: got_transaction_id } =
7621 state.reply_message_received(&options_to_request, &mut rng, msg, time);
7622 {
7623 let Requesting {
7624 client_id: _,
7625 non_temporary_addresses: got_non_temporary_addresses,
7626 delegated_prefixes: _,
7627 server_id,
7628 collected_advertise,
7629 first_request_time: _,
7630 retrans_timeout: _,
7631 transmission_count: _,
7632 solicit_max_rt: _,
7633 } = assert_matches!(&state, ClientState::Requesting(requesting) => requesting);
7634 assert_eq!(server_id[..], SERVER_ID[0]);
7635 assert_eq!(
7636 collected_advertise.clone().into_sorted_vec(),
7637 want_collected_advertise.clone().into_sorted_vec()
7638 );
7639 assert_eq!(*got_non_temporary_addresses, expected_non_temporary_addresses);
7640 }
7641 assert_eq!(got_transaction_id, None);
7642 assert_eq!(actions[..], []);
7643
7644 let options = [
7647 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7648 v6::DhcpOption::ClientId(&CLIENT_ID),
7649 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7650 v6::IAID::new(0),
7651 T1.get(),
7652 T2.get(),
7653 &[],
7654 )),
7655 v6::DhcpOption::StatusCode(v6::ErrorStatusCode::NotOnLink.into(), ""),
7656 ];
7657 let request_transaction_id = transaction_id.unwrap();
7658 let builder =
7659 v6::MessageBuilder::new(v6::MessageType::Reply, request_transaction_id, &options);
7660 let mut buf = vec![0; builder.bytes_len()];
7661 builder.serialize(&mut buf);
7662 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7664 let Transition { state, actions: _, transaction_id } =
7665 state.reply_message_received(&options_to_request, &mut rng, msg, time);
7666
7667 let expected_non_temporary_addresses: HashMap<v6::IAID, AddressEntry<_>> =
7668 HashMap::from([(v6::IAID::new(0), AddressEntry::ToRequest(Default::default()))]);
7669 {
7670 let Requesting {
7671 client_id: _,
7672 non_temporary_addresses: got_non_temporary_addresses,
7673 delegated_prefixes: _,
7674 server_id,
7675 collected_advertise,
7676 first_request_time: _,
7677 retrans_timeout: _,
7678 transmission_count: _,
7679 solicit_max_rt: _,
7680 } = assert_matches!(
7681 &state,
7682 ClientState::Requesting(requesting) => requesting
7683 );
7684 assert_eq!(server_id[..], SERVER_ID[0]);
7685 assert_eq!(
7686 collected_advertise.clone().into_sorted_vec(),
7687 want_collected_advertise.clone().into_sorted_vec()
7688 );
7689 assert_eq!(*got_non_temporary_addresses, expected_non_temporary_addresses);
7690 }
7691 assert!(transaction_id.is_some());
7692
7693 let iana_options =
7696 [v6::DhcpOption::StatusCode(v6::ErrorStatusCode::NoAddrsAvail.into(), "")];
7697 let options = [
7698 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7699 v6::DhcpOption::ClientId(&CLIENT_ID),
7700 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7701 v6::IAID::new(0),
7702 T1.get(),
7703 T2.get(),
7704 &iana_options,
7705 )),
7706 ];
7707 let builder =
7708 v6::MessageBuilder::new(v6::MessageType::Reply, transaction_id.unwrap(), &options);
7709 let mut buf = vec![0; builder.bytes_len()];
7710 builder.serialize(&mut buf);
7711 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7713 let Transition { state, actions, transaction_id } =
7714 state.reply_message_received(&options_to_request, &mut rng, msg, time);
7715 {
7716 let Requesting {
7717 server_id,
7718 collected_advertise,
7719 client_id: _,
7720 non_temporary_addresses: _,
7721 delegated_prefixes: _,
7722 first_request_time: _,
7723 retrans_timeout: _,
7724 transmission_count: _,
7725 solicit_max_rt: _,
7726 } = assert_matches!(
7727 state,
7728 ClientState::Requesting(requesting) => requesting
7729 );
7730 assert_eq!(server_id[..], SERVER_ID[1]);
7731 let _: Option<AdvertiseMessage<_>> = want_collected_advertise.pop();
7732 assert_eq!(
7733 collected_advertise.clone().into_sorted_vec(),
7734 want_collected_advertise.clone().into_sorted_vec(),
7735 );
7736 }
7737 assert_matches!(
7738 &actions[..],
7739 [
7740 Action::CancelTimer(ClientTimerType::Retransmission),
7741 Action::SendMessage(_buf),
7742 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
7743 ] => {
7744 assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
7745 }
7746 );
7747 assert!(transaction_id.is_some());
7748 }
7749
7750 #[test]
7751 fn requesting_receive_reply_with_ia_not_on_link() {
7752 let options_to_request = vec![];
7753 let configured_non_temporary_addresses = testutil::to_configured_addresses(
7754 2,
7755 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
7756 );
7757 let mut rng = StepRng::new(u64::MAX / 2, 0);
7758
7759 let time = Instant::now();
7760 let Transition { state, actions: _, transaction_id } = Requesting::start(
7761 CLIENT_ID.into(),
7762 SERVER_ID[0].to_vec(),
7763 advertise_to_ia_entries(
7764 testutil::to_default_ias_map(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]),
7765 configured_non_temporary_addresses.clone(),
7766 ),
7767 Default::default(), &options_to_request[..],
7769 BinaryHeap::new(),
7770 MAX_SOLICIT_TIMEOUT,
7771 &mut rng,
7772 time,
7773 );
7774
7775 let iana_options1 = [v6::DhcpOption::StatusCode(v6::ErrorStatusCode::NotOnLink.into(), "")];
7779 let iana_options2 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7780 CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
7781 PREFERRED_LIFETIME.get(),
7782 VALID_LIFETIME.get(),
7783 &[],
7784 ))];
7785 let iaid1 = v6::IAID::new(0);
7786 let iaid2 = v6::IAID::new(1);
7787 let options = [
7788 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7789 v6::DhcpOption::ClientId(&CLIENT_ID),
7790 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7791 iaid1,
7792 T1.get(),
7793 T2.get(),
7794 &iana_options1,
7795 )),
7796 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7797 iaid2,
7798 T1.get(),
7799 T2.get(),
7800 &iana_options2,
7801 )),
7802 ];
7803 let builder =
7804 v6::MessageBuilder::new(v6::MessageType::Reply, transaction_id.unwrap(), &options);
7805 let mut buf = vec![0; builder.bytes_len()];
7806 builder.serialize(&mut buf);
7807 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7809 let Transition { state, actions, transaction_id } =
7810 state.reply_message_received(&options_to_request, &mut rng, msg, time);
7811 let expected_non_temporary_addresses = HashMap::from([
7812 (iaid1, AddressEntry::ToRequest(Default::default())),
7813 (
7814 iaid2,
7815 AddressEntry::Assigned(HashMap::from([(
7816 CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
7817 LifetimesInfo {
7818 lifetimes: Lifetimes {
7819 preferred_lifetime: v6::TimeValue::NonZero(
7820 v6::NonZeroTimeValue::Finite(PREFERRED_LIFETIME),
7821 ),
7822 valid_lifetime: v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
7823 },
7824 updated_at: time,
7825 },
7826 )])),
7827 ),
7828 ]);
7829 {
7830 let Assigned {
7831 client_id: _,
7832 non_temporary_addresses,
7833 delegated_prefixes,
7834 server_id,
7835 dns_servers: _,
7836 solicit_max_rt: _,
7837 _marker,
7838 } = assert_matches!(
7839 state,
7840 ClientState::Assigned(assigned) => assigned
7841 );
7842 assert_eq!(server_id[..], SERVER_ID[0]);
7843 assert_eq!(non_temporary_addresses, expected_non_temporary_addresses);
7844 assert_eq!(delegated_prefixes, HashMap::new());
7845 }
7846 assert_matches!(
7847 &actions[..],
7848 [
7849 Action::CancelTimer(ClientTimerType::Retransmission),
7850 Action::ScheduleTimer(ClientTimerType::Renew, t1),
7851 Action::ScheduleTimer(ClientTimerType::Rebind, t2),
7852 Action::IaNaUpdates(iana_updates),
7853 Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
7854 ] => {
7855 assert_eq!(*t1, time.add(Duration::from_secs(T1.get().into())));
7856 assert_eq!(*t2, time.add(Duration::from_secs(T2.get().into())));
7857 assert_eq!(
7858 *restart_time,
7859 time.add(Duration::from_secs(VALID_LIFETIME.get().into())),
7860 );
7861 assert_eq!(
7862 iana_updates,
7863 &HashMap::from([(
7864 iaid2,
7865 HashMap::from([(
7866 CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
7867 IaValueUpdateKind::Added(Lifetimes::new_default()),
7868 )]),
7869 )]),
7870 );
7871 }
7872 );
7873 assert!(transaction_id.is_none());
7874 }
7875
7876 #[test_case(0, VALID_LIFETIME.get(), true)]
7877 #[test_case(PREFERRED_LIFETIME.get(), 0, false)]
7878 #[test_case(VALID_LIFETIME.get() + 1, VALID_LIFETIME.get(), false)]
7879 #[test_case(0, 0, false)]
7880 #[test_case(PREFERRED_LIFETIME.get(), VALID_LIFETIME.get(), true)]
7881 fn requesting_receive_reply_with_invalid_ia_lifetimes(
7882 preferred_lifetime: u32,
7883 valid_lifetime: u32,
7884 valid_ia: bool,
7885 ) {
7886 let options_to_request = vec![];
7887 let configured_non_temporary_addresses = testutil::to_configured_addresses(1, vec![]);
7888 let mut rng = StepRng::new(u64::MAX / 2, 0);
7889
7890 let time = Instant::now();
7891 let Transition { state, actions: _, transaction_id } = Requesting::start(
7892 CLIENT_ID.into(),
7893 SERVER_ID[0].to_vec(),
7894 advertise_to_ia_entries(
7895 testutil::to_default_ias_map(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..1]),
7896 configured_non_temporary_addresses.clone(),
7897 ),
7898 Default::default(), &options_to_request[..],
7900 BinaryHeap::new(),
7901 MAX_SOLICIT_TIMEOUT,
7902 &mut rng,
7903 time,
7904 );
7905
7906 let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7908 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7909 preferred_lifetime,
7910 valid_lifetime,
7911 &[],
7912 ))];
7913 let options = [
7914 v6::DhcpOption::ServerId(&SERVER_ID[0]),
7915 v6::DhcpOption::ClientId(&CLIENT_ID),
7916 v6::DhcpOption::Iana(v6::IanaSerializer::new(
7917 v6::IAID::new(0),
7918 T1.get(),
7919 T2.get(),
7920 &iana_options,
7921 )),
7922 ];
7923 let builder =
7924 v6::MessageBuilder::new(v6::MessageType::Reply, transaction_id.unwrap(), &options);
7925 let mut buf = vec![0; builder.bytes_len()];
7926 builder.serialize(&mut buf);
7927 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7929 let Transition { state, actions: _, transaction_id: _ } =
7930 state.reply_message_received(&options_to_request, &mut rng, msg, time);
7931 match valid_ia {
7932 true =>
7933 {
7936 let Assigned {
7937 client_id: _,
7938 non_temporary_addresses: _,
7939 delegated_prefixes: _,
7940 server_id: _,
7941 dns_servers: _,
7942 solicit_max_rt: _,
7943 _marker,
7944 } = assert_matches!(
7945 state,
7946 ClientState::Assigned(assigned) => assigned
7947 );
7948 }
7949 false =>
7950 {
7953 let ServerDiscovery {
7954 client_id: _,
7955 configured_non_temporary_addresses: _,
7956 configured_delegated_prefixes: _,
7957 first_solicit_time: _,
7958 retrans_timeout: _,
7959 solicit_max_rt: _,
7960 collected_advertise,
7961 collected_sol_max_rt: _,
7962 } = assert_matches!(
7963 state,
7964 ClientState::ServerDiscovery(server_discovery) => server_discovery
7965 );
7966 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
7967 }
7968 }
7969 }
7970
7971 #[test]
7973 fn compute_t1_t2_on_reply_to_request() {
7974 let mut rng = StepRng::new(u64::MAX / 2, 0);
7975
7976 for (
7977 (ia1_preferred_lifetime, ia1_valid_lifetime, ia1_t1, ia1_t2),
7978 (ia2_preferred_lifetime, ia2_valid_lifetime, ia2_t1, ia2_t2),
7979 expected_t1,
7980 expected_t2,
7981 ) in vec![
7982 (
7986 (100, 160, 0, 0),
7987 (120, 180, 0, 0),
7988 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(50).expect("should succeed")),
7989 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(80).expect("should succeed")),
7990 ),
7991 (
7992 (INFINITY, INFINITY, 0, 0),
7993 (120, 180, 0, 0),
7994 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(60).expect("should succeed")),
7995 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(96).expect("should succeed")),
7996 ),
7997 (
8000 (INFINITY, INFINITY, 0, 0),
8001 (INFINITY, INFINITY, 0, 0),
8002 v6::NonZeroTimeValue::Infinity,
8003 v6::NonZeroTimeValue::Infinity,
8004 ),
8005 (
8007 (INFINITY, INFINITY, 50, INFINITY),
8008 (INFINITY, INFINITY, 50, INFINITY),
8009 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(50).expect("should succeed")),
8010 v6::NonZeroTimeValue::Infinity,
8011 ),
8012 (
8017 (100, 160, 40, 70),
8018 (120, 180, 50, 80),
8019 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(40).expect("should succeed")),
8020 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(70).expect("should succeed")),
8021 ),
8022 ] {
8023 let time = Instant::now();
8024 let Transition { state, actions: _, transaction_id } = Requesting::start(
8025 CLIENT_ID.into(),
8026 SERVER_ID[0].to_vec(),
8027 advertise_to_ia_entries(
8028 testutil::to_default_ias_map(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]),
8029 testutil::to_configured_addresses(
8030 2,
8031 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
8032 ),
8033 ),
8034 Default::default(), &[],
8036 BinaryHeap::new(),
8037 MAX_SOLICIT_TIMEOUT,
8038 &mut rng,
8039 time,
8040 );
8041
8042 let iana_options1 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
8043 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
8044 ia1_preferred_lifetime,
8045 ia1_valid_lifetime,
8046 &[],
8047 ))];
8048 let iana_options2 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
8049 CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
8050 ia2_preferred_lifetime,
8051 ia2_valid_lifetime,
8052 &[],
8053 ))];
8054 let iaid1 = v6::IAID::new(0);
8055 let iaid2 = v6::IAID::new(1);
8056 let options = [
8057 v6::DhcpOption::ServerId(&SERVER_ID[0]),
8058 v6::DhcpOption::ClientId(&CLIENT_ID),
8059 v6::DhcpOption::Iana(v6::IanaSerializer::new(
8060 iaid1,
8061 ia1_t1,
8062 ia1_t2,
8063 &iana_options1,
8064 )),
8065 v6::DhcpOption::Iana(v6::IanaSerializer::new(
8066 iaid2,
8067 ia2_t1,
8068 ia2_t2,
8069 &iana_options2,
8070 )),
8071 ];
8072 let builder =
8073 v6::MessageBuilder::new(v6::MessageType::Reply, transaction_id.unwrap(), &options);
8074 let mut buf = vec![0; builder.bytes_len()];
8075 builder.serialize(&mut buf);
8076 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8078 let Transition { state, actions, transaction_id: _ } =
8079 state.reply_message_received(&[], &mut rng, msg, time);
8080 let Assigned {
8081 client_id: _,
8082 non_temporary_addresses: _,
8083 delegated_prefixes: _,
8084 server_id: _,
8085 dns_servers: _,
8086 solicit_max_rt: _,
8087 _marker,
8088 } = assert_matches!(
8089 state,
8090 ClientState::Assigned(assigned) => assigned
8091 );
8092
8093 let update_actions = [Action::IaNaUpdates(HashMap::from([
8094 (
8095 iaid1,
8096 HashMap::from([(
8097 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
8098 IaValueUpdateKind::Added(Lifetimes::new(
8099 ia1_preferred_lifetime,
8100 ia1_valid_lifetime,
8101 )),
8102 )]),
8103 ),
8104 (
8105 iaid2,
8106 HashMap::from([(
8107 CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
8108 IaValueUpdateKind::Added(Lifetimes::new(
8109 ia2_preferred_lifetime,
8110 ia2_valid_lifetime,
8111 )),
8112 )]),
8113 ),
8114 ]))];
8115
8116 let timer_action = |timer, tv| match tv {
8117 v6::NonZeroTimeValue::Finite(tv) => {
8118 Action::ScheduleTimer(timer, time.add(Duration::from_secs(tv.get().into())))
8119 }
8120 v6::NonZeroTimeValue::Infinity => Action::CancelTimer(timer),
8121 };
8122
8123 let non_zero_time_value = |v| {
8124 assert_matches!(
8125 v6::TimeValue::new(v),
8126 v6::TimeValue::NonZero(v) => v
8127 )
8128 };
8129
8130 assert!(expected_t1 <= expected_t2);
8131 assert_eq!(
8132 actions,
8133 [
8134 Action::CancelTimer(ClientTimerType::Retransmission),
8135 timer_action(ClientTimerType::Renew, expected_t1),
8136 timer_action(ClientTimerType::Rebind, expected_t2),
8137 ]
8138 .into_iter()
8139 .chain(update_actions)
8140 .chain([timer_action(
8141 ClientTimerType::RestartServerDiscovery,
8142 std::cmp::max(
8143 non_zero_time_value(ia1_valid_lifetime),
8144 non_zero_time_value(ia2_valid_lifetime),
8145 ),
8146 )])
8147 .collect::<Vec<_>>(),
8148 );
8149 }
8150 }
8151
8152 #[test]
8153 fn use_advertise_from_best_server() {
8154 let time = Instant::now();
8155 let mut client = testutil::start_and_assert_server_discovery(
8156 &(CLIENT_ID.into()),
8157 testutil::to_configured_addresses(
8158 CONFIGURED_NON_TEMPORARY_ADDRESSES.len(),
8159 CONFIGURED_NON_TEMPORARY_ADDRESSES.map(|a| HashSet::from([a])),
8160 ),
8161 testutil::to_configured_prefixes(
8162 CONFIGURED_DELEGATED_PREFIXES.len(),
8163 CONFIGURED_DELEGATED_PREFIXES.map(|a| HashSet::from([a])),
8164 ),
8165 Vec::new(),
8166 StepRng::new(u64::MAX / 2, 0),
8167 time,
8168 );
8169 let transaction_id = client.transaction_id;
8170
8171 let buf = TestMessageBuilder {
8173 transaction_id,
8174 message_type: v6::MessageType::Advertise,
8175 client_id: &CLIENT_ID,
8176 server_id: &SERVER_ID[0],
8177 preference: None,
8178 dns_servers: None,
8179 ia_nas: (0..)
8180 .map(v6::IAID::new)
8181 .zip(CONFIGURED_NON_TEMPORARY_ADDRESSES)
8182 .map(|(iaid, value)| (iaid, TestIa::new_default(value))),
8183 ia_pds: std::iter::empty(),
8184 }
8185 .build();
8186 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8188 assert_matches!(client.handle_message_receive(msg, time)[..], []);
8189
8190 let buf = TestMessageBuilder {
8192 transaction_id,
8193 message_type: v6::MessageType::Advertise,
8194 client_id: &CLIENT_ID,
8195 server_id: &SERVER_ID[1],
8196 preference: None,
8197 dns_servers: None,
8198 ia_nas: std::iter::empty(),
8199 ia_pds: (0..)
8200 .map(v6::IAID::new)
8201 .zip(CONFIGURED_DELEGATED_PREFIXES)
8202 .map(|(iaid, value)| (iaid, TestIa::new_default(value))),
8203 }
8204 .build();
8205 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8207 assert_matches!(client.handle_message_receive(msg, time)[..], []);
8208
8209 let buf = TestMessageBuilder {
8216 transaction_id,
8217 message_type: v6::MessageType::Advertise,
8218 client_id: &CLIENT_ID,
8219 server_id: &SERVER_ID[2],
8220 preference: None,
8221 dns_servers: None,
8222 ia_nas: std::iter::once((
8223 v6::IAID::new(0),
8224 TestIa {
8225 values: HashMap::from([(
8226 REPLY_NON_TEMPORARY_ADDRESSES[0],
8227 Lifetimes {
8228 preferred_lifetime: v6::TimeValue::NonZero(
8229 v6::NonZeroTimeValue::Finite(PREFERRED_LIFETIME),
8230 ),
8231 valid_lifetime: v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
8232 },
8233 )]),
8234 t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
8235 t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8236 },
8237 )),
8238 ia_pds: std::iter::once((
8239 v6::IAID::new(0),
8240 TestIa {
8241 values: HashMap::from([(
8242 REPLY_DELEGATED_PREFIXES[0],
8243 Lifetimes {
8244 preferred_lifetime: v6::TimeValue::NonZero(
8245 v6::NonZeroTimeValue::Finite(PREFERRED_LIFETIME),
8246 ),
8247 valid_lifetime: v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
8248 },
8249 )]),
8250 t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
8251 t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8252 },
8253 )),
8254 }
8255 .build();
8256 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8258 assert_matches!(client.handle_message_receive(msg, time)[..], []);
8259
8260 let actions = client.handle_timeout(ClientTimerType::Retransmission, time);
8269 assert_matches!(
8270 &actions[..],
8271 [
8272 Action::CancelTimer(ClientTimerType::Retransmission),
8273 Action::SendMessage(buf),
8274 Action::ScheduleTimer(ClientTimerType::Retransmission, instant),
8275 ] => {
8276 assert_eq!(testutil::msg_type(buf), v6::MessageType::Request);
8277 assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
8278 }
8279 );
8280 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } = client;
8281 assert_matches!(
8282 state,
8283 Some(ClientState::Requesting(Requesting {
8284 client_id: _,
8285 non_temporary_addresses,
8286 delegated_prefixes,
8287 server_id,
8288 collected_advertise: _,
8289 first_request_time: _,
8290 retrans_timeout: _,
8291 transmission_count: _,
8292 solicit_max_rt: _,
8293 })) => {
8294 assert_eq!(&server_id, &SERVER_ID[2]);
8295 assert_eq!(
8296 non_temporary_addresses,
8297 [REPLY_NON_TEMPORARY_ADDRESSES[0]]
8298 .iter()
8299 .chain(CONFIGURED_NON_TEMPORARY_ADDRESSES[1..3].iter())
8300 .enumerate().map(|(iaid, addr)| {
8301 (v6::IAID::new(iaid.try_into().unwrap()), AddressEntry::ToRequest(HashSet::from([*addr])))
8302 }).collect::<HashMap<_, _>>()
8303 );
8304 assert_eq!(
8305 delegated_prefixes,
8306 [REPLY_DELEGATED_PREFIXES[0]]
8307 .iter()
8308 .chain(CONFIGURED_DELEGATED_PREFIXES[1..3].iter())
8309 .enumerate().map(|(iaid, addr)| {
8310 (v6::IAID::new(iaid.try_into().unwrap()), PrefixEntry::ToRequest(HashSet::from([*addr])))
8311 }).collect::<HashMap<_, _>>()
8312 );
8313 }
8314 );
8315 }
8316
8317 #[test]
8319 fn requesting_retransmit_max_retrans_count() {
8320 let time = Instant::now();
8321 let mut client = testutil::start_and_assert_server_discovery(
8322 &(CLIENT_ID.into()),
8323 testutil::to_configured_addresses(
8324 1,
8325 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
8326 ),
8327 Default::default(),
8328 Vec::new(),
8329 StepRng::new(u64::MAX / 2, 0),
8330 time,
8331 );
8332 let transaction_id = client.transaction_id;
8333
8334 for i in 0..2 {
8335 let buf = TestMessageBuilder {
8336 transaction_id,
8337 message_type: v6::MessageType::Advertise,
8338 client_id: &CLIENT_ID,
8339 server_id: &SERVER_ID[i],
8340 preference: None,
8341 dns_servers: None,
8342 ia_nas: std::iter::once((
8343 v6::IAID::new(0),
8344 TestIa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[i]),
8345 )),
8346 ia_pds: std::iter::empty(),
8347 }
8348 .build();
8349 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8351 assert_matches!(client.handle_message_receive(msg, time)[..], []);
8352 }
8353 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8354 &client;
8355 let ServerDiscovery {
8356 client_id: _,
8357 configured_non_temporary_addresses: _,
8358 configured_delegated_prefixes: _,
8359 first_solicit_time: _,
8360 retrans_timeout: _,
8361 solicit_max_rt: _,
8362 collected_advertise: want_collected_advertise,
8363 collected_sol_max_rt: _,
8364 } = assert_matches!(
8365 state,
8366 Some(ClientState::ServerDiscovery(server_discovery)) => server_discovery
8367 );
8368 let mut want_collected_advertise = want_collected_advertise.clone();
8369 let _: Option<AdvertiseMessage<_>> = want_collected_advertise.pop();
8370
8371 assert_matches!(
8374 &client.handle_timeout(ClientTimerType::Retransmission, time)[..],
8375 [
8376 Action::CancelTimer(ClientTimerType::Retransmission),
8377 Action::SendMessage(buf),
8378 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
8379 ] => {
8380 assert_eq!(testutil::msg_type(buf), v6::MessageType::Request);
8381 assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
8382 }
8383 );
8384 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8385 &client;
8386 {
8387 let Requesting {
8388 client_id: _,
8389 non_temporary_addresses: _,
8390 delegated_prefixes: _,
8391 server_id,
8392 collected_advertise,
8393 first_request_time: _,
8394 retrans_timeout: _,
8395 transmission_count,
8396 solicit_max_rt: _,
8397 } = assert_matches!(state, Some(ClientState::Requesting(requesting)) => requesting);
8398 assert_eq!(
8399 collected_advertise.clone().into_sorted_vec(),
8400 want_collected_advertise.clone().into_sorted_vec()
8401 );
8402 assert_eq!(server_id[..], SERVER_ID[0]);
8403 assert_eq!(*transmission_count, 1);
8404 }
8405
8406 for count in 2..=(REQUEST_MAX_RC + 1) {
8407 assert_matches!(
8408 &client.handle_timeout(ClientTimerType::Retransmission, time)[..],
8409 [
8410 Action::SendMessage(buf),
8411 Action::ScheduleTimer(ClientTimerType::Retransmission, _timeout)
8414 ] if testutil::msg_type(buf) == v6::MessageType::Request
8415 );
8416 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8417 &client;
8418 let Requesting {
8419 client_id: _,
8420 non_temporary_addresses: _,
8421 delegated_prefixes: _,
8422 server_id,
8423 collected_advertise,
8424 first_request_time: _,
8425 retrans_timeout: _,
8426 transmission_count,
8427 solicit_max_rt: _,
8428 } = assert_matches!(state, Some(ClientState::Requesting(requesting)) => requesting);
8429 assert_eq!(
8430 collected_advertise.clone().into_sorted_vec(),
8431 want_collected_advertise.clone().into_sorted_vec()
8432 );
8433 assert_eq!(server_id[..], SERVER_ID[0]);
8434 assert_eq!(*transmission_count, count);
8435 }
8436
8437 assert_matches!(
8440 &client.handle_timeout(ClientTimerType::Retransmission, time)[..],
8441 [
8442 Action::CancelTimer(ClientTimerType::Retransmission),
8443 Action::SendMessage(buf),
8444 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
8445 ] => {
8446 assert_eq!(testutil::msg_type(buf), v6::MessageType::Request);
8447 assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
8448 }
8449 );
8450 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8451 &client;
8452 let Requesting {
8453 client_id: _,
8454 non_temporary_addresses: _,
8455 delegated_prefixes: _,
8456 server_id,
8457 collected_advertise,
8458 first_request_time: _,
8459 retrans_timeout: _,
8460 transmission_count,
8461 solicit_max_rt: _,
8462 } = assert_matches!(state, Some(ClientState::Requesting(requesting)) => requesting);
8463 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
8464 assert_eq!(server_id[..], SERVER_ID[1]);
8465 assert_eq!(*transmission_count, 1);
8466
8467 for count in 2..=(REQUEST_MAX_RC + 1) {
8468 assert_matches!(
8469 &client.handle_timeout(ClientTimerType::Retransmission, time)[..],
8470 [
8471 Action::SendMessage(buf),
8472 Action::ScheduleTimer(ClientTimerType::Retransmission, _timeout)
8475 ] if testutil::msg_type(buf) == v6::MessageType::Request
8476 );
8477 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8478 &client;
8479 let Requesting {
8480 client_id: _,
8481 non_temporary_addresses: _,
8482 delegated_prefixes: _,
8483 server_id,
8484 collected_advertise,
8485 first_request_time: _,
8486 retrans_timeout: _,
8487 transmission_count,
8488 solicit_max_rt: _,
8489 } = assert_matches!(state, Some(ClientState::Requesting(requesting)) => requesting);
8490 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
8491 assert_eq!(server_id[..], SERVER_ID[1]);
8492 assert_eq!(*transmission_count, count);
8493 }
8494
8495 assert_matches!(
8499 &client.handle_timeout(ClientTimerType::Retransmission, time)[..],
8500 [
8501 Action::CancelTimer(ClientTimerType::Retransmission),
8502 Action::CancelTimer(ClientTimerType::Refresh),
8503 Action::CancelTimer(ClientTimerType::Renew),
8504 Action::CancelTimer(ClientTimerType::Rebind),
8505 Action::CancelTimer(ClientTimerType::RestartServerDiscovery),
8506 Action::SendMessage(buf),
8507 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
8508 ] => {
8509 assert_eq!(testutil::msg_type(buf), v6::MessageType::Solicit);
8510 assert_eq!(*instant, time.add(INITIAL_SOLICIT_TIMEOUT));
8511 }
8512 );
8513 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } = client;
8514 assert_matches!(state,
8515 Some(ClientState::ServerDiscovery(ServerDiscovery {
8516 client_id: _,
8517 configured_non_temporary_addresses: _,
8518 configured_delegated_prefixes: _,
8519 first_solicit_time: _,
8520 retrans_timeout: _,
8521 solicit_max_rt: _,
8522 collected_advertise,
8523 collected_sol_max_rt: _,
8524 })) if collected_advertise.is_empty()
8525 );
8526 }
8527
8528 #[test]
8530 fn assignment() {
8531 let now = Instant::now();
8532 let (client, actions) = testutil::assign_and_assert(
8533 &(CLIENT_ID.into()),
8534 SERVER_ID[0],
8535 CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]
8536 .iter()
8537 .copied()
8538 .map(TestIaNa::new_default)
8539 .collect(),
8540 CONFIGURED_DELEGATED_PREFIXES[0..2]
8541 .iter()
8542 .copied()
8543 .map(TestIaPd::new_default)
8544 .collect(),
8545 &[],
8546 StepRng::new(u64::MAX / 2, 0),
8547 now,
8548 );
8549
8550 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8551 &client;
8552 let Assigned {
8553 client_id: _,
8554 non_temporary_addresses: _,
8555 delegated_prefixes: _,
8556 server_id: _,
8557 dns_servers: _,
8558 solicit_max_rt: _,
8559 _marker,
8560 } = assert_matches!(
8561 state,
8562 Some(ClientState::Assigned(assigned)) => assigned
8563 );
8564 assert_matches!(
8565 &actions[..],
8566 [
8567 Action::CancelTimer(ClientTimerType::Retransmission),
8568 Action::ScheduleTimer(ClientTimerType::Renew, t1),
8569 Action::ScheduleTimer(ClientTimerType::Rebind, t2),
8570 Action::IaNaUpdates(iana_updates),
8571 Action::IaPdUpdates(iapd_updates),
8572 Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
8573 ] => {
8574 assert_eq!(*t1, now.add(Duration::from_secs(T1.get().into())));
8575 assert_eq!(*t2, now.add(Duration::from_secs(T2.get().into())));
8576 assert_eq!(
8577 *restart_time,
8578 now.add(Duration::from_secs(VALID_LIFETIME.get().into())),
8579 );
8580 assert_eq!(
8581 iana_updates,
8582 &(0..).map(v6::IAID::new)
8583 .zip(CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2].iter().cloned())
8584 .map(|(iaid, value)| (
8585 iaid,
8586 HashMap::from([(value, IaValueUpdateKind::Added(Lifetimes::new_default()))])
8587 ))
8588 .collect::<HashMap<_, _>>(),
8589 );
8590 assert_eq!(
8591 iapd_updates,
8592 &(0..).map(v6::IAID::new)
8593 .zip(CONFIGURED_DELEGATED_PREFIXES[0..2].iter().cloned())
8594 .map(|(iaid, value)| (
8595 iaid,
8596 HashMap::from([(value, IaValueUpdateKind::Added(Lifetimes::new_default()))])
8597 ))
8598 .collect::<HashMap<_, _>>(),
8599 );
8600 }
8601 );
8602 }
8603
8604 #[test]
8605 fn assigned_get_dns_servers() {
8606 let now = Instant::now();
8607 let (client, actions) = testutil::assign_and_assert(
8608 &(CLIENT_ID.into()),
8609 SERVER_ID[0],
8610 vec![TestIaNa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[0])],
8611 Default::default(), &DNS_SERVERS,
8613 StepRng::new(u64::MAX / 2, 0),
8614 now,
8615 );
8616 assert_matches!(
8617 &actions[..],
8618 [
8619 Action::CancelTimer(ClientTimerType::Retransmission),
8620 Action::ScheduleTimer(ClientTimerType::Renew, t1),
8621 Action::ScheduleTimer(ClientTimerType::Rebind, t2),
8622 Action::UpdateDnsServers(dns_servers),
8623 Action::IaNaUpdates(iana_updates),
8624 Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
8625 ] => {
8626 assert_eq!(dns_servers[..], DNS_SERVERS);
8627 assert_eq!(*t1, now.add(Duration::from_secs(T1.get().into())));
8628 assert_eq!(*t2, now.add(Duration::from_secs(T2.get().into())));
8629 assert_eq!(
8630 *restart_time,
8631 now.add(Duration::from_secs(VALID_LIFETIME.get().into())),
8632 );
8633 assert_eq!(
8634 iana_updates,
8635 &HashMap::from([
8636 (
8637 v6::IAID::new(0),
8638 HashMap::from([(
8639 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
8640 IaValueUpdateKind::Added(Lifetimes::new_default()),
8641 )]),
8642 ),
8643 ]),
8644 );
8645 }
8646 );
8647 assert_eq!(client.get_dns_servers()[..], DNS_SERVERS);
8648 }
8649
8650 #[test]
8651 fn update_sol_max_rt_on_reply_to_request() {
8652 let options_to_request = vec![];
8653 let configured_non_temporary_addresses = testutil::to_configured_addresses(1, vec![]);
8654 let mut rng = StepRng::new(u64::MAX / 2, 0);
8655 let time = Instant::now();
8656 let Transition { state, actions: _, transaction_id } = Requesting::start(
8657 CLIENT_ID.into(),
8658 SERVER_ID[0].to_vec(),
8659 advertise_to_ia_entries(
8660 testutil::to_default_ias_map(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..1]),
8661 configured_non_temporary_addresses.clone(),
8662 ),
8663 Default::default(), &options_to_request[..],
8665 BinaryHeap::new(),
8666 MAX_SOLICIT_TIMEOUT,
8667 &mut rng,
8668 time,
8669 );
8670 {
8671 let Requesting {
8672 collected_advertise,
8673 solicit_max_rt,
8674 client_id: _,
8675 non_temporary_addresses: _,
8676 delegated_prefixes: _,
8677 server_id: _,
8678 first_request_time: _,
8679 retrans_timeout: _,
8680 transmission_count: _,
8681 } = assert_matches!(&state, ClientState::Requesting(requesting) => requesting);
8682 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
8683 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
8684 }
8685 let received_sol_max_rt = 4800;
8686
8687 let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
8690 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
8691 60,
8692 120,
8693 &[],
8694 ))];
8695 let options = [
8696 v6::DhcpOption::ClientId(&CLIENT_ID),
8697 v6::DhcpOption::Iana(v6::IanaSerializer::new(
8698 v6::IAID::new(0),
8699 T1.get(),
8700 T2.get(),
8701 &iana_options,
8702 )),
8703 v6::DhcpOption::SolMaxRt(received_sol_max_rt),
8704 ];
8705 let request_transaction_id = transaction_id.unwrap();
8706 let builder =
8707 v6::MessageBuilder::new(v6::MessageType::Reply, request_transaction_id, &options);
8708 let mut buf = vec![0; builder.bytes_len()];
8709 builder.serialize(&mut buf);
8710 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8712 let Transition { state, actions: _, transaction_id: _ } =
8713 state.reply_message_received(&options_to_request, &mut rng, msg, time);
8714 {
8715 let Requesting {
8716 collected_advertise,
8717 solicit_max_rt,
8718 client_id: _,
8719 non_temporary_addresses: _,
8720 delegated_prefixes: _,
8721 server_id: _,
8722 first_request_time: _,
8723 retrans_timeout: _,
8724 transmission_count: _,
8725 } = assert_matches!(&state, ClientState::Requesting(requesting) => requesting);
8726 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
8727 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
8728 }
8729
8730 let options = [
8733 v6::DhcpOption::ServerId(&SERVER_ID[0]),
8734 v6::DhcpOption::ClientId(&MISMATCHED_CLIENT_ID),
8735 v6::DhcpOption::Iana(v6::IanaSerializer::new(
8736 v6::IAID::new(0),
8737 T1.get(),
8738 T2.get(),
8739 &iana_options,
8740 )),
8741 v6::DhcpOption::SolMaxRt(received_sol_max_rt),
8742 ];
8743 let builder =
8744 v6::MessageBuilder::new(v6::MessageType::Reply, request_transaction_id, &options);
8745 let mut buf = vec![0; builder.bytes_len()];
8746 builder.serialize(&mut buf);
8747 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8749 let Transition { state, actions: _, transaction_id: _ } =
8750 state.reply_message_received(&options_to_request, &mut rng, msg, time);
8751 {
8752 let Requesting {
8753 collected_advertise,
8754 solicit_max_rt,
8755 client_id: _,
8756 non_temporary_addresses: _,
8757 delegated_prefixes: _,
8758 server_id: _,
8759 first_request_time: _,
8760 retrans_timeout: _,
8761 transmission_count: _,
8762 } = assert_matches!(&state, ClientState::Requesting(requesting) => requesting);
8763 assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
8764 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
8765 }
8766
8767 let options = [
8770 v6::DhcpOption::ServerId(&SERVER_ID[0]),
8771 v6::DhcpOption::ClientId(&CLIENT_ID),
8772 v6::DhcpOption::Iana(v6::IanaSerializer::new(
8773 v6::IAID::new(0),
8774 T1.get(),
8775 T2.get(),
8776 &iana_options,
8777 )),
8778 v6::DhcpOption::SolMaxRt(received_sol_max_rt),
8779 ];
8780 let builder =
8781 v6::MessageBuilder::new(v6::MessageType::Reply, request_transaction_id, &options);
8782 let mut buf = vec![0; builder.bytes_len()];
8783 builder.serialize(&mut buf);
8784 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8786 let Transition { state, actions: _, transaction_id: _ } =
8787 state.reply_message_received(&options_to_request, &mut rng, msg, time);
8788 {
8789 let Assigned {
8790 solicit_max_rt,
8791 client_id: _,
8792 non_temporary_addresses: _,
8793 delegated_prefixes: _,
8794 server_id: _,
8795 dns_servers: _,
8796 _marker,
8797 } = assert_matches!(&state, ClientState::Assigned(assigned) => assigned);
8798 assert_eq!(*solicit_max_rt, Duration::from_secs(received_sol_max_rt.into()));
8799 }
8800 }
8801
8802 struct RenewRebindTest {
8803 send_and_assert: fn(
8804 &ClientDuid,
8805 [u8; TEST_SERVER_ID_LEN],
8806 Vec<TestIaNa>,
8807 Vec<TestIaPd>,
8808 Option<&[Ipv6Addr]>,
8809 v6::NonZeroOrMaxU32,
8810 v6::NonZeroOrMaxU32,
8811 v6::NonZeroTimeValue,
8812 StepRng,
8813 Instant,
8814 ) -> ClientStateMachine<Instant, StepRng>,
8815 message_type: v6::MessageType,
8816 expect_server_id: bool,
8817 with_state: fn(&Option<ClientState<Instant>>) -> &RenewingOrRebindingInner<Instant>,
8818 allow_response_from_any_server: bool,
8819 }
8820
8821 const RENEW_TEST: RenewRebindTest = RenewRebindTest {
8822 send_and_assert: testutil::send_renew_and_assert,
8823 message_type: v6::MessageType::Renew,
8824 expect_server_id: true,
8825 with_state: |state| {
8826 assert_matches!(
8827 state,
8828 Some(ClientState::Renewing(RenewingOrRebinding(inner))) => inner
8829 )
8830 },
8831 allow_response_from_any_server: false,
8832 };
8833
8834 const REBIND_TEST: RenewRebindTest = RenewRebindTest {
8835 send_and_assert: testutil::send_rebind_and_assert,
8836 message_type: v6::MessageType::Rebind,
8837 expect_server_id: false,
8838 with_state: |state| {
8839 assert_matches!(
8840 state,
8841 Some(ClientState::Rebinding(RenewingOrRebinding(inner))) => inner
8842 )
8843 },
8844 allow_response_from_any_server: true,
8845 };
8846
8847 struct RenewRebindSendTestCase {
8848 ia_nas: Vec<TestIaNa>,
8849 ia_pds: Vec<TestIaPd>,
8850 }
8851
8852 impl RenewRebindSendTestCase {
8853 fn single_value_per_ia() -> RenewRebindSendTestCase {
8854 RenewRebindSendTestCase {
8855 ia_nas: CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]
8856 .iter()
8857 .map(|&addr| TestIaNa::new_default(addr))
8858 .collect(),
8859 ia_pds: CONFIGURED_DELEGATED_PREFIXES[0..2]
8860 .iter()
8861 .map(|&addr| TestIaPd::new_default(addr))
8862 .collect(),
8863 }
8864 }
8865
8866 fn multiple_values_per_ia() -> RenewRebindSendTestCase {
8867 RenewRebindSendTestCase {
8868 ia_nas: vec![TestIaNa::new_default_with_values(
8869 CONFIGURED_NON_TEMPORARY_ADDRESSES
8870 .into_iter()
8871 .map(|a| (a, Lifetimes::new_default()))
8872 .collect(),
8873 )],
8874 ia_pds: vec![TestIaPd::new_default_with_values(
8875 CONFIGURED_DELEGATED_PREFIXES
8876 .into_iter()
8877 .map(|a| (a, Lifetimes::new_default()))
8878 .collect(),
8879 )],
8880 }
8881 }
8882 }
8883
8884 #[test_case(
8885 RENEW_TEST,
8886 RenewRebindSendTestCase::single_value_per_ia(); "renew single value per IA")]
8887 #[test_case(
8888 RENEW_TEST,
8889 RenewRebindSendTestCase::multiple_values_per_ia(); "renew multiple value per IA")]
8890 #[test_case(
8891 REBIND_TEST,
8892 RenewRebindSendTestCase::single_value_per_ia(); "rebind single value per IA")]
8893 #[test_case(
8894 REBIND_TEST,
8895 RenewRebindSendTestCase::multiple_values_per_ia(); "rebind multiple value per IA")]
8896 fn send(
8897 RenewRebindTest {
8898 send_and_assert,
8899 message_type: _,
8900 expect_server_id: _,
8901 with_state: _,
8902 allow_response_from_any_server: _,
8903 }: RenewRebindTest,
8904 RenewRebindSendTestCase { ia_nas, ia_pds }: RenewRebindSendTestCase,
8905 ) {
8906 let _client = send_and_assert(
8907 &(CLIENT_ID.into()),
8908 SERVER_ID[0],
8909 ia_nas,
8910 ia_pds,
8911 None,
8912 T1,
8913 T2,
8914 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
8915 StepRng::new(u64::MAX / 2, 0),
8916 Instant::now(),
8917 );
8918 }
8919
8920 #[test_case(RENEW_TEST)]
8921 #[test_case(REBIND_TEST)]
8922 fn get_dns_server(
8923 RenewRebindTest {
8924 send_and_assert,
8925 message_type: _,
8926 expect_server_id: _,
8927 with_state: _,
8928 allow_response_from_any_server: _,
8929 }: RenewRebindTest,
8930 ) {
8931 let client = send_and_assert(
8932 &(CLIENT_ID.into()),
8933 SERVER_ID[0],
8934 CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]
8935 .iter()
8936 .map(|&addr| TestIaNa::new_default(addr))
8937 .collect(),
8938 Default::default(), Some(&DNS_SERVERS),
8940 T1,
8941 T2,
8942 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
8943 StepRng::new(u64::MAX / 2, 0),
8944 Instant::now(),
8945 );
8946 assert_eq!(client.get_dns_servers()[..], DNS_SERVERS);
8947 }
8948
8949 struct ScheduleRenewAndRebindTimersAfterAssignmentTestCase {
8950 ia_na_t1: v6::TimeValue,
8951 ia_na_t2: v6::TimeValue,
8952 ia_pd_t1: v6::TimeValue,
8953 ia_pd_t2: v6::TimeValue,
8954 expected_timer_actions: fn(Instant) -> [Action<Instant>; 2],
8955 next_timer: Option<RenewRebindTestState>,
8956 }
8957
8958 #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
8961 ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
8962 ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
8963 ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
8964 ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
8965 expected_timer_actions: |_| [
8966 Action::CancelTimer(ClientTimerType::Renew),
8967 Action::CancelTimer(ClientTimerType::Rebind),
8968 ],
8969 next_timer: None,
8970 }; "all infinite time values")]
8971 #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
8972 ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
8973 ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8974 ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
8975 ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8976 expected_timer_actions: |time| [
8977 Action::ScheduleTimer(
8978 ClientTimerType::Renew,
8979 time.add(Duration::from_secs(T1.get().into())),
8980 ),
8981 Action::ScheduleTimer(
8982 ClientTimerType::Rebind,
8983 time.add(Duration::from_secs(T2.get().into())),
8984 ),
8985 ],
8986 next_timer: Some(RENEW_TEST_STATE),
8987 }; "all finite time values")]
8988 #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
8989 ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8990 ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8991 ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8992 ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8993 expected_timer_actions: |time| [
8994 Action::CancelTimer(ClientTimerType::Renew),
8996 Action::ScheduleTimer(
8997 ClientTimerType::Rebind,
8998 time.add(Duration::from_secs(T2.get().into())),
8999 ),
9000 ],
9001 next_timer: Some(REBIND_TEST_STATE),
9002 }; "finite T1 equals finite T2")]
9003 #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
9004 ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
9005 ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9006 ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9007 ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9008 expected_timer_actions: |time| [
9009 Action::ScheduleTimer(
9010 ClientTimerType::Renew,
9011 time.add(Duration::from_secs(T1.get().into())),
9012 ),
9013 Action::CancelTimer(ClientTimerType::Rebind),
9014 ],
9015 next_timer: Some(RENEW_TEST_STATE),
9016 }; "finite IA_NA T1")]
9017 #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
9018 ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
9019 ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
9020 ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9021 ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9022 expected_timer_actions: |time| [
9023 Action::ScheduleTimer(
9024 ClientTimerType::Renew,
9025 time.add(Duration::from_secs(T1.get().into())),
9026 ),
9027 Action::ScheduleTimer(
9028 ClientTimerType::Rebind,
9029 time.add(Duration::from_secs(T2.get().into())),
9030 ),
9031 ],
9032 next_timer: Some(RENEW_TEST_STATE),
9033 }; "finite IA_NA T1 and T2")]
9034 #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
9035 ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9036 ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9037 ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
9038 ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9039 expected_timer_actions: |time| [
9040 Action::ScheduleTimer(
9041 ClientTimerType::Renew,
9042 time.add(Duration::from_secs(T1.get().into())),
9043 ),
9044 Action::CancelTimer(ClientTimerType::Rebind),
9045 ],
9046 next_timer: Some(RENEW_TEST_STATE),
9047 }; "finite IA_PD t1")]
9048 #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
9049 ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9050 ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9051 ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
9052 ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
9053 expected_timer_actions: |time| [
9054 Action::ScheduleTimer(
9055 ClientTimerType::Renew,
9056 time.add(Duration::from_secs(T1.get().into())),
9057 ),
9058 Action::ScheduleTimer(
9059 ClientTimerType::Rebind,
9060 time.add(Duration::from_secs(T2.get().into())),
9061 ),
9062 ],
9063 next_timer: Some(RENEW_TEST_STATE),
9064 }; "finite IA_PD T1 and T2")]
9065 fn schedule_renew_and_rebind_timers_after_assignment(
9066 ScheduleRenewAndRebindTimersAfterAssignmentTestCase {
9067 ia_na_t1,
9068 ia_na_t2,
9069 ia_pd_t1,
9070 ia_pd_t2,
9071 expected_timer_actions,
9072 next_timer,
9073 }: ScheduleRenewAndRebindTimersAfterAssignmentTestCase,
9074 ) {
9075 fn get_ia_and_updates<V: IaValue>(
9076 t1: v6::TimeValue,
9077 t2: v6::TimeValue,
9078 value: V,
9079 ) -> (TestIa<V>, HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>>) {
9080 (
9081 TestIa { t1, t2, ..TestIa::new_default(value) },
9082 HashMap::from([(
9083 v6::IAID::new(0),
9084 HashMap::from([(value, IaValueUpdateKind::Added(Lifetimes::new_default()))]),
9085 )]),
9086 )
9087 }
9088
9089 let (iana, iana_updates) =
9090 get_ia_and_updates(ia_na_t1, ia_na_t2, CONFIGURED_NON_TEMPORARY_ADDRESSES[0]);
9091 let (iapd, iapd_updates) =
9092 get_ia_and_updates(ia_pd_t1, ia_pd_t2, CONFIGURED_DELEGATED_PREFIXES[0]);
9093 let iana = vec![iana];
9094 let iapd = vec![iapd];
9095 let now = Instant::now();
9096 let (client, actions) = testutil::assign_and_assert(
9097 &(CLIENT_ID.into()),
9098 SERVER_ID[0],
9099 iana.clone(),
9100 iapd.clone(),
9101 &[],
9102 StepRng::new(u64::MAX / 2, 0),
9103 now,
9104 );
9105 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
9106 &client;
9107 let Assigned {
9108 client_id: _,
9109 non_temporary_addresses: _,
9110 delegated_prefixes: _,
9111 server_id: _,
9112 dns_servers: _,
9113 solicit_max_rt: _,
9114 _marker,
9115 } = assert_matches!(
9116 state,
9117 Some(ClientState::Assigned(assigned)) => assigned
9118 );
9119
9120 assert_eq!(
9121 actions,
9122 [Action::CancelTimer(ClientTimerType::Retransmission)]
9123 .into_iter()
9124 .chain(expected_timer_actions(now))
9125 .chain((!iana_updates.is_empty()).then(|| Action::IaNaUpdates(iana_updates)))
9126 .chain((!iapd_updates.is_empty()).then(|| Action::IaPdUpdates(iapd_updates)))
9127 .chain([Action::ScheduleTimer(
9128 ClientTimerType::RestartServerDiscovery,
9129 now.add(Duration::from_secs(VALID_LIFETIME.get().into())),
9130 ),])
9131 .collect::<Vec<_>>()
9132 );
9133
9134 let _client = if let Some(next_timer) = next_timer {
9135 handle_renew_or_rebind_timer(
9136 client,
9137 &CLIENT_ID,
9138 SERVER_ID[0],
9139 iana,
9140 iapd,
9141 &[],
9142 &[],
9143 Instant::now(),
9144 next_timer,
9145 )
9146 } else {
9147 client
9148 };
9149 }
9150
9151 #[test_case(RENEW_TEST)]
9152 #[test_case(REBIND_TEST)]
9153 fn retransmit(
9154 RenewRebindTest {
9155 send_and_assert,
9156 message_type,
9157 expect_server_id,
9158 with_state,
9159 allow_response_from_any_server: _,
9160 }: RenewRebindTest,
9161 ) {
9162 let non_temporary_addresses_to_assign = CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]
9163 .iter()
9164 .map(|&addr| TestIaNa::new_default(addr))
9165 .collect::<Vec<_>>();
9166 let delegated_prefixes_to_assign = CONFIGURED_DELEGATED_PREFIXES[0..2]
9167 .iter()
9168 .map(|&addr| TestIaPd::new_default(addr))
9169 .collect::<Vec<_>>();
9170 let time = Instant::now();
9171 let mut client = send_and_assert(
9172 &(CLIENT_ID.into()),
9173 SERVER_ID[0],
9174 non_temporary_addresses_to_assign.clone(),
9175 delegated_prefixes_to_assign.clone(),
9176 None,
9177 T1,
9178 T2,
9179 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9180 StepRng::new(u64::MAX / 2, 0),
9181 time,
9182 );
9183 let ClientStateMachine { transaction_id, options_to_request: _, state, rng: _ } = &client;
9184 let expected_transaction_id = *transaction_id;
9185 let RenewingOrRebindingInner {
9186 client_id: _,
9187 non_temporary_addresses: _,
9188 delegated_prefixes: _,
9189 server_id: _,
9190 dns_servers: _,
9191 start_time: _,
9192 retrans_timeout: _,
9193 solicit_max_rt: _,
9194 } = with_state(state);
9195
9196 let actions = client.handle_timeout(ClientTimerType::Retransmission, time);
9198 let buf = assert_matches!(
9199 &actions[..],
9200 [
9201 Action::SendMessage(buf),
9202 Action::ScheduleTimer(ClientTimerType::Retransmission, timeout)
9203 ] => {
9204 assert_eq!(*timeout, time.add(2 * INITIAL_RENEW_TIMEOUT));
9205 buf
9206 }
9207 );
9208 let ClientStateMachine { transaction_id, options_to_request: _, state, rng: _ } = &client;
9209 assert_eq!(*transaction_id, expected_transaction_id);
9211 {
9212 let RenewingOrRebindingInner {
9213 client_id,
9214 server_id,
9215 dns_servers,
9216 solicit_max_rt,
9217 non_temporary_addresses: _,
9218 delegated_prefixes: _,
9219 start_time: _,
9220 retrans_timeout: _,
9221 } = with_state(state);
9222 assert_eq!(client_id.as_slice(), &CLIENT_ID);
9223 assert_eq!(server_id[..], SERVER_ID[0]);
9224 assert_eq!(dns_servers, &[] as &[Ipv6Addr]);
9225 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
9226 }
9227 let expected_non_temporary_addresses: HashMap<v6::IAID, HashSet<Ipv6Addr>> = (0..)
9228 .map(v6::IAID::new)
9229 .zip(
9230 non_temporary_addresses_to_assign
9231 .iter()
9232 .map(|TestIaNa { values, t1: _, t2: _ }| values.keys().cloned().collect()),
9233 )
9234 .collect();
9235 let expected_delegated_prefixes: HashMap<v6::IAID, HashSet<Subnet<Ipv6Addr>>> = (0..)
9236 .map(v6::IAID::new)
9237 .zip(
9238 delegated_prefixes_to_assign
9239 .iter()
9240 .map(|TestIaPd { values, t1: _, t2: _ }| values.keys().cloned().collect()),
9241 )
9242 .collect();
9243 testutil::assert_outgoing_stateful_message(
9244 &buf,
9245 message_type,
9246 &CLIENT_ID,
9247 expect_server_id.then(|| &SERVER_ID[0]),
9248 &[],
9249 &expected_non_temporary_addresses,
9250 &expected_delegated_prefixes,
9251 );
9252 }
9253
9254 #[test_case(
9255 RENEW_TEST,
9256 &SERVER_ID[0],
9257 &SERVER_ID[0],
9258 RenewRebindSendTestCase::single_value_per_ia()
9259 )]
9260 #[test_case(
9261 REBIND_TEST,
9262 &SERVER_ID[0],
9263 &SERVER_ID[0],
9264 RenewRebindSendTestCase::single_value_per_ia()
9265 )]
9266 #[test_case(
9267 RENEW_TEST,
9268 &SERVER_ID[0],
9269 &SERVER_ID[1],
9270 RenewRebindSendTestCase::single_value_per_ia()
9271 )]
9272 #[test_case(
9273 REBIND_TEST,
9274 &SERVER_ID[0],
9275 &SERVER_ID[1],
9276 RenewRebindSendTestCase::single_value_per_ia()
9277 )]
9278 #[test_case(
9279 RENEW_TEST,
9280 &SERVER_ID[0],
9281 &SERVER_ID[0],
9282 RenewRebindSendTestCase::multiple_values_per_ia()
9283 )]
9284 #[test_case(
9285 REBIND_TEST,
9286 &SERVER_ID[0],
9287 &SERVER_ID[0],
9288 RenewRebindSendTestCase::multiple_values_per_ia()
9289 )]
9290 #[test_case(
9291 RENEW_TEST,
9292 &SERVER_ID[0],
9293 &SERVER_ID[1],
9294 RenewRebindSendTestCase::multiple_values_per_ia()
9295 )]
9296 #[test_case(
9297 REBIND_TEST,
9298 &SERVER_ID[0],
9299 &SERVER_ID[1],
9300 RenewRebindSendTestCase::multiple_values_per_ia()
9301 )]
9302 fn receive_reply_extends_lifetime(
9303 RenewRebindTest {
9304 send_and_assert,
9305 message_type: _,
9306 expect_server_id: _,
9307 with_state,
9308 allow_response_from_any_server,
9309 }: RenewRebindTest,
9310 original_server_id: &[u8; TEST_SERVER_ID_LEN],
9311 reply_server_id: &[u8],
9312 RenewRebindSendTestCase { ia_nas, ia_pds }: RenewRebindSendTestCase,
9313 ) {
9314 let time = Instant::now();
9315 let mut client = send_and_assert(
9316 &(CLIENT_ID.into()),
9317 original_server_id.clone(),
9318 ia_nas.clone(),
9319 ia_pds.clone(),
9320 None,
9321 T1,
9322 T2,
9323 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9324 StepRng::new(u64::MAX / 2, 0),
9325 time,
9326 );
9327 let ClientStateMachine { transaction_id, options_to_request: _, state, rng: _ } = &client;
9328 let buf = TestMessageBuilder {
9329 transaction_id: *transaction_id,
9330 message_type: v6::MessageType::Reply,
9331 client_id: &CLIENT_ID,
9332 server_id: reply_server_id,
9333 preference: None,
9334 dns_servers: None,
9335 ia_nas: (0..).map(v6::IAID::new).zip(ia_nas.iter().map(
9336 |TestIa { values, t1: _, t2: _ }| {
9337 TestIa::new_renewed_default_with_values(values.keys().cloned())
9338 },
9339 )),
9340 ia_pds: (0..).map(v6::IAID::new).zip(ia_pds.iter().map(
9341 |TestIa { values, t1: _, t2: _ }| {
9342 TestIa::new_renewed_default_with_values(values.keys().cloned())
9343 },
9344 )),
9345 }
9346 .build();
9347 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
9349
9350 let original_state = with_state(state).clone();
9352
9353 let actions = client.handle_message_receive(msg, time);
9354 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
9355 &client;
9356
9357 if original_server_id.as_slice() != reply_server_id && !allow_response_from_any_server {
9358 let RenewingOrRebindingInner {
9363 client_id: original_client_id,
9364 non_temporary_addresses: original_non_temporary_addresses,
9365 delegated_prefixes: original_delegated_prefixes,
9366 server_id: original_server_id,
9367 dns_servers: original_dns_servers,
9368 start_time: original_start_time,
9369 retrans_timeout: original_retrans_timeout,
9370 solicit_max_rt: original_solicit_max_rt,
9371 } = original_state;
9372 let RenewingOrRebindingInner {
9373 client_id: new_client_id,
9374 non_temporary_addresses: new_non_temporary_addresses,
9375 delegated_prefixes: new_delegated_prefixes,
9376 server_id: new_server_id,
9377 dns_servers: new_dns_servers,
9378 start_time: new_start_time,
9379 retrans_timeout: new_retrans_timeout,
9380 solicit_max_rt: new_solicit_max_rt,
9381 } = with_state(state);
9382 assert_eq!(&original_client_id, new_client_id);
9383 assert_eq!(&original_non_temporary_addresses, new_non_temporary_addresses);
9384 assert_eq!(&original_delegated_prefixes, new_delegated_prefixes);
9385 assert_eq!(&original_server_id, new_server_id);
9386 assert_eq!(&original_dns_servers, new_dns_servers);
9387 assert_eq!(&original_start_time, new_start_time);
9388 assert_eq!(&original_retrans_timeout, new_retrans_timeout);
9389 assert_eq!(&original_solicit_max_rt, new_solicit_max_rt);
9390 assert_eq!(actions, []);
9391 return;
9392 }
9393
9394 let expected_non_temporary_addresses = (0..)
9395 .map(v6::IAID::new)
9396 .zip(ia_nas.iter().map(|TestIa { values, t1: _, t2: _ }| {
9397 AddressEntry::Assigned(
9398 values
9399 .keys()
9400 .cloned()
9401 .map(|value| {
9402 (
9403 value,
9404 LifetimesInfo {
9405 lifetimes: Lifetimes::new_renewed(),
9406 updated_at: time,
9407 },
9408 )
9409 })
9410 .collect(),
9411 )
9412 }))
9413 .collect();
9414 let expected_delegated_prefixes = (0..)
9415 .map(v6::IAID::new)
9416 .zip(ia_pds.iter().map(|TestIa { values, t1: _, t2: _ }| {
9417 PrefixEntry::Assigned(
9418 values
9419 .keys()
9420 .cloned()
9421 .map(|value| {
9422 (
9423 value,
9424 LifetimesInfo {
9425 lifetimes: Lifetimes::new_renewed(),
9426 updated_at: time,
9427 },
9428 )
9429 })
9430 .collect(),
9431 )
9432 }))
9433 .collect();
9434 assert_matches!(
9435 &state,
9436 Some(ClientState::Assigned(Assigned {
9437 client_id,
9438 non_temporary_addresses,
9439 delegated_prefixes,
9440 server_id,
9441 dns_servers,
9442 solicit_max_rt,
9443 _marker,
9444 })) => {
9445 assert_eq!(client_id.as_slice(), &CLIENT_ID);
9446 assert_eq!(non_temporary_addresses, &expected_non_temporary_addresses);
9447 assert_eq!(delegated_prefixes, &expected_delegated_prefixes);
9448 assert_eq!(server_id.as_slice(), reply_server_id);
9449 assert_eq!(dns_servers.as_slice(), &[] as &[Ipv6Addr]);
9450 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
9451 }
9452 );
9453 assert_matches!(
9454 &actions[..],
9455 [
9456 Action::CancelTimer(ClientTimerType::Retransmission),
9457 Action::ScheduleTimer(ClientTimerType::Renew, t1),
9458 Action::ScheduleTimer(ClientTimerType::Rebind, t2),
9459 Action::IaNaUpdates(iana_updates),
9460 Action::IaPdUpdates(iapd_updates),
9461 Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
9462 ] => {
9463 assert_eq!(*t1, time.add(Duration::from_secs(RENEWED_T1.get().into())));
9464 assert_eq!(*t2, time.add(Duration::from_secs(RENEWED_T2.get().into())));
9465 assert_eq!(
9466 *restart_time,
9467 time.add(Duration::from_secs(RENEWED_VALID_LIFETIME.get().into()))
9468 );
9469
9470 fn get_updates<V: IaValue>(ias: Vec<TestIa<V>>) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
9471 (0..).map(v6::IAID::new).zip(ias.into_iter().map(
9472 |TestIa { values, t1: _, t2: _ }| {
9473 values.into_keys()
9474 .map(|value| (
9475 value,
9476 IaValueUpdateKind::UpdatedLifetimes(Lifetimes::new_renewed())
9477 ))
9478 .collect()
9479 },
9480 )).collect()
9481 }
9482
9483 assert_eq!(iana_updates, &get_updates(ia_nas));
9484 assert_eq!(iapd_updates, &get_updates(ia_pds));
9485 }
9486 );
9487 }
9488
9489 #[test_case(RENEW_TEST, v6::ErrorStatusCode::UnspecFail)]
9493 #[test_case(RENEW_TEST, v6::ErrorStatusCode::NoBinding)]
9494 #[test_case(RENEW_TEST, v6::ErrorStatusCode::NotOnLink)]
9495 #[test_case(RENEW_TEST, v6::ErrorStatusCode::NoAddrsAvail)]
9496 #[test_case(RENEW_TEST, v6::ErrorStatusCode::NoPrefixAvail)]
9497 #[test_case(REBIND_TEST, v6::ErrorStatusCode::UnspecFail)]
9498 #[test_case(REBIND_TEST, v6::ErrorStatusCode::NoBinding)]
9499 #[test_case(REBIND_TEST, v6::ErrorStatusCode::NotOnLink)]
9500 #[test_case(REBIND_TEST, v6::ErrorStatusCode::NoAddrsAvail)]
9501 #[test_case(REBIND_TEST, v6::ErrorStatusCode::NoPrefixAvail)]
9502 fn renewing_receive_reply_with_error_status(
9503 RenewRebindTest {
9504 send_and_assert,
9505 message_type: _,
9506 expect_server_id: _,
9507 with_state,
9508 allow_response_from_any_server: _,
9509 }: RenewRebindTest,
9510 error_status_code: v6::ErrorStatusCode,
9511 ) {
9512 let time = Instant::now();
9513 let addr = CONFIGURED_NON_TEMPORARY_ADDRESSES[0];
9514 let prefix = CONFIGURED_DELEGATED_PREFIXES[0];
9515 let mut client = send_and_assert(
9516 &(CLIENT_ID.into()),
9517 SERVER_ID[0],
9518 vec![TestIaNa::new_default(addr)],
9519 vec![TestIaPd::new_default(prefix)],
9520 None,
9521 T1,
9522 T2,
9523 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9524 StepRng::new(u64::MAX / 2, 0),
9525 time,
9526 );
9527 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
9528 &client;
9529 let ia_na_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
9530 addr,
9531 RENEWED_PREFERRED_LIFETIME.get(),
9532 RENEWED_VALID_LIFETIME.get(),
9533 &[],
9534 ))];
9535 let sol_max_rt = *VALID_MAX_SOLICIT_TIMEOUT_RANGE.start();
9536 let options = vec![
9537 v6::DhcpOption::ClientId(&CLIENT_ID),
9538 v6::DhcpOption::ServerId(&SERVER_ID[0]),
9539 v6::DhcpOption::StatusCode(error_status_code.into(), ""),
9540 v6::DhcpOption::Iana(v6::IanaSerializer::new(
9541 v6::IAID::new(0),
9542 RENEWED_T1.get(),
9543 RENEWED_T2.get(),
9544 &ia_na_options,
9545 )),
9546 v6::DhcpOption::SolMaxRt(sol_max_rt),
9547 ];
9548 let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
9549 let mut buf = vec![0; builder.bytes_len()];
9550 builder.serialize(&mut buf);
9551 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
9553 let actions = client.handle_message_receive(msg, time);
9554 assert_eq!(actions, &[]);
9555 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
9556 &client;
9557
9558 let RenewingOrRebindingInner {
9559 client_id,
9560 non_temporary_addresses,
9561 delegated_prefixes,
9562 server_id,
9563 dns_servers,
9564 start_time: _,
9565 retrans_timeout: _,
9566 solicit_max_rt: got_sol_max_rt,
9567 } = with_state(state);
9568 assert_eq!(client_id.as_slice(), &CLIENT_ID);
9569 fn expected_values<V: IaValue>(
9570 value: V,
9571 time: Instant,
9572 ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
9573 std::iter::once((
9574 v6::IAID::new(0),
9575 IaEntry::new_assigned(value, PREFERRED_LIFETIME, VALID_LIFETIME, time),
9576 ))
9577 .collect()
9578 }
9579 assert_eq!(*non_temporary_addresses, expected_values(addr, time));
9580 assert_eq!(*delegated_prefixes, expected_values(prefix, time));
9581 assert_eq!(*server_id, SERVER_ID[0]);
9582 assert_eq!(dns_servers, &[] as &[Ipv6Addr]);
9583 assert_eq!(*got_sol_max_rt, Duration::from_secs(sol_max_rt.into()));
9584 assert_matches!(&actions[..], []);
9585 }
9586
9587 struct ReceiveReplyWithMissingIasTestCase {
9588 present_ia_na_iaids: Vec<v6::IAID>,
9589 present_ia_pd_iaids: Vec<v6::IAID>,
9590 }
9591
9592 #[test_case(
9593 REBIND_TEST,
9594 ReceiveReplyWithMissingIasTestCase {
9595 present_ia_na_iaids: Vec::new(),
9596 present_ia_pd_iaids: Vec::new(),
9597 }; "none presenet")]
9598 #[test_case(
9599 RENEW_TEST,
9600 ReceiveReplyWithMissingIasTestCase {
9601 present_ia_na_iaids: vec![v6::IAID::new(0)],
9602 present_ia_pd_iaids: Vec::new(),
9603 }; "only one IA_NA present")]
9604 #[test_case(
9605 RENEW_TEST,
9606 ReceiveReplyWithMissingIasTestCase {
9607 present_ia_na_iaids: Vec::new(),
9608 present_ia_pd_iaids: vec![v6::IAID::new(1)],
9609 }; "only one IA_PD present")]
9610 #[test_case(
9611 REBIND_TEST,
9612 ReceiveReplyWithMissingIasTestCase {
9613 present_ia_na_iaids: vec![v6::IAID::new(0), v6::IAID::new(1)],
9614 present_ia_pd_iaids: Vec::new(),
9615 }; "only both IA_NAs present")]
9616 #[test_case(
9617 REBIND_TEST,
9618 ReceiveReplyWithMissingIasTestCase {
9619 present_ia_na_iaids: Vec::new(),
9620 present_ia_pd_iaids: vec![v6::IAID::new(0), v6::IAID::new(1)],
9621 }; "only both IA_PDs present")]
9622 #[test_case(
9623 REBIND_TEST,
9624 ReceiveReplyWithMissingIasTestCase {
9625 present_ia_na_iaids: vec![v6::IAID::new(1)],
9626 present_ia_pd_iaids: vec![v6::IAID::new(0), v6::IAID::new(1)],
9627 }; "both IA_PDs and one IA_NA present")]
9628 #[test_case(
9629 REBIND_TEST,
9630 ReceiveReplyWithMissingIasTestCase {
9631 present_ia_na_iaids: vec![v6::IAID::new(0), v6::IAID::new(1)],
9632 present_ia_pd_iaids: vec![v6::IAID::new(0)],
9633 }; "both IA_NAs and one IA_PD present")]
9634 fn receive_reply_with_missing_ias(
9635 RenewRebindTest {
9636 send_and_assert,
9637 message_type: _,
9638 expect_server_id: _,
9639 with_state,
9640 allow_response_from_any_server: _,
9641 }: RenewRebindTest,
9642 ReceiveReplyWithMissingIasTestCase {
9643 present_ia_na_iaids,
9644 present_ia_pd_iaids,
9645 }: ReceiveReplyWithMissingIasTestCase,
9646 ) {
9647 let non_temporary_addresses = &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2];
9648 let delegated_prefixes = &CONFIGURED_DELEGATED_PREFIXES[0..2];
9649 let time = Instant::now();
9650 let mut client = send_and_assert(
9651 &(CLIENT_ID.into()),
9652 SERVER_ID[0],
9653 non_temporary_addresses.iter().copied().map(TestIaNa::new_default).collect(),
9654 delegated_prefixes.iter().copied().map(TestIaPd::new_default).collect(),
9655 None,
9656 T1,
9657 T2,
9658 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9659 StepRng::new(u64::MAX / 2, 0),
9660 time,
9661 );
9662 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
9663 &client;
9664 let buf = TestMessageBuilder {
9667 transaction_id: *transaction_id,
9668 message_type: v6::MessageType::Reply,
9669 client_id: &CLIENT_ID,
9670 server_id: &SERVER_ID[0],
9671 preference: None,
9672 dns_servers: None,
9673 ia_nas: present_ia_na_iaids.iter().map(|iaid| {
9674 (
9675 *iaid,
9676 TestIa::new_renewed_default(
9677 CONFIGURED_NON_TEMPORARY_ADDRESSES[iaid.get() as usize],
9678 ),
9679 )
9680 }),
9681 ia_pds: present_ia_pd_iaids.iter().map(|iaid| {
9682 (
9683 *iaid,
9684 TestIa::new_renewed_default(CONFIGURED_DELEGATED_PREFIXES[iaid.get() as usize]),
9685 )
9686 }),
9687 }
9688 .build();
9689 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
9691 let actions = client.handle_message_receive(msg, time);
9692 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
9693 &client;
9694 {
9696 let RenewingOrRebindingInner {
9697 client_id,
9698 non_temporary_addresses: got_non_temporary_addresses,
9699 delegated_prefixes: got_delegated_prefixes,
9700 server_id,
9701 dns_servers,
9702 start_time: _,
9703 retrans_timeout: _,
9704 solicit_max_rt,
9705 } = with_state(state);
9706 assert_eq!(client_id.as_slice(), &CLIENT_ID);
9707 fn expected_values<V: IaValue>(
9708 values: &[V],
9709 present_iaids: Vec<v6::IAID>,
9710 time: Instant,
9711 ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
9712 (0..)
9713 .map(v6::IAID::new)
9714 .zip(values)
9715 .map(|(iaid, &value)| {
9716 (
9717 iaid,
9718 if present_iaids.contains(&iaid) {
9719 IaEntry::new_assigned(
9720 value,
9721 RENEWED_PREFERRED_LIFETIME,
9722 RENEWED_VALID_LIFETIME,
9723 time,
9724 )
9725 } else {
9726 IaEntry::new_assigned(
9727 value,
9728 PREFERRED_LIFETIME,
9729 VALID_LIFETIME,
9730 time,
9731 )
9732 },
9733 )
9734 })
9735 .collect()
9736 }
9737 assert_eq!(
9738 *got_non_temporary_addresses,
9739 expected_values(non_temporary_addresses, present_ia_na_iaids, time)
9740 );
9741 assert_eq!(
9742 *got_delegated_prefixes,
9743 expected_values(delegated_prefixes, present_ia_pd_iaids, time)
9744 );
9745 assert_eq!(*server_id, SERVER_ID[0]);
9746 assert_eq!(dns_servers, &[] as &[Ipv6Addr]);
9747 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
9748 }
9749 assert_matches!(&actions[..], []);
9751 }
9752
9753 #[test_case(RENEW_TEST)]
9754 #[test_case(REBIND_TEST)]
9755 fn receive_reply_with_missing_ia_suboption_for_assigned_entry_does_not_extend_lifetime(
9756 RenewRebindTest {
9757 send_and_assert,
9758 message_type: _,
9759 expect_server_id: _,
9760 with_state: _,
9761 allow_response_from_any_server: _,
9762 }: RenewRebindTest,
9763 ) {
9764 const IA_NA_WITHOUT_ADDRESS_IAID: v6::IAID = v6::IAID::new(0);
9765 const IA_PD_WITHOUT_PREFIX_IAID: v6::IAID = v6::IAID::new(1);
9766
9767 let time = Instant::now();
9768 let mut client = send_and_assert(
9769 &(CLIENT_ID.into()),
9770 SERVER_ID[0],
9771 CONFIGURED_NON_TEMPORARY_ADDRESSES.iter().copied().map(TestIaNa::new_default).collect(),
9772 CONFIGURED_DELEGATED_PREFIXES.iter().copied().map(TestIaPd::new_default).collect(),
9773 None,
9774 T1,
9775 T2,
9776 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9777 StepRng::new(u64::MAX / 2, 0),
9778 time,
9779 );
9780 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
9781 &client;
9782 let iaaddr_opts = (0..)
9784 .map(v6::IAID::new)
9785 .zip(CONFIGURED_NON_TEMPORARY_ADDRESSES)
9786 .map(|(iaid, addr)| {
9787 (
9788 iaid,
9789 (iaid != IA_NA_WITHOUT_ADDRESS_IAID).then(|| {
9790 [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
9791 addr,
9792 RENEWED_PREFERRED_LIFETIME.get(),
9793 RENEWED_VALID_LIFETIME.get(),
9794 &[],
9795 ))]
9796 }),
9797 )
9798 })
9799 .collect::<HashMap<_, _>>();
9800 let iaprefix_opts = (0..)
9801 .map(v6::IAID::new)
9802 .zip(CONFIGURED_DELEGATED_PREFIXES)
9803 .map(|(iaid, prefix)| {
9804 (
9805 iaid,
9806 (iaid != IA_PD_WITHOUT_PREFIX_IAID).then(|| {
9807 [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
9808 RENEWED_PREFERRED_LIFETIME.get(),
9809 RENEWED_VALID_LIFETIME.get(),
9810 prefix,
9811 &[],
9812 ))]
9813 }),
9814 )
9815 })
9816 .collect::<HashMap<_, _>>();
9817 let options =
9818 [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0])]
9819 .into_iter()
9820 .chain(iaaddr_opts.iter().map(|(iaid, iaaddr_opts)| {
9821 v6::DhcpOption::Iana(v6::IanaSerializer::new(
9822 *iaid,
9823 RENEWED_T1.get(),
9824 RENEWED_T2.get(),
9825 iaaddr_opts.as_ref().map_or(&[], AsRef::as_ref),
9826 ))
9827 }))
9828 .chain(iaprefix_opts.iter().map(|(iaid, iaprefix_opts)| {
9829 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
9830 *iaid,
9831 RENEWED_T1.get(),
9832 RENEWED_T2.get(),
9833 iaprefix_opts.as_ref().map_or(&[], AsRef::as_ref),
9834 ))
9835 }))
9836 .collect::<Vec<_>>();
9837 let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
9838 let mut buf = vec![0; builder.bytes_len()];
9839 builder.serialize(&mut buf);
9840 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
9842 let actions = client.handle_message_receive(msg, time);
9843 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
9844 &client;
9845 assert_matches!(
9848 &state,
9849 Some(ClientState::Assigned(Assigned {
9850 client_id,
9851 non_temporary_addresses,
9852 delegated_prefixes,
9853 server_id,
9854 dns_servers,
9855 solicit_max_rt,
9856 _marker,
9857 })) => {
9858 assert_eq!(client_id.as_slice(), &CLIENT_ID);
9859 fn expected_values<V: IaValueTestExt>(
9860 without_value: v6::IAID,
9861 time: Instant,
9862 ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
9863 (0..)
9864 .map(v6::IAID::new)
9865 .zip(V::CONFIGURED)
9866 .map(|(iaid, value)| {
9867 let (preferred_lifetime, valid_lifetime) =
9868 if iaid == without_value {
9869 (PREFERRED_LIFETIME, VALID_LIFETIME)
9870 } else {
9871 (RENEWED_PREFERRED_LIFETIME, RENEWED_VALID_LIFETIME)
9872 };
9873
9874 (
9875 iaid,
9876 IaEntry::new_assigned(
9877 value,
9878 preferred_lifetime,
9879 valid_lifetime,
9880 time,
9881 ),
9882 )
9883 })
9884 .collect()
9885 }
9886 assert_eq!(
9887 non_temporary_addresses,
9888 &expected_values::<Ipv6Addr>(IA_NA_WITHOUT_ADDRESS_IAID, time)
9889 );
9890 assert_eq!(
9891 delegated_prefixes,
9892 &expected_values::<Subnet<Ipv6Addr>>(IA_PD_WITHOUT_PREFIX_IAID, time)
9893 );
9894 assert_eq!(server_id.as_slice(), &SERVER_ID[0]);
9895 assert_eq!(dns_servers.as_slice(), &[] as &[Ipv6Addr]);
9896 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
9897 }
9898 );
9899 assert_matches!(
9900 &actions[..],
9901 [
9902 Action::CancelTimer(ClientTimerType::Retransmission),
9903 Action::ScheduleTimer(ClientTimerType::Renew, t1),
9904 Action::ScheduleTimer(ClientTimerType::Rebind, t2),
9905 Action::IaNaUpdates(iana_updates),
9906 Action::IaPdUpdates(iapd_updates),
9907 Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
9908 ] => {
9909 assert_eq!(*t1, time.add(Duration::from_secs(RENEWED_T1.get().into())));
9910 assert_eq!(*t2, time.add(Duration::from_secs(RENEWED_T2.get().into())));
9911 assert_eq!(
9912 *restart_time,
9913 time.add(Duration::from_secs(std::cmp::max(
9914 VALID_LIFETIME,
9915 RENEWED_VALID_LIFETIME,
9916 ).get().into()))
9917 );
9918
9919 fn get_updates<V: IaValue>(
9920 values: &[V],
9921 omit_iaid: v6::IAID,
9922 ) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
9923 (0..).map(v6::IAID::new)
9924 .zip(values.iter().cloned())
9925 .filter_map(|(iaid, value)| {
9926 (iaid != omit_iaid).then(|| (
9927 iaid,
9928 HashMap::from([(
9929 value,
9930 IaValueUpdateKind::UpdatedLifetimes(Lifetimes::new_renewed()),
9931 )])
9932 ))
9933 })
9934 .collect()
9935 }
9936 assert_eq!(
9937 iana_updates,
9938 &get_updates(&CONFIGURED_NON_TEMPORARY_ADDRESSES, IA_NA_WITHOUT_ADDRESS_IAID),
9939 );
9940 assert_eq!(
9941 iapd_updates,
9942 &get_updates(&CONFIGURED_DELEGATED_PREFIXES, IA_PD_WITHOUT_PREFIX_IAID),
9943 );
9944 }
9945 );
9946 }
9947
9948 #[test_case(RENEW_TEST)]
9949 #[test_case(REBIND_TEST)]
9950 fn receive_reply_with_zero_lifetime(
9951 RenewRebindTest {
9952 send_and_assert,
9953 message_type: _,
9954 expect_server_id: _,
9955 with_state: _,
9956 allow_response_from_any_server: _,
9957 }: RenewRebindTest,
9958 ) {
9959 const IA_NA_ZERO_LIFETIMES_ADDRESS_IAID: v6::IAID = v6::IAID::new(0);
9960 const IA_PD_ZERO_LIFETIMES_PREFIX_IAID: v6::IAID = v6::IAID::new(1);
9961
9962 let time = Instant::now();
9963 let mut client = send_and_assert(
9964 &(CLIENT_ID.into()),
9965 SERVER_ID[0],
9966 CONFIGURED_NON_TEMPORARY_ADDRESSES.iter().copied().map(TestIaNa::new_default).collect(),
9967 CONFIGURED_DELEGATED_PREFIXES.iter().copied().map(TestIaPd::new_default).collect(),
9968 None,
9969 T1,
9970 T2,
9971 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9972 StepRng::new(u64::MAX / 2, 0),
9973 time,
9974 );
9975 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
9976 &client;
9977 let iaaddr_opts = (0..)
9979 .map(v6::IAID::new)
9980 .zip(CONFIGURED_NON_TEMPORARY_ADDRESSES)
9981 .map(|(iaid, addr)| {
9982 let (pl, vl) = if iaid == IA_NA_ZERO_LIFETIMES_ADDRESS_IAID {
9983 (0, 0)
9984 } else {
9985 (RENEWED_PREFERRED_LIFETIME.get(), RENEWED_VALID_LIFETIME.get())
9986 };
9987
9988 (iaid, [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(addr, pl, vl, &[]))])
9989 })
9990 .collect::<HashMap<_, _>>();
9991 let iaprefix_opts = (0..)
9992 .map(v6::IAID::new)
9993 .zip(CONFIGURED_DELEGATED_PREFIXES)
9994 .map(|(iaid, prefix)| {
9995 let (pl, vl) = if iaid == IA_PD_ZERO_LIFETIMES_PREFIX_IAID {
9996 (0, 0)
9997 } else {
9998 (RENEWED_PREFERRED_LIFETIME.get(), RENEWED_VALID_LIFETIME.get())
9999 };
10000
10001 (iaid, [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(pl, vl, prefix, &[]))])
10002 })
10003 .collect::<HashMap<_, _>>();
10004 let options =
10005 [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0])]
10006 .into_iter()
10007 .chain(iaaddr_opts.iter().map(|(iaid, iaaddr_opts)| {
10008 v6::DhcpOption::Iana(v6::IanaSerializer::new(
10009 *iaid,
10010 RENEWED_T1.get(),
10011 RENEWED_T2.get(),
10012 iaaddr_opts.as_ref(),
10013 ))
10014 }))
10015 .chain(iaprefix_opts.iter().map(|(iaid, iaprefix_opts)| {
10016 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
10017 *iaid,
10018 RENEWED_T1.get(),
10019 RENEWED_T2.get(),
10020 iaprefix_opts.as_ref(),
10021 ))
10022 }))
10023 .collect::<Vec<_>>();
10024 let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
10025 let mut buf = vec![0; builder.bytes_len()];
10026 builder.serialize(&mut buf);
10027 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10029 let actions = client.handle_message_receive(msg, time);
10030 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
10031 &client;
10032 assert_matches!(
10035 &state,
10036 Some(ClientState::Assigned(Assigned {
10037 client_id,
10038 non_temporary_addresses,
10039 delegated_prefixes,
10040 server_id,
10041 dns_servers,
10042 solicit_max_rt,
10043 _marker,
10044 })) => {
10045 assert_eq!(client_id.as_slice(), &CLIENT_ID);
10046 fn expected_values<V: IaValueTestExt>(
10047 zero_lifetime_iaid: v6::IAID,
10048 time: Instant,
10049 ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
10050 (0..)
10051 .map(v6::IAID::new)
10052 .zip(V::CONFIGURED)
10053 .map(|(iaid, value)| {
10054 (
10055 iaid,
10056 if iaid == zero_lifetime_iaid {
10057 IaEntry::ToRequest(HashSet::from([value]))
10058 } else {
10059 IaEntry::new_assigned(
10060 value,
10061 RENEWED_PREFERRED_LIFETIME,
10062 RENEWED_VALID_LIFETIME,
10063 time,
10064 )
10065 },
10066 )
10067 })
10068 .collect()
10069 }
10070 assert_eq!(
10071 non_temporary_addresses,
10072 &expected_values::<Ipv6Addr>(IA_NA_ZERO_LIFETIMES_ADDRESS_IAID, time)
10073 );
10074 assert_eq!(
10075 delegated_prefixes,
10076 &expected_values::<Subnet<Ipv6Addr>>(IA_PD_ZERO_LIFETIMES_PREFIX_IAID, time)
10077 );
10078 assert_eq!(server_id.as_slice(), &SERVER_ID[0]);
10079 assert_eq!(dns_servers.as_slice(), &[] as &[Ipv6Addr]);
10080 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
10081 }
10082 );
10083 assert_matches!(
10084 &actions[..],
10085 [
10086 Action::CancelTimer(ClientTimerType::Retransmission),
10087 Action::ScheduleTimer(ClientTimerType::Renew, t1),
10088 Action::ScheduleTimer(ClientTimerType::Rebind, t2),
10089 Action::IaNaUpdates(iana_updates),
10090 Action::IaPdUpdates(iapd_updates),
10091 Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
10092 ] => {
10093 assert_eq!(*t1, time.add(Duration::from_secs(RENEWED_T1.get().into())));
10094 assert_eq!(*t2, time.add(Duration::from_secs(RENEWED_T2.get().into())));
10095 assert_eq!(
10096 *restart_time,
10097 time.add(Duration::from_secs(RENEWED_VALID_LIFETIME.get().into()))
10098 );
10099
10100 fn get_updates<V: IaValue>(
10101 values: &[V],
10102 omit_iaid: v6::IAID,
10103 ) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
10104 (0..).map(v6::IAID::new)
10105 .zip(values.iter().cloned())
10106 .map(|(iaid, value)| (
10107 iaid,
10108 HashMap::from([(
10109 value,
10110 if iaid == omit_iaid {
10111 IaValueUpdateKind::Removed
10112 } else {
10113 IaValueUpdateKind::UpdatedLifetimes(Lifetimes::new_renewed())
10114 }
10115 )]),
10116 ))
10117 .collect()
10118 }
10119 assert_eq!(
10120 iana_updates,
10121 &get_updates(
10122 &CONFIGURED_NON_TEMPORARY_ADDRESSES,
10123 IA_NA_ZERO_LIFETIMES_ADDRESS_IAID,
10124 ),
10125 );
10126 assert_eq!(
10127 iapd_updates,
10128 &get_updates(
10129 &CONFIGURED_DELEGATED_PREFIXES,
10130 IA_PD_ZERO_LIFETIMES_PREFIX_IAID,
10131 ),
10132 );
10133 }
10134 );
10135 }
10136
10137 #[test_case(RENEW_TEST)]
10138 #[test_case(REBIND_TEST)]
10139 fn receive_reply_with_original_ia_value_omitted(
10140 RenewRebindTest {
10141 send_and_assert,
10142 message_type: _,
10143 expect_server_id: _,
10144 with_state: _,
10145 allow_response_from_any_server: _,
10146 }: RenewRebindTest,
10147 ) {
10148 let time = Instant::now();
10149 let mut client = send_and_assert(
10150 &(CLIENT_ID.into()),
10151 SERVER_ID[0],
10152 vec![TestIaNa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[0])],
10153 vec![TestIaPd::new_default(CONFIGURED_DELEGATED_PREFIXES[0])],
10154 None,
10155 T1,
10156 T2,
10157 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
10158 StepRng::new(u64::MAX / 2, 0),
10159 time,
10160 );
10161 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
10162 &client;
10163 let iaid = v6::IAID::new(0);
10166 let buf = TestMessageBuilder {
10167 transaction_id: *transaction_id,
10168 message_type: v6::MessageType::Reply,
10169 client_id: &CLIENT_ID,
10170 server_id: &SERVER_ID[0],
10171 preference: None,
10172 dns_servers: None,
10173 ia_nas: std::iter::once((
10174 iaid,
10175 TestIa::new_renewed_default(RENEW_NON_TEMPORARY_ADDRESSES[0]),
10176 )),
10177 ia_pds: std::iter::once((
10178 iaid,
10179 TestIa::new_renewed_default(RENEW_DELEGATED_PREFIXES[0]),
10180 )),
10181 }
10182 .build();
10183 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10185 let actions = client.handle_message_receive(msg, time);
10186 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
10187 &client;
10188
10189 assert_matches!(
10195 &state,
10196 Some(ClientState::Assigned(Assigned {
10197 client_id,
10198 non_temporary_addresses,
10199 delegated_prefixes,
10200 server_id,
10201 dns_servers,
10202 solicit_max_rt,
10203 _marker,
10204 })) => {
10205 assert_eq!(client_id.as_slice(), &CLIENT_ID);
10206 fn calc_expected<V: IaValue>(
10207 iaid: v6::IAID,
10208 time: Instant,
10209 initial: V,
10210 in_renew: V,
10211 ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
10212 HashMap::from([(
10213 iaid,
10214 IaEntry::Assigned(HashMap::from([
10215 (
10216 initial,
10217 LifetimesInfo {
10218 lifetimes: Lifetimes::new_finite(
10219 PREFERRED_LIFETIME,
10220 VALID_LIFETIME,
10221 ),
10222 updated_at: time,
10223 }
10224 ),
10225 (
10226 in_renew,
10227 LifetimesInfo {
10228 lifetimes: Lifetimes::new_finite(
10229 RENEWED_PREFERRED_LIFETIME,
10230 RENEWED_VALID_LIFETIME,
10231 ),
10232 updated_at: time,
10233 },
10234 ),
10235 ])),
10236 )])
10237 }
10238 assert_eq!(
10239 non_temporary_addresses,
10240 &calc_expected(
10241 iaid,
10242 time,
10243 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
10244 RENEW_NON_TEMPORARY_ADDRESSES[0],
10245 )
10246 );
10247 assert_eq!(
10248 delegated_prefixes,
10249 &calc_expected(
10250 iaid,
10251 time,
10252 CONFIGURED_DELEGATED_PREFIXES[0],
10253 RENEW_DELEGATED_PREFIXES[0],
10254 )
10255 );
10256 assert_eq!(server_id.as_slice(), &SERVER_ID[0]);
10257 assert_eq!(dns_servers.as_slice(), &[] as &[Ipv6Addr]);
10258 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
10259 }
10260 );
10261 assert_matches!(
10262 &actions[..],
10263 [
10264 Action::CancelTimer(ClientTimerType::Retransmission),
10265 Action::ScheduleTimer(ClientTimerType::Renew, t1),
10266 Action::ScheduleTimer(ClientTimerType::Rebind, t2),
10267 Action::IaNaUpdates(iana_updates),
10268 Action::IaPdUpdates(iapd_updates),
10269 Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
10270 ] => {
10271 assert_eq!(*t1, time.add(Duration::from_secs(RENEWED_T1.get().into())));
10272 assert_eq!(*t2, time.add(Duration::from_secs(RENEWED_T2.get().into())));
10273 assert_eq!(
10274 *restart_time,
10275 time.add(Duration::from_secs(std::cmp::max(
10276 VALID_LIFETIME,
10277 RENEWED_VALID_LIFETIME,
10278 ).get().into()))
10279 );
10280
10281 fn get_updates<V: IaValue>(
10282 iaid: v6::IAID,
10283 new_value: V,
10284 ) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
10285 HashMap::from([
10286 (
10287 iaid,
10288 HashMap::from([
10289 (
10290 new_value,
10291 IaValueUpdateKind::Added(Lifetimes::new_renewed()),
10292 ),
10293 ])
10294 ),
10295 ])
10296 }
10297
10298 assert_eq!(
10299 iana_updates,
10300 &get_updates(
10301 iaid,
10302 RENEW_NON_TEMPORARY_ADDRESSES[0],
10303 ),
10304 );
10305 assert_eq!(
10306 iapd_updates,
10307 &get_updates(
10308 iaid,
10309 RENEW_DELEGATED_PREFIXES[0],
10310 ),
10311 );
10312 }
10313 );
10314 }
10315
10316 struct NoBindingTestCase {
10317 ia_na_no_binding: bool,
10318 ia_pd_no_binding: bool,
10319 }
10320
10321 #[test_case(
10322 RENEW_TEST,
10323 NoBindingTestCase {
10324 ia_na_no_binding: true,
10325 ia_pd_no_binding: false,
10326 }
10327 )]
10328 #[test_case(
10329 REBIND_TEST,
10330 NoBindingTestCase {
10331 ia_na_no_binding: true,
10332 ia_pd_no_binding: false,
10333 }
10334 )]
10335 #[test_case(
10336 RENEW_TEST,
10337 NoBindingTestCase {
10338 ia_na_no_binding: false,
10339 ia_pd_no_binding: true,
10340 }
10341 )]
10342 #[test_case(
10343 REBIND_TEST,
10344 NoBindingTestCase {
10345 ia_na_no_binding: false,
10346 ia_pd_no_binding: true,
10347 }
10348 )]
10349 #[test_case(
10350 RENEW_TEST,
10351 NoBindingTestCase {
10352 ia_na_no_binding: true,
10353 ia_pd_no_binding: true,
10354 }
10355 )]
10356 #[test_case(
10357 REBIND_TEST,
10358 NoBindingTestCase {
10359 ia_na_no_binding: true,
10360 ia_pd_no_binding: true,
10361 }
10362 )]
10363 fn no_binding(
10364 RenewRebindTest {
10365 send_and_assert,
10366 message_type: _,
10367 expect_server_id: _,
10368 with_state: _,
10369 allow_response_from_any_server: _,
10370 }: RenewRebindTest,
10371 NoBindingTestCase { ia_na_no_binding, ia_pd_no_binding }: NoBindingTestCase,
10372 ) {
10373 const NUM_IAS: u32 = 2;
10374 const NO_BINDING_IA_IDX: usize = (NUM_IAS - 1) as usize;
10375
10376 fn to_assign<V: IaValueTestExt>() -> Vec<TestIa<V>> {
10377 V::CONFIGURED[0..usize::try_from(NUM_IAS).unwrap()]
10378 .iter()
10379 .copied()
10380 .map(TestIa::new_default)
10381 .collect()
10382 }
10383 let time = Instant::now();
10384 let non_temporary_addresses_to_assign = to_assign::<Ipv6Addr>();
10385 let delegated_prefixes_to_assign = to_assign::<Subnet<Ipv6Addr>>();
10386 let mut client = send_and_assert(
10387 &(CLIENT_ID.into()),
10388 SERVER_ID[0],
10389 non_temporary_addresses_to_assign.clone(),
10390 delegated_prefixes_to_assign.clone(),
10391 None,
10392 T1,
10393 T2,
10394 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
10395 StepRng::new(u64::MAX / 2, 0),
10396 time,
10397 );
10398 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
10399 &client;
10400
10401 let iaaddr_opts = (0..usize::try_from(NUM_IAS).unwrap())
10403 .map(|i| {
10404 if i == NO_BINDING_IA_IDX && ia_na_no_binding {
10405 [v6::DhcpOption::StatusCode(
10406 v6::ErrorStatusCode::NoBinding.into(),
10407 "Binding not found.",
10408 )]
10409 } else {
10410 [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
10411 CONFIGURED_NON_TEMPORARY_ADDRESSES[i],
10412 RENEWED_PREFERRED_LIFETIME.get(),
10413 RENEWED_VALID_LIFETIME.get(),
10414 &[],
10415 ))]
10416 }
10417 })
10418 .collect::<Vec<_>>();
10419 let iaprefix_opts = (0..usize::try_from(NUM_IAS).unwrap())
10420 .map(|i| {
10421 if i == NO_BINDING_IA_IDX && ia_pd_no_binding {
10422 [v6::DhcpOption::StatusCode(
10423 v6::ErrorStatusCode::NoBinding.into(),
10424 "Binding not found.",
10425 )]
10426 } else {
10427 [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
10428 RENEWED_PREFERRED_LIFETIME.get(),
10429 RENEWED_VALID_LIFETIME.get(),
10430 CONFIGURED_DELEGATED_PREFIXES[i],
10431 &[],
10432 ))]
10433 }
10434 })
10435 .collect::<Vec<_>>();
10436 let options =
10437 [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0])]
10438 .into_iter()
10439 .chain((0..NUM_IAS).map(|id| {
10440 v6::DhcpOption::Iana(v6::IanaSerializer::new(
10441 v6::IAID::new(id),
10442 RENEWED_T1.get(),
10443 RENEWED_T2.get(),
10444 &iaaddr_opts[id as usize],
10445 ))
10446 }))
10447 .chain((0..NUM_IAS).map(|id| {
10448 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
10449 v6::IAID::new(id),
10450 RENEWED_T1.get(),
10451 RENEWED_T2.get(),
10452 &iaprefix_opts[id as usize],
10453 ))
10454 }))
10455 .collect::<Vec<_>>();
10456
10457 let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
10458 let mut buf = vec![0; builder.bytes_len()];
10459 builder.serialize(&mut buf);
10460 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10462 let actions = client.handle_message_receive(msg, time);
10463 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
10464 &client;
10465 {
10467 let Requesting {
10468 client_id,
10469 non_temporary_addresses,
10470 delegated_prefixes,
10471 server_id,
10472 collected_advertise: _,
10473 first_request_time: _,
10474 retrans_timeout: _,
10475 transmission_count: _,
10476 solicit_max_rt,
10477 } = assert_matches!(
10478 &state,
10479 Some(ClientState::Requesting(requesting)) => requesting
10480 );
10481 assert_eq!(client_id.as_slice(), &CLIENT_ID);
10482 fn expected_values<V: IaValueTestExt>(
10483 no_binding: bool,
10484 time: Instant,
10485 ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
10486 (0..NUM_IAS)
10487 .map(|i| {
10488 (v6::IAID::new(i), {
10489 let i = usize::try_from(i).unwrap();
10490 if i == NO_BINDING_IA_IDX && no_binding {
10491 IaEntry::ToRequest(HashSet::from([V::CONFIGURED[i]]))
10492 } else {
10493 IaEntry::new_assigned(
10494 V::CONFIGURED[i],
10495 RENEWED_PREFERRED_LIFETIME,
10496 RENEWED_VALID_LIFETIME,
10497 time,
10498 )
10499 }
10500 })
10501 })
10502 .collect()
10503 }
10504 assert_eq!(
10505 *non_temporary_addresses,
10506 expected_values::<Ipv6Addr>(ia_na_no_binding, time)
10507 );
10508 assert_eq!(
10509 *delegated_prefixes,
10510 expected_values::<Subnet<Ipv6Addr>>(ia_pd_no_binding, time)
10511 );
10512 assert_eq!(*server_id, SERVER_ID[0]);
10513 assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
10514 }
10515 let buf = assert_matches!(
10516 &actions[..],
10517 [
10518 Action::CancelTimer(ClientTimerType::Retransmission),
10521 Action::SendMessage(buf),
10522 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
10523 ] => {
10524 assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
10525 buf
10526 }
10527 );
10528 testutil::assert_outgoing_stateful_message(
10531 &buf,
10532 v6::MessageType::Request,
10533 &CLIENT_ID,
10534 Some(&SERVER_ID[0]),
10535 &[],
10536 &(0..NUM_IAS)
10537 .map(v6::IAID::new)
10538 .zip(CONFIGURED_NON_TEMPORARY_ADDRESSES.into_iter().map(|a| HashSet::from([a])))
10539 .collect(),
10540 &(0..NUM_IAS)
10541 .map(v6::IAID::new)
10542 .zip(CONFIGURED_DELEGATED_PREFIXES.into_iter().map(|p| HashSet::from([p])))
10543 .collect(),
10544 );
10545
10546 handle_all_leases_invalidated(
10549 client,
10550 &CLIENT_ID,
10551 non_temporary_addresses_to_assign,
10552 delegated_prefixes_to_assign,
10553 ia_na_no_binding.then_some(NO_BINDING_IA_IDX),
10554 ia_pd_no_binding.then_some(NO_BINDING_IA_IDX),
10555 &[],
10556 )
10557 }
10558
10559 struct ReceiveReplyCalculateT1T2 {
10560 ia_na_success_t1: v6::NonZeroOrMaxU32,
10561 ia_na_success_t2: v6::NonZeroOrMaxU32,
10562 ia_pd_success_t1: v6::NonZeroOrMaxU32,
10563 ia_pd_success_t2: v6::NonZeroOrMaxU32,
10564 }
10565
10566 const TINY_NON_ZERO_OR_MAX_U32: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(10).unwrap();
10567 const SMALL_NON_ZERO_OR_MAX_U32: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(100).unwrap();
10568 const MEDIUM_NON_ZERO_OR_MAX_U32: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(1000).unwrap();
10569 const LARGE_NON_ZERO_OR_MAX_U32: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(10000).unwrap();
10570
10571 #[test_case(
10572 RENEW_TEST,
10573 ReceiveReplyCalculateT1T2 {
10574 ia_na_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10575 ia_na_success_t2: TINY_NON_ZERO_OR_MAX_U32,
10576 ia_pd_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10577 ia_pd_success_t2: TINY_NON_ZERO_OR_MAX_U32,
10578 }; "renew lifetimes matching erroneous IAs")]
10579 #[test_case(
10580 RENEW_TEST,
10581 ReceiveReplyCalculateT1T2 {
10582 ia_na_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10583 ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10584 ia_pd_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10585 ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10586 }; "renew same lifetimes")]
10587 #[test_case(
10588 RENEW_TEST,
10589 ReceiveReplyCalculateT1T2 {
10590 ia_na_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10591 ia_na_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10592 ia_pd_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10593 ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10594 }; "renew IA_NA smaller lifetimes")]
10595 #[test_case(
10596 RENEW_TEST,
10597 ReceiveReplyCalculateT1T2 {
10598 ia_na_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10599 ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10600 ia_pd_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10601 ia_pd_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10602 }; "renew IA_PD smaller lifetimes")]
10603 #[test_case(
10604 RENEW_TEST,
10605 ReceiveReplyCalculateT1T2 {
10606 ia_na_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10607 ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10608 ia_pd_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10609 ia_pd_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10610 }; "renew IA_NA smaller T1 but IA_PD smaller t2")]
10611 #[test_case(
10612 RENEW_TEST,
10613 ReceiveReplyCalculateT1T2 {
10614 ia_na_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10615 ia_na_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10616 ia_pd_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10617 ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10618 }; "renew IA_PD smaller T1 but IA_NA smaller t2")]
10619 #[test_case(
10620 REBIND_TEST,
10621 ReceiveReplyCalculateT1T2 {
10622 ia_na_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10623 ia_na_success_t2: TINY_NON_ZERO_OR_MAX_U32,
10624 ia_pd_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10625 ia_pd_success_t2: TINY_NON_ZERO_OR_MAX_U32,
10626 }; "rebind lifetimes matching erroneous IAs")]
10627 #[test_case(
10628 REBIND_TEST,
10629 ReceiveReplyCalculateT1T2 {
10630 ia_na_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10631 ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10632 ia_pd_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10633 ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10634 }; "rebind same lifetimes")]
10635 #[test_case(
10636 REBIND_TEST,
10637 ReceiveReplyCalculateT1T2 {
10638 ia_na_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10639 ia_na_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10640 ia_pd_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10641 ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10642 }; "rebind IA_NA smaller lifetimes")]
10643 #[test_case(
10644 REBIND_TEST,
10645 ReceiveReplyCalculateT1T2 {
10646 ia_na_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10647 ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10648 ia_pd_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10649 ia_pd_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10650 }; "rebind IA_PD smaller lifetimes")]
10651 #[test_case(
10652 REBIND_TEST,
10653 ReceiveReplyCalculateT1T2 {
10654 ia_na_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10655 ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10656 ia_pd_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10657 ia_pd_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10658 }; "rebind IA_NA smaller T1 but IA_PD smaller t2")]
10659 #[test_case(
10660 REBIND_TEST,
10661 ReceiveReplyCalculateT1T2 {
10662 ia_na_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10663 ia_na_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10664 ia_pd_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10665 ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10666 }; "rebind IA_PD smaller T1 but IA_NA smaller t2")]
10667 fn receive_reply_calculate_t1_t2(
10669 RenewRebindTest {
10670 send_and_assert,
10671 message_type: _,
10672 expect_server_id: _,
10673 with_state: _,
10674 allow_response_from_any_server: _,
10675 }: RenewRebindTest,
10676 ReceiveReplyCalculateT1T2 {
10677 ia_na_success_t1,
10678 ia_na_success_t2,
10679 ia_pd_success_t1,
10680 ia_pd_success_t2,
10681 }: ReceiveReplyCalculateT1T2,
10682 ) {
10683 let time = Instant::now();
10684 let mut client = send_and_assert(
10685 &(CLIENT_ID.into()),
10686 SERVER_ID[0],
10687 CONFIGURED_NON_TEMPORARY_ADDRESSES.into_iter().map(TestIaNa::new_default).collect(),
10688 CONFIGURED_DELEGATED_PREFIXES.into_iter().map(TestIaPd::new_default).collect(),
10689 None,
10690 T1,
10691 T2,
10692 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
10693 StepRng::new(u64::MAX / 2, 0),
10694 time,
10695 );
10696 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
10697 &client;
10698 let ia_addr = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
10699 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
10700 RENEWED_PREFERRED_LIFETIME.get(),
10701 RENEWED_VALID_LIFETIME.get(),
10702 &[],
10703 ))];
10704 let ia_no_addrs_avail = [v6::DhcpOption::StatusCode(
10705 v6::ErrorStatusCode::NoAddrsAvail.into(),
10706 "No address available.",
10707 )];
10708 let ia_prefix = [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
10709 RENEWED_PREFERRED_LIFETIME.get(),
10710 RENEWED_VALID_LIFETIME.get(),
10711 CONFIGURED_DELEGATED_PREFIXES[0],
10712 &[],
10713 ))];
10714 let ia_no_prefixes_avail = [v6::DhcpOption::StatusCode(
10715 v6::ErrorStatusCode::NoPrefixAvail.into(),
10716 "No prefixes available.",
10717 )];
10718 let ok_iaid = v6::IAID::new(0);
10719 let no_value_avail_iaid = v6::IAID::new(1);
10720 let empty_values_iaid = v6::IAID::new(2);
10721 let options = vec![
10722 v6::DhcpOption::ClientId(&CLIENT_ID),
10723 v6::DhcpOption::ServerId(&SERVER_ID[0]),
10724 v6::DhcpOption::Iana(v6::IanaSerializer::new(
10725 ok_iaid,
10726 ia_na_success_t1.get(),
10727 ia_na_success_t2.get(),
10728 &ia_addr,
10729 )),
10730 v6::DhcpOption::Iana(v6::IanaSerializer::new(
10731 no_value_avail_iaid,
10732 TINY_NON_ZERO_OR_MAX_U32.get(),
10736 TINY_NON_ZERO_OR_MAX_U32.get(),
10737 &ia_no_addrs_avail,
10738 )),
10739 v6::DhcpOption::Iana(v6::IanaSerializer::new(
10740 empty_values_iaid,
10741 TINY_NON_ZERO_OR_MAX_U32.get(),
10745 TINY_NON_ZERO_OR_MAX_U32.get(),
10746 &[],
10747 )),
10748 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
10749 ok_iaid,
10750 ia_pd_success_t1.get(),
10751 ia_pd_success_t2.get(),
10752 &ia_prefix,
10753 )),
10754 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
10755 no_value_avail_iaid,
10756 TINY_NON_ZERO_OR_MAX_U32.get(),
10760 TINY_NON_ZERO_OR_MAX_U32.get(),
10761 &ia_no_prefixes_avail,
10762 )),
10763 v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
10764 empty_values_iaid,
10765 TINY_NON_ZERO_OR_MAX_U32.get(),
10769 TINY_NON_ZERO_OR_MAX_U32.get(),
10770 &[],
10771 )),
10772 ];
10773
10774 let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
10775 let mut buf = vec![0; builder.bytes_len()];
10776 builder.serialize(&mut buf);
10777 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10779
10780 fn get_updates<V: IaValue>(
10781 ok_iaid: v6::IAID,
10782 ok_value: V,
10783 no_value_avail_iaid: v6::IAID,
10784 no_value_avail_value: V,
10785 ) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
10786 HashMap::from([
10787 (
10788 ok_iaid,
10789 HashMap::from([(
10790 ok_value,
10791 IaValueUpdateKind::UpdatedLifetimes(Lifetimes::new_renewed()),
10792 )]),
10793 ),
10794 (
10795 no_value_avail_iaid,
10796 HashMap::from([(no_value_avail_value, IaValueUpdateKind::Removed)]),
10797 ),
10798 ])
10799 }
10800 let expected_t1 = std::cmp::min(ia_na_success_t1, ia_pd_success_t1);
10801 let expected_t2 = std::cmp::min(ia_na_success_t2, ia_pd_success_t2);
10802 assert_eq!(
10803 client.handle_message_receive(msg, time),
10804 [
10805 Action::CancelTimer(ClientTimerType::Retransmission),
10806 if expected_t1 == expected_t2 {
10807 Action::CancelTimer(ClientTimerType::Renew)
10809 } else {
10810 Action::ScheduleTimer(
10811 ClientTimerType::Renew,
10812 time.add(Duration::from_secs(expected_t1.get().into())),
10813 )
10814 },
10815 Action::ScheduleTimer(
10816 ClientTimerType::Rebind,
10817 time.add(Duration::from_secs(expected_t2.get().into())),
10818 ),
10819 Action::IaNaUpdates(get_updates(
10820 ok_iaid,
10821 CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
10822 no_value_avail_iaid,
10823 CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
10824 )),
10825 Action::IaPdUpdates(get_updates(
10826 ok_iaid,
10827 CONFIGURED_DELEGATED_PREFIXES[0],
10828 no_value_avail_iaid,
10829 CONFIGURED_DELEGATED_PREFIXES[1],
10830 )),
10831 Action::ScheduleTimer(
10832 ClientTimerType::RestartServerDiscovery,
10833 time.add(Duration::from_secs(
10834 std::cmp::max(VALID_LIFETIME, RENEWED_VALID_LIFETIME,).get().into()
10835 )),
10836 ),
10837 ],
10838 );
10839 }
10840
10841 #[test]
10842 fn unexpected_messages_are_ignored() {
10843 let (mut client, _) = ClientStateMachine::start_stateless(
10844 Vec::new(),
10845 StepRng::new(u64::MAX / 2, 0),
10846 Instant::now(),
10847 );
10848
10849 let builder = v6::MessageBuilder::new(
10850 v6::MessageType::Reply,
10851 [4, 5, 6],
10853 &[],
10854 );
10855 let mut buf = vec![0; builder.bytes_len()];
10856 builder.serialize(&mut buf);
10857 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10859
10860 assert!(client.handle_message_receive(msg, Instant::now()).is_empty());
10861
10862 for msg_type in [
10864 v6::MessageType::Solicit,
10865 v6::MessageType::Advertise,
10866 v6::MessageType::Request,
10867 v6::MessageType::Confirm,
10868 v6::MessageType::Renew,
10869 v6::MessageType::Rebind,
10870 v6::MessageType::Release,
10871 v6::MessageType::Decline,
10872 v6::MessageType::Reconfigure,
10873 v6::MessageType::InformationRequest,
10874 v6::MessageType::RelayForw,
10875 v6::MessageType::RelayRepl,
10876 ] {
10877 let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
10878 &client;
10879 let builder = v6::MessageBuilder::new(msg_type, *transaction_id, &[]);
10880 let mut buf = vec![0; builder.bytes_len()];
10881 builder.serialize(&mut buf);
10882 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10884
10885 assert!(client.handle_message_receive(msg, Instant::now()).is_empty());
10886 }
10887 }
10888
10889 #[test]
10890 #[should_panic(expected = "received unexpected refresh timeout")]
10891 fn information_requesting_refresh_timeout_is_unreachable() {
10892 let (mut client, _) = ClientStateMachine::start_stateless(
10893 Vec::new(),
10894 StepRng::new(u64::MAX / 2, 0),
10895 Instant::now(),
10896 );
10897
10898 let _actions = client.handle_timeout(ClientTimerType::Refresh, Instant::now());
10901 }
10902
10903 #[test]
10904 #[should_panic(expected = "received unexpected retransmission timeout")]
10905 fn information_received_retransmission_timeout_is_unreachable() {
10906 let (mut client, _) = ClientStateMachine::start_stateless(
10907 Vec::new(),
10908 StepRng::new(u64::MAX / 2, 0),
10909 Instant::now(),
10910 );
10911 let ClientStateMachine { transaction_id, options_to_request: _, state, rng: _ } = &client;
10912 assert_matches!(
10913 *state,
10914 Some(ClientState::InformationRequesting(InformationRequesting {
10915 retrans_timeout: INITIAL_INFO_REQ_TIMEOUT,
10916 _marker,
10917 }))
10918 );
10919
10920 let options = [v6::DhcpOption::ServerId(&SERVER_ID[0])];
10921 let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
10922 let mut buf = vec![0; builder.bytes_len()];
10923 builder.serialize(&mut buf);
10924 let mut buf = &buf[..]; let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10926 let time = Instant::now();
10928 let actions = client.handle_message_receive(msg, time);
10929 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
10930 &client;
10931 assert_matches!(
10932 state,
10933 Some(ClientState::InformationReceived(InformationReceived { dns_servers, _marker }))
10934 if dns_servers.is_empty()
10935 );
10936 assert_eq!(
10937 actions[..],
10938 [
10939 Action::CancelTimer(ClientTimerType::Retransmission),
10940 Action::ScheduleTimer(ClientTimerType::Refresh, time.add(IRT_DEFAULT)),
10941 ]
10942 );
10943
10944 let _actions = client.handle_timeout(ClientTimerType::Retransmission, time);
10947 }
10948
10949 #[test]
10950 #[should_panic(expected = "received unexpected refresh timeout")]
10951 fn server_discovery_refresh_timeout_is_unreachable() {
10952 let time = Instant::now();
10953 let mut client = testutil::start_and_assert_server_discovery(
10954 &(CLIENT_ID.into()),
10955 testutil::to_configured_addresses(
10956 1,
10957 std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
10958 ),
10959 Default::default(),
10960 Vec::new(),
10961 StepRng::new(u64::MAX / 2, 0),
10962 time,
10963 );
10964
10965 let _actions = client.handle_timeout(ClientTimerType::Refresh, time);
10967 }
10968
10969 #[test]
10970 #[should_panic(expected = "received unexpected refresh timeout")]
10971 fn requesting_refresh_timeout_is_unreachable() {
10972 let time = Instant::now();
10973 let (mut client, _transaction_id) = testutil::request_and_assert(
10974 &(CLIENT_ID.into()),
10975 SERVER_ID[0],
10976 vec![TestIaNa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[0])],
10977 Default::default(),
10978 &[],
10979 StepRng::new(u64::MAX / 2, 0),
10980 time,
10981 );
10982
10983 let _actions = client.handle_timeout(ClientTimerType::Refresh, time);
10985 }
10986
10987 #[test_case(ClientTimerType::Refresh)]
10988 #[test_case(ClientTimerType::Retransmission)]
10989 #[should_panic(expected = "received unexpected")]
10990 fn address_assiged_unexpected_timeout_is_unreachable(timeout: ClientTimerType) {
10991 let time = Instant::now();
10992 let (mut client, _actions) = testutil::assign_and_assert(
10993 &(CLIENT_ID.into()),
10994 SERVER_ID[0],
10995 vec![TestIaNa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[0])],
10996 Default::default(), &[],
10998 StepRng::new(u64::MAX / 2, 0),
10999 time,
11000 );
11001
11002 let _actions = client.handle_timeout(timeout, time);
11005 }
11006
11007 #[test_case(RENEW_TEST)]
11008 #[test_case(REBIND_TEST)]
11009 #[should_panic(expected = "received unexpected refresh timeout")]
11010 fn refresh_timeout_is_unreachable(
11011 RenewRebindTest {
11012 send_and_assert,
11013 message_type: _,
11014 expect_server_id: _,
11015 with_state: _,
11016 allow_response_from_any_server: _,
11017 }: RenewRebindTest,
11018 ) {
11019 let time = Instant::now();
11020 let mut client = send_and_assert(
11021 &(CLIENT_ID.into()),
11022 SERVER_ID[0],
11023 vec![TestIaNa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[0])],
11024 Default::default(), None,
11026 T1,
11027 T2,
11028 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
11029 StepRng::new(u64::MAX / 2, 0),
11030 time,
11031 );
11032
11033 let _actions = client.handle_timeout(ClientTimerType::Refresh, time);
11035 }
11036
11037 fn handle_all_leases_invalidated<R: Rng>(
11038 mut client: ClientStateMachine<Instant, R>,
11039 client_id: &[u8],
11040 non_temporary_addresses_to_assign: Vec<TestIaNa>,
11041 delegated_prefixes_to_assign: Vec<TestIaPd>,
11042 skip_removed_event_for_test_iana_idx: Option<usize>,
11043 skip_removed_event_for_test_iapd_idx: Option<usize>,
11044 options_to_request: &[v6::OptionCode],
11045 ) {
11046 let time = Instant::now();
11047 let actions = client.handle_timeout(ClientTimerType::RestartServerDiscovery, time);
11048 let buf = assert_matches!(
11049 &actions[..],
11050 [
11051 Action::CancelTimer(ClientTimerType::Retransmission),
11052 Action::CancelTimer(ClientTimerType::Refresh),
11053 Action::CancelTimer(ClientTimerType::Renew),
11054 Action::CancelTimer(ClientTimerType::Rebind),
11055 Action::CancelTimer(ClientTimerType::RestartServerDiscovery),
11056 Action::IaNaUpdates(ia_na_updates),
11057 Action::IaPdUpdates(ia_pd_updates),
11058 Action::SendMessage(buf),
11059 Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
11060 ] => {
11061 fn get_updates<V: IaValue>(
11062 to_assign: &Vec<TestIa<V>>,
11063 skip_idx: Option<usize>,
11064 ) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
11065 (0..).zip(to_assign.iter())
11066 .filter_map(|(iaid, TestIa { values, t1: _, t2: _})| {
11067 skip_idx
11068 .map_or(true, |skip_idx| skip_idx != iaid)
11069 .then(|| (
11070 v6::IAID::new(iaid.try_into().unwrap()),
11071 values.keys().copied().map(|value| (
11072 value,
11073 IaValueUpdateKind::Removed,
11074 )).collect(),
11075 ))
11076 })
11077 .collect()
11078 }
11079 assert_eq!(
11080 ia_na_updates,
11081 &get_updates(
11082 &non_temporary_addresses_to_assign,
11083 skip_removed_event_for_test_iana_idx
11084 ),
11085 );
11086 assert_eq!(
11087 ia_pd_updates,
11088 &get_updates(
11089 &delegated_prefixes_to_assign,
11090 skip_removed_event_for_test_iapd_idx,
11091 ),
11092 );
11093 assert_eq!(*instant, time.add(INITIAL_SOLICIT_TIMEOUT));
11094 buf
11095 }
11096 );
11097
11098 let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
11099 &client;
11100 testutil::assert_server_discovery(
11101 state,
11102 client_id,
11103 testutil::to_configured_addresses(
11104 non_temporary_addresses_to_assign.len(),
11105 non_temporary_addresses_to_assign
11106 .iter()
11107 .map(|TestIaNa { values, t1: _, t2: _ }| values.keys().cloned().collect()),
11108 ),
11109 testutil::to_configured_prefixes(
11110 delegated_prefixes_to_assign.len(),
11111 delegated_prefixes_to_assign
11112 .iter()
11113 .map(|TestIaPd { values, t1: _, t2: _ }| values.keys().cloned().collect()),
11114 ),
11115 time,
11116 buf,
11117 options_to_request,
11118 )
11119 }
11120
11121 #[test]
11122 fn assigned_handle_all_leases_invalidated() {
11123 let non_temporary_addresses_to_assign = CONFIGURED_NON_TEMPORARY_ADDRESSES
11124 .iter()
11125 .copied()
11126 .map(TestIaNa::new_default)
11127 .collect::<Vec<_>>();
11128 let delegated_prefixes_to_assign = CONFIGURED_DELEGATED_PREFIXES
11129 .iter()
11130 .copied()
11131 .map(TestIaPd::new_default)
11132 .collect::<Vec<_>>();
11133 let (client, _actions) = testutil::assign_and_assert(
11134 &(CLIENT_ID.into()),
11135 SERVER_ID[0],
11136 non_temporary_addresses_to_assign.clone(),
11137 delegated_prefixes_to_assign.clone(),
11138 &[],
11139 StepRng::new(u64::MAX / 2, 0),
11140 Instant::now(),
11141 );
11142
11143 handle_all_leases_invalidated(
11144 client,
11145 &CLIENT_ID,
11146 non_temporary_addresses_to_assign,
11147 delegated_prefixes_to_assign,
11148 None,
11149 None,
11150 &[],
11151 )
11152 }
11153
11154 #[test_case(RENEW_TEST)]
11155 #[test_case(REBIND_TEST)]
11156 fn renew_rebind_handle_all_leases_invalidated(
11157 RenewRebindTest {
11158 send_and_assert,
11159 message_type: _,
11160 expect_server_id: _,
11161 with_state: _,
11162 allow_response_from_any_server: _,
11163 }: RenewRebindTest,
11164 ) {
11165 let non_temporary_addresses_to_assign = CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]
11166 .iter()
11167 .map(|&addr| TestIaNa::new_default(addr))
11168 .collect::<Vec<_>>();
11169 let delegated_prefixes_to_assign = CONFIGURED_DELEGATED_PREFIXES[0..2]
11170 .iter()
11171 .map(|&addr| TestIaPd::new_default(addr))
11172 .collect::<Vec<_>>();
11173 let client = send_and_assert(
11174 &(CLIENT_ID.into()),
11175 SERVER_ID[0],
11176 non_temporary_addresses_to_assign.clone(),
11177 delegated_prefixes_to_assign.clone(),
11178 None,
11179 T1,
11180 T2,
11181 v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
11182 StepRng::new(u64::MAX / 2, 0),
11183 Instant::now(),
11184 );
11185
11186 handle_all_leases_invalidated(
11187 client,
11188 &CLIENT_ID,
11189 non_temporary_addresses_to_assign,
11190 delegated_prefixes_to_assign,
11191 None,
11192 None,
11193 &[],
11194 )
11195 }
11196
11197 #[test]
11200 fn retransmission_timeout() {
11201 let mut rng = StepRng::new(u64::MAX / 2, 0);
11202
11203 let initial_rt = Duration::from_secs(1);
11204 let max_rt = Duration::from_secs(100);
11205
11206 let t =
11208 super::retransmission_timeout(Duration::from_nanos(0), initial_rt, max_rt, &mut rng);
11209 assert_eq!(t.as_millis(), initial_rt.as_millis());
11210
11211 let t =
11213 super::retransmission_timeout(Duration::from_secs(10), initial_rt, max_rt, &mut rng);
11214 assert_eq!(t, Duration::from_secs(20));
11215
11216 let t = super::retransmission_timeout(100 * max_rt, initial_rt, max_rt, &mut rng);
11218 assert_eq!(t.as_millis(), max_rt.as_millis());
11219 let t = super::retransmission_timeout(MAX_DURATION, initial_rt, max_rt, &mut rng);
11220 assert_eq!(t.as_millis(), max_rt.as_millis());
11221 let t = super::retransmission_timeout(
11223 100 * max_rt,
11224 initial_rt,
11225 Duration::from_nanos(0),
11226 &mut rng,
11227 );
11228 assert_eq!(t.as_millis(), (200 * max_rt).as_millis());
11229 let t = super::retransmission_timeout(
11231 MAX_DURATION,
11232 initial_rt,
11233 Duration::from_nanos(0),
11234 &mut rng,
11235 );
11236 assert_eq!(t.as_millis(), MAX_DURATION.as_millis());
11237
11238 let mut rng = StepRng::new(0, u64::MAX / 5);
11240 [
11241 (Duration::from_millis(10000), 19000),
11242 (Duration::from_millis(10000), 19400),
11243 (Duration::from_millis(10000), 19800),
11244 (Duration::from_millis(10000), 20200),
11245 (Duration::from_millis(10000), 20600),
11246 (Duration::from_millis(10000), 21000),
11247 (Duration::from_millis(10000), 19400),
11248 (100 * max_rt, 98000),
11250 (100 * max_rt, 102000),
11251 (100 * max_rt, 106000),
11252 (100 * max_rt, 110000),
11253 (100 * max_rt, 94000),
11254 (100 * max_rt, 98000),
11255 ]
11256 .iter()
11257 .for_each(|(rt, want_ms)| {
11258 let t = super::retransmission_timeout(*rt, initial_rt, max_rt, &mut rng);
11259 assert_eq!(t.as_millis(), *want_ms);
11260 });
11261 }
11262
11263 #[test_case(v6::TimeValue::Zero, v6::TimeValue::Zero, v6::TimeValue::Zero)]
11264 #[test_case(
11265 v6::TimeValue::Zero,
11266 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11267 v6::NonZeroOrMaxU32::new(120)
11268 .expect("should succeed for non-zero or u32::MAX values")
11269 )),
11270 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11271 v6::NonZeroOrMaxU32::new(120)
11272 .expect("should succeed for non-zero or u32::MAX values")
11273 ))
11274 )]
11275 #[test_case(
11276 v6::TimeValue::Zero,
11277 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11278 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity)
11279 )]
11280 #[test_case(
11281 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11282 v6::NonZeroOrMaxU32::new(120)
11283 .expect("should succeed for non-zero or u32::MAX values")
11284 )),
11285 v6::TimeValue::Zero,
11286 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11287 v6::NonZeroOrMaxU32::new(120)
11288 .expect("should succeed for non-zero or u32::MAX values")
11289 ))
11290 )]
11291 #[test_case(
11292 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11293 v6::NonZeroOrMaxU32::new(120)
11294 .expect("should succeed for non-zero or u32::MAX values")
11295 )),
11296 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11297 v6::NonZeroOrMaxU32::new(60)
11298 .expect("should succeed for non-zero or u32::MAX values")
11299 )),
11300 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11301 v6::NonZeroOrMaxU32::new(60)
11302 .expect("should succeed for non-zero or u32::MAX values")
11303 ))
11304 )]
11305 #[test_case(
11306 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11307 v6::NonZeroOrMaxU32::new(120)
11308 .expect("should succeed for non-zero or u32::MAX values")
11309 )),
11310 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11311 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11312 v6::NonZeroOrMaxU32::new(120)
11313 .expect("should succeed for non-zero or u32::MAX values")
11314 ))
11315 )]
11316 #[test_case(
11317 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11318 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11319 v6::NonZeroOrMaxU32::new(120)
11320 .expect("should succeed for non-zero or u32::MAX values")
11321 )),
11322 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11323 v6::NonZeroOrMaxU32::new(120)
11324 .expect("should succeed for non-zero or u32::MAX values")
11325 ))
11326 )]
11327 #[test_case(
11328 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11329 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11330 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity)
11331 )]
11332 fn maybe_get_nonzero_min(
11333 old_value: v6::TimeValue,
11334 new_value: v6::TimeValue,
11335 expected_value: v6::TimeValue,
11336 ) {
11337 assert_eq!(super::maybe_get_nonzero_min(old_value, new_value), expected_value);
11338 }
11339
11340 #[test_case(
11341 v6::NonZeroTimeValue::Finite(
11342 v6::NonZeroOrMaxU32::new(120)
11343 .expect("should succeed for non-zero or u32::MAX values")
11344 ),
11345 v6::TimeValue::Zero,
11346 v6::NonZeroTimeValue::Finite(
11347 v6::NonZeroOrMaxU32::new(120)
11348 .expect("should succeed for non-zero or u32::MAX values")
11349 )
11350 )]
11351 #[test_case(
11352 v6::NonZeroTimeValue::Finite(
11353 v6::NonZeroOrMaxU32::new(120)
11354 .expect("should succeed for non-zero or u32::MAX values")
11355 ),
11356 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11357 v6::NonZeroOrMaxU32::new(60)
11358 .expect("should succeed for non-zero or u32::MAX values")
11359 )),
11360 v6::NonZeroTimeValue::Finite(
11361 v6::NonZeroOrMaxU32::new(60)
11362 .expect("should succeed for non-zero or u32::MAX values")
11363 )
11364 )]
11365 #[test_case(
11366 v6::NonZeroTimeValue::Finite(
11367 v6::NonZeroOrMaxU32::new(120)
11368 .expect("should succeed for non-zero or u32::MAX values")
11369 ),
11370 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11371 v6::NonZeroTimeValue::Finite(
11372 v6::NonZeroOrMaxU32::new(120)
11373 .expect("should succeed for non-zero or u32::MAX values")
11374 )
11375 )]
11376 #[test_case(
11377 v6::NonZeroTimeValue::Infinity,
11378 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11379 v6::NonZeroOrMaxU32::new(120)
11380 .expect("should succeed for non-zero or u32::MAX values"))
11381 ),
11382 v6::NonZeroTimeValue::Finite(
11383 v6::NonZeroOrMaxU32::new(120)
11384 .expect("should succeed for non-zero or u32::MAX values")
11385 )
11386 )]
11387 #[test_case(
11388 v6::NonZeroTimeValue::Infinity,
11389 v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11390 v6::NonZeroTimeValue::Infinity
11391 )]
11392 #[test_case(
11393 v6::NonZeroTimeValue::Infinity,
11394 v6::TimeValue::Zero,
11395 v6::NonZeroTimeValue::Infinity
11396 )]
11397 fn get_nonzero_min(
11398 old_value: v6::NonZeroTimeValue,
11399 new_value: v6::TimeValue,
11400 expected_value: v6::NonZeroTimeValue,
11401 ) {
11402 assert_eq!(super::get_nonzero_min(old_value, new_value), expected_value);
11403 }
11404
11405 #[test_case(
11406 v6::NonZeroTimeValue::Infinity,
11407 T1_MIN_LIFETIME_RATIO,
11408 v6::NonZeroTimeValue::Infinity
11409 )]
11410 #[test_case(
11411 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(100).expect("should succeed")),
11412 T1_MIN_LIFETIME_RATIO,
11413 v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(50).expect("should succeed"))
11414 )]
11415 #[test_case(v6::NonZeroTimeValue::Infinity, T2_T1_RATIO, v6::NonZeroTimeValue::Infinity)]
11416 #[test_case(
11417 v6::NonZeroTimeValue::Finite(
11418 v6::NonZeroOrMaxU32::new(INFINITY - 1)
11419 .expect("should succeed")
11420 ),
11421 T2_T1_RATIO,
11422 v6::NonZeroTimeValue::Infinity
11423 )]
11424 fn compute_t(min: v6::NonZeroTimeValue, ratio: Ratio<u32>, expected_t: v6::NonZeroTimeValue) {
11425 assert_eq!(super::compute_t(min, ratio), expected_t);
11426 }
11427}