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