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