dhcpv6_core/
client.rs

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