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