Skip to main content

dhcpv6_core/
client.rs

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