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::RngCore;
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    #[derive(Debug)]
6184    struct StepRng {
6185        state: u64,
6186        increment: u64,
6187    }
6188
6189    impl StepRng {
6190        pub fn new(initial: u64, increment: u64) -> Self {
6191            Self { state: initial, increment: increment }
6192        }
6193    }
6194
6195    impl RngCore for StepRng {
6196        fn next_u32(&mut self) -> u32 {
6197            self.next_u64() as u32
6198        }
6199
6200        fn next_u64(&mut self) -> u64 {
6201            let r = self.state;
6202            self.state = self.state.wrapping_add(self.increment);
6203            r
6204        }
6205
6206        fn fill_bytes(&mut self, _dst: &mut [u8]) {
6207            unimplemented!();
6208        }
6209    }
6210
6211    #[test]
6212    fn send_information_request_and_receive_reply() {
6213        // Try to start information request with different list of requested options.
6214        for options in [
6215            Vec::new(),
6216            vec![v6::OptionCode::DnsServers],
6217            vec![v6::OptionCode::DnsServers, v6::OptionCode::DomainList],
6218        ] {
6219            let now = Instant::now();
6220            let (mut client, actions) = ClientStateMachine::start_stateless(
6221                [0, 1, 2],
6222                options.clone(),
6223                StepRng::new(u64::MAX / 2, 0),
6224                now,
6225            );
6226
6227            let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
6228                &client;
6229            assert_matches!(
6230                *state,
6231                Some(ClientState::InformationRequesting(InformationRequesting {
6232                    retrans_timeout: INITIAL_INFO_REQ_TIMEOUT,
6233                    _marker,
6234                }))
6235            );
6236
6237            // Start of information requesting should send an information request and schedule a
6238            // retransmission timer.
6239            let want_options_array = [v6::DhcpOption::Oro(&options)];
6240            let want_options = if options.is_empty() { &[][..] } else { &want_options_array[..] };
6241            let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
6242                &client;
6243            let builder = v6::MessageBuilder::new(
6244                v6::MessageType::InformationRequest,
6245                *transaction_id,
6246                want_options,
6247            );
6248            let mut want_buf = vec![0; builder.bytes_len()];
6249            builder.serialize(&mut want_buf);
6250            assert_eq!(
6251                actions[..],
6252                [
6253                    Action::SendMessage(want_buf),
6254                    Action::ScheduleTimer(
6255                        ClientTimerType::Retransmission,
6256                        now.add(INITIAL_INFO_REQ_TIMEOUT),
6257                    )
6258                ]
6259            );
6260
6261            let test_dhcp_refresh_time = 42u32;
6262            let options = [
6263                v6::DhcpOption::ServerId(&SERVER_ID[0]),
6264                v6::DhcpOption::InformationRefreshTime(test_dhcp_refresh_time),
6265                v6::DhcpOption::DnsServers(&DNS_SERVERS),
6266            ];
6267            let builder =
6268                v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
6269            let mut buf = vec![0; builder.bytes_len()];
6270            builder.serialize(&mut buf);
6271            let mut buf = &buf[..]; // Implements BufferView.
6272            let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6273
6274            let now = Instant::now();
6275            let actions = client.handle_message_receive(msg, now);
6276            let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
6277                client;
6278
6279            {
6280                assert_matches!(
6281                    state,
6282                    Some(ClientState::InformationReceived(InformationReceived { dns_servers, _marker }))
6283                        if dns_servers == DNS_SERVERS.to_vec()
6284                );
6285            }
6286            // Upon receiving a valid reply, client should set up for refresh based on the reply.
6287            assert_eq!(
6288                actions[..],
6289                [
6290                    Action::CancelTimer(ClientTimerType::Retransmission),
6291                    Action::ScheduleTimer(
6292                        ClientTimerType::Refresh,
6293                        now.add(Duration::from_secs(u64::from(test_dhcp_refresh_time))),
6294                    ),
6295                    Action::UpdateDnsServers(DNS_SERVERS.to_vec()),
6296                ]
6297            );
6298        }
6299    }
6300
6301    #[test]
6302    fn send_information_request_on_retransmission_timeout() {
6303        let now = Instant::now();
6304        let (mut client, actions) = ClientStateMachine::start_stateless(
6305            [0, 1, 2],
6306            Vec::new(),
6307            StepRng::new(u64::MAX / 2, 0),
6308            now,
6309        );
6310        assert_matches!(
6311            actions[..],
6312            [_, Action::ScheduleTimer(ClientTimerType::Retransmission, instant)] => {
6313                assert_eq!(instant, now.add(INITIAL_INFO_REQ_TIMEOUT));
6314            }
6315        );
6316
6317        let actions = client.handle_timeout(ClientTimerType::Retransmission, now);
6318        // Following exponential backoff defined in https://tools.ietf.org/html/rfc8415#section-15.
6319        assert_matches!(
6320            actions[..],
6321            [
6322                _,
6323                Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
6324            ] => assert_eq!(instant, now.add(2 * INITIAL_INFO_REQ_TIMEOUT))
6325        );
6326    }
6327
6328    #[test]
6329    fn send_information_request_on_refresh_timeout() {
6330        let (mut client, _) = ClientStateMachine::start_stateless(
6331            [0, 1, 2],
6332            Vec::new(),
6333            StepRng::new(u64::MAX / 2, 0),
6334            Instant::now(),
6335        );
6336
6337        let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
6338            &client;
6339        let options = [v6::DhcpOption::ServerId(&SERVER_ID[0])];
6340        let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
6341        let mut buf = vec![0; builder.bytes_len()];
6342        builder.serialize(&mut buf);
6343        let mut buf = &buf[..]; // Implements BufferView.
6344        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6345
6346        // Transition to InformationReceived state.
6347        let time = Instant::now();
6348        assert_eq!(
6349            client.handle_message_receive(msg, time)[..],
6350            [
6351                Action::CancelTimer(ClientTimerType::Retransmission),
6352                Action::ScheduleTimer(ClientTimerType::Refresh, time.add(IRT_DEFAULT))
6353            ]
6354        );
6355
6356        // Refresh should start another round of information request.
6357        let actions = client.handle_timeout(ClientTimerType::Refresh, time);
6358        let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
6359            &client;
6360        let builder =
6361            v6::MessageBuilder::new(v6::MessageType::InformationRequest, *transaction_id, &[]);
6362        let mut want_buf = vec![0; builder.bytes_len()];
6363        builder.serialize(&mut want_buf);
6364        assert_eq!(
6365            actions[..],
6366            [
6367                Action::SendMessage(want_buf),
6368                Action::ScheduleTimer(
6369                    ClientTimerType::Retransmission,
6370                    time.add(INITIAL_INFO_REQ_TIMEOUT)
6371                )
6372            ]
6373        );
6374    }
6375
6376    // Test starting the client in stateful mode with different address
6377    // and prefix configurations.
6378    #[test_case(
6379        0, std::iter::empty(),
6380        2, (&CONFIGURED_DELEGATED_PREFIXES[0..2]).iter().copied(),
6381        Vec::new()
6382    )]
6383    #[test_case(
6384        2, (&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]).iter().copied(),
6385        0, std::iter::empty(),
6386        vec![v6::OptionCode::DnsServers]
6387    )]
6388    #[test_case(
6389        1, std::iter::empty(),
6390        2, (&CONFIGURED_DELEGATED_PREFIXES[0..2]).iter().copied(),
6391        Vec::new()
6392    )]
6393    #[test_case(
6394        2, std::iter::once(CONFIGURED_NON_TEMPORARY_ADDRESSES[0]),
6395        1, std::iter::empty(),
6396        vec![v6::OptionCode::DnsServers]
6397    )]
6398    #[test_case(
6399        2, (&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]).iter().copied(),
6400        2, std::iter::once(CONFIGURED_DELEGATED_PREFIXES[0]),
6401        vec![v6::OptionCode::DnsServers]
6402    )]
6403    fn send_solicit(
6404        address_count: usize,
6405        preferred_non_temporary_addresses: impl IntoIterator<Item = Ipv6Addr>,
6406        prefix_count: usize,
6407        preferred_delegated_prefixes: impl IntoIterator<Item = Subnet<Ipv6Addr>>,
6408        options_to_request: Vec<v6::OptionCode>,
6409    ) {
6410        // The client is checked inside `start_and_assert_server_discovery`.
6411        let _client = testutil::start_and_assert_server_discovery(
6412            [0, 1, 2],
6413            &(CLIENT_ID.into()),
6414            testutil::to_configured_addresses(
6415                address_count,
6416                preferred_non_temporary_addresses.into_iter().map(|a| HashSet::from([a])),
6417            ),
6418            testutil::to_configured_prefixes(
6419                prefix_count,
6420                preferred_delegated_prefixes.into_iter().map(|a| HashSet::from([a])),
6421            ),
6422            options_to_request,
6423            StepRng::new(u64::MAX / 2, 0),
6424            Instant::now(),
6425        );
6426    }
6427
6428    #[test_case(
6429        1, std::iter::empty(), std::iter::once(CONFIGURED_NON_TEMPORARY_ADDRESSES[0]), 0;
6430        "zero"
6431    )]
6432    #[test_case(
6433        2, CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2].iter().copied(), CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2].iter().copied(), 2;
6434        "two"
6435    )]
6436    #[test_case(
6437        4,
6438        CONFIGURED_NON_TEMPORARY_ADDRESSES.iter().copied(),
6439        std::iter::once(CONFIGURED_NON_TEMPORARY_ADDRESSES[0]).chain(REPLY_NON_TEMPORARY_ADDRESSES.iter().copied()),
6440        1;
6441        "one"
6442    )]
6443    fn compute_preferred_address_count(
6444        configure_count: usize,
6445        hints: impl IntoIterator<Item = Ipv6Addr>,
6446        got_addresses: impl IntoIterator<Item = Ipv6Addr>,
6447        want: usize,
6448    ) {
6449        // No preferred addresses configured.
6450        let got_addresses: HashMap<_, _> = (0..)
6451            .map(v6::IAID::new)
6452            .zip(got_addresses.into_iter().map(|a| HashSet::from([a])))
6453            .collect();
6454        let configured_non_temporary_addresses = testutil::to_configured_addresses(
6455            configure_count,
6456            hints.into_iter().map(|a| HashSet::from([a])),
6457        );
6458        assert_eq!(
6459            super::compute_preferred_ia_count(&got_addresses, &configured_non_temporary_addresses),
6460            want,
6461        );
6462    }
6463
6464    #[test_case(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2], &CONFIGURED_DELEGATED_PREFIXES[0..2], true)]
6465    #[test_case(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..1], &CONFIGURED_DELEGATED_PREFIXES[0..1], true)]
6466    #[test_case(&REPLY_NON_TEMPORARY_ADDRESSES[0..2], &REPLY_DELEGATED_PREFIXES[0..2], true)]
6467    #[test_case(&[], &[], false)]
6468    fn advertise_message_has_ias(
6469        non_temporary_addresses: &[Ipv6Addr],
6470        delegated_prefixes: &[Subnet<Ipv6Addr>],
6471        expected: bool,
6472    ) {
6473        let configured_non_temporary_addresses = testutil::to_configured_addresses(
6474            2,
6475            std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
6476        );
6477
6478        let configured_delegated_prefixes = testutil::to_configured_prefixes(
6479            2,
6480            std::iter::once(HashSet::from([CONFIGURED_DELEGATED_PREFIXES[0]])),
6481        );
6482
6483        // Advertise is acceptable even though it does not contain the solicited
6484        // preferred address.
6485        let advertise = AdvertiseMessage::new_default(
6486            SERVER_ID[0],
6487            non_temporary_addresses,
6488            delegated_prefixes,
6489            &[],
6490            &configured_non_temporary_addresses,
6491            &configured_delegated_prefixes,
6492        );
6493        assert_eq!(advertise.has_ias(), expected);
6494    }
6495
6496    struct AdvertiseMessageOrdTestCase<'a> {
6497        adv1_non_temporary_addresses: &'a [Ipv6Addr],
6498        adv1_delegated_prefixes: &'a [Subnet<Ipv6Addr>],
6499        adv2_non_temporary_addresses: &'a [Ipv6Addr],
6500        adv2_delegated_prefixes: &'a [Subnet<Ipv6Addr>],
6501        expected: Ordering,
6502    }
6503
6504    #[test_case(AdvertiseMessageOrdTestCase{
6505        adv1_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2],
6506        adv1_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..2],
6507        adv2_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..3],
6508        adv2_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..3],
6509        expected: Ordering::Less,
6510    }; "adv1 has less IAs")]
6511    #[test_case(AdvertiseMessageOrdTestCase{
6512        adv1_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2],
6513        adv1_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..2],
6514        adv2_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[1..3],
6515        adv2_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[1..3],
6516        expected: Ordering::Greater,
6517    }; "adv1 has IAs matching hint")]
6518    #[test_case(AdvertiseMessageOrdTestCase{
6519        adv1_non_temporary_addresses: &[],
6520        adv1_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..3],
6521        adv2_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..1],
6522        adv2_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..1],
6523        expected: Ordering::Less,
6524    }; "adv1 missing IA_NA")]
6525    #[test_case(AdvertiseMessageOrdTestCase{
6526        adv1_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..3],
6527        adv1_delegated_prefixes: &CONFIGURED_DELEGATED_PREFIXES[0..1],
6528        adv2_non_temporary_addresses: &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..3],
6529        adv2_delegated_prefixes: &[],
6530        expected: Ordering::Greater,
6531    }; "adv2 missing IA_PD")]
6532    fn advertise_message_ord(
6533        AdvertiseMessageOrdTestCase {
6534            adv1_non_temporary_addresses,
6535            adv1_delegated_prefixes,
6536            adv2_non_temporary_addresses,
6537            adv2_delegated_prefixes,
6538            expected,
6539        }: AdvertiseMessageOrdTestCase<'_>,
6540    ) {
6541        let configured_non_temporary_addresses = testutil::to_configured_addresses(
6542            3,
6543            std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
6544        );
6545
6546        let configured_delegated_prefixes = testutil::to_configured_prefixes(
6547            3,
6548            std::iter::once(HashSet::from([CONFIGURED_DELEGATED_PREFIXES[0]])),
6549        );
6550
6551        let advertise1 = AdvertiseMessage::new_default(
6552            SERVER_ID[0],
6553            adv1_non_temporary_addresses,
6554            adv1_delegated_prefixes,
6555            &[],
6556            &configured_non_temporary_addresses,
6557            &configured_delegated_prefixes,
6558        );
6559        let advertise2 = AdvertiseMessage::new_default(
6560            SERVER_ID[1],
6561            adv2_non_temporary_addresses,
6562            adv2_delegated_prefixes,
6563            &[],
6564            &configured_non_temporary_addresses,
6565            &configured_delegated_prefixes,
6566        );
6567        assert_eq!(advertise1.cmp(&advertise2), expected);
6568    }
6569
6570    #[test_case(v6::DhcpOption::StatusCode(v6::StatusCode::Success.into(), ""); "status_code")]
6571    #[test_case(v6::DhcpOption::ClientId(&CLIENT_ID); "client_id")]
6572    #[test_case(v6::DhcpOption::ServerId(&SERVER_ID[0]); "server_id")]
6573    #[test_case(v6::DhcpOption::Preference(ADVERTISE_MAX_PREFERENCE); "preference")]
6574    #[test_case(v6::DhcpOption::SolMaxRt(*VALID_MAX_SOLICIT_TIMEOUT_RANGE.end()); "sol_max_rt")]
6575    #[test_case(v6::DhcpOption::DnsServers(&DNS_SERVERS); "dns_servers")]
6576    fn process_options_duplicates<'a>(opt: v6::DhcpOption<'a>) {
6577        let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6578            CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
6579            60,
6580            60,
6581            &[],
6582        ))];
6583        let iaid = v6::IAID::new(0);
6584        let options = [
6585            v6::DhcpOption::StatusCode(v6::StatusCode::Success.into(), ""),
6586            v6::DhcpOption::ClientId(&CLIENT_ID),
6587            v6::DhcpOption::ServerId(&SERVER_ID[0]),
6588            v6::DhcpOption::Preference(ADVERTISE_MAX_PREFERENCE),
6589            v6::DhcpOption::SolMaxRt(*VALID_MAX_SOLICIT_TIMEOUT_RANGE.end()),
6590            v6::DhcpOption::Iana(v6::IanaSerializer::new(iaid, T1.get(), T2.get(), &iana_options)),
6591            v6::DhcpOption::DnsServers(&DNS_SERVERS),
6592            opt,
6593        ];
6594        let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6595        let mut buf = vec![0; builder.bytes_len()];
6596        builder.serialize(&mut buf);
6597        let mut buf = &buf[..]; // Implements BufferView.
6598        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6599        let requested_ia_nas = HashMap::from([(iaid, None::<Ipv6Addr>)]);
6600        assert_matches!(
6601            process_options(
6602                &msg,
6603                ExchangeType::AdvertiseToSolicit,
6604                Some(&CLIENT_ID),
6605                &requested_ia_nas,
6606                &NoIaRequested
6607            ),
6608            Err(OptionsError::DuplicateOption(_, _, _))
6609        );
6610    }
6611
6612    #[derive(Copy, Clone)]
6613    enum DupIaValue {
6614        Address,
6615        Prefix,
6616    }
6617
6618    impl DupIaValue {
6619        fn second_address(self) -> Ipv6Addr {
6620            match self {
6621                DupIaValue::Address => CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
6622                DupIaValue::Prefix => CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
6623            }
6624        }
6625
6626        fn second_prefix(self) -> Subnet<Ipv6Addr> {
6627            match self {
6628                DupIaValue::Address => CONFIGURED_DELEGATED_PREFIXES[1],
6629                DupIaValue::Prefix => CONFIGURED_DELEGATED_PREFIXES[0],
6630            }
6631        }
6632    }
6633
6634    #[test_case(
6635        DupIaValue::Address,
6636        |res| {
6637            assert_matches!(
6638                res,
6639                Err(OptionsError::IaNaError(IaOptionError::DuplicateIaValue {
6640                    value,
6641                    first_lifetimes,
6642                    second_lifetimes,
6643                })) => {
6644                    assert_eq!(value, CONFIGURED_NON_TEMPORARY_ADDRESSES[0]);
6645                    (first_lifetimes, second_lifetimes)
6646                }
6647            )
6648        }; "duplicate address")]
6649    #[test_case(
6650        DupIaValue::Prefix,
6651        |res| {
6652            assert_matches!(
6653                res,
6654                Err(OptionsError::IaPdError(IaPdOptionError::IaOptionError(
6655                    IaOptionError::DuplicateIaValue {
6656                        value,
6657                        first_lifetimes,
6658                        second_lifetimes,
6659                    }
6660                ))) => {
6661                    assert_eq!(value, CONFIGURED_DELEGATED_PREFIXES[0]);
6662                    (first_lifetimes, second_lifetimes)
6663                }
6664            )
6665        }; "duplicate prefix")]
6666    fn process_options_duplicate_ia_value(
6667        dup_ia_value: DupIaValue,
6668        check: fn(
6669            Result<ProcessedOptions, OptionsError>,
6670        )
6671            -> (Result<Lifetimes, LifetimesError>, Result<Lifetimes, LifetimesError>),
6672    ) {
6673        const IA_VALUE1_LIFETIME: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(60).unwrap();
6674        const IA_VALUE2_LIFETIME: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(100).unwrap();
6675        let iana_options = [
6676            v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6677                CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
6678                IA_VALUE1_LIFETIME.get(),
6679                IA_VALUE1_LIFETIME.get(),
6680                &[],
6681            )),
6682            v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6683                dup_ia_value.second_address(),
6684                IA_VALUE2_LIFETIME.get(),
6685                IA_VALUE2_LIFETIME.get(),
6686                &[],
6687            )),
6688        ];
6689        let iapd_options = [
6690            v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
6691                IA_VALUE1_LIFETIME.get(),
6692                IA_VALUE1_LIFETIME.get(),
6693                CONFIGURED_DELEGATED_PREFIXES[0],
6694                &[],
6695            )),
6696            v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
6697                IA_VALUE2_LIFETIME.get(),
6698                IA_VALUE2_LIFETIME.get(),
6699                dup_ia_value.second_prefix(),
6700                &[],
6701            )),
6702        ];
6703        let iaid = v6::IAID::new(0);
6704        let options = [
6705            v6::DhcpOption::ClientId(&CLIENT_ID),
6706            v6::DhcpOption::ServerId(&SERVER_ID[0]),
6707            v6::DhcpOption::Iana(v6::IanaSerializer::new(iaid, T1.get(), T2.get(), &iana_options)),
6708            v6::DhcpOption::IaPd(v6::IaPdSerializer::new(iaid, T1.get(), T2.get(), &iapd_options)),
6709        ];
6710        let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6711        let mut buf = vec![0; builder.bytes_len()];
6712        builder.serialize(&mut buf);
6713        let mut buf = &buf[..]; // Implements BufferView.
6714        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6715        let requested_ia_nas = HashMap::from([(iaid, None::<Ipv6Addr>)]);
6716        let (first_lifetimes, second_lifetimes) = check(process_options(
6717            &msg,
6718            ExchangeType::AdvertiseToSolicit,
6719            Some(&CLIENT_ID),
6720            &requested_ia_nas,
6721            &NoIaRequested,
6722        ));
6723        assert_eq!(
6724            first_lifetimes,
6725            Ok(Lifetimes::new_finite(IA_VALUE1_LIFETIME, IA_VALUE1_LIFETIME))
6726        );
6727        assert_eq!(
6728            second_lifetimes,
6729            Ok(Lifetimes::new_finite(IA_VALUE2_LIFETIME, IA_VALUE2_LIFETIME))
6730        )
6731    }
6732
6733    #[test]
6734    fn process_options_t1_greather_than_t2() {
6735        let iana_options1 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6736            CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
6737            MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6738            MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6739            &[],
6740        ))];
6741        let iana_options2 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6742            CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
6743            MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6744            MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6745            &[],
6746        ))];
6747        let iapd_options1 = [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
6748            LARGE_NON_ZERO_OR_MAX_U32.get(),
6749            LARGE_NON_ZERO_OR_MAX_U32.get(),
6750            CONFIGURED_DELEGATED_PREFIXES[0],
6751            &[],
6752        ))];
6753        let iapd_options2 = [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
6754            LARGE_NON_ZERO_OR_MAX_U32.get(),
6755            LARGE_NON_ZERO_OR_MAX_U32.get(),
6756            CONFIGURED_DELEGATED_PREFIXES[1],
6757            &[],
6758        ))];
6759
6760        let iaid1 = v6::IAID::new(1);
6761        let iaid2 = v6::IAID::new(2);
6762        let options = [
6763            v6::DhcpOption::ClientId(&CLIENT_ID),
6764            v6::DhcpOption::ServerId(&SERVER_ID[0]),
6765            v6::DhcpOption::Iana(v6::IanaSerializer::new(
6766                iaid1,
6767                MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6768                SMALL_NON_ZERO_OR_MAX_U32.get(),
6769                &iana_options1,
6770            )),
6771            v6::DhcpOption::Iana(v6::IanaSerializer::new(
6772                iaid2,
6773                SMALL_NON_ZERO_OR_MAX_U32.get(),
6774                MEDIUM_NON_ZERO_OR_MAX_U32.get(),
6775                &iana_options2,
6776            )),
6777            v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
6778                iaid1,
6779                LARGE_NON_ZERO_OR_MAX_U32.get(),
6780                TINY_NON_ZERO_OR_MAX_U32.get(),
6781                &iapd_options1,
6782            )),
6783            v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
6784                iaid2,
6785                TINY_NON_ZERO_OR_MAX_U32.get(),
6786                LARGE_NON_ZERO_OR_MAX_U32.get(),
6787                &iapd_options2,
6788            )),
6789        ];
6790        let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6791        let mut buf = vec![0; builder.bytes_len()];
6792        builder.serialize(&mut buf);
6793        let mut buf = &buf[..]; // Implements BufferView.
6794        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6795        let requested_ia_nas = HashMap::from([(iaid1, None::<Ipv6Addr>), (iaid2, None)]);
6796        let requested_ia_pds = HashMap::from([(iaid1, None::<Subnet<Ipv6Addr>>), (iaid2, None)]);
6797        assert_matches!(
6798            process_options(&msg, ExchangeType::AdvertiseToSolicit, Some(&CLIENT_ID), &requested_ia_nas, &requested_ia_pds),
6799            Ok(ProcessedOptions {
6800                server_id: _,
6801                solicit_max_rt_opt: _,
6802                result: Ok(Options {
6803                    success_status_message: _,
6804                    next_contact_time: _,
6805                    non_temporary_addresses,
6806                    delegated_prefixes,
6807                    dns_servers: _,
6808                    preference: _,
6809                }),
6810            }) => {
6811                assert_eq!(non_temporary_addresses, HashMap::from([(iaid2, IaOption::Success {
6812                    status_message: None,
6813                    t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(SMALL_NON_ZERO_OR_MAX_U32)),
6814                    t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(MEDIUM_NON_ZERO_OR_MAX_U32)),
6815                    ia_values: HashMap::from([(CONFIGURED_NON_TEMPORARY_ADDRESSES[1], Ok(Lifetimes{
6816                        preferred_lifetime: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(MEDIUM_NON_ZERO_OR_MAX_U32)),
6817                        valid_lifetime: v6::NonZeroTimeValue::Finite(MEDIUM_NON_ZERO_OR_MAX_U32),
6818                    }))]),
6819                })]));
6820                assert_eq!(delegated_prefixes, HashMap::from([(iaid2, IaOption::Success {
6821                    status_message: None,
6822                    t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(TINY_NON_ZERO_OR_MAX_U32)),
6823                    t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(LARGE_NON_ZERO_OR_MAX_U32)),
6824                    ia_values: HashMap::from([(CONFIGURED_DELEGATED_PREFIXES[1], Ok(Lifetimes{
6825                        preferred_lifetime: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(LARGE_NON_ZERO_OR_MAX_U32)),
6826                        valid_lifetime: v6::NonZeroTimeValue::Finite(LARGE_NON_ZERO_OR_MAX_U32),
6827                    }))]),
6828                })]));
6829            }
6830        );
6831    }
6832
6833    #[test]
6834    fn process_options_duplicate_ia_na_id() {
6835        let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
6836            CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
6837            60,
6838            60,
6839            &[],
6840        ))];
6841        let iaid = v6::IAID::new(0);
6842        let options = [
6843            v6::DhcpOption::ClientId(&CLIENT_ID),
6844            v6::DhcpOption::ServerId(&SERVER_ID[0]),
6845            v6::DhcpOption::Iana(v6::IanaSerializer::new(iaid, T1.get(), T2.get(), &iana_options)),
6846            v6::DhcpOption::Iana(v6::IanaSerializer::new(iaid, T1.get(), T2.get(), &iana_options)),
6847        ];
6848        let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6849        let mut buf = vec![0; builder.bytes_len()];
6850        builder.serialize(&mut buf);
6851        let mut buf = &buf[..]; // Implements BufferView.
6852        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6853        let requested_ia_nas = HashMap::from([(iaid, None::<Ipv6Addr>)]);
6854        assert_matches!(
6855            process_options(&msg, ExchangeType::AdvertiseToSolicit, Some(&CLIENT_ID), &requested_ia_nas, &NoIaRequested),
6856            Err(OptionsError::DuplicateIaNaId(got_iaid, _, _)) if got_iaid == iaid
6857        );
6858    }
6859
6860    #[test]
6861    fn process_options_missing_server_id() {
6862        let options = [v6::DhcpOption::ClientId(&CLIENT_ID)];
6863        let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6864        let mut buf = vec![0; builder.bytes_len()];
6865        builder.serialize(&mut buf);
6866        let mut buf = &buf[..]; // Implements BufferView.
6867        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6868        assert_matches!(
6869            process_options(
6870                &msg,
6871                ExchangeType::AdvertiseToSolicit,
6872                Some(&CLIENT_ID),
6873                &NoIaRequested,
6874                &NoIaRequested
6875            ),
6876            Err(OptionsError::MissingServerId)
6877        );
6878    }
6879
6880    #[test]
6881    fn process_options_missing_client_id() {
6882        let options = [v6::DhcpOption::ServerId(&SERVER_ID[0])];
6883        let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6884        let mut buf = vec![0; builder.bytes_len()];
6885        builder.serialize(&mut buf);
6886        let mut buf = &buf[..]; // Implements BufferView.
6887        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6888        assert_matches!(
6889            process_options(
6890                &msg,
6891                ExchangeType::AdvertiseToSolicit,
6892                Some(&CLIENT_ID),
6893                &NoIaRequested,
6894                &NoIaRequested
6895            ),
6896            Err(OptionsError::MissingClientId)
6897        );
6898    }
6899
6900    #[test]
6901    fn process_options_mismatched_client_id() {
6902        let options = [
6903            v6::DhcpOption::ClientId(&MISMATCHED_CLIENT_ID),
6904            v6::DhcpOption::ServerId(&SERVER_ID[0]),
6905        ];
6906        let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, [0, 1, 2], &options);
6907        let mut buf = vec![0; builder.bytes_len()];
6908        builder.serialize(&mut buf);
6909        let mut buf = &buf[..]; // Implements BufferView.
6910        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6911        assert_matches!(
6912            process_options(&msg, ExchangeType::AdvertiseToSolicit, Some(&CLIENT_ID), &NoIaRequested, &NoIaRequested),
6913            Err(OptionsError::MismatchedClientId { got, want })
6914                if got[..] == MISMATCHED_CLIENT_ID && want == CLIENT_ID
6915        );
6916    }
6917
6918    #[test]
6919    fn process_options_unexpected_client_id() {
6920        let options =
6921            [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0])];
6922        let builder = v6::MessageBuilder::new(v6::MessageType::Reply, [0, 1, 2], &options);
6923        let mut buf = vec![0; builder.bytes_len()];
6924        builder.serialize(&mut buf);
6925        let mut buf = &buf[..]; // Implements BufferView.
6926        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6927        assert_matches!(
6928            process_options(&msg, ExchangeType::ReplyToInformationRequest, None, &NoIaRequested, &NoIaRequested),
6929            Err(OptionsError::UnexpectedClientId(got))
6930                if got[..] == CLIENT_ID
6931        );
6932    }
6933
6934    #[test_case(
6935        v6::MessageType::Reply,
6936        ExchangeType::ReplyToInformationRequest,
6937        v6::DhcpOption::Iana(v6::IanaSerializer::new(v6::IAID::new(0), T1.get(),T2.get(), &[]));
6938        "reply_to_information_request_ia_na"
6939    )]
6940    fn process_options_drop<'a>(
6941        message_type: v6::MessageType,
6942        exchange_type: ExchangeType,
6943        opt: v6::DhcpOption<'a>,
6944    ) {
6945        let options =
6946            [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0]), opt];
6947        let builder = v6::MessageBuilder::new(message_type, [0, 1, 2], &options);
6948        let mut buf = vec![0; builder.bytes_len()];
6949        builder.serialize(&mut buf);
6950        let mut buf = &buf[..]; // Implements BufferView.
6951        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6952        assert_matches!(
6953            process_options(&msg, exchange_type, Some(&CLIENT_ID), &NoIaRequested, &NoIaRequested),
6954            Err(OptionsError::InvalidOption(_))
6955        );
6956    }
6957
6958    #[test_case(
6959        v6::MessageType::Reply,
6960        ExchangeType::ReplyToInformationRequest;
6961        "reply_to_information_request"
6962    )]
6963    #[test_case(
6964        v6::MessageType::Reply,
6965        ExchangeType::ReplyWithLeases(RequestLeasesMessageType::Request);
6966        "reply_to_request"
6967    )]
6968    fn process_options_ignore_preference<'a>(
6969        message_type: v6::MessageType,
6970        exchange_type: ExchangeType,
6971    ) {
6972        let options = [
6973            v6::DhcpOption::ClientId(&CLIENT_ID),
6974            v6::DhcpOption::ServerId(&SERVER_ID[0]),
6975            v6::DhcpOption::Preference(ADVERTISE_MAX_PREFERENCE),
6976        ];
6977        let builder = v6::MessageBuilder::new(message_type, [0, 1, 2], &options);
6978        let mut buf = vec![0; builder.bytes_len()];
6979        builder.serialize(&mut buf);
6980        let mut buf = &buf[..]; // Implements BufferView.
6981        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
6982        assert_matches!(
6983            process_options(&msg, exchange_type, Some(&CLIENT_ID), &NoIaRequested, &NoIaRequested),
6984            Ok(ProcessedOptions { result: Ok(Options { preference: None, .. }), .. })
6985        );
6986    }
6987
6988    #[test_case(
6989        v6::MessageType::Advertise,
6990        ExchangeType::AdvertiseToSolicit;
6991        "advertise_to_solicit"
6992    )]
6993    #[test_case(
6994        v6::MessageType::Reply,
6995        ExchangeType::ReplyWithLeases(RequestLeasesMessageType::Request);
6996        "reply_to_request"
6997    )]
6998    fn process_options_ignore_information_refresh_time<'a>(
6999        message_type: v6::MessageType,
7000        exchange_type: ExchangeType,
7001    ) {
7002        let options = [
7003            v6::DhcpOption::ClientId(&CLIENT_ID),
7004            v6::DhcpOption::ServerId(&SERVER_ID[0]),
7005            v6::DhcpOption::InformationRefreshTime(42u32),
7006        ];
7007        let builder = v6::MessageBuilder::new(message_type, [0, 1, 2], &options);
7008        let mut buf = vec![0; builder.bytes_len()];
7009        builder.serialize(&mut buf);
7010        let mut buf = &buf[..]; // Implements BufferView.
7011        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7012        assert_matches!(
7013            process_options(&msg, exchange_type, Some(&CLIENT_ID), &NoIaRequested, &NoIaRequested),
7014            Ok(ProcessedOptions {
7015                result: Ok(Options {
7016                    next_contact_time: NextContactTime::RenewRebind { t1, t2 },
7017                    ..
7018                }),
7019                ..
7020            }) => {
7021                assert_eq!(t1, v6::NonZeroTimeValue::Infinity);
7022                assert_eq!(t2, v6::NonZeroTimeValue::Infinity);
7023            }
7024        );
7025    }
7026
7027    mod process_reply_with_leases_unexpected_iaid {
7028        use super::*;
7029
7030        use test_case::test_case;
7031
7032        const EXPECTED_IAID: v6::IAID = v6::IAID::new(1);
7033        const UNEXPECTED_IAID: v6::IAID = v6::IAID::new(2);
7034
7035        struct TestCase {
7036            assigned_addresses: fn(Instant) -> HashMap<v6::IAID, AddressEntry<Instant>>,
7037            assigned_prefixes: fn(Instant) -> HashMap<v6::IAID, PrefixEntry<Instant>>,
7038            check_res: fn(Result<ProcessedReplyWithLeases<Instant>, ReplyWithLeasesError>),
7039        }
7040
7041        fn expected_iaids<V: IaValueTestExt>(
7042            time: Instant,
7043        ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
7044            HashMap::from([(
7045                EXPECTED_IAID,
7046                IaEntry::new_assigned(V::CONFIGURED[0], PREFERRED_LIFETIME, VALID_LIFETIME, time),
7047            )])
7048        }
7049
7050        fn unexpected_iaids<V: IaValueTestExt>(
7051            time: Instant,
7052        ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
7053            [EXPECTED_IAID, UNEXPECTED_IAID]
7054                .into_iter()
7055                .enumerate()
7056                .map(|(i, iaid)| {
7057                    (
7058                        iaid,
7059                        IaEntry::new_assigned(
7060                            V::CONFIGURED[i],
7061                            PREFERRED_LIFETIME,
7062                            VALID_LIFETIME,
7063                            time,
7064                        ),
7065                    )
7066                })
7067                .collect()
7068        }
7069
7070        #[test_case(
7071            TestCase {
7072                assigned_addresses: expected_iaids::<Ipv6Addr>,
7073                assigned_prefixes: unexpected_iaids::<Subnet<Ipv6Addr>>,
7074                check_res: |res| {
7075                    assert_matches!(
7076                        res,
7077                        Err(ReplyWithLeasesError::OptionsError(
7078                            OptionsError::UnexpectedIaNa(iaid, _),
7079                        )) => {
7080                            assert_eq!(iaid, UNEXPECTED_IAID);
7081                        }
7082                    );
7083                },
7084            }
7085        ; "unknown IA_NA IAID")]
7086        #[test_case(
7087            TestCase {
7088                assigned_addresses: unexpected_iaids::<Ipv6Addr>,
7089                assigned_prefixes: expected_iaids::<Subnet<Ipv6Addr>>,
7090                check_res: |res| {
7091                    assert_matches!(
7092                        res,
7093                        Err(ReplyWithLeasesError::OptionsError(
7094                            OptionsError::UnexpectedIaPd(iaid, _),
7095                        )) => {
7096                            assert_eq!(iaid, UNEXPECTED_IAID);
7097                        }
7098                    );
7099                },
7100            }
7101        ; "unknown IA_PD IAID")]
7102        fn test(TestCase { assigned_addresses, assigned_prefixes, check_res }: TestCase) {
7103            let options =
7104                [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0])]
7105                    .into_iter()
7106                    .chain([EXPECTED_IAID, UNEXPECTED_IAID].into_iter().map(|iaid| {
7107                        v6::DhcpOption::Iana(v6::IanaSerializer::new(iaid, T1.get(), T2.get(), &[]))
7108                    }))
7109                    .chain([EXPECTED_IAID, UNEXPECTED_IAID].into_iter().map(|iaid| {
7110                        v6::DhcpOption::IaPd(v6::IaPdSerializer::new(iaid, T1.get(), T2.get(), &[]))
7111                    }))
7112                    .collect::<Vec<_>>();
7113            let builder =
7114                v6::MessageBuilder::new(v6::MessageType::Reply, [0, 1, 2], options.as_slice());
7115            let mut buf = vec![0; builder.bytes_len()];
7116            builder.serialize(&mut buf);
7117            let mut buf = &buf[..]; // Implements BufferView.
7118            let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7119
7120            let mut solicit_max_rt = MAX_SOLICIT_TIMEOUT;
7121            let time = Instant::now();
7122            check_res(process_reply_with_leases(
7123                &CLIENT_ID,
7124                &SERVER_ID[0],
7125                &assigned_addresses(time),
7126                &assigned_prefixes(time),
7127                &mut solicit_max_rt,
7128                &msg,
7129                RequestLeasesMessageType::Request,
7130                time,
7131            ))
7132        }
7133    }
7134
7135    #[test]
7136    fn ignore_advertise_with_unknown_ia() {
7137        let time = Instant::now();
7138        let mut client = testutil::start_and_assert_server_discovery(
7139            [0, 1, 2],
7140            &(CLIENT_ID.into()),
7141            testutil::to_configured_addresses(
7142                1,
7143                std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
7144            ),
7145            Default::default(),
7146            Vec::new(),
7147            StepRng::new(u64::MAX / 2, 0),
7148            time,
7149        );
7150
7151        let iana_options_0 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7152            CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7153            60,
7154            60,
7155            &[],
7156        ))];
7157        let iana_options_99 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7158            CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
7159            60,
7160            60,
7161            &[],
7162        ))];
7163        let options = [
7164            v6::DhcpOption::ClientId(&CLIENT_ID),
7165            v6::DhcpOption::ServerId(&SERVER_ID[0]),
7166            v6::DhcpOption::Preference(42),
7167            v6::DhcpOption::Iana(v6::IanaSerializer::new(
7168                v6::IAID::new(0),
7169                T1.get(),
7170                T2.get(),
7171                &iana_options_0,
7172            )),
7173            // An IA_NA with an IAID that was not included in the sent solicit
7174            // message.
7175            v6::DhcpOption::Iana(v6::IanaSerializer::new(
7176                v6::IAID::new(99),
7177                T1.get(),
7178                T2.get(),
7179                &iana_options_99,
7180            )),
7181        ];
7182
7183        let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
7184            &client;
7185        let builder =
7186            v6::MessageBuilder::new(v6::MessageType::Advertise, *transaction_id, &options);
7187        let mut buf = vec![0; builder.bytes_len()];
7188        builder.serialize(&mut buf);
7189        let mut buf = &buf[..]; // Implements BufferView.
7190        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7191
7192        // The client should have dropped the Advertise with the unrecognized
7193        // IA_NA IAID.
7194        assert_eq!(client.handle_message_receive(msg, time), []);
7195        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
7196            &client;
7197        assert_matches!(
7198            state,
7199            Some(ClientState::ServerDiscovery(ServerDiscovery {
7200                client_id: _,
7201                configured_non_temporary_addresses: _,
7202                configured_delegated_prefixes: _,
7203                first_solicit_time: _,
7204                retrans_timeout: _,
7205                solicit_max_rt: _,
7206                collected_advertise,
7207                collected_sol_max_rt: _,
7208            })) => {
7209                assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
7210            }
7211        );
7212    }
7213
7214    #[test]
7215    fn receive_advertise_with_max_preference() {
7216        let time = Instant::now();
7217        let mut client = testutil::start_and_assert_server_discovery(
7218            [0, 1, 2],
7219            &(CLIENT_ID.into()),
7220            testutil::to_configured_addresses(
7221                2,
7222                std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
7223            ),
7224            Default::default(),
7225            Vec::new(),
7226            StepRng::new(u64::MAX / 2, 0),
7227            time,
7228        );
7229
7230        let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7231            CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7232            60,
7233            60,
7234            &[],
7235        ))];
7236
7237        // The client should stay in ServerDiscovery when it gets an Advertise
7238        // with:
7239        //   - Preference < 255 & and at least one IA, or...
7240        //   - Preference == 255 but no IAs
7241        for (preference, iana) in [
7242            (
7243                42,
7244                Some(v6::DhcpOption::Iana(v6::IanaSerializer::new(
7245                    v6::IAID::new(0),
7246                    T1.get(),
7247                    T2.get(),
7248                    &iana_options,
7249                ))),
7250            ),
7251            (255, None),
7252        ]
7253        .into_iter()
7254        {
7255            let options = [
7256                v6::DhcpOption::ClientId(&CLIENT_ID),
7257                v6::DhcpOption::ServerId(&SERVER_ID[0]),
7258                v6::DhcpOption::Preference(preference),
7259            ]
7260            .into_iter()
7261            .chain(iana)
7262            .collect::<Vec<_>>();
7263            let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
7264                &client;
7265            let builder =
7266                v6::MessageBuilder::new(v6::MessageType::Advertise, *transaction_id, &options);
7267            let mut buf = vec![0; builder.bytes_len()];
7268            builder.serialize(&mut buf);
7269            let mut buf = &buf[..]; // Implements BufferView.
7270            let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7271            assert_eq!(client.handle_message_receive(msg, time), []);
7272        }
7273        let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7274            CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7275            60,
7276            60,
7277            &[],
7278        ))];
7279        let options = [
7280            v6::DhcpOption::ClientId(&CLIENT_ID),
7281            v6::DhcpOption::ServerId(&SERVER_ID[0]),
7282            v6::DhcpOption::Preference(255),
7283            v6::DhcpOption::Iana(v6::IanaSerializer::new(
7284                v6::IAID::new(0),
7285                T1.get(),
7286                T2.get(),
7287                &iana_options,
7288            )),
7289        ];
7290        let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
7291            &client;
7292        let builder =
7293            v6::MessageBuilder::new(v6::MessageType::Advertise, *transaction_id, &options);
7294        let mut buf = vec![0; builder.bytes_len()];
7295        builder.serialize(&mut buf);
7296        let mut buf = &buf[..]; // Implements BufferView.
7297        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7298
7299        // The client should transition to Requesting when receiving a complete
7300        // advertise with preference 255.
7301        let actions = client.handle_message_receive(msg, time);
7302        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } = client;
7303        let Requesting {
7304            client_id: _,
7305            non_temporary_addresses: _,
7306            delegated_prefixes: _,
7307            server_id: _,
7308            collected_advertise: _,
7309            first_request_time: _,
7310            retrans_timeout: _,
7311            transmission_count: _,
7312            solicit_max_rt: _,
7313        } = assert_matches!(
7314            state,
7315            Some(ClientState::Requesting(requesting)) => requesting
7316        );
7317        let buf = assert_matches!(
7318            &actions[..],
7319            [
7320                Action::CancelTimer(ClientTimerType::Retransmission),
7321                Action::SendMessage(buf),
7322                Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
7323            ] => {
7324                assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
7325                buf
7326            }
7327        );
7328        assert_eq!(testutil::msg_type(buf), v6::MessageType::Request);
7329    }
7330
7331    // T1 and T2 are non-zero and T1 > T2, the client should ignore this IA_NA option.
7332    #[test_case(T2.get() + 1, T2.get(), true)]
7333    #[test_case(INFINITY, T2.get(), true)]
7334    // T1 > T2, but T2 is zero, the client should process this IA_NA option.
7335    #[test_case(T1.get(), 0, false)]
7336    // T1 is zero, the client should process this IA_NA option.
7337    #[test_case(0, T2.get(), false)]
7338    // T1 <= T2, the client should process this IA_NA option.
7339    #[test_case(T1.get(), T2.get(), false)]
7340    #[test_case(T1.get(), INFINITY, false)]
7341    #[test_case(INFINITY, INFINITY, false)]
7342    fn receive_advertise_with_invalid_iana(t1: u32, t2: u32, ignore_iana: bool) {
7343        let transaction_id = [0, 1, 2];
7344        let time = Instant::now();
7345        let mut client = testutil::start_and_assert_server_discovery(
7346            transaction_id,
7347            &(CLIENT_ID.into()),
7348            testutil::to_configured_addresses(
7349                1,
7350                std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
7351            ),
7352            Default::default(),
7353            Vec::new(),
7354            StepRng::new(u64::MAX / 2, 0),
7355            time,
7356        );
7357
7358        let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7359            CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7360            PREFERRED_LIFETIME.get(),
7361            VALID_LIFETIME.get(),
7362            &[],
7363        ))];
7364        let options = [
7365            v6::DhcpOption::ClientId(&CLIENT_ID),
7366            v6::DhcpOption::ServerId(&SERVER_ID[0]),
7367            v6::DhcpOption::Iana(v6::IanaSerializer::new(v6::IAID::new(0), t1, t2, &iana_options)),
7368        ];
7369        let builder = v6::MessageBuilder::new(v6::MessageType::Advertise, transaction_id, &options);
7370        let mut buf = vec![0; builder.bytes_len()];
7371        builder.serialize(&mut buf);
7372        let mut buf = &buf[..]; // Implements BufferView.
7373        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7374
7375        assert_matches!(client.handle_message_receive(msg, time)[..], []);
7376        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
7377            &client;
7378        let collected_advertise = assert_matches!(
7379            state,
7380            Some(ClientState::ServerDiscovery(ServerDiscovery {
7381                client_id: _,
7382                configured_non_temporary_addresses: _,
7383                configured_delegated_prefixes: _,
7384                first_solicit_time: _,
7385                retrans_timeout: _,
7386                solicit_max_rt: _,
7387                collected_advertise,
7388                collected_sol_max_rt: _,
7389            })) => collected_advertise
7390        );
7391        match ignore_iana {
7392            true => assert!(collected_advertise.is_empty(), "{:?}", collected_advertise),
7393            false => {
7394                assert_matches!(
7395                    collected_advertise.peek(),
7396                    Some(AdvertiseMessage {
7397                        server_id: _,
7398                        non_temporary_addresses,
7399                        delegated_prefixes: _,
7400                        dns_servers: _,
7401                        preference: _,
7402                        receive_time: _,
7403                        preferred_non_temporary_addresses_count: _,
7404                        preferred_delegated_prefixes_count: _,
7405                    }) => {
7406                        assert_eq!(
7407                            non_temporary_addresses,
7408                            &HashMap::from([(
7409                                v6::IAID::new(0),
7410                                HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])
7411                            )])
7412                        );
7413                    }
7414                )
7415            }
7416        }
7417    }
7418
7419    #[test]
7420    fn select_first_server_while_retransmitting() {
7421        let time = Instant::now();
7422        let mut client = testutil::start_and_assert_server_discovery(
7423            [0, 1, 2],
7424            &(CLIENT_ID.into()),
7425            testutil::to_configured_addresses(
7426                1,
7427                std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
7428            ),
7429            Default::default(),
7430            Vec::new(),
7431            StepRng::new(u64::MAX / 2, 0),
7432            time,
7433        );
7434
7435        // On transmission timeout, if no advertise were received the client
7436        // should stay in server discovery and resend solicit.
7437        let actions = client.handle_timeout(ClientTimerType::Retransmission, time);
7438        assert_matches!(
7439            &actions[..],
7440            [
7441                Action::SendMessage(buf),
7442                Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
7443            ] => {
7444                assert_eq!(testutil::msg_type(buf), v6::MessageType::Solicit);
7445                assert_eq!(*instant, time.add(2 * INITIAL_SOLICIT_TIMEOUT));
7446                buf
7447            }
7448        );
7449        let ClientStateMachine { transaction_id, options_to_request: _, state, rng: _ } = &client;
7450        {
7451            let ServerDiscovery {
7452                client_id: _,
7453                configured_non_temporary_addresses: _,
7454                configured_delegated_prefixes: _,
7455                first_solicit_time: _,
7456                retrans_timeout: _,
7457                solicit_max_rt: _,
7458                collected_advertise,
7459                collected_sol_max_rt: _,
7460            } = assert_matches!(
7461                state,
7462                Some(ClientState::ServerDiscovery(server_discovery)) => server_discovery
7463            );
7464            assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
7465        }
7466
7467        let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7468            CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7469            60,
7470            60,
7471            &[],
7472        ))];
7473        let options = [
7474            v6::DhcpOption::ClientId(&CLIENT_ID),
7475            v6::DhcpOption::ServerId(&SERVER_ID[0]),
7476            v6::DhcpOption::Iana(v6::IanaSerializer::new(
7477                v6::IAID::new(0),
7478                T1.get(),
7479                T2.get(),
7480                &iana_options,
7481            )),
7482        ];
7483        let builder =
7484            v6::MessageBuilder::new(v6::MessageType::Advertise, *transaction_id, &options);
7485        let mut buf = vec![0; builder.bytes_len()];
7486        builder.serialize(&mut buf);
7487        let mut buf = &buf[..]; // Implements BufferView.
7488        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7489
7490        // The client should transition to Requesting when receiving any
7491        // advertise while retransmitting.
7492        let actions = client.handle_message_receive(msg, time);
7493        assert_matches!(
7494            &actions[..],
7495            [
7496                Action::CancelTimer(ClientTimerType::Retransmission),
7497                Action::SendMessage(buf),
7498                Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
7499            ] => {
7500                assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
7501                assert_eq!(testutil::msg_type(buf), v6::MessageType::Request);
7502        }
7503        );
7504        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } = client;
7505        let Requesting {
7506            client_id: _,
7507            non_temporary_addresses: _,
7508            delegated_prefixes: _,
7509            server_id: _,
7510            collected_advertise,
7511            first_request_time: _,
7512            retrans_timeout: _,
7513            transmission_count: _,
7514            solicit_max_rt: _,
7515        } = assert_matches!(
7516            state,
7517            Some(ClientState::Requesting(requesting )) => requesting
7518        );
7519        assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
7520    }
7521
7522    #[test]
7523    fn send_request() {
7524        let (mut _client, _transaction_id) = testutil::request_and_assert(
7525            &(CLIENT_ID.into()),
7526            SERVER_ID[0],
7527            CONFIGURED_NON_TEMPORARY_ADDRESSES.into_iter().map(TestIaNa::new_default).collect(),
7528            CONFIGURED_DELEGATED_PREFIXES.into_iter().map(TestIaPd::new_default).collect(),
7529            &[],
7530            StepRng::new(u64::MAX / 2, 0),
7531            Instant::now(),
7532        );
7533    }
7534
7535    // TODO(https://fxbug.dev/42060598): Refactor this test into independent test cases.
7536    #[test]
7537    fn requesting_receive_reply_with_failure_status_code() {
7538        let options_to_request = vec![];
7539        let configured_non_temporary_addresses = testutil::to_configured_addresses(1, vec![]);
7540        let advertised_non_temporary_addresses = [CONFIGURED_NON_TEMPORARY_ADDRESSES[0]];
7541        let configured_delegated_prefixes = HashMap::new();
7542        let mut want_collected_advertise = [
7543            AdvertiseMessage::new_default(
7544                SERVER_ID[1],
7545                &CONFIGURED_NON_TEMPORARY_ADDRESSES[1..=1],
7546                &[],
7547                &[],
7548                &configured_non_temporary_addresses,
7549                &configured_delegated_prefixes,
7550            ),
7551            AdvertiseMessage::new_default(
7552                SERVER_ID[2],
7553                &CONFIGURED_NON_TEMPORARY_ADDRESSES[2..=2],
7554                &[],
7555                &[],
7556                &configured_non_temporary_addresses,
7557                &configured_delegated_prefixes,
7558            ),
7559        ]
7560        .into_iter()
7561        .collect::<BinaryHeap<_>>();
7562        let mut rng = StepRng::new(u64::MAX / 2, 0);
7563
7564        let time = Instant::now();
7565        let Transition { state, actions: _, transaction_id } = Requesting::start(
7566            CLIENT_ID.into(),
7567            SERVER_ID[0].to_vec(),
7568            advertise_to_ia_entries(
7569                testutil::to_default_ias_map(&advertised_non_temporary_addresses),
7570                configured_non_temporary_addresses.clone(),
7571            ),
7572            Default::default(), /* delegated_prefixes */
7573            &options_to_request[..],
7574            want_collected_advertise.clone(),
7575            MAX_SOLICIT_TIMEOUT,
7576            &mut rng,
7577            time,
7578        );
7579
7580        let expected_non_temporary_addresses = (0..)
7581            .map(v6::IAID::new)
7582            .zip(
7583                advertised_non_temporary_addresses
7584                    .iter()
7585                    .map(|addr| AddressEntry::ToRequest(HashSet::from([*addr]))),
7586            )
7587            .collect::<HashMap<v6::IAID, AddressEntry<_>>>();
7588        {
7589            let Requesting {
7590                non_temporary_addresses: got_non_temporary_addresses,
7591                delegated_prefixes: _,
7592                server_id,
7593                collected_advertise,
7594                client_id: _,
7595                first_request_time: _,
7596                retrans_timeout: _,
7597                transmission_count: _,
7598                solicit_max_rt: _,
7599            } = assert_matches!(&state, ClientState::Requesting(requesting) => requesting);
7600            assert_eq!(server_id[..], SERVER_ID[0]);
7601            assert_eq!(*got_non_temporary_addresses, expected_non_temporary_addresses);
7602            assert_eq!(
7603                collected_advertise.clone().into_sorted_vec(),
7604                want_collected_advertise.clone().into_sorted_vec()
7605            );
7606        }
7607
7608        // If the reply contains a top level UnspecFail status code, the reply
7609        // should be ignored.
7610        let options = [
7611            v6::DhcpOption::ServerId(&SERVER_ID[0]),
7612            v6::DhcpOption::ClientId(&CLIENT_ID),
7613            v6::DhcpOption::Iana(v6::IanaSerializer::new(
7614                v6::IAID::new(0),
7615                T1.get(),
7616                T2.get(),
7617                &[],
7618            )),
7619            v6::DhcpOption::StatusCode(v6::ErrorStatusCode::UnspecFail.into(), ""),
7620        ];
7621        let request_transaction_id = transaction_id.unwrap();
7622        let builder =
7623            v6::MessageBuilder::new(v6::MessageType::Reply, request_transaction_id, &options);
7624        let mut buf = vec![0; builder.bytes_len()];
7625        builder.serialize(&mut buf);
7626        let mut buf = &buf[..]; // Implements BufferView.
7627        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7628        let Transition { state, actions, transaction_id: got_transaction_id } =
7629            state.reply_message_received(&options_to_request, &mut rng, msg, time);
7630        {
7631            let Requesting {
7632                client_id: _,
7633                non_temporary_addresses: got_non_temporary_addresses,
7634                delegated_prefixes: _,
7635                server_id,
7636                collected_advertise,
7637                first_request_time: _,
7638                retrans_timeout: _,
7639                transmission_count: _,
7640                solicit_max_rt: _,
7641            } = assert_matches!(&state, ClientState::Requesting(requesting) => requesting);
7642            assert_eq!(server_id[..], SERVER_ID[0]);
7643            assert_eq!(
7644                collected_advertise.clone().into_sorted_vec(),
7645                want_collected_advertise.clone().into_sorted_vec()
7646            );
7647            assert_eq!(*got_non_temporary_addresses, expected_non_temporary_addresses);
7648        }
7649        assert_eq!(got_transaction_id, None);
7650        assert_eq!(actions[..], []);
7651
7652        // If the reply contains a top level NotOnLink status code, the
7653        // request should be resent without specifying any addresses.
7654        let options = [
7655            v6::DhcpOption::ServerId(&SERVER_ID[0]),
7656            v6::DhcpOption::ClientId(&CLIENT_ID),
7657            v6::DhcpOption::Iana(v6::IanaSerializer::new(
7658                v6::IAID::new(0),
7659                T1.get(),
7660                T2.get(),
7661                &[],
7662            )),
7663            v6::DhcpOption::StatusCode(v6::ErrorStatusCode::NotOnLink.into(), ""),
7664        ];
7665        let request_transaction_id = transaction_id.unwrap();
7666        let builder =
7667            v6::MessageBuilder::new(v6::MessageType::Reply, request_transaction_id, &options);
7668        let mut buf = vec![0; builder.bytes_len()];
7669        builder.serialize(&mut buf);
7670        let mut buf = &buf[..]; // Implements BufferView.
7671        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7672        let Transition { state, actions: _, transaction_id } =
7673            state.reply_message_received(&options_to_request, &mut rng, msg, time);
7674
7675        let expected_non_temporary_addresses: HashMap<v6::IAID, AddressEntry<_>> =
7676            HashMap::from([(v6::IAID::new(0), AddressEntry::ToRequest(Default::default()))]);
7677        {
7678            let Requesting {
7679                client_id: _,
7680                non_temporary_addresses: got_non_temporary_addresses,
7681                delegated_prefixes: _,
7682                server_id,
7683                collected_advertise,
7684                first_request_time: _,
7685                retrans_timeout: _,
7686                transmission_count: _,
7687                solicit_max_rt: _,
7688            } = assert_matches!(
7689                &state,
7690                ClientState::Requesting(requesting) => requesting
7691            );
7692            assert_eq!(server_id[..], SERVER_ID[0]);
7693            assert_eq!(
7694                collected_advertise.clone().into_sorted_vec(),
7695                want_collected_advertise.clone().into_sorted_vec()
7696            );
7697            assert_eq!(*got_non_temporary_addresses, expected_non_temporary_addresses);
7698        }
7699        assert!(transaction_id.is_some());
7700
7701        // If the reply contains no usable addresses, the client selects
7702        // another server and sends a request to it.
7703        let iana_options =
7704            [v6::DhcpOption::StatusCode(v6::ErrorStatusCode::NoAddrsAvail.into(), "")];
7705        let options = [
7706            v6::DhcpOption::ServerId(&SERVER_ID[0]),
7707            v6::DhcpOption::ClientId(&CLIENT_ID),
7708            v6::DhcpOption::Iana(v6::IanaSerializer::new(
7709                v6::IAID::new(0),
7710                T1.get(),
7711                T2.get(),
7712                &iana_options,
7713            )),
7714        ];
7715        let builder =
7716            v6::MessageBuilder::new(v6::MessageType::Reply, transaction_id.unwrap(), &options);
7717        let mut buf = vec![0; builder.bytes_len()];
7718        builder.serialize(&mut buf);
7719        let mut buf = &buf[..]; // Implements BufferView.
7720        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7721        let Transition { state, actions, transaction_id } =
7722            state.reply_message_received(&options_to_request, &mut rng, msg, time);
7723        {
7724            let Requesting {
7725                server_id,
7726                collected_advertise,
7727                client_id: _,
7728                non_temporary_addresses: _,
7729                delegated_prefixes: _,
7730                first_request_time: _,
7731                retrans_timeout: _,
7732                transmission_count: _,
7733                solicit_max_rt: _,
7734            } = assert_matches!(
7735                state,
7736                ClientState::Requesting(requesting) => requesting
7737            );
7738            assert_eq!(server_id[..], SERVER_ID[1]);
7739            let _: Option<AdvertiseMessage<_>> = want_collected_advertise.pop();
7740            assert_eq!(
7741                collected_advertise.clone().into_sorted_vec(),
7742                want_collected_advertise.clone().into_sorted_vec(),
7743            );
7744        }
7745        assert_matches!(
7746            &actions[..],
7747            [
7748                Action::CancelTimer(ClientTimerType::Retransmission),
7749                Action::SendMessage(_buf),
7750                Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
7751            ] => {
7752                assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
7753            }
7754        );
7755        assert!(transaction_id.is_some());
7756    }
7757
7758    #[test]
7759    fn requesting_receive_reply_with_ia_not_on_link() {
7760        let options_to_request = vec![];
7761        let configured_non_temporary_addresses = testutil::to_configured_addresses(
7762            2,
7763            std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
7764        );
7765        let mut rng = StepRng::new(u64::MAX / 2, 0);
7766
7767        let time = Instant::now();
7768        let Transition { state, actions: _, transaction_id } = Requesting::start(
7769            CLIENT_ID.into(),
7770            SERVER_ID[0].to_vec(),
7771            advertise_to_ia_entries(
7772                testutil::to_default_ias_map(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]),
7773                configured_non_temporary_addresses.clone(),
7774            ),
7775            Default::default(), /* delegated_prefixes */
7776            &options_to_request[..],
7777            BinaryHeap::new(),
7778            MAX_SOLICIT_TIMEOUT,
7779            &mut rng,
7780            time,
7781        );
7782
7783        // If the reply contains an address with status code NotOnLink, the
7784        // client should request the IAs without specifying any addresses in
7785        // subsequent messages.
7786        let iana_options1 = [v6::DhcpOption::StatusCode(v6::ErrorStatusCode::NotOnLink.into(), "")];
7787        let iana_options2 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7788            CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
7789            PREFERRED_LIFETIME.get(),
7790            VALID_LIFETIME.get(),
7791            &[],
7792        ))];
7793        let iaid1 = v6::IAID::new(0);
7794        let iaid2 = v6::IAID::new(1);
7795        let options = [
7796            v6::DhcpOption::ServerId(&SERVER_ID[0]),
7797            v6::DhcpOption::ClientId(&CLIENT_ID),
7798            v6::DhcpOption::Iana(v6::IanaSerializer::new(
7799                iaid1,
7800                T1.get(),
7801                T2.get(),
7802                &iana_options1,
7803            )),
7804            v6::DhcpOption::Iana(v6::IanaSerializer::new(
7805                iaid2,
7806                T1.get(),
7807                T2.get(),
7808                &iana_options2,
7809            )),
7810        ];
7811        let builder =
7812            v6::MessageBuilder::new(v6::MessageType::Reply, transaction_id.unwrap(), &options);
7813        let mut buf = vec![0; builder.bytes_len()];
7814        builder.serialize(&mut buf);
7815        let mut buf = &buf[..]; // Implements BufferView.
7816        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7817        let Transition { state, actions, transaction_id } =
7818            state.reply_message_received(&options_to_request, &mut rng, msg, time);
7819        let expected_non_temporary_addresses = HashMap::from([
7820            (iaid1, AddressEntry::ToRequest(Default::default())),
7821            (
7822                iaid2,
7823                AddressEntry::Assigned(HashMap::from([(
7824                    CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
7825                    LifetimesInfo {
7826                        lifetimes: Lifetimes {
7827                            preferred_lifetime: v6::TimeValue::NonZero(
7828                                v6::NonZeroTimeValue::Finite(PREFERRED_LIFETIME),
7829                            ),
7830                            valid_lifetime: v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
7831                        },
7832                        updated_at: time,
7833                    },
7834                )])),
7835            ),
7836        ]);
7837        {
7838            let Assigned {
7839                client_id: _,
7840                non_temporary_addresses,
7841                delegated_prefixes,
7842                server_id,
7843                dns_servers: _,
7844                solicit_max_rt: _,
7845                _marker,
7846            } = assert_matches!(
7847                state,
7848                ClientState::Assigned(assigned) => assigned
7849            );
7850            assert_eq!(server_id[..], SERVER_ID[0]);
7851            assert_eq!(non_temporary_addresses, expected_non_temporary_addresses);
7852            assert_eq!(delegated_prefixes, HashMap::new());
7853        }
7854        assert_matches!(
7855            &actions[..],
7856            [
7857                Action::CancelTimer(ClientTimerType::Retransmission),
7858                Action::ScheduleTimer(ClientTimerType::Renew, t1),
7859                Action::ScheduleTimer(ClientTimerType::Rebind, t2),
7860                Action::IaNaUpdates(iana_updates),
7861                Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
7862            ] => {
7863                assert_eq!(*t1, time.add(Duration::from_secs(T1.get().into())));
7864                assert_eq!(*t2, time.add(Duration::from_secs(T2.get().into())));
7865                assert_eq!(
7866                    *restart_time,
7867                    time.add(Duration::from_secs(VALID_LIFETIME.get().into())),
7868                );
7869                assert_eq!(
7870                    iana_updates,
7871                    &HashMap::from([(
7872                        iaid2,
7873                        HashMap::from([(
7874                            CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
7875                            IaValueUpdateKind::Added(Lifetimes::new_default()),
7876                        )]),
7877                    )]),
7878                );
7879            }
7880        );
7881        assert!(transaction_id.is_none());
7882    }
7883
7884    #[test_case(0, VALID_LIFETIME.get(), true)]
7885    #[test_case(PREFERRED_LIFETIME.get(), 0, false)]
7886    #[test_case(VALID_LIFETIME.get() + 1, VALID_LIFETIME.get(), false)]
7887    #[test_case(0, 0, false)]
7888    #[test_case(PREFERRED_LIFETIME.get(), VALID_LIFETIME.get(), true)]
7889    fn requesting_receive_reply_with_invalid_ia_lifetimes(
7890        preferred_lifetime: u32,
7891        valid_lifetime: u32,
7892        valid_ia: bool,
7893    ) {
7894        let options_to_request = vec![];
7895        let configured_non_temporary_addresses = testutil::to_configured_addresses(1, vec![]);
7896        let mut rng = StepRng::new(u64::MAX / 2, 0);
7897
7898        let time = Instant::now();
7899        let Transition { state, actions: _, transaction_id } = Requesting::start(
7900            CLIENT_ID.into(),
7901            SERVER_ID[0].to_vec(),
7902            advertise_to_ia_entries(
7903                testutil::to_default_ias_map(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..1]),
7904                configured_non_temporary_addresses.clone(),
7905            ),
7906            Default::default(), /* delegated_prefixes */
7907            &options_to_request[..],
7908            BinaryHeap::new(),
7909            MAX_SOLICIT_TIMEOUT,
7910            &mut rng,
7911            time,
7912        );
7913
7914        // The client should discard the IAs with invalid lifetimes.
7915        let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
7916            CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
7917            preferred_lifetime,
7918            valid_lifetime,
7919            &[],
7920        ))];
7921        let options = [
7922            v6::DhcpOption::ServerId(&SERVER_ID[0]),
7923            v6::DhcpOption::ClientId(&CLIENT_ID),
7924            v6::DhcpOption::Iana(v6::IanaSerializer::new(
7925                v6::IAID::new(0),
7926                T1.get(),
7927                T2.get(),
7928                &iana_options,
7929            )),
7930        ];
7931        let builder =
7932            v6::MessageBuilder::new(v6::MessageType::Reply, transaction_id.unwrap(), &options);
7933        let mut buf = vec![0; builder.bytes_len()];
7934        builder.serialize(&mut buf);
7935        let mut buf = &buf[..]; // Implements BufferView.
7936        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
7937        let Transition { state, actions: _, transaction_id: _ } =
7938            state.reply_message_received(&options_to_request, &mut rng, msg, time);
7939        match valid_ia {
7940            true =>
7941            // The client should transition to Assigned if the reply contains
7942            // a valid IA.
7943            {
7944                let Assigned {
7945                    client_id: _,
7946                    non_temporary_addresses: _,
7947                    delegated_prefixes: _,
7948                    server_id: _,
7949                    dns_servers: _,
7950                    solicit_max_rt: _,
7951                    _marker,
7952                } = assert_matches!(
7953                    state,
7954                    ClientState::Assigned(assigned) => assigned
7955                );
7956            }
7957            false =>
7958            // The client should transition to ServerDiscovery if the reply contains
7959            // no valid IAs.
7960            {
7961                let ServerDiscovery {
7962                    client_id: _,
7963                    configured_non_temporary_addresses: _,
7964                    configured_delegated_prefixes: _,
7965                    first_solicit_time: _,
7966                    retrans_timeout: _,
7967                    solicit_max_rt: _,
7968                    collected_advertise,
7969                    collected_sol_max_rt: _,
7970                } = assert_matches!(
7971                    state,
7972                    ClientState::ServerDiscovery(server_discovery) => server_discovery
7973                );
7974                assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
7975            }
7976        }
7977    }
7978
7979    // Test that T1/T2 are calculated correctly on receiving a Reply to Request.
7980    #[test]
7981    fn compute_t1_t2_on_reply_to_request() {
7982        let mut rng = StepRng::new(u64::MAX / 2, 0);
7983
7984        for (
7985            (ia1_preferred_lifetime, ia1_valid_lifetime, ia1_t1, ia1_t2),
7986            (ia2_preferred_lifetime, ia2_valid_lifetime, ia2_t1, ia2_t2),
7987            expected_t1,
7988            expected_t2,
7989        ) in vec![
7990            // If T1/T2 are 0, they should be computed as as 0.5 * minimum
7991            // preferred lifetime, and 0.8 * minimum preferred lifetime
7992            // respectively.
7993            (
7994                (100, 160, 0, 0),
7995                (120, 180, 0, 0),
7996                v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(50).expect("should succeed")),
7997                v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(80).expect("should succeed")),
7998            ),
7999            (
8000                (INFINITY, INFINITY, 0, 0),
8001                (120, 180, 0, 0),
8002                v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(60).expect("should succeed")),
8003                v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(96).expect("should succeed")),
8004            ),
8005            // If T1/T2 are 0, and the minimum preferred lifetime, is infinity,
8006            // T1/T2 should also be infinity.
8007            (
8008                (INFINITY, INFINITY, 0, 0),
8009                (INFINITY, INFINITY, 0, 0),
8010                v6::NonZeroTimeValue::Infinity,
8011                v6::NonZeroTimeValue::Infinity,
8012            ),
8013            // T2 may be infinite if T1 is finite.
8014            (
8015                (INFINITY, INFINITY, 50, INFINITY),
8016                (INFINITY, INFINITY, 50, INFINITY),
8017                v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(50).expect("should succeed")),
8018                v6::NonZeroTimeValue::Infinity,
8019            ),
8020            // If T1/T2 are set, and have different values across IAs, T1/T2
8021            // should be computed as the minimum T1/T2. NOTE: the server should
8022            // send the same T1/T2 across all IA, but the client should be
8023            // prepared for the server sending different T1/T2 values.
8024            (
8025                (100, 160, 40, 70),
8026                (120, 180, 50, 80),
8027                v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(40).expect("should succeed")),
8028                v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(70).expect("should succeed")),
8029            ),
8030        ] {
8031            let time = Instant::now();
8032            let Transition { state, actions: _, transaction_id } = Requesting::start(
8033                CLIENT_ID.into(),
8034                SERVER_ID[0].to_vec(),
8035                advertise_to_ia_entries(
8036                    testutil::to_default_ias_map(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]),
8037                    testutil::to_configured_addresses(
8038                        2,
8039                        std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
8040                    ),
8041                ),
8042                Default::default(), /* delegated_prefixes */
8043                &[],
8044                BinaryHeap::new(),
8045                MAX_SOLICIT_TIMEOUT,
8046                &mut rng,
8047                time,
8048            );
8049
8050            let iana_options1 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
8051                CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
8052                ia1_preferred_lifetime,
8053                ia1_valid_lifetime,
8054                &[],
8055            ))];
8056            let iana_options2 = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
8057                CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
8058                ia2_preferred_lifetime,
8059                ia2_valid_lifetime,
8060                &[],
8061            ))];
8062            let iaid1 = v6::IAID::new(0);
8063            let iaid2 = v6::IAID::new(1);
8064            let options = [
8065                v6::DhcpOption::ServerId(&SERVER_ID[0]),
8066                v6::DhcpOption::ClientId(&CLIENT_ID),
8067                v6::DhcpOption::Iana(v6::IanaSerializer::new(
8068                    iaid1,
8069                    ia1_t1,
8070                    ia1_t2,
8071                    &iana_options1,
8072                )),
8073                v6::DhcpOption::Iana(v6::IanaSerializer::new(
8074                    iaid2,
8075                    ia2_t1,
8076                    ia2_t2,
8077                    &iana_options2,
8078                )),
8079            ];
8080            let builder =
8081                v6::MessageBuilder::new(v6::MessageType::Reply, transaction_id.unwrap(), &options);
8082            let mut buf = vec![0; builder.bytes_len()];
8083            builder.serialize(&mut buf);
8084            let mut buf = &buf[..]; // Implements BufferView.
8085            let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8086            let Transition { state, actions, transaction_id: _ } =
8087                state.reply_message_received(&[], &mut rng, msg, time);
8088            let Assigned {
8089                client_id: _,
8090                non_temporary_addresses: _,
8091                delegated_prefixes: _,
8092                server_id: _,
8093                dns_servers: _,
8094                solicit_max_rt: _,
8095                _marker,
8096            } = assert_matches!(
8097                state,
8098                ClientState::Assigned(assigned) => assigned
8099            );
8100
8101            let update_actions = [Action::IaNaUpdates(HashMap::from([
8102                (
8103                    iaid1,
8104                    HashMap::from([(
8105                        CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
8106                        IaValueUpdateKind::Added(Lifetimes::new(
8107                            ia1_preferred_lifetime,
8108                            ia1_valid_lifetime,
8109                        )),
8110                    )]),
8111                ),
8112                (
8113                    iaid2,
8114                    HashMap::from([(
8115                        CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
8116                        IaValueUpdateKind::Added(Lifetimes::new(
8117                            ia2_preferred_lifetime,
8118                            ia2_valid_lifetime,
8119                        )),
8120                    )]),
8121                ),
8122            ]))];
8123
8124            let timer_action = |timer, tv| match tv {
8125                v6::NonZeroTimeValue::Finite(tv) => {
8126                    Action::ScheduleTimer(timer, time.add(Duration::from_secs(tv.get().into())))
8127                }
8128                v6::NonZeroTimeValue::Infinity => Action::CancelTimer(timer),
8129            };
8130
8131            let non_zero_time_value = |v| {
8132                assert_matches!(
8133                    v6::TimeValue::new(v),
8134                    v6::TimeValue::NonZero(v) => v
8135                )
8136            };
8137
8138            assert!(expected_t1 <= expected_t2);
8139            assert_eq!(
8140                actions,
8141                [
8142                    Action::CancelTimer(ClientTimerType::Retransmission),
8143                    timer_action(ClientTimerType::Renew, expected_t1),
8144                    timer_action(ClientTimerType::Rebind, expected_t2),
8145                ]
8146                .into_iter()
8147                .chain(update_actions)
8148                .chain([timer_action(
8149                    ClientTimerType::RestartServerDiscovery,
8150                    std::cmp::max(
8151                        non_zero_time_value(ia1_valid_lifetime),
8152                        non_zero_time_value(ia2_valid_lifetime),
8153                    ),
8154                )])
8155                .collect::<Vec<_>>(),
8156            );
8157        }
8158    }
8159
8160    #[test]
8161    fn use_advertise_from_best_server() {
8162        let transaction_id = [0, 1, 2];
8163        let time = Instant::now();
8164        let mut client = testutil::start_and_assert_server_discovery(
8165            transaction_id,
8166            &(CLIENT_ID.into()),
8167            testutil::to_configured_addresses(
8168                CONFIGURED_NON_TEMPORARY_ADDRESSES.len(),
8169                CONFIGURED_NON_TEMPORARY_ADDRESSES.map(|a| HashSet::from([a])),
8170            ),
8171            testutil::to_configured_prefixes(
8172                CONFIGURED_DELEGATED_PREFIXES.len(),
8173                CONFIGURED_DELEGATED_PREFIXES.map(|a| HashSet::from([a])),
8174            ),
8175            Vec::new(),
8176            StepRng::new(u64::MAX / 2, 0),
8177            time,
8178        );
8179
8180        // Server0 advertises only IA_NA but all matching our hints.
8181        let buf = TestMessageBuilder {
8182            transaction_id,
8183            message_type: v6::MessageType::Advertise,
8184            client_id: &CLIENT_ID,
8185            server_id: &SERVER_ID[0],
8186            preference: None,
8187            dns_servers: None,
8188            ia_nas: (0..)
8189                .map(v6::IAID::new)
8190                .zip(CONFIGURED_NON_TEMPORARY_ADDRESSES)
8191                .map(|(iaid, value)| (iaid, TestIa::new_default(value))),
8192            ia_pds: std::iter::empty(),
8193        }
8194        .build();
8195        let mut buf = &buf[..]; // Implements BufferView.
8196        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8197        assert_matches!(client.handle_message_receive(msg, time)[..], []);
8198
8199        // Server1 advertises only IA_PD but all matching our hints.
8200        let buf = TestMessageBuilder {
8201            transaction_id,
8202            message_type: v6::MessageType::Advertise,
8203            client_id: &CLIENT_ID,
8204            server_id: &SERVER_ID[1],
8205            preference: None,
8206            dns_servers: None,
8207            ia_nas: std::iter::empty(),
8208            ia_pds: (0..)
8209                .map(v6::IAID::new)
8210                .zip(CONFIGURED_DELEGATED_PREFIXES)
8211                .map(|(iaid, value)| (iaid, TestIa::new_default(value))),
8212        }
8213        .build();
8214        let mut buf = &buf[..]; // Implements BufferView.
8215        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8216        assert_matches!(client.handle_message_receive(msg, time)[..], []);
8217
8218        // Server2 advertises only a single IA_NA and IA_PD but not matching our
8219        // hint.
8220        //
8221        // This should be the best advertisement the client receives since it
8222        // allows the client to get the most diverse set of IAs which the client
8223        // prefers over a large quantity of a single IA type.
8224        let buf = TestMessageBuilder {
8225            transaction_id,
8226            message_type: v6::MessageType::Advertise,
8227            client_id: &CLIENT_ID,
8228            server_id: &SERVER_ID[2],
8229            preference: None,
8230            dns_servers: None,
8231            ia_nas: std::iter::once((
8232                v6::IAID::new(0),
8233                TestIa {
8234                    values: HashMap::from([(
8235                        REPLY_NON_TEMPORARY_ADDRESSES[0],
8236                        Lifetimes {
8237                            preferred_lifetime: v6::TimeValue::NonZero(
8238                                v6::NonZeroTimeValue::Finite(PREFERRED_LIFETIME),
8239                            ),
8240                            valid_lifetime: v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
8241                        },
8242                    )]),
8243                    t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
8244                    t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8245                },
8246            )),
8247            ia_pds: std::iter::once((
8248                v6::IAID::new(0),
8249                TestIa {
8250                    values: HashMap::from([(
8251                        REPLY_DELEGATED_PREFIXES[0],
8252                        Lifetimes {
8253                            preferred_lifetime: v6::TimeValue::NonZero(
8254                                v6::NonZeroTimeValue::Finite(PREFERRED_LIFETIME),
8255                            ),
8256                            valid_lifetime: v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
8257                        },
8258                    )]),
8259                    t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
8260                    t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8261                },
8262            )),
8263        }
8264        .build();
8265        let mut buf = &buf[..]; // Implements BufferView.
8266        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8267        assert_matches!(client.handle_message_receive(msg, time)[..], []);
8268
8269        // Handle the retransmission timeout for the first time which should
8270        // pick a server and transition to requesting with the best server.
8271        //
8272        // The best server should be `SERVER_ID[2]` and we should have replaced
8273        // our hint for IA_NA/IA_PD with IAID == 0 to what was in the server's
8274        // advertise message. We keep the hints for the other IAIDs since the
8275        // server did not include those IAID in its advertise so the client will
8276        // continue to request the hints with the selected server.
8277        let actions = client.handle_timeout(ClientTimerType::Retransmission, time);
8278        assert_matches!(
8279            &actions[..],
8280            [
8281                Action::CancelTimer(ClientTimerType::Retransmission),
8282                Action::SendMessage(buf),
8283                Action::ScheduleTimer(ClientTimerType::Retransmission, instant),
8284            ] => {
8285                assert_eq!(testutil::msg_type(buf), v6::MessageType::Request);
8286                assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
8287            }
8288        );
8289        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } = client;
8290        assert_matches!(
8291            state,
8292            Some(ClientState::Requesting(Requesting {
8293                client_id: _,
8294                non_temporary_addresses,
8295                delegated_prefixes,
8296                server_id,
8297                collected_advertise: _,
8298                first_request_time: _,
8299                retrans_timeout: _,
8300                transmission_count: _,
8301                solicit_max_rt: _,
8302            })) => {
8303                assert_eq!(&server_id, &SERVER_ID[2]);
8304                assert_eq!(
8305                    non_temporary_addresses,
8306                    [REPLY_NON_TEMPORARY_ADDRESSES[0]]
8307                        .iter()
8308                        .chain(CONFIGURED_NON_TEMPORARY_ADDRESSES[1..3].iter())
8309                        .enumerate().map(|(iaid, addr)| {
8310                            (v6::IAID::new(iaid.try_into().unwrap()), AddressEntry::ToRequest(HashSet::from([*addr])))
8311                        }).collect::<HashMap<_, _>>()
8312                );
8313                assert_eq!(
8314                    delegated_prefixes,
8315                    [REPLY_DELEGATED_PREFIXES[0]]
8316                        .iter()
8317                        .chain(CONFIGURED_DELEGATED_PREFIXES[1..3].iter())
8318                        .enumerate().map(|(iaid, addr)| {
8319                            (v6::IAID::new(iaid.try_into().unwrap()), PrefixEntry::ToRequest(HashSet::from([*addr])))
8320                        }).collect::<HashMap<_, _>>()
8321                );
8322            }
8323        );
8324    }
8325
8326    // Test that Request retransmission respects max retransmission count.
8327    #[test]
8328    fn requesting_retransmit_max_retrans_count() {
8329        let transaction_id = [0, 1, 2];
8330        let time = Instant::now();
8331        let mut client = testutil::start_and_assert_server_discovery(
8332            transaction_id,
8333            &(CLIENT_ID.into()),
8334            testutil::to_configured_addresses(
8335                1,
8336                std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
8337            ),
8338            Default::default(),
8339            Vec::new(),
8340            StepRng::new(u64::MAX / 2, 0),
8341            time,
8342        );
8343
8344        for i in 0..2 {
8345            let buf = TestMessageBuilder {
8346                transaction_id,
8347                message_type: v6::MessageType::Advertise,
8348                client_id: &CLIENT_ID,
8349                server_id: &SERVER_ID[i],
8350                preference: None,
8351                dns_servers: None,
8352                ia_nas: std::iter::once((
8353                    v6::IAID::new(0),
8354                    TestIa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[i]),
8355                )),
8356                ia_pds: std::iter::empty(),
8357            }
8358            .build();
8359            let mut buf = &buf[..]; // Implements BufferView.
8360            let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8361            assert_matches!(client.handle_message_receive(msg, time)[..], []);
8362        }
8363        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8364            &client;
8365        let ServerDiscovery {
8366            client_id: _,
8367            configured_non_temporary_addresses: _,
8368            configured_delegated_prefixes: _,
8369            first_solicit_time: _,
8370            retrans_timeout: _,
8371            solicit_max_rt: _,
8372            collected_advertise: want_collected_advertise,
8373            collected_sol_max_rt: _,
8374        } = assert_matches!(
8375            state,
8376            Some(ClientState::ServerDiscovery(server_discovery)) => server_discovery
8377        );
8378        let mut want_collected_advertise = want_collected_advertise.clone();
8379        let _: Option<AdvertiseMessage<_>> = want_collected_advertise.pop();
8380
8381        // The client should transition to Requesting and select the server that
8382        // sent the best advertise.
8383        assert_matches!(
8384            &client.handle_timeout(ClientTimerType::Retransmission, time)[..],
8385           [
8386                Action::CancelTimer(ClientTimerType::Retransmission),
8387                Action::SendMessage(buf),
8388                Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
8389           ] => {
8390               assert_eq!(testutil::msg_type(buf), v6::MessageType::Request);
8391               assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
8392           }
8393        );
8394        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8395            &client;
8396        {
8397            let Requesting {
8398                client_id: _,
8399                non_temporary_addresses: _,
8400                delegated_prefixes: _,
8401                server_id,
8402                collected_advertise,
8403                first_request_time: _,
8404                retrans_timeout: _,
8405                transmission_count,
8406                solicit_max_rt: _,
8407            } = assert_matches!(state, Some(ClientState::Requesting(requesting)) => requesting);
8408            assert_eq!(
8409                collected_advertise.clone().into_sorted_vec(),
8410                want_collected_advertise.clone().into_sorted_vec()
8411            );
8412            assert_eq!(server_id[..], SERVER_ID[0]);
8413            assert_eq!(*transmission_count, 1);
8414        }
8415
8416        for count in 2..=(REQUEST_MAX_RC + 1) {
8417            assert_matches!(
8418                &client.handle_timeout(ClientTimerType::Retransmission, time)[..],
8419               [
8420                    Action::SendMessage(buf),
8421                    // `_timeout` is not checked because retransmission timeout
8422                    // calculation is covered in its respective test.
8423                    Action::ScheduleTimer(ClientTimerType::Retransmission, _timeout)
8424               ] if testutil::msg_type(buf) == v6::MessageType::Request
8425            );
8426            let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8427                &client;
8428            let Requesting {
8429                client_id: _,
8430                non_temporary_addresses: _,
8431                delegated_prefixes: _,
8432                server_id,
8433                collected_advertise,
8434                first_request_time: _,
8435                retrans_timeout: _,
8436                transmission_count,
8437                solicit_max_rt: _,
8438            } = assert_matches!(state, Some(ClientState::Requesting(requesting)) => requesting);
8439            assert_eq!(
8440                collected_advertise.clone().into_sorted_vec(),
8441                want_collected_advertise.clone().into_sorted_vec()
8442            );
8443            assert_eq!(server_id[..], SERVER_ID[0]);
8444            assert_eq!(*transmission_count, count);
8445        }
8446
8447        // When the retransmission count reaches REQUEST_MAX_RC, the client
8448        // should select another server.
8449        assert_matches!(
8450            &client.handle_timeout(ClientTimerType::Retransmission, time)[..],
8451           [
8452                Action::CancelTimer(ClientTimerType::Retransmission),
8453                Action::SendMessage(buf),
8454                Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
8455           ] => {
8456               assert_eq!(testutil::msg_type(buf), v6::MessageType::Request);
8457               assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
8458           }
8459        );
8460        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8461            &client;
8462        let Requesting {
8463            client_id: _,
8464            non_temporary_addresses: _,
8465            delegated_prefixes: _,
8466            server_id,
8467            collected_advertise,
8468            first_request_time: _,
8469            retrans_timeout: _,
8470            transmission_count,
8471            solicit_max_rt: _,
8472        } = assert_matches!(state, Some(ClientState::Requesting(requesting)) => requesting);
8473        assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
8474        assert_eq!(server_id[..], SERVER_ID[1]);
8475        assert_eq!(*transmission_count, 1);
8476
8477        for count in 2..=(REQUEST_MAX_RC + 1) {
8478            assert_matches!(
8479                &client.handle_timeout(ClientTimerType::Retransmission, time)[..],
8480               [
8481                    Action::SendMessage(buf),
8482                    // `_timeout` is not checked because retransmission timeout
8483                    // calculation is covered in its respective test.
8484                    Action::ScheduleTimer(ClientTimerType::Retransmission, _timeout)
8485               ] if testutil::msg_type(buf) == v6::MessageType::Request
8486            );
8487            let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8488                &client;
8489            let Requesting {
8490                client_id: _,
8491                non_temporary_addresses: _,
8492                delegated_prefixes: _,
8493                server_id,
8494                collected_advertise,
8495                first_request_time: _,
8496                retrans_timeout: _,
8497                transmission_count,
8498                solicit_max_rt: _,
8499            } = assert_matches!(state, Some(ClientState::Requesting(requesting)) => requesting);
8500            assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
8501            assert_eq!(server_id[..], SERVER_ID[1]);
8502            assert_eq!(*transmission_count, count);
8503        }
8504
8505        // When the retransmission count reaches REQUEST_MAX_RC, and the client
8506        // does not have information about another server, the client should
8507        // restart server discovery.
8508        assert_matches!(
8509            &client.handle_timeout(ClientTimerType::Retransmission, time)[..],
8510            [
8511                Action::CancelTimer(ClientTimerType::Retransmission),
8512                Action::CancelTimer(ClientTimerType::Refresh),
8513                Action::CancelTimer(ClientTimerType::Renew),
8514                Action::CancelTimer(ClientTimerType::Rebind),
8515                Action::CancelTimer(ClientTimerType::RestartServerDiscovery),
8516                Action::SendMessage(buf),
8517                Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
8518            ] => {
8519                assert_eq!(testutil::msg_type(buf), v6::MessageType::Solicit);
8520                assert_eq!(*instant, time.add(INITIAL_SOLICIT_TIMEOUT));
8521            }
8522        );
8523        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } = client;
8524        assert_matches!(state,
8525            Some(ClientState::ServerDiscovery(ServerDiscovery {
8526                client_id: _,
8527                configured_non_temporary_addresses: _,
8528                configured_delegated_prefixes: _,
8529                first_solicit_time: _,
8530                retrans_timeout: _,
8531                solicit_max_rt: _,
8532                collected_advertise,
8533                collected_sol_max_rt: _,
8534            })) if collected_advertise.is_empty()
8535        );
8536    }
8537
8538    // Test 4-msg exchange for assignment.
8539    #[test]
8540    fn assignment() {
8541        let now = Instant::now();
8542        let (client, actions) = testutil::assign_and_assert(
8543            &(CLIENT_ID.into()),
8544            SERVER_ID[0],
8545            CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]
8546                .iter()
8547                .copied()
8548                .map(TestIaNa::new_default)
8549                .collect(),
8550            CONFIGURED_DELEGATED_PREFIXES[0..2]
8551                .iter()
8552                .copied()
8553                .map(TestIaPd::new_default)
8554                .collect(),
8555            &[],
8556            StepRng::new(u64::MAX / 2, 0),
8557            now,
8558        );
8559
8560        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
8561            &client;
8562        let Assigned {
8563            client_id: _,
8564            non_temporary_addresses: _,
8565            delegated_prefixes: _,
8566            server_id: _,
8567            dns_servers: _,
8568            solicit_max_rt: _,
8569            _marker,
8570        } = assert_matches!(
8571            state,
8572            Some(ClientState::Assigned(assigned)) => assigned
8573        );
8574        assert_matches!(
8575            &actions[..],
8576            [
8577                Action::CancelTimer(ClientTimerType::Retransmission),
8578                Action::ScheduleTimer(ClientTimerType::Renew, t1),
8579                Action::ScheduleTimer(ClientTimerType::Rebind, t2),
8580                Action::IaNaUpdates(iana_updates),
8581                Action::IaPdUpdates(iapd_updates),
8582                Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
8583            ] => {
8584                assert_eq!(*t1, now.add(Duration::from_secs(T1.get().into())));
8585                assert_eq!(*t2, now.add(Duration::from_secs(T2.get().into())));
8586                assert_eq!(
8587                    *restart_time,
8588                    now.add(Duration::from_secs(VALID_LIFETIME.get().into())),
8589                );
8590                assert_eq!(
8591                    iana_updates,
8592                    &(0..).map(v6::IAID::new)
8593                        .zip(CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2].iter().cloned())
8594                        .map(|(iaid, value)| (
8595                            iaid,
8596                            HashMap::from([(value, IaValueUpdateKind::Added(Lifetimes::new_default()))])
8597                        ))
8598                        .collect::<HashMap<_, _>>(),
8599                );
8600                assert_eq!(
8601                    iapd_updates,
8602                    &(0..).map(v6::IAID::new)
8603                        .zip(CONFIGURED_DELEGATED_PREFIXES[0..2].iter().cloned())
8604                        .map(|(iaid, value)| (
8605                            iaid,
8606                            HashMap::from([(value, IaValueUpdateKind::Added(Lifetimes::new_default()))])
8607                        ))
8608                        .collect::<HashMap<_, _>>(),
8609                );
8610            }
8611        );
8612    }
8613
8614    #[test]
8615    fn assigned_get_dns_servers() {
8616        let now = Instant::now();
8617        let (client, actions) = testutil::assign_and_assert(
8618            &(CLIENT_ID.into()),
8619            SERVER_ID[0],
8620            vec![TestIaNa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[0])],
8621            Default::default(), /* delegated_prefixes_to_assign */
8622            &DNS_SERVERS,
8623            StepRng::new(u64::MAX / 2, 0),
8624            now,
8625        );
8626        assert_matches!(
8627            &actions[..],
8628            [
8629                Action::CancelTimer(ClientTimerType::Retransmission),
8630                Action::ScheduleTimer(ClientTimerType::Renew, t1),
8631                Action::ScheduleTimer(ClientTimerType::Rebind, t2),
8632                Action::UpdateDnsServers(dns_servers),
8633                Action::IaNaUpdates(iana_updates),
8634                Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
8635            ] => {
8636                assert_eq!(dns_servers[..], DNS_SERVERS);
8637                assert_eq!(*t1, now.add(Duration::from_secs(T1.get().into())));
8638                assert_eq!(*t2, now.add(Duration::from_secs(T2.get().into())));
8639                assert_eq!(
8640                    *restart_time,
8641                    now.add(Duration::from_secs(VALID_LIFETIME.get().into())),
8642                );
8643                assert_eq!(
8644                    iana_updates,
8645                    &HashMap::from([
8646                        (
8647                            v6::IAID::new(0),
8648                            HashMap::from([(
8649                                CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
8650                                IaValueUpdateKind::Added(Lifetimes::new_default()),
8651                            )]),
8652                        ),
8653                    ]),
8654                );
8655            }
8656        );
8657        assert_eq!(client.get_dns_servers()[..], DNS_SERVERS);
8658    }
8659
8660    #[test]
8661    fn update_sol_max_rt_on_reply_to_request() {
8662        let options_to_request = vec![];
8663        let configured_non_temporary_addresses = testutil::to_configured_addresses(1, vec![]);
8664        let mut rng = StepRng::new(u64::MAX / 2, 0);
8665        let time = Instant::now();
8666        let Transition { state, actions: _, transaction_id } = Requesting::start(
8667            CLIENT_ID.into(),
8668            SERVER_ID[0].to_vec(),
8669            advertise_to_ia_entries(
8670                testutil::to_default_ias_map(&CONFIGURED_NON_TEMPORARY_ADDRESSES[0..1]),
8671                configured_non_temporary_addresses.clone(),
8672            ),
8673            Default::default(), /* delegated_prefixes */
8674            &options_to_request[..],
8675            BinaryHeap::new(),
8676            MAX_SOLICIT_TIMEOUT,
8677            &mut rng,
8678            time,
8679        );
8680        {
8681            let Requesting {
8682                collected_advertise,
8683                solicit_max_rt,
8684                client_id: _,
8685                non_temporary_addresses: _,
8686                delegated_prefixes: _,
8687                server_id: _,
8688                first_request_time: _,
8689                retrans_timeout: _,
8690                transmission_count: _,
8691            } = assert_matches!(&state, ClientState::Requesting(requesting) => requesting);
8692            assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
8693            assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
8694        }
8695        let received_sol_max_rt = 4800;
8696
8697        // If the reply does not contain a server ID, the reply should be
8698        // discarded and the `solicit_max_rt` should not be updated.
8699        let iana_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
8700            CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
8701            60,
8702            120,
8703            &[],
8704        ))];
8705        let options = [
8706            v6::DhcpOption::ClientId(&CLIENT_ID),
8707            v6::DhcpOption::Iana(v6::IanaSerializer::new(
8708                v6::IAID::new(0),
8709                T1.get(),
8710                T2.get(),
8711                &iana_options,
8712            )),
8713            v6::DhcpOption::SolMaxRt(received_sol_max_rt),
8714        ];
8715        let request_transaction_id = transaction_id.unwrap();
8716        let builder =
8717            v6::MessageBuilder::new(v6::MessageType::Reply, request_transaction_id, &options);
8718        let mut buf = vec![0; builder.bytes_len()];
8719        builder.serialize(&mut buf);
8720        let mut buf = &buf[..]; // Implements BufferView.
8721        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8722        let Transition { state, actions: _, transaction_id: _ } =
8723            state.reply_message_received(&options_to_request, &mut rng, msg, time);
8724        {
8725            let Requesting {
8726                collected_advertise,
8727                solicit_max_rt,
8728                client_id: _,
8729                non_temporary_addresses: _,
8730                delegated_prefixes: _,
8731                server_id: _,
8732                first_request_time: _,
8733                retrans_timeout: _,
8734                transmission_count: _,
8735            } = assert_matches!(&state, ClientState::Requesting(requesting) => requesting);
8736            assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
8737            assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
8738        }
8739
8740        // If the reply has a different client ID than the test client's client ID,
8741        // the `solicit_max_rt` should not be updated.
8742        let options = [
8743            v6::DhcpOption::ServerId(&SERVER_ID[0]),
8744            v6::DhcpOption::ClientId(&MISMATCHED_CLIENT_ID),
8745            v6::DhcpOption::Iana(v6::IanaSerializer::new(
8746                v6::IAID::new(0),
8747                T1.get(),
8748                T2.get(),
8749                &iana_options,
8750            )),
8751            v6::DhcpOption::SolMaxRt(received_sol_max_rt),
8752        ];
8753        let builder =
8754            v6::MessageBuilder::new(v6::MessageType::Reply, request_transaction_id, &options);
8755        let mut buf = vec![0; builder.bytes_len()];
8756        builder.serialize(&mut buf);
8757        let mut buf = &buf[..]; // Implements BufferView.
8758        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8759        let Transition { state, actions: _, transaction_id: _ } =
8760            state.reply_message_received(&options_to_request, &mut rng, msg, time);
8761        {
8762            let Requesting {
8763                collected_advertise,
8764                solicit_max_rt,
8765                client_id: _,
8766                non_temporary_addresses: _,
8767                delegated_prefixes: _,
8768                server_id: _,
8769                first_request_time: _,
8770                retrans_timeout: _,
8771                transmission_count: _,
8772            } = assert_matches!(&state, ClientState::Requesting(requesting) => requesting);
8773            assert!(collected_advertise.is_empty(), "{:?}", collected_advertise);
8774            assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
8775        }
8776
8777        // If the client receives a valid reply containing a SOL_MAX_RT option,
8778        // the `solicit_max_rt` should be updated.
8779        let options = [
8780            v6::DhcpOption::ServerId(&SERVER_ID[0]),
8781            v6::DhcpOption::ClientId(&CLIENT_ID),
8782            v6::DhcpOption::Iana(v6::IanaSerializer::new(
8783                v6::IAID::new(0),
8784                T1.get(),
8785                T2.get(),
8786                &iana_options,
8787            )),
8788            v6::DhcpOption::SolMaxRt(received_sol_max_rt),
8789        ];
8790        let builder =
8791            v6::MessageBuilder::new(v6::MessageType::Reply, request_transaction_id, &options);
8792        let mut buf = vec![0; builder.bytes_len()];
8793        builder.serialize(&mut buf);
8794        let mut buf = &buf[..]; // Implements BufferView.
8795        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
8796        let Transition { state, actions: _, transaction_id: _ } =
8797            state.reply_message_received(&options_to_request, &mut rng, msg, time);
8798        {
8799            let Assigned {
8800                solicit_max_rt,
8801                client_id: _,
8802                non_temporary_addresses: _,
8803                delegated_prefixes: _,
8804                server_id: _,
8805                dns_servers: _,
8806                _marker,
8807            } = assert_matches!(&state, ClientState::Assigned(assigned) => assigned);
8808            assert_eq!(*solicit_max_rt, Duration::from_secs(received_sol_max_rt.into()));
8809        }
8810    }
8811
8812    struct RenewRebindTest {
8813        send_and_assert: fn(
8814            &ClientDuid,
8815            [u8; TEST_SERVER_ID_LEN],
8816            Vec<TestIaNa>,
8817            Vec<TestIaPd>,
8818            Option<&[Ipv6Addr]>,
8819            v6::NonZeroOrMaxU32,
8820            v6::NonZeroOrMaxU32,
8821            v6::NonZeroTimeValue,
8822            StepRng,
8823            Instant,
8824        ) -> ClientStateMachine<Instant, StepRng>,
8825        message_type: v6::MessageType,
8826        expect_server_id: bool,
8827        with_state: fn(&Option<ClientState<Instant>>) -> &RenewingOrRebindingInner<Instant>,
8828        allow_response_from_any_server: bool,
8829    }
8830
8831    const RENEW_TEST: RenewRebindTest = RenewRebindTest {
8832        send_and_assert: testutil::send_renew_and_assert,
8833        message_type: v6::MessageType::Renew,
8834        expect_server_id: true,
8835        with_state: |state| {
8836            assert_matches!(
8837                state,
8838                Some(ClientState::Renewing(RenewingOrRebinding(inner))) => inner
8839            )
8840        },
8841        allow_response_from_any_server: false,
8842    };
8843
8844    const REBIND_TEST: RenewRebindTest = RenewRebindTest {
8845        send_and_assert: testutil::send_rebind_and_assert,
8846        message_type: v6::MessageType::Rebind,
8847        expect_server_id: false,
8848        with_state: |state| {
8849            assert_matches!(
8850                state,
8851                Some(ClientState::Rebinding(RenewingOrRebinding(inner))) => inner
8852            )
8853        },
8854        allow_response_from_any_server: true,
8855    };
8856
8857    struct RenewRebindSendTestCase {
8858        ia_nas: Vec<TestIaNa>,
8859        ia_pds: Vec<TestIaPd>,
8860    }
8861
8862    impl RenewRebindSendTestCase {
8863        fn single_value_per_ia() -> RenewRebindSendTestCase {
8864            RenewRebindSendTestCase {
8865                ia_nas: CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]
8866                    .into_iter()
8867                    .map(|&addr| TestIaNa::new_default(addr))
8868                    .collect(),
8869                ia_pds: CONFIGURED_DELEGATED_PREFIXES[0..2]
8870                    .into_iter()
8871                    .map(|&addr| TestIaPd::new_default(addr))
8872                    .collect(),
8873            }
8874        }
8875
8876        fn multiple_values_per_ia() -> RenewRebindSendTestCase {
8877            RenewRebindSendTestCase {
8878                ia_nas: vec![TestIaNa::new_default_with_values(
8879                    CONFIGURED_NON_TEMPORARY_ADDRESSES
8880                        .into_iter()
8881                        .map(|a| (a, Lifetimes::new_default()))
8882                        .collect(),
8883                )],
8884                ia_pds: vec![TestIaPd::new_default_with_values(
8885                    CONFIGURED_DELEGATED_PREFIXES
8886                        .into_iter()
8887                        .map(|a| (a, Lifetimes::new_default()))
8888                        .collect(),
8889                )],
8890            }
8891        }
8892    }
8893
8894    #[test_case(
8895        RENEW_TEST,
8896        RenewRebindSendTestCase::single_value_per_ia(); "renew single value per IA")]
8897    #[test_case(
8898        RENEW_TEST,
8899        RenewRebindSendTestCase::multiple_values_per_ia(); "renew multiple value per IA")]
8900    #[test_case(
8901        REBIND_TEST,
8902        RenewRebindSendTestCase::single_value_per_ia(); "rebind single value per IA")]
8903    #[test_case(
8904        REBIND_TEST,
8905        RenewRebindSendTestCase::multiple_values_per_ia(); "rebind multiple value per IA")]
8906    fn send(
8907        RenewRebindTest {
8908            send_and_assert,
8909            message_type: _,
8910            expect_server_id: _,
8911            with_state: _,
8912            allow_response_from_any_server: _,
8913        }: RenewRebindTest,
8914        RenewRebindSendTestCase { ia_nas, ia_pds }: RenewRebindSendTestCase,
8915    ) {
8916        let _client = send_and_assert(
8917            &(CLIENT_ID.into()),
8918            SERVER_ID[0],
8919            ia_nas,
8920            ia_pds,
8921            None,
8922            T1,
8923            T2,
8924            v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
8925            StepRng::new(u64::MAX / 2, 0),
8926            Instant::now(),
8927        );
8928    }
8929
8930    #[test_case(RENEW_TEST)]
8931    #[test_case(REBIND_TEST)]
8932    fn get_dns_server(
8933        RenewRebindTest {
8934            send_and_assert,
8935            message_type: _,
8936            expect_server_id: _,
8937            with_state: _,
8938            allow_response_from_any_server: _,
8939        }: RenewRebindTest,
8940    ) {
8941        let client = send_and_assert(
8942            &(CLIENT_ID.into()),
8943            SERVER_ID[0],
8944            CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]
8945                .into_iter()
8946                .map(|&addr| TestIaNa::new_default(addr))
8947                .collect(),
8948            Default::default(), /* delegated_prefixes_to_assign */
8949            Some(&DNS_SERVERS),
8950            T1,
8951            T2,
8952            v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
8953            StepRng::new(u64::MAX / 2, 0),
8954            Instant::now(),
8955        );
8956        assert_eq!(client.get_dns_servers()[..], DNS_SERVERS);
8957    }
8958
8959    struct ScheduleRenewAndRebindTimersAfterAssignmentTestCase {
8960        ia_na_t1: v6::TimeValue,
8961        ia_na_t2: v6::TimeValue,
8962        ia_pd_t1: v6::TimeValue,
8963        ia_pd_t2: v6::TimeValue,
8964        expected_timer_actions: fn(Instant) -> [Action<Instant>; 2],
8965        next_timer: Option<RenewRebindTestState>,
8966    }
8967
8968    // Make sure that both IA_NA and IA_PD is considered when calculating
8969    // renew/rebind timers.
8970    #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
8971        ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
8972        ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
8973        ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
8974        ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
8975        expected_timer_actions: |_| [
8976            Action::CancelTimer(ClientTimerType::Renew),
8977            Action::CancelTimer(ClientTimerType::Rebind),
8978        ],
8979        next_timer: None,
8980    }; "all infinite time values")]
8981    #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
8982        ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
8983        ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8984        ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
8985        ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
8986        expected_timer_actions: |time| [
8987            Action::ScheduleTimer(
8988                ClientTimerType::Renew,
8989                time.add(Duration::from_secs(T1.get().into())),
8990            ),
8991            Action::ScheduleTimer(
8992                ClientTimerType::Rebind,
8993                time.add(Duration::from_secs(T2.get().into())),
8994            ),
8995        ],
8996        next_timer: Some(RENEW_TEST_STATE),
8997    }; "all finite time values")]
8998    #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
8999        ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
9000        ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
9001        ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
9002        ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
9003        expected_timer_actions: |time| [
9004            // Skip Renew and just go to Rebind when T2 == T1.
9005            Action::CancelTimer(ClientTimerType::Renew),
9006            Action::ScheduleTimer(
9007                ClientTimerType::Rebind,
9008                time.add(Duration::from_secs(T2.get().into())),
9009            ),
9010        ],
9011        next_timer: Some(REBIND_TEST_STATE),
9012    }; "finite T1 equals finite T2")]
9013    #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
9014        ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
9015        ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9016        ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9017        ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9018        expected_timer_actions: |time| [
9019            Action::ScheduleTimer(
9020                ClientTimerType::Renew,
9021                time.add(Duration::from_secs(T1.get().into())),
9022            ),
9023            Action::CancelTimer(ClientTimerType::Rebind),
9024        ],
9025        next_timer: Some(RENEW_TEST_STATE),
9026    }; "finite IA_NA T1")]
9027    #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
9028        ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
9029        ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
9030        ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9031        ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9032        expected_timer_actions: |time| [
9033            Action::ScheduleTimer(
9034                ClientTimerType::Renew,
9035                time.add(Duration::from_secs(T1.get().into())),
9036            ),
9037            Action::ScheduleTimer(
9038                ClientTimerType::Rebind,
9039                time.add(Duration::from_secs(T2.get().into())),
9040            ),
9041        ],
9042        next_timer: Some(RENEW_TEST_STATE),
9043    }; "finite IA_NA T1 and T2")]
9044    #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
9045        ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9046        ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9047        ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
9048        ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9049        expected_timer_actions: |time| [
9050            Action::ScheduleTimer(
9051                ClientTimerType::Renew,
9052                time.add(Duration::from_secs(T1.get().into())),
9053            ),
9054            Action::CancelTimer(ClientTimerType::Rebind),
9055        ],
9056        next_timer: Some(RENEW_TEST_STATE),
9057    }; "finite IA_PD t1")]
9058    #[test_case(ScheduleRenewAndRebindTimersAfterAssignmentTestCase{
9059        ia_na_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9060        ia_na_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
9061        ia_pd_t1: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T1)),
9062        ia_pd_t2: v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(T2)),
9063        expected_timer_actions: |time| [
9064            Action::ScheduleTimer(
9065                ClientTimerType::Renew,
9066                time.add(Duration::from_secs(T1.get().into())),
9067            ),
9068            Action::ScheduleTimer(
9069                ClientTimerType::Rebind,
9070                time.add(Duration::from_secs(T2.get().into())),
9071            ),
9072        ],
9073        next_timer: Some(RENEW_TEST_STATE),
9074    }; "finite IA_PD T1 and T2")]
9075    fn schedule_renew_and_rebind_timers_after_assignment(
9076        ScheduleRenewAndRebindTimersAfterAssignmentTestCase {
9077            ia_na_t1,
9078            ia_na_t2,
9079            ia_pd_t1,
9080            ia_pd_t2,
9081            expected_timer_actions,
9082            next_timer,
9083        }: ScheduleRenewAndRebindTimersAfterAssignmentTestCase,
9084    ) {
9085        fn get_ia_and_updates<V: IaValue>(
9086            t1: v6::TimeValue,
9087            t2: v6::TimeValue,
9088            value: V,
9089        ) -> (TestIa<V>, HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>>) {
9090            (
9091                TestIa { t1, t2, ..TestIa::new_default(value) },
9092                HashMap::from([(
9093                    v6::IAID::new(0),
9094                    HashMap::from([(value, IaValueUpdateKind::Added(Lifetimes::new_default()))]),
9095                )]),
9096            )
9097        }
9098
9099        let (iana, iana_updates) =
9100            get_ia_and_updates(ia_na_t1, ia_na_t2, CONFIGURED_NON_TEMPORARY_ADDRESSES[0]);
9101        let (iapd, iapd_updates) =
9102            get_ia_and_updates(ia_pd_t1, ia_pd_t2, CONFIGURED_DELEGATED_PREFIXES[0]);
9103        let iana = vec![iana];
9104        let iapd = vec![iapd];
9105        let now = Instant::now();
9106        let (client, actions) = testutil::assign_and_assert(
9107            &(CLIENT_ID.into()),
9108            SERVER_ID[0],
9109            iana.clone(),
9110            iapd.clone(),
9111            &[],
9112            StepRng::new(u64::MAX / 2, 0),
9113            now,
9114        );
9115        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
9116            &client;
9117        let Assigned {
9118            client_id: _,
9119            non_temporary_addresses: _,
9120            delegated_prefixes: _,
9121            server_id: _,
9122            dns_servers: _,
9123            solicit_max_rt: _,
9124            _marker,
9125        } = assert_matches!(
9126            state,
9127            Some(ClientState::Assigned(assigned)) => assigned
9128        );
9129
9130        assert_eq!(
9131            actions,
9132            [Action::CancelTimer(ClientTimerType::Retransmission)]
9133                .into_iter()
9134                .chain(expected_timer_actions(now))
9135                .chain((!iana_updates.is_empty()).then(|| Action::IaNaUpdates(iana_updates)))
9136                .chain((!iapd_updates.is_empty()).then(|| Action::IaPdUpdates(iapd_updates)))
9137                .chain([Action::ScheduleTimer(
9138                    ClientTimerType::RestartServerDiscovery,
9139                    now.add(Duration::from_secs(VALID_LIFETIME.get().into())),
9140                ),])
9141                .collect::<Vec<_>>()
9142        );
9143
9144        let _client = if let Some(next_timer) = next_timer {
9145            handle_renew_or_rebind_timer(
9146                client,
9147                &CLIENT_ID,
9148                SERVER_ID[0],
9149                iana,
9150                iapd,
9151                &[],
9152                &[],
9153                Instant::now(),
9154                next_timer,
9155            )
9156        } else {
9157            client
9158        };
9159    }
9160
9161    #[test_case(RENEW_TEST)]
9162    #[test_case(REBIND_TEST)]
9163    fn retransmit(
9164        RenewRebindTest {
9165            send_and_assert,
9166            message_type,
9167            expect_server_id,
9168            with_state,
9169            allow_response_from_any_server: _,
9170        }: RenewRebindTest,
9171    ) {
9172        let non_temporary_addresses_to_assign = CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]
9173            .into_iter()
9174            .map(|&addr| TestIaNa::new_default(addr))
9175            .collect::<Vec<_>>();
9176        let delegated_prefixes_to_assign = CONFIGURED_DELEGATED_PREFIXES[0..2]
9177            .into_iter()
9178            .map(|&addr| TestIaPd::new_default(addr))
9179            .collect::<Vec<_>>();
9180        let time = Instant::now();
9181        let mut client = send_and_assert(
9182            &(CLIENT_ID.into()),
9183            SERVER_ID[0],
9184            non_temporary_addresses_to_assign.clone(),
9185            delegated_prefixes_to_assign.clone(),
9186            None,
9187            T1,
9188            T2,
9189            v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9190            StepRng::new(u64::MAX / 2, 0),
9191            time,
9192        );
9193        let ClientStateMachine { transaction_id, options_to_request: _, state, rng: _ } = &client;
9194        let expected_transaction_id = *transaction_id;
9195        let RenewingOrRebindingInner {
9196            client_id: _,
9197            non_temporary_addresses: _,
9198            delegated_prefixes: _,
9199            server_id: _,
9200            dns_servers: _,
9201            start_time: _,
9202            retrans_timeout: _,
9203            solicit_max_rt: _,
9204        } = with_state(state);
9205
9206        // Assert renew is retransmitted on retransmission timeout.
9207        let actions = client.handle_timeout(ClientTimerType::Retransmission, time);
9208        let buf = assert_matches!(
9209            &actions[..],
9210            [
9211                Action::SendMessage(buf),
9212                Action::ScheduleTimer(ClientTimerType::Retransmission, timeout)
9213            ] => {
9214                assert_eq!(*timeout, time.add(2 * INITIAL_RENEW_TIMEOUT));
9215                buf
9216            }
9217        );
9218        let ClientStateMachine { transaction_id, options_to_request: _, state, rng: _ } = &client;
9219        // Check that the retransmitted renew is part of the same transaction.
9220        assert_eq!(*transaction_id, expected_transaction_id);
9221        {
9222            let RenewingOrRebindingInner {
9223                client_id,
9224                server_id,
9225                dns_servers,
9226                solicit_max_rt,
9227                non_temporary_addresses: _,
9228                delegated_prefixes: _,
9229                start_time: _,
9230                retrans_timeout: _,
9231            } = with_state(state);
9232            assert_eq!(client_id.as_slice(), &CLIENT_ID);
9233            assert_eq!(server_id[..], SERVER_ID[0]);
9234            assert_eq!(dns_servers, &[] as &[Ipv6Addr]);
9235            assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
9236        }
9237        let expected_non_temporary_addresses: HashMap<v6::IAID, HashSet<Ipv6Addr>> = (0..)
9238            .map(v6::IAID::new)
9239            .zip(
9240                non_temporary_addresses_to_assign
9241                    .iter()
9242                    .map(|TestIaNa { values, t1: _, t2: _ }| values.keys().cloned().collect()),
9243            )
9244            .collect();
9245        let expected_delegated_prefixes: HashMap<v6::IAID, HashSet<Subnet<Ipv6Addr>>> = (0..)
9246            .map(v6::IAID::new)
9247            .zip(
9248                delegated_prefixes_to_assign
9249                    .iter()
9250                    .map(|TestIaPd { values, t1: _, t2: _ }| values.keys().cloned().collect()),
9251            )
9252            .collect();
9253        testutil::assert_outgoing_stateful_message(
9254            &buf,
9255            message_type,
9256            &CLIENT_ID,
9257            expect_server_id.then(|| &SERVER_ID[0]),
9258            &[],
9259            &expected_non_temporary_addresses,
9260            &expected_delegated_prefixes,
9261        );
9262    }
9263
9264    #[test_case(
9265        RENEW_TEST,
9266        &SERVER_ID[0],
9267        &SERVER_ID[0],
9268        RenewRebindSendTestCase::single_value_per_ia()
9269    )]
9270    #[test_case(
9271        REBIND_TEST,
9272        &SERVER_ID[0],
9273        &SERVER_ID[0],
9274        RenewRebindSendTestCase::single_value_per_ia()
9275    )]
9276    #[test_case(
9277        RENEW_TEST,
9278        &SERVER_ID[0],
9279        &SERVER_ID[1],
9280        RenewRebindSendTestCase::single_value_per_ia()
9281    )]
9282    #[test_case(
9283        REBIND_TEST,
9284        &SERVER_ID[0],
9285        &SERVER_ID[1],
9286        RenewRebindSendTestCase::single_value_per_ia()
9287    )]
9288    #[test_case(
9289        RENEW_TEST,
9290        &SERVER_ID[0],
9291        &SERVER_ID[0],
9292        RenewRebindSendTestCase::multiple_values_per_ia()
9293    )]
9294    #[test_case(
9295        REBIND_TEST,
9296        &SERVER_ID[0],
9297        &SERVER_ID[0],
9298        RenewRebindSendTestCase::multiple_values_per_ia()
9299    )]
9300    #[test_case(
9301        RENEW_TEST,
9302        &SERVER_ID[0],
9303        &SERVER_ID[1],
9304        RenewRebindSendTestCase::multiple_values_per_ia()
9305    )]
9306    #[test_case(
9307        REBIND_TEST,
9308        &SERVER_ID[0],
9309        &SERVER_ID[1],
9310        RenewRebindSendTestCase::multiple_values_per_ia()
9311    )]
9312    fn receive_reply_extends_lifetime(
9313        RenewRebindTest {
9314            send_and_assert,
9315            message_type: _,
9316            expect_server_id: _,
9317            with_state,
9318            allow_response_from_any_server,
9319        }: RenewRebindTest,
9320        original_server_id: &[u8; TEST_SERVER_ID_LEN],
9321        reply_server_id: &[u8],
9322        RenewRebindSendTestCase { ia_nas, ia_pds }: RenewRebindSendTestCase,
9323    ) {
9324        let time = Instant::now();
9325        let mut client = send_and_assert(
9326            &(CLIENT_ID.into()),
9327            original_server_id.clone(),
9328            ia_nas.clone(),
9329            ia_pds.clone(),
9330            None,
9331            T1,
9332            T2,
9333            v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9334            StepRng::new(u64::MAX / 2, 0),
9335            time,
9336        );
9337        let ClientStateMachine { transaction_id, options_to_request: _, state, rng: _ } = &client;
9338        let buf = TestMessageBuilder {
9339            transaction_id: *transaction_id,
9340            message_type: v6::MessageType::Reply,
9341            client_id: &CLIENT_ID,
9342            server_id: reply_server_id,
9343            preference: None,
9344            dns_servers: None,
9345            ia_nas: (0..).map(v6::IAID::new).zip(ia_nas.iter().map(
9346                |TestIa { values, t1: _, t2: _ }| {
9347                    TestIa::new_renewed_default_with_values(values.keys().cloned())
9348                },
9349            )),
9350            ia_pds: (0..).map(v6::IAID::new).zip(ia_pds.iter().map(
9351                |TestIa { values, t1: _, t2: _ }| {
9352                    TestIa::new_renewed_default_with_values(values.keys().cloned())
9353                },
9354            )),
9355        }
9356        .build();
9357        let mut buf = &buf[..]; // Implements BufferView.
9358        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
9359
9360        // Make sure we are in renewing/rebinding before we handle the message.
9361        let original_state = with_state(state).clone();
9362
9363        let actions = client.handle_message_receive(msg, time);
9364        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
9365            &client;
9366
9367        if original_server_id.as_slice() != reply_server_id && !allow_response_from_any_server {
9368            // Renewing does not allow us to receive replies from a different
9369            // server but Rebinding does. If we aren't allowed to accept a
9370            // response from a different server, just make sure we are in the
9371            // same state.
9372            let RenewingOrRebindingInner {
9373                client_id: original_client_id,
9374                non_temporary_addresses: original_non_temporary_addresses,
9375                delegated_prefixes: original_delegated_prefixes,
9376                server_id: original_server_id,
9377                dns_servers: original_dns_servers,
9378                start_time: original_start_time,
9379                retrans_timeout: original_retrans_timeout,
9380                solicit_max_rt: original_solicit_max_rt,
9381            } = original_state;
9382            let RenewingOrRebindingInner {
9383                client_id: new_client_id,
9384                non_temporary_addresses: new_non_temporary_addresses,
9385                delegated_prefixes: new_delegated_prefixes,
9386                server_id: new_server_id,
9387                dns_servers: new_dns_servers,
9388                start_time: new_start_time,
9389                retrans_timeout: new_retrans_timeout,
9390                solicit_max_rt: new_solicit_max_rt,
9391            } = with_state(state);
9392            assert_eq!(&original_client_id, new_client_id);
9393            assert_eq!(&original_non_temporary_addresses, new_non_temporary_addresses);
9394            assert_eq!(&original_delegated_prefixes, new_delegated_prefixes);
9395            assert_eq!(&original_server_id, new_server_id);
9396            assert_eq!(&original_dns_servers, new_dns_servers);
9397            assert_eq!(&original_start_time, new_start_time);
9398            assert_eq!(&original_retrans_timeout, new_retrans_timeout);
9399            assert_eq!(&original_solicit_max_rt, new_solicit_max_rt);
9400            assert_eq!(actions, []);
9401            return;
9402        }
9403
9404        let expected_non_temporary_addresses = (0..)
9405            .map(v6::IAID::new)
9406            .zip(ia_nas.iter().map(|TestIa { values, t1: _, t2: _ }| {
9407                AddressEntry::Assigned(
9408                    values
9409                        .keys()
9410                        .cloned()
9411                        .map(|value| {
9412                            (
9413                                value,
9414                                LifetimesInfo {
9415                                    lifetimes: Lifetimes::new_renewed(),
9416                                    updated_at: time,
9417                                },
9418                            )
9419                        })
9420                        .collect(),
9421                )
9422            }))
9423            .collect();
9424        let expected_delegated_prefixes = (0..)
9425            .map(v6::IAID::new)
9426            .zip(ia_pds.iter().map(|TestIa { values, t1: _, t2: _ }| {
9427                PrefixEntry::Assigned(
9428                    values
9429                        .keys()
9430                        .cloned()
9431                        .map(|value| {
9432                            (
9433                                value,
9434                                LifetimesInfo {
9435                                    lifetimes: Lifetimes::new_renewed(),
9436                                    updated_at: time,
9437                                },
9438                            )
9439                        })
9440                        .collect(),
9441                )
9442            }))
9443            .collect();
9444        assert_matches!(
9445            &state,
9446            Some(ClientState::Assigned(Assigned {
9447                client_id,
9448                non_temporary_addresses,
9449                delegated_prefixes,
9450                server_id,
9451                dns_servers,
9452                solicit_max_rt,
9453                _marker,
9454            })) => {
9455            assert_eq!(client_id.as_slice(), &CLIENT_ID);
9456                assert_eq!(non_temporary_addresses, &expected_non_temporary_addresses);
9457                assert_eq!(delegated_prefixes, &expected_delegated_prefixes);
9458                assert_eq!(server_id.as_slice(), reply_server_id);
9459                assert_eq!(dns_servers.as_slice(), &[] as &[Ipv6Addr]);
9460                assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
9461            }
9462        );
9463        assert_matches!(
9464            &actions[..],
9465            [
9466                Action::CancelTimer(ClientTimerType::Retransmission),
9467                Action::ScheduleTimer(ClientTimerType::Renew, t1),
9468                Action::ScheduleTimer(ClientTimerType::Rebind, t2),
9469                Action::IaNaUpdates(iana_updates),
9470                Action::IaPdUpdates(iapd_updates),
9471                Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
9472            ] => {
9473                assert_eq!(*t1, time.add(Duration::from_secs(RENEWED_T1.get().into())));
9474                assert_eq!(*t2, time.add(Duration::from_secs(RENEWED_T2.get().into())));
9475                assert_eq!(
9476                    *restart_time,
9477                    time.add(Duration::from_secs(RENEWED_VALID_LIFETIME.get().into()))
9478                );
9479
9480                fn get_updates<V: IaValue>(ias: Vec<TestIa<V>>) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
9481                    (0..).map(v6::IAID::new).zip(ias.into_iter().map(
9482                        |TestIa { values, t1: _, t2: _ }| {
9483                            values.into_keys()
9484                                .map(|value| (
9485                                    value,
9486                                    IaValueUpdateKind::UpdatedLifetimes(Lifetimes::new_renewed())
9487                                ))
9488                                .collect()
9489                        },
9490                    )).collect()
9491                }
9492
9493                assert_eq!(iana_updates, &get_updates(ia_nas));
9494                assert_eq!(iapd_updates, &get_updates(ia_pds));
9495            }
9496        );
9497    }
9498
9499    // Tests that receiving a Reply with an error status code other than
9500    // UseMulticast results in only SOL_MAX_RT being updated, with the rest
9501    // of the message contents ignored.
9502    #[test_case(RENEW_TEST, v6::ErrorStatusCode::UnspecFail)]
9503    #[test_case(RENEW_TEST, v6::ErrorStatusCode::NoBinding)]
9504    #[test_case(RENEW_TEST, v6::ErrorStatusCode::NotOnLink)]
9505    #[test_case(RENEW_TEST, v6::ErrorStatusCode::NoAddrsAvail)]
9506    #[test_case(RENEW_TEST, v6::ErrorStatusCode::NoPrefixAvail)]
9507    #[test_case(REBIND_TEST, v6::ErrorStatusCode::UnspecFail)]
9508    #[test_case(REBIND_TEST, v6::ErrorStatusCode::NoBinding)]
9509    #[test_case(REBIND_TEST, v6::ErrorStatusCode::NotOnLink)]
9510    #[test_case(REBIND_TEST, v6::ErrorStatusCode::NoAddrsAvail)]
9511    #[test_case(REBIND_TEST, v6::ErrorStatusCode::NoPrefixAvail)]
9512    fn renewing_receive_reply_with_error_status(
9513        RenewRebindTest {
9514            send_and_assert,
9515            message_type: _,
9516            expect_server_id: _,
9517            with_state,
9518            allow_response_from_any_server: _,
9519        }: RenewRebindTest,
9520        error_status_code: v6::ErrorStatusCode,
9521    ) {
9522        let time = Instant::now();
9523        let addr = CONFIGURED_NON_TEMPORARY_ADDRESSES[0];
9524        let prefix = CONFIGURED_DELEGATED_PREFIXES[0];
9525        let mut client = send_and_assert(
9526            &(CLIENT_ID.into()),
9527            SERVER_ID[0],
9528            vec![TestIaNa::new_default(addr)],
9529            vec![TestIaPd::new_default(prefix)],
9530            None,
9531            T1,
9532            T2,
9533            v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9534            StepRng::new(u64::MAX / 2, 0),
9535            time,
9536        );
9537        let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
9538            &client;
9539        let ia_na_options = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
9540            addr,
9541            RENEWED_PREFERRED_LIFETIME.get(),
9542            RENEWED_VALID_LIFETIME.get(),
9543            &[],
9544        ))];
9545        let sol_max_rt = *VALID_MAX_SOLICIT_TIMEOUT_RANGE.start();
9546        let options = vec![
9547            v6::DhcpOption::ClientId(&CLIENT_ID),
9548            v6::DhcpOption::ServerId(&SERVER_ID[0]),
9549            v6::DhcpOption::StatusCode(error_status_code.into(), ""),
9550            v6::DhcpOption::Iana(v6::IanaSerializer::new(
9551                v6::IAID::new(0),
9552                RENEWED_T1.get(),
9553                RENEWED_T2.get(),
9554                &ia_na_options,
9555            )),
9556            v6::DhcpOption::SolMaxRt(sol_max_rt),
9557        ];
9558        let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
9559        let mut buf = vec![0; builder.bytes_len()];
9560        builder.serialize(&mut buf);
9561        let mut buf = &buf[..]; // Implements BufferView.
9562        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
9563        let actions = client.handle_message_receive(msg, time);
9564        assert_eq!(actions, &[]);
9565        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
9566            &client;
9567
9568        let RenewingOrRebindingInner {
9569            client_id,
9570            non_temporary_addresses,
9571            delegated_prefixes,
9572            server_id,
9573            dns_servers,
9574            start_time: _,
9575            retrans_timeout: _,
9576            solicit_max_rt: got_sol_max_rt,
9577        } = with_state(state);
9578        assert_eq!(client_id.as_slice(), &CLIENT_ID);
9579        fn expected_values<V: IaValue>(
9580            value: V,
9581            time: Instant,
9582        ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
9583            std::iter::once((
9584                v6::IAID::new(0),
9585                IaEntry::new_assigned(value, PREFERRED_LIFETIME, VALID_LIFETIME, time),
9586            ))
9587            .collect()
9588        }
9589        assert_eq!(*non_temporary_addresses, expected_values(addr, time));
9590        assert_eq!(*delegated_prefixes, expected_values(prefix, time));
9591        assert_eq!(*server_id, SERVER_ID[0]);
9592        assert_eq!(dns_servers, &[] as &[Ipv6Addr]);
9593        assert_eq!(*got_sol_max_rt, Duration::from_secs(sol_max_rt.into()));
9594        assert_matches!(&actions[..], []);
9595    }
9596
9597    struct ReceiveReplyWithMissingIasTestCase {
9598        present_ia_na_iaids: Vec<v6::IAID>,
9599        present_ia_pd_iaids: Vec<v6::IAID>,
9600    }
9601
9602    #[test_case(
9603        REBIND_TEST,
9604        ReceiveReplyWithMissingIasTestCase {
9605            present_ia_na_iaids: Vec::new(),
9606            present_ia_pd_iaids: Vec::new(),
9607        }; "none presenet")]
9608    #[test_case(
9609        RENEW_TEST,
9610        ReceiveReplyWithMissingIasTestCase {
9611            present_ia_na_iaids: vec![v6::IAID::new(0)],
9612            present_ia_pd_iaids: Vec::new(),
9613        }; "only one IA_NA present")]
9614    #[test_case(
9615        RENEW_TEST,
9616        ReceiveReplyWithMissingIasTestCase {
9617            present_ia_na_iaids: Vec::new(),
9618            present_ia_pd_iaids: vec![v6::IAID::new(1)],
9619        }; "only one IA_PD present")]
9620    #[test_case(
9621        REBIND_TEST,
9622        ReceiveReplyWithMissingIasTestCase {
9623            present_ia_na_iaids: vec![v6::IAID::new(0), v6::IAID::new(1)],
9624            present_ia_pd_iaids: Vec::new(),
9625        }; "only both IA_NAs present")]
9626    #[test_case(
9627        REBIND_TEST,
9628        ReceiveReplyWithMissingIasTestCase {
9629            present_ia_na_iaids: Vec::new(),
9630            present_ia_pd_iaids: vec![v6::IAID::new(0), v6::IAID::new(1)],
9631        }; "only both IA_PDs present")]
9632    #[test_case(
9633        REBIND_TEST,
9634        ReceiveReplyWithMissingIasTestCase {
9635            present_ia_na_iaids: vec![v6::IAID::new(1)],
9636            present_ia_pd_iaids: vec![v6::IAID::new(0), v6::IAID::new(1)],
9637        }; "both IA_PDs and one IA_NA present")]
9638    #[test_case(
9639        REBIND_TEST,
9640        ReceiveReplyWithMissingIasTestCase {
9641            present_ia_na_iaids: vec![v6::IAID::new(0), v6::IAID::new(1)],
9642            present_ia_pd_iaids: vec![v6::IAID::new(0)],
9643        }; "both IA_NAs and one IA_PD present")]
9644    fn receive_reply_with_missing_ias(
9645        RenewRebindTest {
9646            send_and_assert,
9647            message_type: _,
9648            expect_server_id: _,
9649            with_state,
9650            allow_response_from_any_server: _,
9651        }: RenewRebindTest,
9652        ReceiveReplyWithMissingIasTestCase {
9653            present_ia_na_iaids,
9654            present_ia_pd_iaids,
9655        }: ReceiveReplyWithMissingIasTestCase,
9656    ) {
9657        let non_temporary_addresses = &CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2];
9658        let delegated_prefixes = &CONFIGURED_DELEGATED_PREFIXES[0..2];
9659        let time = Instant::now();
9660        let mut client = send_and_assert(
9661            &(CLIENT_ID.into()),
9662            SERVER_ID[0],
9663            non_temporary_addresses.iter().copied().map(TestIaNa::new_default).collect(),
9664            delegated_prefixes.iter().copied().map(TestIaPd::new_default).collect(),
9665            None,
9666            T1,
9667            T2,
9668            v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9669            StepRng::new(u64::MAX / 2, 0),
9670            time,
9671        );
9672        let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
9673            &client;
9674        // The server includes only the IA with ID equal to `present_iaid` in the
9675        // reply.
9676        let buf = TestMessageBuilder {
9677            transaction_id: *transaction_id,
9678            message_type: v6::MessageType::Reply,
9679            client_id: &CLIENT_ID,
9680            server_id: &SERVER_ID[0],
9681            preference: None,
9682            dns_servers: None,
9683            ia_nas: present_ia_na_iaids.iter().map(|iaid| {
9684                (
9685                    *iaid,
9686                    TestIa::new_renewed_default(
9687                        CONFIGURED_NON_TEMPORARY_ADDRESSES[iaid.get() as usize],
9688                    ),
9689                )
9690            }),
9691            ia_pds: present_ia_pd_iaids.iter().map(|iaid| {
9692                (
9693                    *iaid,
9694                    TestIa::new_renewed_default(CONFIGURED_DELEGATED_PREFIXES[iaid.get() as usize]),
9695                )
9696            }),
9697        }
9698        .build();
9699        let mut buf = &buf[..]; // Implements BufferView.
9700        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
9701        let actions = client.handle_message_receive(msg, time);
9702        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
9703            &client;
9704        // Only the IA that is present will have its lifetimes updated.
9705        {
9706            let RenewingOrRebindingInner {
9707                client_id,
9708                non_temporary_addresses: got_non_temporary_addresses,
9709                delegated_prefixes: got_delegated_prefixes,
9710                server_id,
9711                dns_servers,
9712                start_time: _,
9713                retrans_timeout: _,
9714                solicit_max_rt,
9715            } = with_state(state);
9716            assert_eq!(client_id.as_slice(), &CLIENT_ID);
9717            fn expected_values<V: IaValue>(
9718                values: &[V],
9719                present_iaids: Vec<v6::IAID>,
9720                time: Instant,
9721            ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
9722                (0..)
9723                    .map(v6::IAID::new)
9724                    .zip(values)
9725                    .map(|(iaid, &value)| {
9726                        (
9727                            iaid,
9728                            if present_iaids.contains(&iaid) {
9729                                IaEntry::new_assigned(
9730                                    value,
9731                                    RENEWED_PREFERRED_LIFETIME,
9732                                    RENEWED_VALID_LIFETIME,
9733                                    time,
9734                                )
9735                            } else {
9736                                IaEntry::new_assigned(
9737                                    value,
9738                                    PREFERRED_LIFETIME,
9739                                    VALID_LIFETIME,
9740                                    time,
9741                                )
9742                            },
9743                        )
9744                    })
9745                    .collect()
9746            }
9747            assert_eq!(
9748                *got_non_temporary_addresses,
9749                expected_values(non_temporary_addresses, present_ia_na_iaids, time)
9750            );
9751            assert_eq!(
9752                *got_delegated_prefixes,
9753                expected_values(delegated_prefixes, present_ia_pd_iaids, time)
9754            );
9755            assert_eq!(*server_id, SERVER_ID[0]);
9756            assert_eq!(dns_servers, &[] as &[Ipv6Addr]);
9757            assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
9758        }
9759        // The client relies on retransmission to send another Renew, so no actions are needed.
9760        assert_matches!(&actions[..], []);
9761    }
9762
9763    #[test_case(RENEW_TEST)]
9764    #[test_case(REBIND_TEST)]
9765    fn receive_reply_with_missing_ia_suboption_for_assigned_entry_does_not_extend_lifetime(
9766        RenewRebindTest {
9767            send_and_assert,
9768            message_type: _,
9769            expect_server_id: _,
9770            with_state: _,
9771            allow_response_from_any_server: _,
9772        }: RenewRebindTest,
9773    ) {
9774        const IA_NA_WITHOUT_ADDRESS_IAID: v6::IAID = v6::IAID::new(0);
9775        const IA_PD_WITHOUT_PREFIX_IAID: v6::IAID = v6::IAID::new(1);
9776
9777        let time = Instant::now();
9778        let mut client = send_and_assert(
9779            &(CLIENT_ID.into()),
9780            SERVER_ID[0],
9781            CONFIGURED_NON_TEMPORARY_ADDRESSES.iter().copied().map(TestIaNa::new_default).collect(),
9782            CONFIGURED_DELEGATED_PREFIXES.iter().copied().map(TestIaPd::new_default).collect(),
9783            None,
9784            T1,
9785            T2,
9786            v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9787            StepRng::new(u64::MAX / 2, 0),
9788            time,
9789        );
9790        let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
9791            &client;
9792        // The server includes an IA Address/Prefix option in only one of the IAs.
9793        let iaaddr_opts = (0..)
9794            .map(v6::IAID::new)
9795            .zip(CONFIGURED_NON_TEMPORARY_ADDRESSES)
9796            .map(|(iaid, addr)| {
9797                (
9798                    iaid,
9799                    (iaid != IA_NA_WITHOUT_ADDRESS_IAID).then(|| {
9800                        [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
9801                            addr,
9802                            RENEWED_PREFERRED_LIFETIME.get(),
9803                            RENEWED_VALID_LIFETIME.get(),
9804                            &[],
9805                        ))]
9806                    }),
9807                )
9808            })
9809            .collect::<HashMap<_, _>>();
9810        let iaprefix_opts = (0..)
9811            .map(v6::IAID::new)
9812            .zip(CONFIGURED_DELEGATED_PREFIXES)
9813            .map(|(iaid, prefix)| {
9814                (
9815                    iaid,
9816                    (iaid != IA_PD_WITHOUT_PREFIX_IAID).then(|| {
9817                        [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
9818                            RENEWED_PREFERRED_LIFETIME.get(),
9819                            RENEWED_VALID_LIFETIME.get(),
9820                            prefix,
9821                            &[],
9822                        ))]
9823                    }),
9824                )
9825            })
9826            .collect::<HashMap<_, _>>();
9827        let options =
9828            [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0])]
9829                .into_iter()
9830                .chain(iaaddr_opts.iter().map(|(iaid, iaaddr_opts)| {
9831                    v6::DhcpOption::Iana(v6::IanaSerializer::new(
9832                        *iaid,
9833                        RENEWED_T1.get(),
9834                        RENEWED_T2.get(),
9835                        iaaddr_opts.as_ref().map_or(&[], AsRef::as_ref),
9836                    ))
9837                }))
9838                .chain(iaprefix_opts.iter().map(|(iaid, iaprefix_opts)| {
9839                    v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
9840                        *iaid,
9841                        RENEWED_T1.get(),
9842                        RENEWED_T2.get(),
9843                        iaprefix_opts.as_ref().map_or(&[], AsRef::as_ref),
9844                    ))
9845                }))
9846                .collect::<Vec<_>>();
9847        let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
9848        let mut buf = vec![0; builder.bytes_len()];
9849        builder.serialize(&mut buf);
9850        let mut buf = &buf[..]; // Implements BufferView.
9851        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
9852        let actions = client.handle_message_receive(msg, time);
9853        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
9854            &client;
9855        // Expect the client to transition to Assigned and only extend
9856        // the lifetime for one IA.
9857        assert_matches!(
9858            &state,
9859            Some(ClientState::Assigned(Assigned {
9860                client_id,
9861                non_temporary_addresses,
9862                delegated_prefixes,
9863                server_id,
9864                dns_servers,
9865                solicit_max_rt,
9866                _marker,
9867            })) => {
9868            assert_eq!(client_id.as_slice(), &CLIENT_ID);
9869                fn expected_values<V: IaValueTestExt>(
9870                    without_value: v6::IAID,
9871                    time: Instant,
9872                ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
9873                    (0..)
9874                        .map(v6::IAID::new)
9875                        .zip(V::CONFIGURED)
9876                        .map(|(iaid, value)| {
9877                            let (preferred_lifetime, valid_lifetime) =
9878                                if iaid == without_value {
9879                                    (PREFERRED_LIFETIME, VALID_LIFETIME)
9880                                } else {
9881                                    (RENEWED_PREFERRED_LIFETIME, RENEWED_VALID_LIFETIME)
9882                                };
9883
9884                            (
9885                                iaid,
9886                                IaEntry::new_assigned(
9887                                    value,
9888                                    preferred_lifetime,
9889                                    valid_lifetime,
9890                                    time,
9891                                ),
9892                            )
9893                        })
9894                        .collect()
9895                }
9896                assert_eq!(
9897                    non_temporary_addresses,
9898                    &expected_values::<Ipv6Addr>(IA_NA_WITHOUT_ADDRESS_IAID, time)
9899                );
9900                assert_eq!(
9901                    delegated_prefixes,
9902                    &expected_values::<Subnet<Ipv6Addr>>(IA_PD_WITHOUT_PREFIX_IAID, time)
9903                );
9904                assert_eq!(server_id.as_slice(), &SERVER_ID[0]);
9905                assert_eq!(dns_servers.as_slice(), &[] as &[Ipv6Addr]);
9906                assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
9907            }
9908        );
9909        assert_matches!(
9910            &actions[..],
9911            [
9912                Action::CancelTimer(ClientTimerType::Retransmission),
9913                Action::ScheduleTimer(ClientTimerType::Renew, t1),
9914                Action::ScheduleTimer(ClientTimerType::Rebind, t2),
9915                Action::IaNaUpdates(iana_updates),
9916                Action::IaPdUpdates(iapd_updates),
9917                 Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
9918            ] => {
9919                assert_eq!(*t1, time.add(Duration::from_secs(RENEWED_T1.get().into())));
9920                assert_eq!(*t2, time.add(Duration::from_secs(RENEWED_T2.get().into())));
9921                assert_eq!(
9922                    *restart_time,
9923                    time.add(Duration::from_secs(std::cmp::max(
9924                        VALID_LIFETIME,
9925                        RENEWED_VALID_LIFETIME,
9926                    ).get().into()))
9927                );
9928
9929                fn get_updates<V: IaValue>(
9930                    values: &[V],
9931                    omit_iaid: v6::IAID,
9932                ) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
9933                    (0..).map(v6::IAID::new)
9934                        .zip(values.iter().cloned())
9935                        .filter_map(|(iaid, value)| {
9936                            (iaid != omit_iaid).then(|| (
9937                                iaid,
9938                                HashMap::from([(
9939                                    value,
9940                                    IaValueUpdateKind::UpdatedLifetimes(Lifetimes::new_renewed()),
9941                                )])
9942                            ))
9943                        })
9944                        .collect()
9945                }
9946                assert_eq!(
9947                    iana_updates,
9948                    &get_updates(&CONFIGURED_NON_TEMPORARY_ADDRESSES, IA_NA_WITHOUT_ADDRESS_IAID),
9949                );
9950                assert_eq!(
9951                    iapd_updates,
9952                    &get_updates(&CONFIGURED_DELEGATED_PREFIXES, IA_PD_WITHOUT_PREFIX_IAID),
9953                );
9954            }
9955        );
9956    }
9957
9958    #[test_case(RENEW_TEST)]
9959    #[test_case(REBIND_TEST)]
9960    fn receive_reply_with_zero_lifetime(
9961        RenewRebindTest {
9962            send_and_assert,
9963            message_type: _,
9964            expect_server_id: _,
9965            with_state: _,
9966            allow_response_from_any_server: _,
9967        }: RenewRebindTest,
9968    ) {
9969        const IA_NA_ZERO_LIFETIMES_ADDRESS_IAID: v6::IAID = v6::IAID::new(0);
9970        const IA_PD_ZERO_LIFETIMES_PREFIX_IAID: v6::IAID = v6::IAID::new(1);
9971
9972        let time = Instant::now();
9973        let mut client = send_and_assert(
9974            &(CLIENT_ID.into()),
9975            SERVER_ID[0],
9976            CONFIGURED_NON_TEMPORARY_ADDRESSES.iter().copied().map(TestIaNa::new_default).collect(),
9977            CONFIGURED_DELEGATED_PREFIXES.iter().copied().map(TestIaPd::new_default).collect(),
9978            None,
9979            T1,
9980            T2,
9981            v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
9982            StepRng::new(u64::MAX / 2, 0),
9983            time,
9984        );
9985        let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
9986            &client;
9987        // The server includes an IA Address/Prefix option in only one of the IAs.
9988        let iaaddr_opts = (0..)
9989            .map(v6::IAID::new)
9990            .zip(CONFIGURED_NON_TEMPORARY_ADDRESSES)
9991            .map(|(iaid, addr)| {
9992                let (pl, vl) = if iaid == IA_NA_ZERO_LIFETIMES_ADDRESS_IAID {
9993                    (0, 0)
9994                } else {
9995                    (RENEWED_PREFERRED_LIFETIME.get(), RENEWED_VALID_LIFETIME.get())
9996                };
9997
9998                (iaid, [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(addr, pl, vl, &[]))])
9999            })
10000            .collect::<HashMap<_, _>>();
10001        let iaprefix_opts = (0..)
10002            .map(v6::IAID::new)
10003            .zip(CONFIGURED_DELEGATED_PREFIXES)
10004            .map(|(iaid, prefix)| {
10005                let (pl, vl) = if iaid == IA_PD_ZERO_LIFETIMES_PREFIX_IAID {
10006                    (0, 0)
10007                } else {
10008                    (RENEWED_PREFERRED_LIFETIME.get(), RENEWED_VALID_LIFETIME.get())
10009                };
10010
10011                (iaid, [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(pl, vl, prefix, &[]))])
10012            })
10013            .collect::<HashMap<_, _>>();
10014        let options =
10015            [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0])]
10016                .into_iter()
10017                .chain(iaaddr_opts.iter().map(|(iaid, iaaddr_opts)| {
10018                    v6::DhcpOption::Iana(v6::IanaSerializer::new(
10019                        *iaid,
10020                        RENEWED_T1.get(),
10021                        RENEWED_T2.get(),
10022                        iaaddr_opts.as_ref(),
10023                    ))
10024                }))
10025                .chain(iaprefix_opts.iter().map(|(iaid, iaprefix_opts)| {
10026                    v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
10027                        *iaid,
10028                        RENEWED_T1.get(),
10029                        RENEWED_T2.get(),
10030                        iaprefix_opts.as_ref(),
10031                    ))
10032                }))
10033                .collect::<Vec<_>>();
10034        let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
10035        let mut buf = vec![0; builder.bytes_len()];
10036        builder.serialize(&mut buf);
10037        let mut buf = &buf[..]; // Implements BufferView.
10038        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10039        let actions = client.handle_message_receive(msg, time);
10040        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
10041            &client;
10042        // Expect the client to transition to Assigned and only extend
10043        // the lifetime for one IA.
10044        assert_matches!(
10045            &state,
10046            Some(ClientState::Assigned(Assigned {
10047                client_id,
10048                non_temporary_addresses,
10049                delegated_prefixes,
10050                server_id,
10051                dns_servers,
10052                solicit_max_rt,
10053                _marker,
10054            })) => {
10055            assert_eq!(client_id.as_slice(), &CLIENT_ID);
10056                fn expected_values<V: IaValueTestExt>(
10057                    zero_lifetime_iaid: v6::IAID,
10058                    time: Instant,
10059                ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
10060                    (0..)
10061                        .map(v6::IAID::new)
10062                        .zip(V::CONFIGURED)
10063                        .map(|(iaid, value)| {
10064                            (
10065                                iaid,
10066                                if iaid == zero_lifetime_iaid {
10067                                    IaEntry::ToRequest(HashSet::from([value]))
10068                                } else {
10069                                    IaEntry::new_assigned(
10070                                        value,
10071                                        RENEWED_PREFERRED_LIFETIME,
10072                                        RENEWED_VALID_LIFETIME,
10073                                        time,
10074                                    )
10075                                },
10076                            )
10077                        })
10078                        .collect()
10079                }
10080                assert_eq!(
10081                    non_temporary_addresses,
10082                    &expected_values::<Ipv6Addr>(IA_NA_ZERO_LIFETIMES_ADDRESS_IAID, time)
10083                );
10084                assert_eq!(
10085                    delegated_prefixes,
10086                    &expected_values::<Subnet<Ipv6Addr>>(IA_PD_ZERO_LIFETIMES_PREFIX_IAID, time)
10087                );
10088                assert_eq!(server_id.as_slice(), &SERVER_ID[0]);
10089                assert_eq!(dns_servers.as_slice(), &[] as &[Ipv6Addr]);
10090                assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
10091            }
10092        );
10093        assert_matches!(
10094            &actions[..],
10095            [
10096                Action::CancelTimer(ClientTimerType::Retransmission),
10097                Action::ScheduleTimer(ClientTimerType::Renew, t1),
10098                Action::ScheduleTimer(ClientTimerType::Rebind, t2),
10099                Action::IaNaUpdates(iana_updates),
10100                Action::IaPdUpdates(iapd_updates),
10101                Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
10102            ] => {
10103                assert_eq!(*t1, time.add(Duration::from_secs(RENEWED_T1.get().into())));
10104                assert_eq!(*t2, time.add(Duration::from_secs(RENEWED_T2.get().into())));
10105                assert_eq!(
10106                    *restart_time,
10107                    time.add(Duration::from_secs(RENEWED_VALID_LIFETIME.get().into()))
10108                );
10109
10110                fn get_updates<V: IaValue>(
10111                    values: &[V],
10112                    omit_iaid: v6::IAID,
10113                ) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
10114                    (0..).map(v6::IAID::new)
10115                        .zip(values.iter().cloned())
10116                        .map(|(iaid, value)| (
10117                            iaid,
10118                            HashMap::from([(
10119                                value,
10120                                if iaid == omit_iaid {
10121                                    IaValueUpdateKind::Removed
10122                                } else {
10123                                    IaValueUpdateKind::UpdatedLifetimes(Lifetimes::new_renewed())
10124                                }
10125                            )]),
10126                        ))
10127                        .collect()
10128                }
10129                assert_eq!(
10130                    iana_updates,
10131                    &get_updates(
10132                        &CONFIGURED_NON_TEMPORARY_ADDRESSES,
10133                        IA_NA_ZERO_LIFETIMES_ADDRESS_IAID,
10134                    ),
10135                );
10136                assert_eq!(
10137                    iapd_updates,
10138                    &get_updates(
10139                        &CONFIGURED_DELEGATED_PREFIXES,
10140                        IA_PD_ZERO_LIFETIMES_PREFIX_IAID,
10141                    ),
10142                );
10143            }
10144        );
10145    }
10146
10147    #[test_case(RENEW_TEST)]
10148    #[test_case(REBIND_TEST)]
10149    fn receive_reply_with_original_ia_value_omitted(
10150        RenewRebindTest {
10151            send_and_assert,
10152            message_type: _,
10153            expect_server_id: _,
10154            with_state: _,
10155            allow_response_from_any_server: _,
10156        }: RenewRebindTest,
10157    ) {
10158        let time = Instant::now();
10159        let mut client = send_and_assert(
10160            &(CLIENT_ID.into()),
10161            SERVER_ID[0],
10162            vec![TestIaNa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[0])],
10163            vec![TestIaPd::new_default(CONFIGURED_DELEGATED_PREFIXES[0])],
10164            None,
10165            T1,
10166            T2,
10167            v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
10168            StepRng::new(u64::MAX / 2, 0),
10169            time,
10170        );
10171        let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
10172            &client;
10173        // The server includes IAs with different values from what was
10174        // previously assigned.
10175        let iaid = v6::IAID::new(0);
10176        let buf = TestMessageBuilder {
10177            transaction_id: *transaction_id,
10178            message_type: v6::MessageType::Reply,
10179            client_id: &CLIENT_ID,
10180            server_id: &SERVER_ID[0],
10181            preference: None,
10182            dns_servers: None,
10183            ia_nas: std::iter::once((
10184                iaid,
10185                TestIa::new_renewed_default(RENEW_NON_TEMPORARY_ADDRESSES[0]),
10186            )),
10187            ia_pds: std::iter::once((
10188                iaid,
10189                TestIa::new_renewed_default(RENEW_DELEGATED_PREFIXES[0]),
10190            )),
10191        }
10192        .build();
10193        let mut buf = &buf[..]; // Implements BufferView.
10194        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10195        let actions = client.handle_message_receive(msg, time);
10196        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
10197            &client;
10198
10199        // Expect the client to transition to Assigned with both the new value
10200        // found in the latest Reply and the original value found when we first
10201        // transitioned to Assigned above. We always keep the old value even
10202        // though it was missing from the received Reply since the server did
10203        // not send an IA Address/Prefix option with the zero valid lifetime.
10204        assert_matches!(
10205            &state,
10206            Some(ClientState::Assigned(Assigned {
10207                client_id,
10208                non_temporary_addresses,
10209                delegated_prefixes,
10210                server_id,
10211                dns_servers,
10212                solicit_max_rt,
10213                _marker,
10214            })) => {
10215            assert_eq!(client_id.as_slice(), &CLIENT_ID);
10216                fn calc_expected<V: IaValue>(
10217                    iaid: v6::IAID,
10218                    time: Instant,
10219                    initial: V,
10220                    in_renew: V,
10221                ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
10222                    HashMap::from([(
10223                        iaid,
10224                        IaEntry::Assigned(HashMap::from([
10225                            (
10226                                initial,
10227                                LifetimesInfo {
10228                                    lifetimes: Lifetimes::new_finite(
10229                                        PREFERRED_LIFETIME,
10230                                        VALID_LIFETIME,
10231                                    ),
10232                                    updated_at: time,
10233                                }
10234                            ),
10235                            (
10236                                in_renew,
10237                                LifetimesInfo {
10238                                    lifetimes: Lifetimes::new_finite(
10239                                        RENEWED_PREFERRED_LIFETIME,
10240                                        RENEWED_VALID_LIFETIME,
10241                                    ),
10242                                    updated_at: time,
10243                                },
10244                            ),
10245                        ])),
10246                    )])
10247                }
10248                assert_eq!(
10249                    non_temporary_addresses,
10250                    &calc_expected(
10251                        iaid,
10252                        time,
10253                        CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
10254                        RENEW_NON_TEMPORARY_ADDRESSES[0],
10255                    )
10256                );
10257                assert_eq!(
10258                    delegated_prefixes,
10259                    &calc_expected(
10260                        iaid,
10261                        time,
10262                        CONFIGURED_DELEGATED_PREFIXES[0],
10263                        RENEW_DELEGATED_PREFIXES[0],
10264                    )
10265                );
10266                assert_eq!(server_id.as_slice(), &SERVER_ID[0]);
10267                assert_eq!(dns_servers.as_slice(), &[] as &[Ipv6Addr]);
10268                assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
10269            }
10270        );
10271        assert_matches!(
10272            &actions[..],
10273            [
10274                Action::CancelTimer(ClientTimerType::Retransmission),
10275                Action::ScheduleTimer(ClientTimerType::Renew, t1),
10276                Action::ScheduleTimer(ClientTimerType::Rebind, t2),
10277                Action::IaNaUpdates(iana_updates),
10278                Action::IaPdUpdates(iapd_updates),
10279                Action::ScheduleTimer(ClientTimerType::RestartServerDiscovery, restart_time),
10280            ] => {
10281                assert_eq!(*t1, time.add(Duration::from_secs(RENEWED_T1.get().into())));
10282                assert_eq!(*t2, time.add(Duration::from_secs(RENEWED_T2.get().into())));
10283                assert_eq!(
10284                    *restart_time,
10285                    time.add(Duration::from_secs(std::cmp::max(
10286                        VALID_LIFETIME,
10287                        RENEWED_VALID_LIFETIME,
10288                    ).get().into()))
10289                );
10290
10291                fn get_updates<V: IaValue>(
10292                    iaid: v6::IAID,
10293                    new_value: V,
10294                ) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
10295                    HashMap::from([
10296                        (
10297                            iaid,
10298                            HashMap::from([
10299                                (
10300                                    new_value,
10301                                    IaValueUpdateKind::Added(Lifetimes::new_renewed()),
10302                                ),
10303                            ])
10304                        ),
10305                    ])
10306                }
10307
10308                assert_eq!(
10309                    iana_updates,
10310                    &get_updates(
10311                        iaid,
10312                        RENEW_NON_TEMPORARY_ADDRESSES[0],
10313                    ),
10314                );
10315                assert_eq!(
10316                    iapd_updates,
10317                    &get_updates(
10318                        iaid,
10319                        RENEW_DELEGATED_PREFIXES[0],
10320                    ),
10321                );
10322            }
10323        );
10324    }
10325
10326    struct NoBindingTestCase {
10327        ia_na_no_binding: bool,
10328        ia_pd_no_binding: bool,
10329    }
10330
10331    #[test_case(
10332        RENEW_TEST,
10333        NoBindingTestCase {
10334            ia_na_no_binding: true,
10335            ia_pd_no_binding: false,
10336        }
10337    )]
10338    #[test_case(
10339        REBIND_TEST,
10340        NoBindingTestCase {
10341            ia_na_no_binding: true,
10342            ia_pd_no_binding: false,
10343        }
10344    )]
10345    #[test_case(
10346        RENEW_TEST,
10347        NoBindingTestCase {
10348            ia_na_no_binding: false,
10349            ia_pd_no_binding: true,
10350        }
10351    )]
10352    #[test_case(
10353        REBIND_TEST,
10354        NoBindingTestCase {
10355            ia_na_no_binding: false,
10356            ia_pd_no_binding: true,
10357        }
10358    )]
10359    #[test_case(
10360        RENEW_TEST,
10361        NoBindingTestCase {
10362            ia_na_no_binding: true,
10363            ia_pd_no_binding: true,
10364        }
10365    )]
10366    #[test_case(
10367        REBIND_TEST,
10368        NoBindingTestCase {
10369            ia_na_no_binding: true,
10370            ia_pd_no_binding: true,
10371        }
10372    )]
10373    fn no_binding(
10374        RenewRebindTest {
10375            send_and_assert,
10376            message_type: _,
10377            expect_server_id: _,
10378            with_state: _,
10379            allow_response_from_any_server: _,
10380        }: RenewRebindTest,
10381        NoBindingTestCase { ia_na_no_binding, ia_pd_no_binding }: NoBindingTestCase,
10382    ) {
10383        const NUM_IAS: u32 = 2;
10384        const NO_BINDING_IA_IDX: usize = (NUM_IAS - 1) as usize;
10385
10386        fn to_assign<V: IaValueTestExt>() -> Vec<TestIa<V>> {
10387            V::CONFIGURED[0..usize::try_from(NUM_IAS).unwrap()]
10388                .iter()
10389                .copied()
10390                .map(TestIa::new_default)
10391                .collect()
10392        }
10393        let time = Instant::now();
10394        let non_temporary_addresses_to_assign = to_assign::<Ipv6Addr>();
10395        let delegated_prefixes_to_assign = to_assign::<Subnet<Ipv6Addr>>();
10396        let mut client = send_and_assert(
10397            &(CLIENT_ID.into()),
10398            SERVER_ID[0],
10399            non_temporary_addresses_to_assign.clone(),
10400            delegated_prefixes_to_assign.clone(),
10401            None,
10402            T1,
10403            T2,
10404            v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
10405            StepRng::new(u64::MAX / 2, 0),
10406            time,
10407        );
10408        let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
10409            &client;
10410
10411        // Build a reply with NoBinding status..
10412        let iaaddr_opts = (0..usize::try_from(NUM_IAS).unwrap())
10413            .map(|i| {
10414                if i == NO_BINDING_IA_IDX && ia_na_no_binding {
10415                    [v6::DhcpOption::StatusCode(
10416                        v6::ErrorStatusCode::NoBinding.into(),
10417                        "Binding not found.",
10418                    )]
10419                } else {
10420                    [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
10421                        CONFIGURED_NON_TEMPORARY_ADDRESSES[i],
10422                        RENEWED_PREFERRED_LIFETIME.get(),
10423                        RENEWED_VALID_LIFETIME.get(),
10424                        &[],
10425                    ))]
10426                }
10427            })
10428            .collect::<Vec<_>>();
10429        let iaprefix_opts = (0..usize::try_from(NUM_IAS).unwrap())
10430            .map(|i| {
10431                if i == NO_BINDING_IA_IDX && ia_pd_no_binding {
10432                    [v6::DhcpOption::StatusCode(
10433                        v6::ErrorStatusCode::NoBinding.into(),
10434                        "Binding not found.",
10435                    )]
10436                } else {
10437                    [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
10438                        RENEWED_PREFERRED_LIFETIME.get(),
10439                        RENEWED_VALID_LIFETIME.get(),
10440                        CONFIGURED_DELEGATED_PREFIXES[i],
10441                        &[],
10442                    ))]
10443                }
10444            })
10445            .collect::<Vec<_>>();
10446        let options =
10447            [v6::DhcpOption::ClientId(&CLIENT_ID), v6::DhcpOption::ServerId(&SERVER_ID[0])]
10448                .into_iter()
10449                .chain((0..NUM_IAS).map(|id| {
10450                    v6::DhcpOption::Iana(v6::IanaSerializer::new(
10451                        v6::IAID::new(id),
10452                        RENEWED_T1.get(),
10453                        RENEWED_T2.get(),
10454                        &iaaddr_opts[id as usize],
10455                    ))
10456                }))
10457                .chain((0..NUM_IAS).map(|id| {
10458                    v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
10459                        v6::IAID::new(id),
10460                        RENEWED_T1.get(),
10461                        RENEWED_T2.get(),
10462                        &iaprefix_opts[id as usize],
10463                    ))
10464                }))
10465                .collect::<Vec<_>>();
10466
10467        let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
10468        let mut buf = vec![0; builder.bytes_len()];
10469        builder.serialize(&mut buf);
10470        let mut buf = &buf[..]; // Implements BufferView.
10471        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10472        let actions = client.handle_message_receive(msg, time);
10473        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
10474            &client;
10475        // Expect the client to transition to Requesting.
10476        {
10477            let Requesting {
10478                client_id,
10479                non_temporary_addresses,
10480                delegated_prefixes,
10481                server_id,
10482                collected_advertise: _,
10483                first_request_time: _,
10484                retrans_timeout: _,
10485                transmission_count: _,
10486                solicit_max_rt,
10487            } = assert_matches!(
10488                &state,
10489                Some(ClientState::Requesting(requesting)) => requesting
10490            );
10491            assert_eq!(client_id.as_slice(), &CLIENT_ID);
10492            fn expected_values<V: IaValueTestExt>(
10493                no_binding: bool,
10494                time: Instant,
10495            ) -> HashMap<v6::IAID, IaEntry<V, Instant>> {
10496                (0..NUM_IAS)
10497                    .map(|i| {
10498                        (v6::IAID::new(i), {
10499                            let i = usize::try_from(i).unwrap();
10500                            if i == NO_BINDING_IA_IDX && no_binding {
10501                                IaEntry::ToRequest(HashSet::from([V::CONFIGURED[i]]))
10502                            } else {
10503                                IaEntry::new_assigned(
10504                                    V::CONFIGURED[i],
10505                                    RENEWED_PREFERRED_LIFETIME,
10506                                    RENEWED_VALID_LIFETIME,
10507                                    time,
10508                                )
10509                            }
10510                        })
10511                    })
10512                    .collect()
10513            }
10514            assert_eq!(
10515                *non_temporary_addresses,
10516                expected_values::<Ipv6Addr>(ia_na_no_binding, time)
10517            );
10518            assert_eq!(
10519                *delegated_prefixes,
10520                expected_values::<Subnet<Ipv6Addr>>(ia_pd_no_binding, time)
10521            );
10522            assert_eq!(*server_id, SERVER_ID[0]);
10523            assert_eq!(*solicit_max_rt, MAX_SOLICIT_TIMEOUT);
10524        }
10525        let buf = assert_matches!(
10526            &actions[..],
10527            [
10528                // TODO(https://fxbug.dev/42178817): should include action to
10529                // remove the address of IA with NoBinding status.
10530                Action::CancelTimer(ClientTimerType::Retransmission),
10531                Action::SendMessage(buf),
10532                Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
10533            ] => {
10534                assert_eq!(*instant, time.add(INITIAL_REQUEST_TIMEOUT));
10535                buf
10536            }
10537        );
10538        // Expect that the Request message contains both the assigned address
10539        // and the address to request.
10540        testutil::assert_outgoing_stateful_message(
10541            &buf,
10542            v6::MessageType::Request,
10543            &CLIENT_ID,
10544            Some(&SERVER_ID[0]),
10545            &[],
10546            &(0..NUM_IAS)
10547                .map(v6::IAID::new)
10548                .zip(CONFIGURED_NON_TEMPORARY_ADDRESSES.into_iter().map(|a| HashSet::from([a])))
10549                .collect(),
10550            &(0..NUM_IAS)
10551                .map(v6::IAID::new)
10552                .zip(CONFIGURED_DELEGATED_PREFIXES.into_iter().map(|p| HashSet::from([p])))
10553                .collect(),
10554        );
10555
10556        // While we are in requesting state after being in Assigned, make sure
10557        // all addresses may be invalidated.
10558        handle_all_leases_invalidated(
10559            client,
10560            &CLIENT_ID,
10561            non_temporary_addresses_to_assign,
10562            delegated_prefixes_to_assign,
10563            ia_na_no_binding.then_some(NO_BINDING_IA_IDX),
10564            ia_pd_no_binding.then_some(NO_BINDING_IA_IDX),
10565            &[],
10566        )
10567    }
10568
10569    struct ReceiveReplyCalculateT1T2 {
10570        ia_na_success_t1: v6::NonZeroOrMaxU32,
10571        ia_na_success_t2: v6::NonZeroOrMaxU32,
10572        ia_pd_success_t1: v6::NonZeroOrMaxU32,
10573        ia_pd_success_t2: v6::NonZeroOrMaxU32,
10574    }
10575
10576    const TINY_NON_ZERO_OR_MAX_U32: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(10).unwrap();
10577    const SMALL_NON_ZERO_OR_MAX_U32: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(100).unwrap();
10578    const MEDIUM_NON_ZERO_OR_MAX_U32: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(1000).unwrap();
10579    const LARGE_NON_ZERO_OR_MAX_U32: v6::NonZeroOrMaxU32 = v6::NonZeroOrMaxU32::new(10000).unwrap();
10580
10581    #[test_case(
10582        RENEW_TEST,
10583        ReceiveReplyCalculateT1T2 {
10584            ia_na_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10585            ia_na_success_t2: TINY_NON_ZERO_OR_MAX_U32,
10586            ia_pd_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10587            ia_pd_success_t2: TINY_NON_ZERO_OR_MAX_U32,
10588        }; "renew lifetimes matching erroneous IAs")]
10589    #[test_case(
10590        RENEW_TEST,
10591        ReceiveReplyCalculateT1T2 {
10592            ia_na_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10593            ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10594            ia_pd_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10595            ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10596        }; "renew same lifetimes")]
10597    #[test_case(
10598        RENEW_TEST,
10599        ReceiveReplyCalculateT1T2 {
10600            ia_na_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10601            ia_na_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10602            ia_pd_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10603            ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10604        }; "renew IA_NA smaller lifetimes")]
10605    #[test_case(
10606        RENEW_TEST,
10607        ReceiveReplyCalculateT1T2 {
10608            ia_na_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10609            ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10610            ia_pd_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10611            ia_pd_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10612        }; "renew IA_PD smaller lifetimes")]
10613    #[test_case(
10614        RENEW_TEST,
10615        ReceiveReplyCalculateT1T2 {
10616            ia_na_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10617            ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10618            ia_pd_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10619            ia_pd_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10620        }; "renew IA_NA smaller T1 but IA_PD smaller t2")]
10621    #[test_case(
10622        RENEW_TEST,
10623        ReceiveReplyCalculateT1T2 {
10624            ia_na_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10625            ia_na_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10626            ia_pd_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10627            ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10628        }; "renew IA_PD smaller T1 but IA_NA smaller t2")]
10629    #[test_case(
10630        REBIND_TEST,
10631        ReceiveReplyCalculateT1T2 {
10632            ia_na_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10633            ia_na_success_t2: TINY_NON_ZERO_OR_MAX_U32,
10634            ia_pd_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10635            ia_pd_success_t2: TINY_NON_ZERO_OR_MAX_U32,
10636        }; "rebind lifetimes matching erroneous IAs")]
10637    #[test_case(
10638        REBIND_TEST,
10639        ReceiveReplyCalculateT1T2 {
10640            ia_na_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10641            ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10642            ia_pd_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10643            ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10644        }; "rebind same lifetimes")]
10645    #[test_case(
10646        REBIND_TEST,
10647        ReceiveReplyCalculateT1T2 {
10648            ia_na_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10649            ia_na_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10650            ia_pd_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10651            ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10652        }; "rebind IA_NA smaller lifetimes")]
10653    #[test_case(
10654        REBIND_TEST,
10655        ReceiveReplyCalculateT1T2 {
10656            ia_na_success_t1: MEDIUM_NON_ZERO_OR_MAX_U32,
10657            ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10658            ia_pd_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10659            ia_pd_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10660        }; "rebind IA_PD smaller lifetimes")]
10661    #[test_case(
10662        REBIND_TEST,
10663        ReceiveReplyCalculateT1T2 {
10664            ia_na_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10665            ia_na_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10666            ia_pd_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10667            ia_pd_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10668        }; "rebind IA_NA smaller T1 but IA_PD smaller t2")]
10669    #[test_case(
10670        REBIND_TEST,
10671        ReceiveReplyCalculateT1T2 {
10672            ia_na_success_t1: SMALL_NON_ZERO_OR_MAX_U32,
10673            ia_na_success_t2: MEDIUM_NON_ZERO_OR_MAX_U32,
10674            ia_pd_success_t1: TINY_NON_ZERO_OR_MAX_U32,
10675            ia_pd_success_t2: LARGE_NON_ZERO_OR_MAX_U32,
10676        }; "rebind IA_PD smaller T1 but IA_NA smaller t2")]
10677    // Tests that only valid IAs are considered when calculating T1/T2.
10678    fn receive_reply_calculate_t1_t2(
10679        RenewRebindTest {
10680            send_and_assert,
10681            message_type: _,
10682            expect_server_id: _,
10683            with_state: _,
10684            allow_response_from_any_server: _,
10685        }: RenewRebindTest,
10686        ReceiveReplyCalculateT1T2 {
10687            ia_na_success_t1,
10688            ia_na_success_t2,
10689            ia_pd_success_t1,
10690            ia_pd_success_t2,
10691        }: ReceiveReplyCalculateT1T2,
10692    ) {
10693        let time = Instant::now();
10694        let mut client = send_and_assert(
10695            &(CLIENT_ID.into()),
10696            SERVER_ID[0],
10697            CONFIGURED_NON_TEMPORARY_ADDRESSES.into_iter().map(TestIaNa::new_default).collect(),
10698            CONFIGURED_DELEGATED_PREFIXES.into_iter().map(TestIaPd::new_default).collect(),
10699            None,
10700            T1,
10701            T2,
10702            v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
10703            StepRng::new(u64::MAX / 2, 0),
10704            time,
10705        );
10706        let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
10707            &client;
10708        let ia_addr = [v6::DhcpOption::IaAddr(v6::IaAddrSerializer::new(
10709            CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
10710            RENEWED_PREFERRED_LIFETIME.get(),
10711            RENEWED_VALID_LIFETIME.get(),
10712            &[],
10713        ))];
10714        let ia_no_addrs_avail = [v6::DhcpOption::StatusCode(
10715            v6::ErrorStatusCode::NoAddrsAvail.into(),
10716            "No address available.",
10717        )];
10718        let ia_prefix = [v6::DhcpOption::IaPrefix(v6::IaPrefixSerializer::new(
10719            RENEWED_PREFERRED_LIFETIME.get(),
10720            RENEWED_VALID_LIFETIME.get(),
10721            CONFIGURED_DELEGATED_PREFIXES[0],
10722            &[],
10723        ))];
10724        let ia_no_prefixes_avail = [v6::DhcpOption::StatusCode(
10725            v6::ErrorStatusCode::NoPrefixAvail.into(),
10726            "No prefixes available.",
10727        )];
10728        let ok_iaid = v6::IAID::new(0);
10729        let no_value_avail_iaid = v6::IAID::new(1);
10730        let empty_values_iaid = v6::IAID::new(2);
10731        let options = vec![
10732            v6::DhcpOption::ClientId(&CLIENT_ID),
10733            v6::DhcpOption::ServerId(&SERVER_ID[0]),
10734            v6::DhcpOption::Iana(v6::IanaSerializer::new(
10735                ok_iaid,
10736                ia_na_success_t1.get(),
10737                ia_na_success_t2.get(),
10738                &ia_addr,
10739            )),
10740            v6::DhcpOption::Iana(v6::IanaSerializer::new(
10741                no_value_avail_iaid,
10742                // If the server returns an IA with status code indicating
10743                // failure, the T1/T2 values for that IA should not be included
10744                // in the T1/T2 calculation.
10745                TINY_NON_ZERO_OR_MAX_U32.get(),
10746                TINY_NON_ZERO_OR_MAX_U32.get(),
10747                &ia_no_addrs_avail,
10748            )),
10749            v6::DhcpOption::Iana(v6::IanaSerializer::new(
10750                empty_values_iaid,
10751                // If the server returns an IA_NA with no IA Address option, the
10752                // T1/T2 values for that IA should not be included in the T1/T2
10753                // calculation.
10754                TINY_NON_ZERO_OR_MAX_U32.get(),
10755                TINY_NON_ZERO_OR_MAX_U32.get(),
10756                &[],
10757            )),
10758            v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
10759                ok_iaid,
10760                ia_pd_success_t1.get(),
10761                ia_pd_success_t2.get(),
10762                &ia_prefix,
10763            )),
10764            v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
10765                no_value_avail_iaid,
10766                // If the server returns an IA with status code indicating
10767                // failure, the T1/T2 values for that IA should not be included
10768                // in the T1/T2 calculation.
10769                TINY_NON_ZERO_OR_MAX_U32.get(),
10770                TINY_NON_ZERO_OR_MAX_U32.get(),
10771                &ia_no_prefixes_avail,
10772            )),
10773            v6::DhcpOption::IaPd(v6::IaPdSerializer::new(
10774                empty_values_iaid,
10775                // If the server returns an IA_PD with no IA Prefix option, the
10776                // T1/T2 values for that IA should not be included in the T1/T2
10777                // calculation.
10778                TINY_NON_ZERO_OR_MAX_U32.get(),
10779                TINY_NON_ZERO_OR_MAX_U32.get(),
10780                &[],
10781            )),
10782        ];
10783
10784        let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
10785        let mut buf = vec![0; builder.bytes_len()];
10786        builder.serialize(&mut buf);
10787        let mut buf = &buf[..]; // Implements BufferView.
10788        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10789
10790        fn get_updates<V: IaValue>(
10791            ok_iaid: v6::IAID,
10792            ok_value: V,
10793            no_value_avail_iaid: v6::IAID,
10794            no_value_avail_value: V,
10795        ) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
10796            HashMap::from([
10797                (
10798                    ok_iaid,
10799                    HashMap::from([(
10800                        ok_value,
10801                        IaValueUpdateKind::UpdatedLifetimes(Lifetimes::new_renewed()),
10802                    )]),
10803                ),
10804                (
10805                    no_value_avail_iaid,
10806                    HashMap::from([(no_value_avail_value, IaValueUpdateKind::Removed)]),
10807                ),
10808            ])
10809        }
10810        let expected_t1 = std::cmp::min(ia_na_success_t1, ia_pd_success_t1);
10811        let expected_t2 = std::cmp::min(ia_na_success_t2, ia_pd_success_t2);
10812        assert_eq!(
10813            client.handle_message_receive(msg, time),
10814            [
10815                Action::CancelTimer(ClientTimerType::Retransmission),
10816                if expected_t1 == expected_t2 {
10817                    // Skip Renew and just go to Rebind when T2 == T1.
10818                    Action::CancelTimer(ClientTimerType::Renew)
10819                } else {
10820                    Action::ScheduleTimer(
10821                        ClientTimerType::Renew,
10822                        time.add(Duration::from_secs(expected_t1.get().into())),
10823                    )
10824                },
10825                Action::ScheduleTimer(
10826                    ClientTimerType::Rebind,
10827                    time.add(Duration::from_secs(expected_t2.get().into())),
10828                ),
10829                Action::IaNaUpdates(get_updates(
10830                    ok_iaid,
10831                    CONFIGURED_NON_TEMPORARY_ADDRESSES[0],
10832                    no_value_avail_iaid,
10833                    CONFIGURED_NON_TEMPORARY_ADDRESSES[1],
10834                )),
10835                Action::IaPdUpdates(get_updates(
10836                    ok_iaid,
10837                    CONFIGURED_DELEGATED_PREFIXES[0],
10838                    no_value_avail_iaid,
10839                    CONFIGURED_DELEGATED_PREFIXES[1],
10840                )),
10841                Action::ScheduleTimer(
10842                    ClientTimerType::RestartServerDiscovery,
10843                    time.add(Duration::from_secs(
10844                        std::cmp::max(VALID_LIFETIME, RENEWED_VALID_LIFETIME,).get().into()
10845                    )),
10846                ),
10847            ],
10848        );
10849    }
10850
10851    #[test]
10852    fn unexpected_messages_are_ignored() {
10853        let (mut client, _) = ClientStateMachine::start_stateless(
10854            [0, 1, 2],
10855            Vec::new(),
10856            StepRng::new(u64::MAX / 2, 0),
10857            Instant::now(),
10858        );
10859
10860        let builder = v6::MessageBuilder::new(
10861            v6::MessageType::Reply,
10862            // Transaction ID is different from the client's.
10863            [4, 5, 6],
10864            &[],
10865        );
10866        let mut buf = vec![0; builder.bytes_len()];
10867        builder.serialize(&mut buf);
10868        let mut buf = &buf[..]; // Implements BufferView.
10869        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10870
10871        assert!(client.handle_message_receive(msg, Instant::now()).is_empty());
10872
10873        // Messages with unsupported/unexpected types are discarded.
10874        for msg_type in [
10875            v6::MessageType::Solicit,
10876            v6::MessageType::Advertise,
10877            v6::MessageType::Request,
10878            v6::MessageType::Confirm,
10879            v6::MessageType::Renew,
10880            v6::MessageType::Rebind,
10881            v6::MessageType::Release,
10882            v6::MessageType::Decline,
10883            v6::MessageType::Reconfigure,
10884            v6::MessageType::InformationRequest,
10885            v6::MessageType::RelayForw,
10886            v6::MessageType::RelayRepl,
10887        ] {
10888            let ClientStateMachine { transaction_id, options_to_request: _, state: _, rng: _ } =
10889                &client;
10890            let builder = v6::MessageBuilder::new(msg_type, *transaction_id, &[]);
10891            let mut buf = vec![0; builder.bytes_len()];
10892            builder.serialize(&mut buf);
10893            let mut buf = &buf[..]; // Implements BufferView.
10894            let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10895
10896            assert!(client.handle_message_receive(msg, Instant::now()).is_empty());
10897        }
10898    }
10899
10900    #[test]
10901    #[should_panic(expected = "received unexpected refresh timeout")]
10902    fn information_requesting_refresh_timeout_is_unreachable() {
10903        let (mut client, _) = ClientStateMachine::start_stateless(
10904            [0, 1, 2],
10905            Vec::new(),
10906            StepRng::new(u64::MAX / 2, 0),
10907            Instant::now(),
10908        );
10909
10910        // Should panic if Refresh timeout is received while in
10911        // InformationRequesting state.
10912        let _actions = client.handle_timeout(ClientTimerType::Refresh, Instant::now());
10913    }
10914
10915    #[test]
10916    #[should_panic(expected = "received unexpected retransmission timeout")]
10917    fn information_received_retransmission_timeout_is_unreachable() {
10918        let (mut client, _) = ClientStateMachine::start_stateless(
10919            [0, 1, 2],
10920            Vec::new(),
10921            StepRng::new(u64::MAX / 2, 0),
10922            Instant::now(),
10923        );
10924        let ClientStateMachine { transaction_id, options_to_request: _, state, rng: _ } = &client;
10925        assert_matches!(
10926            *state,
10927            Some(ClientState::InformationRequesting(InformationRequesting {
10928                retrans_timeout: INITIAL_INFO_REQ_TIMEOUT,
10929                _marker,
10930            }))
10931        );
10932
10933        let options = [v6::DhcpOption::ServerId(&SERVER_ID[0])];
10934        let builder = v6::MessageBuilder::new(v6::MessageType::Reply, *transaction_id, &options);
10935        let mut buf = vec![0; builder.bytes_len()];
10936        builder.serialize(&mut buf);
10937        let mut buf = &buf[..]; // Implements BufferView.
10938        let msg = v6::Message::parse(&mut buf, ()).expect("failed to parse test buffer");
10939        // Transition to InformationReceived state.
10940        let time = Instant::now();
10941        let actions = client.handle_message_receive(msg, time);
10942        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
10943            &client;
10944        assert_matches!(
10945            state,
10946            Some(ClientState::InformationReceived(InformationReceived { dns_servers, _marker }))
10947                if dns_servers.is_empty()
10948        );
10949        assert_eq!(
10950            actions[..],
10951            [
10952                Action::CancelTimer(ClientTimerType::Retransmission),
10953                Action::ScheduleTimer(ClientTimerType::Refresh, time.add(IRT_DEFAULT)),
10954            ]
10955        );
10956
10957        // Should panic if Retransmission timeout is received while in
10958        // InformationReceived state.
10959        let _actions = client.handle_timeout(ClientTimerType::Retransmission, time);
10960    }
10961
10962    #[test]
10963    #[should_panic(expected = "received unexpected refresh timeout")]
10964    fn server_discovery_refresh_timeout_is_unreachable() {
10965        let time = Instant::now();
10966        let mut client = testutil::start_and_assert_server_discovery(
10967            [0, 1, 2],
10968            &(CLIENT_ID.into()),
10969            testutil::to_configured_addresses(
10970                1,
10971                std::iter::once(HashSet::from([CONFIGURED_NON_TEMPORARY_ADDRESSES[0]])),
10972            ),
10973            Default::default(),
10974            Vec::new(),
10975            StepRng::new(u64::MAX / 2, 0),
10976            time,
10977        );
10978
10979        // Should panic if Refresh is received while in ServerDiscovery state.
10980        let _actions = client.handle_timeout(ClientTimerType::Refresh, time);
10981    }
10982
10983    #[test]
10984    #[should_panic(expected = "received unexpected refresh timeout")]
10985    fn requesting_refresh_timeout_is_unreachable() {
10986        let time = Instant::now();
10987        let (mut client, _transaction_id) = testutil::request_and_assert(
10988            &(CLIENT_ID.into()),
10989            SERVER_ID[0],
10990            vec![TestIaNa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[0])],
10991            Default::default(),
10992            &[],
10993            StepRng::new(u64::MAX / 2, 0),
10994            time,
10995        );
10996
10997        // Should panic if Refresh is received while in Requesting state.
10998        let _actions = client.handle_timeout(ClientTimerType::Refresh, time);
10999    }
11000
11001    #[test_case(ClientTimerType::Refresh)]
11002    #[test_case(ClientTimerType::Retransmission)]
11003    #[should_panic(expected = "received unexpected")]
11004    fn address_assiged_unexpected_timeout_is_unreachable(timeout: ClientTimerType) {
11005        let time = Instant::now();
11006        let (mut client, _actions) = testutil::assign_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            &[],
11012            StepRng::new(u64::MAX / 2, 0),
11013            time,
11014        );
11015
11016        // Should panic if Refresh or Retransmission timeout is received while
11017        // in Assigned state.
11018        let _actions = client.handle_timeout(timeout, time);
11019    }
11020
11021    #[test_case(RENEW_TEST)]
11022    #[test_case(REBIND_TEST)]
11023    #[should_panic(expected = "received unexpected refresh timeout")]
11024    fn refresh_timeout_is_unreachable(
11025        RenewRebindTest {
11026            send_and_assert,
11027            message_type: _,
11028            expect_server_id: _,
11029            with_state: _,
11030            allow_response_from_any_server: _,
11031        }: RenewRebindTest,
11032    ) {
11033        let time = Instant::now();
11034        let mut client = send_and_assert(
11035            &(CLIENT_ID.into()),
11036            SERVER_ID[0],
11037            vec![TestIaNa::new_default(CONFIGURED_NON_TEMPORARY_ADDRESSES[0])],
11038            Default::default(), /* delegated_prefixes_to_assign */
11039            None,
11040            T1,
11041            T2,
11042            v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
11043            StepRng::new(u64::MAX / 2, 0),
11044            time,
11045        );
11046
11047        // Should panic if Refresh is received while in Renewing state.
11048        let _actions = client.handle_timeout(ClientTimerType::Refresh, time);
11049    }
11050
11051    fn handle_all_leases_invalidated<R: Rng>(
11052        mut client: ClientStateMachine<Instant, R>,
11053        client_id: &[u8],
11054        non_temporary_addresses_to_assign: Vec<TestIaNa>,
11055        delegated_prefixes_to_assign: Vec<TestIaPd>,
11056        skip_removed_event_for_test_iana_idx: Option<usize>,
11057        skip_removed_event_for_test_iapd_idx: Option<usize>,
11058        options_to_request: &[v6::OptionCode],
11059    ) {
11060        let time = Instant::now();
11061        let actions = client.handle_timeout(ClientTimerType::RestartServerDiscovery, time);
11062        let buf = assert_matches!(
11063            &actions[..],
11064            [
11065                Action::CancelTimer(ClientTimerType::Retransmission),
11066                Action::CancelTimer(ClientTimerType::Refresh),
11067                Action::CancelTimer(ClientTimerType::Renew),
11068                Action::CancelTimer(ClientTimerType::Rebind),
11069                Action::CancelTimer(ClientTimerType::RestartServerDiscovery),
11070                Action::IaNaUpdates(ia_na_updates),
11071                Action::IaPdUpdates(ia_pd_updates),
11072                Action::SendMessage(buf),
11073                Action::ScheduleTimer(ClientTimerType::Retransmission, instant)
11074            ] => {
11075                fn get_updates<V: IaValue>(
11076                    to_assign: &Vec<TestIa<V>>,
11077                    skip_idx: Option<usize>,
11078                ) -> HashMap<v6::IAID, HashMap<V, IaValueUpdateKind>> {
11079                    (0..).zip(to_assign.iter())
11080                        .filter_map(|(iaid, TestIa { values, t1: _, t2: _})| {
11081                            skip_idx
11082                                .map_or(true, |skip_idx| skip_idx != iaid)
11083                                .then(|| (
11084                                    v6::IAID::new(iaid.try_into().unwrap()),
11085                                    values.keys().copied().map(|value| (
11086                                        value,
11087                                        IaValueUpdateKind::Removed,
11088                                    )).collect(),
11089                                ))
11090                        })
11091                        .collect()
11092                }
11093                assert_eq!(
11094                    ia_na_updates,
11095                    &get_updates(
11096                        &non_temporary_addresses_to_assign,
11097                        skip_removed_event_for_test_iana_idx
11098                    ),
11099                );
11100                assert_eq!(
11101                    ia_pd_updates,
11102                    &get_updates(
11103                        &delegated_prefixes_to_assign,
11104                        skip_removed_event_for_test_iapd_idx,
11105                    ),
11106                );
11107                assert_eq!(*instant, time.add(INITIAL_SOLICIT_TIMEOUT));
11108                buf
11109            }
11110        );
11111
11112        let ClientStateMachine { transaction_id: _, options_to_request: _, state, rng: _ } =
11113            &client;
11114        testutil::assert_server_discovery(
11115            state,
11116            client_id,
11117            testutil::to_configured_addresses(
11118                non_temporary_addresses_to_assign.len(),
11119                non_temporary_addresses_to_assign
11120                    .iter()
11121                    .map(|TestIaNa { values, t1: _, t2: _ }| values.keys().cloned().collect()),
11122            ),
11123            testutil::to_configured_prefixes(
11124                delegated_prefixes_to_assign.len(),
11125                delegated_prefixes_to_assign
11126                    .iter()
11127                    .map(|TestIaPd { values, t1: _, t2: _ }| values.keys().cloned().collect()),
11128            ),
11129            time,
11130            buf,
11131            options_to_request,
11132        )
11133    }
11134
11135    #[test]
11136    fn assigned_handle_all_leases_invalidated() {
11137        let non_temporary_addresses_to_assign = CONFIGURED_NON_TEMPORARY_ADDRESSES
11138            .iter()
11139            .copied()
11140            .map(TestIaNa::new_default)
11141            .collect::<Vec<_>>();
11142        let delegated_prefixes_to_assign = CONFIGURED_DELEGATED_PREFIXES
11143            .iter()
11144            .copied()
11145            .map(TestIaPd::new_default)
11146            .collect::<Vec<_>>();
11147        let (client, _actions) = testutil::assign_and_assert(
11148            &(CLIENT_ID.into()),
11149            SERVER_ID[0],
11150            non_temporary_addresses_to_assign.clone(),
11151            delegated_prefixes_to_assign.clone(),
11152            &[],
11153            StepRng::new(u64::MAX / 2, 0),
11154            Instant::now(),
11155        );
11156
11157        handle_all_leases_invalidated(
11158            client,
11159            &CLIENT_ID,
11160            non_temporary_addresses_to_assign,
11161            delegated_prefixes_to_assign,
11162            None,
11163            None,
11164            &[],
11165        )
11166    }
11167
11168    #[test_case(RENEW_TEST)]
11169    #[test_case(REBIND_TEST)]
11170    fn renew_rebind_handle_all_leases_invalidated(
11171        RenewRebindTest {
11172            send_and_assert,
11173            message_type: _,
11174            expect_server_id: _,
11175            with_state: _,
11176            allow_response_from_any_server: _,
11177        }: RenewRebindTest,
11178    ) {
11179        let non_temporary_addresses_to_assign = CONFIGURED_NON_TEMPORARY_ADDRESSES[0..2]
11180            .into_iter()
11181            .map(|&addr| TestIaNa::new_default(addr))
11182            .collect::<Vec<_>>();
11183        let delegated_prefixes_to_assign = CONFIGURED_DELEGATED_PREFIXES[0..2]
11184            .into_iter()
11185            .map(|&addr| TestIaPd::new_default(addr))
11186            .collect::<Vec<_>>();
11187        let client = send_and_assert(
11188            &(CLIENT_ID.into()),
11189            SERVER_ID[0],
11190            non_temporary_addresses_to_assign.clone(),
11191            delegated_prefixes_to_assign.clone(),
11192            None,
11193            T1,
11194            T2,
11195            v6::NonZeroTimeValue::Finite(VALID_LIFETIME),
11196            StepRng::new(u64::MAX / 2, 0),
11197            Instant::now(),
11198        );
11199
11200        handle_all_leases_invalidated(
11201            client,
11202            &CLIENT_ID,
11203            non_temporary_addresses_to_assign,
11204            delegated_prefixes_to_assign,
11205            None,
11206            None,
11207            &[],
11208        )
11209    }
11210
11211    // NOTE: All comparisons are done on millisecond, so this test is not affected by precision
11212    // loss from floating point arithmetic.
11213    #[test]
11214    fn retransmission_timeout() {
11215        let mut rng = StepRng::new(u64::MAX / 2, 0);
11216
11217        let initial_rt = Duration::from_secs(1);
11218        let max_rt = Duration::from_secs(100);
11219
11220        // Start with initial timeout if previous timeout is zero.
11221        let t =
11222            super::retransmission_timeout(Duration::from_nanos(0), initial_rt, max_rt, &mut rng);
11223        assert_eq!(t.as_millis(), initial_rt.as_millis());
11224
11225        // Use previous timeout when it's not zero and apply the formula.
11226        let t =
11227            super::retransmission_timeout(Duration::from_secs(10), initial_rt, max_rt, &mut rng);
11228        assert_eq!(t, Duration::from_secs(20));
11229
11230        // Cap at max timeout.
11231        let t = super::retransmission_timeout(100 * max_rt, initial_rt, max_rt, &mut rng);
11232        assert_eq!(t.as_millis(), max_rt.as_millis());
11233        let t = super::retransmission_timeout(MAX_DURATION, initial_rt, max_rt, &mut rng);
11234        assert_eq!(t.as_millis(), max_rt.as_millis());
11235        // Zero max means no cap.
11236        let t = super::retransmission_timeout(
11237            100 * max_rt,
11238            initial_rt,
11239            Duration::from_nanos(0),
11240            &mut rng,
11241        );
11242        assert_eq!(t.as_millis(), (200 * max_rt).as_millis());
11243        // Overflow durations are clipped.
11244        let t = super::retransmission_timeout(
11245            MAX_DURATION,
11246            initial_rt,
11247            Duration::from_nanos(0),
11248            &mut rng,
11249        );
11250        assert_eq!(t.as_millis(), MAX_DURATION.as_millis());
11251
11252        // Steps through the range with deterministic randomness, 20% at a time.
11253        let mut rng = StepRng::new(0, u64::MAX / 5);
11254        [
11255            (Duration::from_millis(10000), 19000),
11256            (Duration::from_millis(10000), 19400),
11257            (Duration::from_millis(10000), 19800),
11258            (Duration::from_millis(10000), 20200),
11259            (Duration::from_millis(10000), 20600),
11260            (Duration::from_millis(10000), 21000),
11261            (Duration::from_millis(10000), 19400),
11262            // Cap at max timeout with randomness.
11263            (100 * max_rt, 98000),
11264            (100 * max_rt, 102000),
11265            (100 * max_rt, 106000),
11266            (100 * max_rt, 110000),
11267            (100 * max_rt, 94000),
11268            (100 * max_rt, 98000),
11269        ]
11270        .iter()
11271        .for_each(|(rt, want_ms)| {
11272            let t = super::retransmission_timeout(*rt, initial_rt, max_rt, &mut rng);
11273            assert_eq!(t.as_millis(), *want_ms);
11274        });
11275    }
11276
11277    #[test_case(v6::TimeValue::Zero, v6::TimeValue::Zero, v6::TimeValue::Zero)]
11278    #[test_case(
11279        v6::TimeValue::Zero,
11280        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11281            v6::NonZeroOrMaxU32::new(120)
11282                .expect("should succeed for non-zero or u32::MAX values")
11283        )),
11284        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11285            v6::NonZeroOrMaxU32::new(120)
11286                .expect("should succeed for non-zero or u32::MAX values")
11287        ))
11288     )]
11289    #[test_case(
11290        v6::TimeValue::Zero,
11291        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11292        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity)
11293    )]
11294    #[test_case(
11295        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11296            v6::NonZeroOrMaxU32::new(120)
11297                .expect("should succeed for non-zero or u32::MAX values")
11298        )),
11299        v6::TimeValue::Zero,
11300        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11301            v6::NonZeroOrMaxU32::new(120)
11302                .expect("should succeed for non-zero or u32::MAX values")
11303        ))
11304     )]
11305    #[test_case(
11306        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11307            v6::NonZeroOrMaxU32::new(120)
11308                .expect("should succeed for non-zero or u32::MAX values")
11309        )),
11310        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11311            v6::NonZeroOrMaxU32::new(60)
11312                .expect("should succeed for non-zero or u32::MAX values")
11313        )),
11314        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11315            v6::NonZeroOrMaxU32::new(60)
11316                .expect("should succeed for non-zero or u32::MAX values")
11317        ))
11318     )]
11319    #[test_case(
11320        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11321            v6::NonZeroOrMaxU32::new(120)
11322                .expect("should succeed for non-zero or u32::MAX values")
11323        )),
11324        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11325        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11326            v6::NonZeroOrMaxU32::new(120)
11327                .expect("should succeed for non-zero or u32::MAX values")
11328        ))
11329     )]
11330    #[test_case(
11331        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11332        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11333            v6::NonZeroOrMaxU32::new(120)
11334                .expect("should succeed for non-zero or u32::MAX values")
11335        )),
11336        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11337            v6::NonZeroOrMaxU32::new(120)
11338                .expect("should succeed for non-zero or u32::MAX values")
11339        ))
11340     )]
11341    #[test_case(
11342        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11343        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11344        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity)
11345    )]
11346    fn maybe_get_nonzero_min(
11347        old_value: v6::TimeValue,
11348        new_value: v6::TimeValue,
11349        expected_value: v6::TimeValue,
11350    ) {
11351        assert_eq!(super::maybe_get_nonzero_min(old_value, new_value), expected_value);
11352    }
11353
11354    #[test_case(
11355        v6::NonZeroTimeValue::Finite(
11356            v6::NonZeroOrMaxU32::new(120)
11357                .expect("should succeed for non-zero or u32::MAX values")
11358        ),
11359        v6::TimeValue::Zero,
11360        v6::NonZeroTimeValue::Finite(
11361            v6::NonZeroOrMaxU32::new(120)
11362                .expect("should succeed for non-zero or u32::MAX values")
11363        )
11364    )]
11365    #[test_case(
11366        v6::NonZeroTimeValue::Finite(
11367            v6::NonZeroOrMaxU32::new(120)
11368                .expect("should succeed for non-zero or u32::MAX values")
11369        ),
11370        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11371            v6::NonZeroOrMaxU32::new(60)
11372                .expect("should succeed for non-zero or u32::MAX values")
11373        )),
11374        v6::NonZeroTimeValue::Finite(
11375            v6::NonZeroOrMaxU32::new(60)
11376                .expect("should succeed for non-zero or u32::MAX values")
11377        )
11378    )]
11379    #[test_case(
11380        v6::NonZeroTimeValue::Finite(
11381            v6::NonZeroOrMaxU32::new(120)
11382                .expect("should succeed for non-zero or u32::MAX values")
11383        ),
11384        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11385        v6::NonZeroTimeValue::Finite(
11386            v6::NonZeroOrMaxU32::new(120)
11387                .expect("should succeed for non-zero or u32::MAX values")
11388        )
11389    )]
11390    #[test_case(
11391        v6::NonZeroTimeValue::Infinity,
11392        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Finite(
11393            v6::NonZeroOrMaxU32::new(120)
11394                .expect("should succeed for non-zero or u32::MAX values"))
11395        ),
11396        v6::NonZeroTimeValue::Finite(
11397            v6::NonZeroOrMaxU32::new(120)
11398                .expect("should succeed for non-zero or u32::MAX values")
11399        )
11400    )]
11401    #[test_case(
11402        v6::NonZeroTimeValue::Infinity,
11403        v6::TimeValue::NonZero(v6::NonZeroTimeValue::Infinity),
11404        v6::NonZeroTimeValue::Infinity
11405    )]
11406    #[test_case(
11407        v6::NonZeroTimeValue::Infinity,
11408        v6::TimeValue::Zero,
11409        v6::NonZeroTimeValue::Infinity
11410    )]
11411    fn get_nonzero_min(
11412        old_value: v6::NonZeroTimeValue,
11413        new_value: v6::TimeValue,
11414        expected_value: v6::NonZeroTimeValue,
11415    ) {
11416        assert_eq!(super::get_nonzero_min(old_value, new_value), expected_value);
11417    }
11418
11419    #[test_case(
11420        v6::NonZeroTimeValue::Infinity,
11421        T1_MIN_LIFETIME_RATIO,
11422        v6::NonZeroTimeValue::Infinity
11423    )]
11424    #[test_case(
11425        v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(100).expect("should succeed")),
11426        T1_MIN_LIFETIME_RATIO,
11427        v6::NonZeroTimeValue::Finite(v6::NonZeroOrMaxU32::new(50).expect("should succeed"))
11428    )]
11429    #[test_case(v6::NonZeroTimeValue::Infinity, T2_T1_RATIO, v6::NonZeroTimeValue::Infinity)]
11430    #[test_case(
11431        v6::NonZeroTimeValue::Finite(
11432            v6::NonZeroOrMaxU32::new(INFINITY - 1)
11433                .expect("should succeed")
11434        ),
11435        T2_T1_RATIO,
11436        v6::NonZeroTimeValue::Infinity
11437    )]
11438    fn compute_t(min: v6::NonZeroTimeValue, ratio: Ratio<u32>, expected_t: v6::NonZeroTimeValue) {
11439        assert_eq!(super::compute_t(min, ratio), expected_t);
11440    }
11441}