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