Skip to main content

dhcpv6_core/
client.rs

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