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