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