Skip to main content

dhcp_client_core/
client.rs

1// Copyright 2023 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//! Implements the DHCP client state machine.
6
7use crate::deps::{self, DatagramInfo, Instant as _, Socket as _};
8use crate::inspect::{
9    Counters, MessagingRelatedCounters, RebindingCounters, RenewingCounters, RequestingCounters,
10    SelectingCounters, record_optional_duration_secs,
11};
12use crate::parse::{
13    FieldsFromOfferToUseInRequest, FieldsToRetainFromAck, FieldsToRetainFromNak, OptionCodeMap,
14    OptionRequested,
15};
16
17use anyhow::Context as _;
18use bstr::BString;
19use dhcp_protocol::{AtLeast, AtMostBytes, CLIENT_PORT, SERVER_PORT};
20use diagnostics_traits::Inspector;
21use futures::channel::mpsc;
22use futures::stream::FusedStream;
23use futures::{FutureExt as _, Stream, StreamExt as _, TryStreamExt as _, select, select_biased};
24use net_types::ethernet::Mac;
25use net_types::{SpecifiedAddr, Witness as _};
26use rand::Rng as _;
27
28use std::fmt::{Debug, Display};
29use std::net::Ipv4Addr;
30use std::num::{NonZeroU32, NonZeroU64};
31use std::ops::Deref as _;
32use std::pin::pin;
33use std::time::Duration;
34
35/// Unexpected, non-recoverable errors encountered by the DHCP client.
36#[derive(thiserror::Error, Debug)]
37pub enum Error {
38    /// Error encountered while performing a socket operation.
39    #[error("error while using socket: {0:?}")]
40    Socket(deps::SocketError),
41    /// The AddressEvent receiver unexpectedly ended.
42    #[error("the address_event_receiver was unexpectedly empty")]
43    AddressEventReceiverEnded,
44}
45
46/// The reason the DHCP client exited.
47#[derive(Debug)]
48pub enum ExitReason<R> {
49    /// Executed due to a request for graceful shutdown.
50    GracefulShutdown,
51    /// Exiting because the address was removed.
52    AddressRemoved(R),
53}
54
55/// All possible core state machine states from the state-transition diagram in
56/// [RFC 2131].
57///
58/// [RFC 2131]: https://datatracker.ietf.org/doc/html/rfc2131#section-4.4
59#[derive(Debug, Clone, Copy)]
60pub enum State<I> {
61    /// The default initial state of the state machine (no known
62    /// currently-assigned IP address or DHCP server).
63    Init(Init),
64    /// The Selecting state (broadcasting DHCPDISCOVERs and receiving
65    /// DHCPOFFERs).
66    Selecting(Selecting<I>),
67    /// The Requesting state (broadcasting DHCPREQUESTs and receiving DHCPACKs
68    /// and DHCPNAKs).
69    Requesting(Requesting<I>),
70    /// The Bound state (we actively have a lease and are waiting to transition
71    /// to Renewing).
72    Bound(Bound<I>),
73    /// The Renewing state (we actively have a lease that we are trying to
74    /// renew by unicasting requests to our known DHCP server).
75    Renewing(Renewing<I>),
76    /// The Rebinding state (we actively have a lease that we are trying to
77    /// renew by broadcasting requests to any DHCP server).
78    Rebinding(Rebinding<I>),
79    /// Waiting to restart the configuration process (via transitioning to Init).
80    WaitingToRestart(WaitingToRestart<I>),
81}
82
83/// The next step to take after running the core state machine for one step.
84#[derive(Debug)]
85pub enum Step<I, R> {
86    /// Transition to another state.
87    NextState(Transition<I>),
88    /// Exit the client.
89    Exit(ExitReason<R>),
90}
91
92/// A state-transition to execute (see `State` enum variant documentation for a
93/// description of each state).
94#[derive(Debug)]
95pub enum Transition<I> {
96    /// Transition to Init.
97    Init(Init),
98    /// Transition to Selecting.
99    Selecting(Selecting<I>),
100    /// Transition to Requesting.
101    Requesting(Requesting<I>),
102    /// Transition to Bound, having newly acquired a lease.
103    BoundWithNewLease(Bound<I>, NewlyAcquiredLease<I>),
104    /// Transition to Bound::Assigned from Bound::AwaitingAssignment.
105    BoundAssigned(Bound<I>),
106    /// Transition to Bound, having renewed a previously-acquired lease.
107    BoundWithRenewedLease(Bound<I>, LeaseRenewal<I>),
108    /// Transition to Renewing.
109    Renewing(Renewing<I>),
110    /// Transition to Rebinding.
111    Rebinding(Rebinding<I>),
112    /// Transition to wait to restart the configuration process.
113    WaitingToRestart(WaitingToRestart<I>),
114}
115
116/// A side-effect of a state transition.
117#[must_use]
118#[derive(Debug)]
119pub enum TransitionEffect<I> {
120    /// Drop the existing lease.
121    DropLease {
122        /// True if the Lease is being dropped because the address was rejected.
123        address_rejected: bool,
124    },
125    /// Handle a newly-acquired lease.
126    HandleNewLease(NewlyAcquiredLease<I>),
127    /// Handle a renewed lease.
128    HandleRenewedLease(LeaseRenewal<I>),
129}
130
131/// Outcome of handling an address rejection.
132#[derive(Debug)]
133pub enum AddressRejectionOutcome<I> {
134    /// Observing an address rejection in this state should be impossible due to
135    /// not having an active lease.
136    ShouldBeImpossible,
137    /// Transition to a new state.
138    NextState(State<I>),
139}
140
141/// State of an address.
142#[derive(Debug)]
143pub enum AddressAssignmentState {
144    /// The address is assigned to the interface and is available for use.
145    Assigned,
146    /// The address is tentatively assigned to the interface and is unavailable
147    /// for use. It will become `Assigned` once Duplicate Address Detection
148    /// completes.
149    Tentative,
150    /// The address is unavailable for use, and is not progressing towards
151    /// becoming `Assigned`. This may happen if the address's interface is
152    /// offline.
153    Unavailable,
154}
155
156/// Events for an addr that are produced outside of the core DHCP state machine.
157#[derive(Debug)]
158pub enum AddressEvent<R> {
159    /// The address was rejected during assignment. A DHCPDECLINE message should
160    /// be sent.
161    Rejected,
162    /// The address was removed. Unlike `Rejected`, a DHCPDECLINE message should
163    /// not be sent.
164    ///
165    /// Holds an opaque reason `R` indicating why the address was removed.
166    Removed(R),
167    /// The address's assignment state changed.
168    AssignmentStateChanged(AddressAssignmentState),
169}
170
171// Per RFC 2131 section 3.1, after sending a DHCPDECLINE message, "the client
172// SHOULD wait a minimum of ten seconds before restarting the configuration
173// process to avoid excessive network traffic in case of looping."
174const WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION: Duration = Duration::from_secs(10);
175
176impl<I: deps::Instant> State<I> {
177    /// Run the client state machine for one "step".
178    pub async fn run<C: deps::Clock<Instant = I>, R>(
179        &self,
180        config: &ClientConfig,
181        packet_socket_provider: &impl deps::PacketSocketProvider,
182        udp_socket_provider: &impl deps::UdpSocketProvider,
183        rng: &mut impl deps::RngProvider,
184        clock: &C,
185        stop_receiver: &mut mpsc::UnboundedReceiver<()>,
186        address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
187        counters: &Counters,
188    ) -> Result<Step<I, R>, Error> {
189        let step = self
190            .run_inner(
191                config,
192                packet_socket_provider,
193                udp_socket_provider,
194                rng,
195                clock,
196                stop_receiver,
197                address_event_receiver,
198                counters,
199            )
200            .await?;
201
202        match &step {
203            Step::NextState(transition) => {
204                let counter_to_increment = match transition {
205                    Transition::Init(_) => &counters.init.entered,
206                    Transition::Selecting(_) => &counters.selecting.entered,
207                    Transition::Requesting(_) => &counters.requesting.entered,
208                    Transition::BoundWithNewLease(_, _) => &counters.bound.entered,
209                    Transition::BoundAssigned(_) => &counters.bound.assigned,
210                    Transition::BoundWithRenewedLease(_, _) => &counters.bound.entered,
211                    Transition::Renewing(_) => &counters.renewing.entered,
212                    Transition::Rebinding(_) => &counters.rebinding.entered,
213                    Transition::WaitingToRestart(_) => &counters.waiting_to_restart.entered,
214                };
215                counter_to_increment.increment();
216            }
217            Step::Exit(_) => (),
218        };
219
220        Ok(step)
221    }
222
223    async fn run_inner<C: deps::Clock<Instant = I>, R>(
224        &self,
225        config: &ClientConfig,
226        packet_socket_provider: &impl deps::PacketSocketProvider,
227        udp_socket_provider: &impl deps::UdpSocketProvider,
228        rng: &mut impl deps::RngProvider,
229        clock: &C,
230        stop_receiver: &mut mpsc::UnboundedReceiver<()>,
231        address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
232        counters: &Counters,
233    ) -> Result<Step<I, R>, Error> {
234        let debug_log_prefix = &config.debug_log_prefix;
235        match self {
236            State::Init(init) => {
237                Ok(Step::NextState(Transition::Selecting(init.do_init(rng, clock))))
238            }
239            State::Selecting(selecting) => match selecting
240                .do_selecting(config, packet_socket_provider, rng, clock, stop_receiver, counters)
241                .await?
242            {
243                SelectingOutcome::GracefulShutdown => Ok(Step::Exit(ExitReason::GracefulShutdown)),
244                SelectingOutcome::Requesting(requesting) => {
245                    Ok(Step::NextState(Transition::Requesting(requesting)))
246                }
247            },
248            State::Requesting(requesting) => {
249                match requesting
250                    .do_requesting(
251                        config,
252                        packet_socket_provider,
253                        rng,
254                        clock,
255                        stop_receiver,
256                        counters,
257                    )
258                    .await?
259                {
260                    RequestingOutcome::RanOutOfRetransmits => {
261                        log::info!(
262                            "{debug_log_prefix} Returning to Init due to \
263                            running out of DHCPREQUEST retransmits"
264                        );
265                        Ok(Step::NextState(Transition::Init(Init)))
266                    }
267                    RequestingOutcome::GracefulShutdown => {
268                        Ok(Step::Exit(ExitReason::GracefulShutdown))
269                    }
270                    RequestingOutcome::Bound(lease_state, parameters) => {
271                        let LeaseState {
272                            discover_options: _,
273                            yiaddr,
274                            server_identifier: _,
275                            ip_address_lease_time,
276                            renewal_time: _,
277                            rebinding_time: _,
278                            start_time,
279                        } = &lease_state;
280                        let newly_acquired_lease = NewlyAcquiredLease {
281                            ip_address: *yiaddr,
282                            start_time: *start_time,
283                            lease_time: *ip_address_lease_time,
284                            parameters,
285                        };
286                        Ok(Step::NextState(Transition::BoundWithNewLease(
287                            Bound::AwaitingAssignment { lease_state },
288                            newly_acquired_lease,
289                        )))
290                    }
291                    RequestingOutcome::Nak(nak) => {
292                        // Per RFC 2131 section 3.1: "If the client receives a
293                        // DHCPNAK message, the client restarts the
294                        // configuration process."
295                        log::warn!(
296                            "{debug_log_prefix} Returning to Init due to DHCPNAK: {:?}",
297                            nak
298                        );
299                        Ok(Step::NextState(Transition::Init(Init)))
300                    }
301                }
302            }
303            State::Bound(bound) => match bound
304                .do_bound(
305                    config,
306                    clock,
307                    stop_receiver,
308                    packet_socket_provider,
309                    address_event_receiver,
310                    counters,
311                )
312                .await?
313            {
314                BoundOutcome::GracefulShutdown => Ok(Step::Exit(ExitReason::GracefulShutdown)),
315                BoundOutcome::Renewing(renewing) => {
316                    Ok(Step::NextState(Transition::Renewing(renewing)))
317                }
318                BoundOutcome::Restart(init) => Ok(Step::NextState(Transition::Init(init))),
319                BoundOutcome::AddressRemoved(reason) => {
320                    Ok(Step::Exit(ExitReason::AddressRemoved(reason)))
321                }
322                BoundOutcome::AddressRejected(waiting_to_restart) => {
323                    Ok(Step::NextState(Transition::WaitingToRestart(waiting_to_restart)))
324                }
325                BoundOutcome::Assigned(bound) => {
326                    Ok(Step::NextState(Transition::BoundAssigned(bound)))
327                }
328            },
329            State::Renewing(renewing) => {
330                match renewing
331                    .do_renewing(
332                        config,
333                        udp_socket_provider,
334                        packet_socket_provider,
335                        clock,
336                        stop_receiver,
337                        address_event_receiver,
338                        counters,
339                    )
340                    .await?
341                {
342                    RenewingOutcome::GracefulShutdown => {
343                        Ok(Step::Exit(ExitReason::GracefulShutdown))
344                    }
345                    RenewingOutcome::Renewed(lease_state, parameters) => {
346                        let LeaseState {
347                            discover_options: _,
348                            yiaddr: _,
349                            server_identifier: _,
350                            ip_address_lease_time,
351                            renewal_time: _,
352                            rebinding_time: _,
353                            start_time,
354                        } = &lease_state;
355                        let lease_renewal = LeaseRenewal {
356                            start_time: *start_time,
357                            lease_time: *ip_address_lease_time,
358                            parameters,
359                        };
360                        Ok(Step::NextState(Transition::BoundWithRenewedLease(
361                            Bound::Assigned { lease_state },
362                            lease_renewal,
363                        )))
364                    }
365                    RenewingOutcome::NewAddress(lease_state, parameters) => {
366                        let LeaseState {
367                            discover_options: _,
368                            yiaddr,
369                            server_identifier: _,
370                            ip_address_lease_time,
371                            renewal_time: _,
372                            rebinding_time: _,
373                            start_time,
374                        } = &lease_state;
375                        let new_lease = NewlyAcquiredLease {
376                            ip_address: *yiaddr,
377                            start_time: *start_time,
378                            lease_time: *ip_address_lease_time,
379                            parameters,
380                        };
381                        Ok(Step::NextState(Transition::BoundWithNewLease(
382                            Bound::AwaitingAssignment { lease_state },
383                            new_lease,
384                        )))
385                    }
386                    RenewingOutcome::Rebinding(rebinding) => {
387                        Ok(Step::NextState(Transition::Rebinding(rebinding)))
388                    }
389                    RenewingOutcome::Nak(nak) => {
390                        // Per RFC 2131 section 3.1: "If the client receives a
391                        // DHCPNAK message, the client restarts the
392                        // configuration process."
393
394                        let Renewing {
395                            lease_state:
396                                LeaseState {
397                                    discover_options: _,
398                                    yiaddr,
399                                    server_identifier: _,
400                                    ip_address_lease_time: _,
401                                    start_time: _,
402                                    renewal_time: _,
403                                    rebinding_time: _,
404                                },
405                        } = renewing;
406                        log::warn!(
407                            "{debug_log_prefix} Dropping lease on {} \
408                            and returning to Init due to DHCPNAK: {:?}",
409                            yiaddr,
410                            nak
411                        );
412                        Ok(Step::NextState(Transition::Init(Init)))
413                    }
414                    RenewingOutcome::AddressRemoved(reason) => {
415                        Ok(Step::Exit(ExitReason::AddressRemoved(reason)))
416                    }
417                    RenewingOutcome::AddressRejected(waiting_to_restart) => {
418                        Ok(Step::NextState(Transition::WaitingToRestart(waiting_to_restart)))
419                    }
420                }
421            }
422            State::Rebinding(rebinding) => {
423                match rebinding
424                    .do_rebinding(
425                        config,
426                        udp_socket_provider,
427                        packet_socket_provider,
428                        clock,
429                        stop_receiver,
430                        address_event_receiver,
431                        counters,
432                    )
433                    .await?
434                {
435                    RebindingOutcome::GracefulShutdown => {
436                        Ok(Step::Exit(ExitReason::GracefulShutdown))
437                    }
438                    RebindingOutcome::Renewed(lease_state, parameters) => {
439                        let LeaseState {
440                            discover_options: _,
441                            yiaddr: _,
442                            server_identifier: _,
443                            ip_address_lease_time,
444                            renewal_time: _,
445                            rebinding_time: _,
446                            start_time,
447                        } = &lease_state;
448                        let renewal = LeaseRenewal {
449                            start_time: *start_time,
450                            lease_time: *ip_address_lease_time,
451                            parameters,
452                        };
453                        Ok(Step::NextState(Transition::BoundWithRenewedLease(
454                            Bound::Assigned { lease_state },
455                            renewal,
456                        )))
457                    }
458                    RebindingOutcome::NewAddress(lease_state, parameters) => {
459                        let LeaseState {
460                            discover_options: _,
461                            yiaddr,
462                            server_identifier: _,
463                            ip_address_lease_time,
464                            renewal_time: _,
465                            rebinding_time: _,
466                            start_time,
467                        } = &lease_state;
468                        let new_lease = NewlyAcquiredLease {
469                            ip_address: *yiaddr,
470                            start_time: *start_time,
471                            lease_time: *ip_address_lease_time,
472                            parameters,
473                        };
474                        Ok(Step::NextState(Transition::BoundWithNewLease(
475                            Bound::AwaitingAssignment { lease_state },
476                            new_lease,
477                        )))
478                    }
479                    RebindingOutcome::Nak(nak) => {
480                        // Per RFC 2131 section 3.1: "If the client receives a
481                        // DHCPNAK message, the client restarts the
482                        // configuration process."
483
484                        let Rebinding {
485                            lease_state:
486                                LeaseState {
487                                    discover_options: _,
488                                    yiaddr,
489                                    server_identifier: _,
490                                    ip_address_lease_time: _,
491                                    start_time: _,
492                                    renewal_time: _,
493                                    rebinding_time: _,
494                                },
495                        } = rebinding;
496                        log::warn!(
497                            "{debug_log_prefix} Dropping lease on {} \
498                            and returning to Init due to DHCPNAK: {:?}",
499                            yiaddr,
500                            nak
501                        );
502                        Ok(Step::NextState(Transition::Init(Init)))
503                    }
504                    RebindingOutcome::TimedOut => {
505                        let Rebinding {
506                            lease_state:
507                                LeaseState {
508                                    discover_options: _,
509                                    yiaddr,
510                                    server_identifier: _,
511                                    ip_address_lease_time: _,
512                                    start_time: _,
513                                    renewal_time: _,
514                                    rebinding_time: _,
515                                },
516                        } = rebinding;
517                        log::warn!(
518                            "{debug_log_prefix} Dropping lease on {} \
519                            and returning to Init due to lease expiration",
520                            yiaddr,
521                        );
522                        Ok(Step::NextState(Transition::Init(Init)))
523                    }
524                    RebindingOutcome::AddressRemoved(reason) => {
525                        Ok(Step::Exit(ExitReason::AddressRemoved(reason)))
526                    }
527                    RebindingOutcome::AddressRejected(waiting_to_restart) => {
528                        Ok(Step::NextState(Transition::WaitingToRestart(waiting_to_restart)))
529                    }
530                }
531            }
532            State::WaitingToRestart(waiting_to_restart) => {
533                match waiting_to_restart.do_waiting_to_restart(clock, stop_receiver).await {
534                    WaitingToRestartOutcome::GracefulShutdown => {
535                        Ok(Step::Exit(ExitReason::GracefulShutdown))
536                    }
537                    WaitingToRestartOutcome::Init(init) => {
538                        Ok(Step::NextState(Transition::Init(init)))
539                    }
540                }
541            }
542        }
543    }
544
545    /// Provides a human-readable rendition of the state machine state for
546    /// exposure in debugging environments.
547    pub fn state_name(&self) -> &'static str {
548        match self {
549            State::Init(_) => "Init",
550            State::Selecting(_) => "Selecting",
551            State::Requesting(_) => "Requesting",
552            State::Bound(Bound::Assigned { .. }) => "Bound and assigned",
553            State::Bound(Bound::AwaitingAssignment { .. }) => "Bound and awaiting assignment",
554            State::Renewing(_) => "Renewing",
555            State::Rebinding(_) => "Rebinding",
556            State::WaitingToRestart(_) => "Waiting to Restart",
557        }
558    }
559
560    fn has_lease(&self) -> bool {
561        match self {
562            State::Init(_) => false,
563            State::Selecting(_) => false,
564            State::Requesting(_) => false,
565            State::Bound(_) => true,
566            State::Renewing(_) => true,
567            State::Rebinding(_) => true,
568            State::WaitingToRestart(_) => false,
569        }
570    }
571
572    /// Applies a state-transition to `self`, returning the next state and
573    /// effects that need to be performed by bindings as a result of the transition.
574    pub fn apply(
575        &self,
576        config: &ClientConfig,
577        transition: Transition<I>,
578    ) -> (State<I>, Option<TransitionEffect<I>>) {
579        let debug_log_prefix = &config.debug_log_prefix;
580
581        let (next_state, effect) = match transition {
582            Transition::Init(init) => (State::Init(init), None),
583            Transition::Selecting(selecting) => (State::Selecting(selecting), None),
584            Transition::Requesting(requesting) => (State::Requesting(requesting), None),
585            Transition::BoundWithRenewedLease(bound, lease_renewal) => {
586                (State::Bound(bound), Some(TransitionEffect::HandleRenewedLease(lease_renewal)))
587            }
588            Transition::BoundWithNewLease(bound, new_lease) => {
589                (State::Bound(bound), Some(TransitionEffect::HandleNewLease(new_lease)))
590            }
591            Transition::BoundAssigned(bound) => (State::Bound(bound), None),
592            Transition::Renewing(renewing) => (State::Renewing(renewing), None),
593            Transition::Rebinding(rebinding) => (State::Rebinding(rebinding), None),
594            Transition::WaitingToRestart(waiting) => (State::WaitingToRestart(waiting), None),
595        };
596
597        log::info!(
598            "{debug_log_prefix} transitioning from {} to {}",
599            self.state_name(),
600            next_state.state_name()
601        );
602
603        let effect = match effect {
604            Some(effect) => Some(effect),
605            None => match (self.has_lease(), next_state.has_lease()) {
606                (true, false) => {
607                    let address_rejected = matches!(next_state, State::WaitingToRestart(..));
608                    Some(TransitionEffect::DropLease { address_rejected })
609                }
610                (false, true) => {
611                    unreachable!("should already have decided on TransitionEffect::HandleNewLease")
612                }
613                (false, false) | (true, true) => None,
614            },
615        };
616        (next_state, effect)
617    }
618}
619
620/// Takes necessary actions on observing an address rejection (i.e. sends a
621/// DHCPDECLINE message).
622async fn handle_address_rejection<I: deps::Instant, C: deps::Clock<Instant = I>>(
623    config: &ClientConfig,
624    lease_state: &LeaseState<I>,
625    packet_socket_provider: &impl deps::PacketSocketProvider,
626    clock: &C,
627) -> Result<WaitingToRestart<I>, Error> {
628    let LeaseState {
629        discover_options,
630        yiaddr,
631        server_identifier,
632        ip_address_lease_time: _,
633        start_time: _,
634        renewal_time: _,
635        rebinding_time: _,
636    } = lease_state;
637
638    let socket = packet_socket_provider.get_packet_socket().await.map_err(Error::Socket)?;
639    let message = build_decline(config, discover_options, *yiaddr, *server_identifier);
640
641    // Despite knowing the address of the server, we still send DHCPDECLINE
642    // messages via broadcast. See RFC 2131 section 4.4.4:
643    //     Because the client is declining the use of the IP address supplied by
644    //     the server, the client broadcasts DHCPDECLINE messages.
645    socket
646        .send_to(
647            crate::parse::serialize_dhcp_message_to_ip_packet(
648                message,
649                Ipv4Addr::UNSPECIFIED,
650                CLIENT_PORT,
651                Ipv4Addr::BROADCAST,
652                SERVER_PORT,
653            )
654            .as_ref(),
655            Mac::BROADCAST,
656        )
657        .await
658        .map_err(Error::Socket)?;
659
660    let debug_log_prefix = config.debug_log_prefix;
661    log::info!("{debug_log_prefix} sent DHCPDECLINE for {}; waiting to restart", yiaddr);
662
663    Ok(WaitingToRestart {
664        waiting_until: clock.now().add(WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION),
665    })
666}
667
668impl<I> Default for State<I> {
669    fn default() -> Self {
670        State::Init(Init::default())
671    }
672}
673
674impl<I: deps::Instant> diagnostics_traits::InspectableValue for State<I> {
675    fn record<II: diagnostics_traits::Inspector>(&self, name: &str, inspector: &mut II) {
676        inspector.record_child(name, |inspector| {
677            inspector.record_str("Kind", self.state_name());
678            match self {
679                State::Init(Init) => (),
680                State::Selecting(selecting) => {
681                    selecting.record(inspector);
682                }
683                State::Requesting(requesting) => {
684                    requesting.record(inspector);
685                }
686                State::Bound(Bound::Assigned { lease_state }) => {
687                    lease_state.record(inspector);
688                }
689                State::Bound(Bound::AwaitingAssignment { lease_state }) => {
690                    lease_state.record(inspector);
691                }
692                State::Renewing(Renewing { lease_state }) => {
693                    lease_state.record(inspector);
694                }
695                State::Rebinding(Rebinding { lease_state }) => {
696                    lease_state.record(inspector);
697                }
698                State::WaitingToRestart(WaitingToRestart { waiting_until }) => {
699                    inspector.record_instant(
700                        diagnostics_traits::instant_property_name!("WaitingToRestartUntil"),
701                        waiting_until,
702                    );
703                }
704            }
705        });
706    }
707}
708
709/// Debug information to include in log messages about the client.
710#[derive(Clone, Copy)]
711pub struct DebugLogPrefix {
712    /// The numerical interface ID the client is running on.
713    pub interface_id: NonZeroU64,
714}
715
716impl Display for DebugLogPrefix {
717    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
718        let Self { interface_id } = self;
719        f.write_fmt(format_args!("(interface_id = {interface_id})"))
720    }
721}
722
723/// Configuration for the DHCP client to be used while negotiating with DHCP
724/// servers.
725#[derive(Clone)]
726pub struct ClientConfig {
727    /// The hardware address of the interface on which the DHCP client is run.
728    pub client_hardware_address: Mac,
729    /// If set, a unique-on-the-local-network string to be used to identify this
730    /// device while negotiating with DHCP servers.
731    pub client_identifier:
732        Option<AtLeast<2, AtMostBytes<{ dhcp_protocol::U8_MAX_AS_USIZE }, Vec<u8>>>>,
733    /// Parameters to request from DHCP servers.
734    pub requested_parameters: OptionCodeMap<OptionRequested>,
735    /// If set, the preferred IP address lease time in seconds.
736    pub preferred_lease_time_secs: Option<NonZeroU32>,
737    /// If set, the IP address to request from DHCP servers.
738    pub requested_ip_address: Option<SpecifiedAddr<net_types::ip::Ipv4Addr>>,
739    /// Debug information to include in log messages about the client.
740    pub debug_log_prefix: DebugLogPrefix,
741}
742
743#[derive(Clone, Debug, PartialEq, Copy)]
744struct DiscoverOptions {
745    xid: TransactionId,
746}
747
748impl DiscoverOptions {
749    fn record(&self, inspector: &mut impl Inspector) {
750        let Self { xid: TransactionId(xid) } = self;
751        inspector.record_uint("Xid", xid.get());
752    }
753}
754
755/// Transaction ID for an exchange of DHCP messages.
756///
757/// Per [RFC 2131], "Transaction ID, a random number chosen by the client, used
758/// by the client and server to associate messages and responses between a
759/// client and a server."
760///
761/// [RFC 2131]: https://datatracker.ietf.org/doc/html/rfc2131#section-4.3.1
762#[derive(Clone, Copy, Debug, PartialEq)]
763struct TransactionId(
764    // While the DHCP RFC does not require that the XID be nonzero, it's helpful
765    // to maintain that it is nonzero in order to make it clear that it is set
766    // while debugging.
767    NonZeroU32,
768);
769
770/// The initial state as depicted in the state-transition diagram in [RFC 2131].
771/// [RFC 2131]: https://datatracker.ietf.org/doc/html/rfc2131#section-4.4
772#[derive(Default, Debug, PartialEq, Clone, Copy)]
773pub struct Init;
774
775impl Init {
776    /// Generates a random transaction ID, records the starting time, and
777    /// transitions to Selecting.
778    fn do_init<C: deps::Clock>(
779        &self,
780        rng: &mut impl deps::RngProvider,
781        clock: &C,
782    ) -> Selecting<C::Instant> {
783        let discover_options = DiscoverOptions {
784            xid: TransactionId(NonZeroU32::new(rng.get_rng().random_range(1..=u32::MAX)).unwrap()),
785        };
786        Selecting {
787            discover_options,
788            // Per RFC 2131 section 4.4.1, "The client records its own local time
789            // for later use in computing the lease expiration" when it starts
790            // sending DHCPDISCOVERs.
791            start_time: clock.now(),
792        }
793    }
794}
795
796/// The state of waiting to restart the configuration process.
797#[derive(Debug, Clone, Copy, PartialEq)]
798pub struct WaitingToRestart<I> {
799    waiting_until: I,
800}
801
802#[derive(Debug, PartialEq)]
803enum WaitingToRestartOutcome {
804    GracefulShutdown,
805    Init(Init),
806}
807
808impl<I: deps::Instant> WaitingToRestart<I> {
809    async fn do_waiting_to_restart<C: deps::Clock<Instant = I>>(
810        &self,
811        clock: &C,
812        stop_receiver: &mut mpsc::UnboundedReceiver<()>,
813    ) -> WaitingToRestartOutcome {
814        let Self { waiting_until } = self;
815        let wait_fut = clock.wait_until(*waiting_until).fuse();
816        let mut wait_fut = pin!(wait_fut);
817
818        select! {
819            () = wait_fut => WaitingToRestartOutcome::Init(Init::default()),
820            () = stop_receiver.select_next_some() => WaitingToRestartOutcome::GracefulShutdown,
821        }
822    }
823}
824
825fn build_decline(
826    client_config: &ClientConfig,
827    discover_options: &DiscoverOptions,
828    ip_address: SpecifiedAddr<net_types::ip::Ipv4Addr>,
829    server_identifier: SpecifiedAddr<net_types::ip::Ipv4Addr>,
830) -> dhcp_protocol::Message {
831    build_outgoing_message(
832        client_config,
833        discover_options,
834        OutgoingOptions {
835            ciaddr: None,
836            requested_ip_address: Some(ip_address),
837            ip_address_lease_time_secs: None,
838            message_type: dhcp_protocol::MessageType::DHCPDECLINE,
839            server_identifier: Some(server_identifier),
840            include_parameter_request_list: false,
841        },
842    )
843}
844
845async fn send_with_retransmits<T: Clone + Send + Debug>(
846    time: &impl deps::Clock,
847    retransmit_schedule: impl IntoIterator<Item = Duration>,
848    message: &[u8],
849    socket: &impl deps::Socket<T>,
850    dest: T,
851    debug_log_prefix: DebugLogPrefix,
852    counters: &MessagingRelatedCounters,
853) -> Result<(), Error> {
854    send_with_retransmits_at_instants(
855        time,
856        retransmit_schedule.into_iter().map(|duration| time.now().add(duration)),
857        message,
858        socket,
859        dest,
860        debug_log_prefix,
861        counters,
862    )
863    .await
864}
865
866async fn send_with_retransmits_at_instants<I: deps::Instant, T: Clone + Send + Debug>(
867    time: &impl deps::Clock<Instant = I>,
868    retransmit_schedule: impl IntoIterator<Item = I>,
869    message: &[u8],
870    socket: &impl deps::Socket<T>,
871    dest: T,
872    debug_log_prefix: DebugLogPrefix,
873    counters: &MessagingRelatedCounters,
874) -> Result<(), Error> {
875    let MessagingRelatedCounters { send_message, recv_time_out, .. } = counters;
876    for wait_until in std::iter::once(None).chain(retransmit_schedule.into_iter().map(Some)) {
877        if let Some(wait_until) = wait_until {
878            time.wait_until(wait_until).await;
879            recv_time_out.increment();
880        }
881        let result = socket.send_to(message, dest.clone()).await;
882        match result {
883            Ok(()) => {
884                send_message.increment();
885            }
886            Err(e) => match e {
887                // We view these errors as non-recoverable, so we bubble them up
888                // to bindings:
889                deps::SocketError::FailedToOpen(_)
890                | deps::SocketError::NoInterface
891                | deps::SocketError::NetworkUnreachable
892                | deps::SocketError::UnsupportedHardwareType
893                | deps::SocketError::AddrNotAvailable
894                | deps::SocketError::BrokenPipe
895                | deps::SocketError::ConnectionAborted => return Err(Error::Socket(e)),
896                // We view EHOSTUNREACH as a recoverable error, as the desired
897                // destination could only be temporarily offline, and this does
898                // not necessarily indicate an issue with our own network stack.
899                // Log a warning and continue retransmitting.
900                deps::SocketError::HostUnreachable => {
901                    log::warn!("{debug_log_prefix} destination host unreachable: {:?}", dest);
902                }
903                // For errors that we don't recognize, default to logging an
904                // error and continuing to operate.
905                deps::SocketError::Other(_) => {
906                    log::error!(
907                        "{debug_log_prefix} socket error while sending to {:?}: {:?}",
908                        dest,
909                        e
910                    );
911                }
912            },
913        }
914    }
915    Ok(())
916}
917
918fn retransmit_schedule_during_acquisition(
919    rng: &mut (impl rand::Rng + ?Sized),
920) -> impl Iterator<Item = Duration> + '_ {
921    const MILLISECONDS_PER_SECOND: i32 = 1000;
922    [4i32, 8, 16, 32]
923        .into_iter()
924        .chain(std::iter::repeat(64))
925        // Per RFC 2131 Section 4.3.1, "the delay before the first
926        // retransmission SHOULD be 4 seconds randomized by the value of a
927        // uniform random number chosen from the range -1 to +1.  [...] The
928        // delay before the next retransmission SHOULD be 8 seconds randomized
929        // by the value of a uniform number chosen from the range -1 to +1.  The
930        // retransmission delay SHOULD be doubled with subsequent
931        // retransmissions up to a maximum of 64 seconds."
932        .zip(std::iter::from_fn(|| {
933            Some(rng.random_range((-MILLISECONDS_PER_SECOND)..=MILLISECONDS_PER_SECOND))
934        }))
935        .map(|(base_seconds, jitter_millis)| {
936            let millis = u64::try_from(base_seconds * MILLISECONDS_PER_SECOND + jitter_millis)
937                .expect("retransmit wait is never negative");
938            Duration::from_millis(millis)
939        })
940}
941
942// This is assumed to be an appropriate buffer size due to Ethernet's common MTU
943// of 1500 bytes.
944const BUFFER_SIZE: usize = 1500;
945
946fn recv_stream<'a, T: 'a, U: Send>(
947    socket: &'a impl deps::Socket<U>,
948    recv_buf: &'a mut [u8],
949    parser: impl Fn(&[u8], U) -> T + 'a,
950    debug_log_prefix: DebugLogPrefix,
951    counters: &'a MessagingRelatedCounters,
952) -> impl Stream<Item = Result<T, Error>> + 'a {
953    let MessagingRelatedCounters {
954        recv_message,
955        recv_message_fatal_socket_error,
956        recv_message_non_fatal_socket_error,
957        ..
958    } = counters;
959    futures::stream::try_unfold((recv_buf, parser), move |(recv_buf, parser)| async move {
960        let result = socket.recv_from(recv_buf).await;
961        let DatagramInfo { length, address } = match result {
962            Ok(datagram_info) => {
963                recv_message.increment();
964                datagram_info
965            }
966            Err(e) => match e {
967                // We view these errors as non-recoverable, so we bubble them up
968                // to bindings:
969                deps::SocketError::FailedToOpen(_)
970                | deps::SocketError::NoInterface
971                | deps::SocketError::NetworkUnreachable
972                | deps::SocketError::UnsupportedHardwareType
973                | deps::SocketError::AddrNotAvailable
974                | deps::SocketError::BrokenPipe
975                | deps::SocketError::ConnectionAborted => {
976                    recv_message_fatal_socket_error.increment();
977                    return Err(Error::Socket(e));
978                }
979                // We view EHOSTUNREACH as a recoverable error, as the server
980                // we're communicating with could only be temporarily offline,
981                // and this does not indicate an issue with our own network
982                // stack. Log a warning and continue operating.
983                // (While it seems like this ought not to be relevant while
984                // receiving, there are instances where this could be observed,
985                // like when IP_RECVERR is set on the socket and link resolution
986                // fails, or as a result of an ICMP message.)
987                deps::SocketError::HostUnreachable => {
988                    log::warn!("{debug_log_prefix} EHOSTUNREACH from recv_from");
989                    recv_message_non_fatal_socket_error.increment();
990                    return Ok(Some((None, (recv_buf, parser))));
991                }
992                // For errors that we don't recognize, default to logging an
993                // error and continuing to operate.
994                deps::SocketError::Other(_) => {
995                    log::error!("{debug_log_prefix} socket error while receiving: {:?}", e);
996                    recv_message_non_fatal_socket_error.increment();
997                    return Ok(Some((None, (recv_buf, parser))));
998                }
999            },
1000        };
1001        let raw_msg = &recv_buf[..length];
1002        let parsed = parser(raw_msg, address);
1003        Ok(Some((Some(parsed), (recv_buf, parser))))
1004    })
1005    .try_filter_map(|item| futures::future::ok(item))
1006}
1007
1008struct OutgoingOptions {
1009    ciaddr: Option<SpecifiedAddr<net_types::ip::Ipv4Addr>>,
1010    requested_ip_address: Option<SpecifiedAddr<net_types::ip::Ipv4Addr>>,
1011    ip_address_lease_time_secs: Option<NonZeroU32>,
1012    message_type: dhcp_protocol::MessageType,
1013    server_identifier: Option<SpecifiedAddr<net_types::ip::Ipv4Addr>>,
1014    include_parameter_request_list: bool,
1015}
1016
1017fn build_outgoing_message(
1018    ClientConfig {
1019        client_hardware_address,
1020        client_identifier,
1021        requested_parameters,
1022        preferred_lease_time_secs: _,
1023        requested_ip_address: _,
1024        debug_log_prefix: _,
1025    }: &ClientConfig,
1026    DiscoverOptions { xid: TransactionId(xid) }: &DiscoverOptions,
1027    OutgoingOptions {
1028        ciaddr,
1029        requested_ip_address,
1030        ip_address_lease_time_secs,
1031        message_type,
1032        server_identifier,
1033        include_parameter_request_list,
1034    }: OutgoingOptions,
1035) -> dhcp_protocol::Message {
1036    use dhcp_protocol::DhcpOption;
1037
1038    dhcp_protocol::Message {
1039        op: dhcp_protocol::OpCode::BOOTREQUEST,
1040        xid: xid.get(),
1041        secs: 0,
1042        bdcast_flag: false,
1043        ciaddr: ciaddr.map(|ip| ip.get().into()).unwrap_or(Ipv4Addr::UNSPECIFIED),
1044        yiaddr: Ipv4Addr::UNSPECIFIED,
1045        siaddr: Ipv4Addr::UNSPECIFIED,
1046        giaddr: Ipv4Addr::UNSPECIFIED,
1047        chaddr: *client_hardware_address,
1048        sname: BString::default(),
1049        file: BString::default(),
1050        // NB: The DHCP Options RFC (RFC 2132) does not impose any requirements
1051        // on the order of options.  However, there is an industry norm to place
1052        // the DHCP Message Type first.
1053        //
1054        // For the sake of maximum compliance with DHCP Servers in the wild,
1055        // we write our options in the same order as Netstack2 did:
1056        // * Message Type,
1057        // * Requested Parameters,
1058        // * Server Identifier,
1059        // * Requested IP Address,
1060        //
1061        // Note, NS2 didn't support any other options, so we tag all remaining
1062        // options on the end of the list.
1063        options: [
1064            Some(DhcpOption::DhcpMessageType(message_type)),
1065            include_parameter_request_list
1066                .then(|| requested_parameters.try_to_parameter_request_list())
1067                .flatten()
1068                .map(DhcpOption::ParameterRequestList),
1069            server_identifier.map(|ip| DhcpOption::ServerIdentifier(ip.get().into())),
1070            requested_ip_address.map(|ip| DhcpOption::RequestedIpAddress(ip.get().into())),
1071            ip_address_lease_time_secs.map(|time| DhcpOption::IpAddressLeaseTime(time.get())),
1072            client_identifier.clone().map(DhcpOption::ClientIdentifier),
1073        ]
1074        .into_iter()
1075        .flatten()
1076        .collect(),
1077    }
1078}
1079
1080fn build_discover(
1081    client_config: &ClientConfig,
1082    discover_options: &DiscoverOptions,
1083) -> dhcp_protocol::Message {
1084    let ClientConfig {
1085        client_hardware_address: _,
1086        client_identifier: _,
1087        requested_parameters: _,
1088        preferred_lease_time_secs,
1089        requested_ip_address,
1090        debug_log_prefix: _,
1091    } = client_config;
1092
1093    // Per the table in RFC 2131 section 4.4.1:
1094    //
1095    // 'ciaddr'                 0 (DHCPDISCOVER)
1096    // Requested IP Address     MAY (DISCOVER)
1097    // IP address lease time    MAY
1098    // DHCP message type        DHCPDISCOVER
1099    // Server Identifier        MUST NOT
1100    // Parameter Request List   MAY
1101    build_outgoing_message(
1102        client_config,
1103        discover_options,
1104        OutgoingOptions {
1105            ciaddr: None,
1106            requested_ip_address: *requested_ip_address,
1107            ip_address_lease_time_secs: *preferred_lease_time_secs,
1108            message_type: dhcp_protocol::MessageType::DHCPDISCOVER,
1109            server_identifier: None,
1110            include_parameter_request_list: true,
1111        },
1112    )
1113}
1114
1115// Returns Ok(Some) if a DHCP message was successfully parsed, Ok(None) if the
1116// IP packet should just be discarded (but does not indicate an error), and
1117// Err if the IP packet indicates that some error should be logged.
1118fn parse_incoming_dhcp_message_from_ip_packet(
1119    packet: &[u8],
1120    debug_log_prefix: DebugLogPrefix,
1121) -> Result<Option<(net_types::ip::Ipv4Addr, dhcp_protocol::Message)>, anyhow::Error> {
1122    match crate::parse::parse_dhcp_message_from_ip_packet(packet, CLIENT_PORT) {
1123        Ok(message) => Ok(Some(message)),
1124        Err(err) => match err {
1125            crate::parse::ParseError::NotUdp => {
1126                log::debug!("{debug_log_prefix} ignoring non-UDP incoming packet");
1127                return Ok(None);
1128            }
1129            crate::parse::ParseError::WrongPort(port) => {
1130                log::debug!(
1131                    "{debug_log_prefix} ignoring incoming UDP packet \
1132                    to non-DHCP-client port {port}"
1133                );
1134                return Ok(None);
1135            }
1136            err @ (crate::parse::ParseError::Ipv4(_)
1137            | crate::parse::ParseError::Udp(_)
1138            | crate::parse::ParseError::WrongSource(_)
1139            | crate::parse::ParseError::Dhcp(_)) => {
1140                return Err(err).context("error while parsing DHCP message from IP packet");
1141            }
1142        },
1143    }
1144}
1145
1146#[derive(Debug)]
1147pub(crate) enum SelectingOutcome<I> {
1148    GracefulShutdown,
1149    Requesting(Requesting<I>),
1150}
1151
1152/// The Selecting state as depicted in the state-transition diagram in [RFC 2131].
1153///
1154/// [RFC 2131]: https://datatracker.ietf.org/doc/html/rfc2131#section-4.4
1155#[derive(Debug, Clone, Copy)]
1156pub struct Selecting<I> {
1157    discover_options: DiscoverOptions,
1158    // The time at which the DHCP transaction was initiated (used as the offset
1159    // from which lease expiration times are computed).
1160    start_time: I,
1161}
1162
1163impl<I: deps::Instant> Selecting<I> {
1164    /// Executes the Selecting state.
1165    ///
1166    /// Transmits (and retransmits, if necessary) DHCPDISCOVER messages, and
1167    /// receives DHCPOFFER messages, on a packet socket. Tries to select a
1168    /// DHCPOFFER. If successful, transitions to Requesting.
1169    async fn do_selecting<C: deps::Clock<Instant = I>>(
1170        &self,
1171        client_config: &ClientConfig,
1172        packet_socket_provider: &impl deps::PacketSocketProvider,
1173        rng: &mut impl deps::RngProvider,
1174        time: &C,
1175        stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1176        counters: &Counters,
1177    ) -> Result<SelectingOutcome<I>, Error> {
1178        let Counters { selecting: SelectingCounters { messaging, recv_error, .. }, .. } = counters;
1179        // TODO(https://fxbug.dev/42075580): avoid dropping/recreating the packet
1180        // socket unnecessarily by taking an `&impl
1181        // deps::Socket<net_types::ethernet::Mac>` here instead.
1182        let socket = packet_socket_provider.get_packet_socket().await.map_err(Error::Socket)?;
1183        let Selecting { discover_options, start_time } = self;
1184        let message = build_discover(client_config, discover_options);
1185
1186        let ClientConfig {
1187            client_hardware_address: _,
1188            client_identifier: _,
1189            requested_parameters,
1190            preferred_lease_time_secs: _,
1191            requested_ip_address: _,
1192            debug_log_prefix,
1193        } = client_config;
1194
1195        let message = crate::parse::serialize_dhcp_message_to_ip_packet(
1196            message,
1197            Ipv4Addr::UNSPECIFIED, // src_ip
1198            CLIENT_PORT,
1199            Ipv4Addr::BROADCAST, // dst_ip
1200            SERVER_PORT,
1201        );
1202
1203        let mut send_fut = pin!(
1204            send_with_retransmits(
1205                time,
1206                retransmit_schedule_during_acquisition(rng.get_rng()),
1207                message.as_ref(),
1208                &socket,
1209                /* dest= */ Mac::BROADCAST,
1210                *debug_log_prefix,
1211                messaging
1212            )
1213            .fuse()
1214        );
1215
1216        let mut recv_buf = [0u8; BUFFER_SIZE];
1217        let mut offer_fields_stream = pin!(
1218            recv_stream(
1219                &socket,
1220                &mut recv_buf,
1221                |packet, src_addr| {
1222                    // We don't care about the src addr of incoming offers, because we
1223                    // identify DHCP servers via the Server Identifier option.
1224                    let _: Mac = src_addr;
1225
1226                    let (src_addr, message) =
1227                        match parse_incoming_dhcp_message_from_ip_packet(packet, *debug_log_prefix)
1228                            .inspect_err(|_| messaging.recv_failed_dhcp_parse.increment())?
1229                        {
1230                            Some(message) => message,
1231                            None => return Ok(None),
1232                        };
1233                    validate_message(discover_options, client_config, &message)
1234                        .inspect_err(|e| e.increment(&messaging))
1235                        .context("invalid DHCP message")?;
1236                    crate::parse::fields_to_retain_from_selecting(requested_parameters, message)
1237                        .inspect_err(|e| recv_error.increment(&e))
1238                        .map(|(fields, soft_errors)| {
1239                            messaging.increment_soft_errors(soft_errors);
1240                            Some((src_addr, fields))
1241                        })
1242                        .context(
1243                            "error while retrieving fields to use in DHCPREQUEST from DHCP message",
1244                        )
1245                },
1246                *debug_log_prefix,
1247                messaging
1248            )
1249            .try_filter_map(|parse_result| {
1250                futures::future::ok(match parse_result {
1251                    Ok(fields) => fields,
1252                    Err(error) => {
1253                        log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
1254                        None
1255                    }
1256                })
1257            })
1258            .fuse()
1259        );
1260
1261        select_biased! {
1262            fields_to_use_in_request_result = offer_fields_stream.select_next_some() => {
1263                let (src_addr, fields_from_offer_to_use_in_request) =
1264                    fields_to_use_in_request_result?;
1265                log_offer(&debug_log_prefix, &src_addr, &fields_from_offer_to_use_in_request);
1266                // Currently, we take the naive approach of accepting the first
1267                // DHCPOFFER we see without doing any special selection logic.
1268                Ok(SelectingOutcome::Requesting(Requesting {
1269                    discover_options: discover_options.clone(),
1270                    fields_from_offer_to_use_in_request,
1271                    start_time: *start_time,
1272                }))
1273            },
1274            () = stop_receiver.select_next_some() => {
1275                Ok(SelectingOutcome::GracefulShutdown)
1276            },
1277            send_discovers_result = send_fut => {
1278                send_discovers_result?;
1279                unreachable!("should never stop retransmitting DHCPDISCOVER unless we hit an error");
1280            }
1281        }
1282    }
1283}
1284
1285impl<I: deps::Instant> Selecting<I> {
1286    fn record(&self, inspector: &mut impl Inspector) {
1287        let Self { discover_options, start_time } = self;
1288        inspector.record_instant(diagnostics_traits::instant_property_name!("Start"), start_time);
1289        discover_options.record(inspector);
1290    }
1291}
1292
1293#[derive(thiserror::Error, Debug, PartialEq)]
1294enum ValidateMessageError {
1295    #[error("xid {actual} doesn't match expected xid {expected}")]
1296    WrongXid { expected: u32, actual: u32 },
1297    #[error("chaddr {actual} doesn't match expected chaddr {expected}")]
1298    WrongChaddr { expected: Mac, actual: Mac },
1299}
1300
1301impl ValidateMessageError {
1302    fn increment(&self, counters: &MessagingRelatedCounters) {
1303        match self {
1304            ValidateMessageError::WrongXid { .. } => counters.recv_wrong_xid.increment(),
1305            ValidateMessageError::WrongChaddr { .. } => counters.recv_wrong_chaddr.increment(),
1306        }
1307    }
1308}
1309
1310fn validate_message(
1311    DiscoverOptions { xid: TransactionId(my_xid) }: &DiscoverOptions,
1312    ClientConfig {
1313        client_hardware_address: my_chaddr,
1314        client_identifier: _,
1315        requested_parameters: _,
1316        preferred_lease_time_secs: _,
1317        requested_ip_address: _,
1318        debug_log_prefix: _,
1319    }: &ClientConfig,
1320    dhcp_protocol::Message {
1321        op: _,
1322        xid: msg_xid,
1323        secs: _,
1324        bdcast_flag: _,
1325        ciaddr: _,
1326        yiaddr: _,
1327        siaddr: _,
1328        giaddr: _,
1329        chaddr: msg_chaddr,
1330        sname: _,
1331        file: _,
1332        options: _,
1333    }: &dhcp_protocol::Message,
1334) -> Result<(), ValidateMessageError> {
1335    if *msg_xid != u32::from(*my_xid) {
1336        return Err(ValidateMessageError::WrongXid { expected: my_xid.get(), actual: *msg_xid });
1337    }
1338
1339    if msg_chaddr != my_chaddr {
1340        return Err(ValidateMessageError::WrongChaddr {
1341            expected: *my_chaddr,
1342            actual: *msg_chaddr,
1343        });
1344    }
1345    Ok(())
1346}
1347
1348#[derive(Debug, PartialEq)]
1349pub(crate) enum RequestingOutcome<I> {
1350    RanOutOfRetransmits,
1351    GracefulShutdown,
1352    Bound(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
1353    Nak(FieldsToRetainFromNak),
1354}
1355
1356/// The Requesting state as depicted in the state-transition diagram in [RFC 2131].
1357///
1358/// [RFC 2131]: https://datatracker.ietf.org/doc/html/rfc2131#section-4.4
1359#[derive(Debug, PartialEq, Clone, Copy)]
1360pub struct Requesting<I> {
1361    discover_options: DiscoverOptions,
1362    fields_from_offer_to_use_in_request: crate::parse::FieldsFromOfferToUseInRequest,
1363    start_time: I,
1364}
1365
1366// Per RFC 2131, section 3.1: "a client retransmitting as described in section
1367// 4.1 might retransmit the DHCPREQUEST message four times, for a total delay of
1368// 60 seconds, before restarting the initialization procedure".
1369const NUM_REQUEST_RETRANSMITS: usize = 4;
1370
1371/// The default lease time of an IP Address if none was present in the DHCPACK.
1372///
1373/// RFC 2131 section 4.3.1 states that DHCP Servers MUST explicitly provide the
1374/// "IP address lease time" option when sending a DHCPACK in response to a
1375/// DHCPREQUEST:
1376///
1377///   Option                    DHCPOFFER    DHCPACK            DHCPNAK
1378///   ------                    ---------    -------            -------
1379///   ...
1380///   IP address lease time     MUST         MUST (DHCPREQUEST) MUST NOT
1381///
1382/// However, we've observed DHCP servers in the field that fail to set the lease
1383/// time (https://fxbug.dev/486403240).
1384///
1385/// The RFC does not specify how a client should handle a DHCPACK that omits the
1386/// lease time. After sampling several DHCP Client implementations it's clear
1387/// that the community is split on the best approach.
1388/// * ISC's dhclient chooses to reject the ACK and restart the client.
1389/// * FreeBSD's dhclient chooses to fill in a default lease time (12 hours).
1390/// * Netstack2's DHCP client (this implementations predecessor) choose to fill
1391///   in a default lease time (12 hours).
1392///
1393/// For the sake of compatibility with such servers, we've chosen to fill in a
1394/// default lease time.
1395const DEFAULT_LEASE_TIME: Duration = Duration::from_hours(12);
1396
1397impl<I: deps::Instant> Requesting<I> {
1398    /// Executes the Requesting state.
1399    ///
1400    /// Transmits (and retransmits, if necessary) DHCPREQUEST messages, and
1401    /// receives DHCPACK and DHCPNAK messages, on a packet socket. Upon
1402    /// receiving a DHCPACK, transitions to Bound.
1403    async fn do_requesting<C: deps::Clock<Instant = I>>(
1404        &self,
1405        client_config: &ClientConfig,
1406        packet_socket_provider: &impl deps::PacketSocketProvider,
1407        rng: &mut impl deps::RngProvider,
1408        time: &C,
1409        stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1410        counters: &Counters,
1411    ) -> Result<RequestingOutcome<I>, Error> {
1412        let Counters {
1413            requesting: RequestingCounters { messaging, recv_error, recv_nak, .. }, ..
1414        } = counters;
1415        let socket = packet_socket_provider.get_packet_socket().await.map_err(Error::Socket)?;
1416        let Requesting { discover_options, fields_from_offer_to_use_in_request, start_time } = self;
1417        let message = build_request_during_address_acquisition(
1418            client_config,
1419            discover_options,
1420            fields_from_offer_to_use_in_request,
1421        );
1422
1423        let message = crate::parse::serialize_dhcp_message_to_ip_packet(
1424            message,
1425            Ipv4Addr::UNSPECIFIED, // src_ip
1426            CLIENT_PORT,
1427            Ipv4Addr::BROADCAST, // dst_ip
1428            SERVER_PORT,
1429        );
1430
1431        let ClientConfig {
1432            client_hardware_address: _,
1433            client_identifier: _,
1434            requested_parameters,
1435            preferred_lease_time_secs: _,
1436            requested_ip_address: _,
1437            debug_log_prefix,
1438        } = client_config;
1439
1440        let mut send_fut = pin!(
1441            send_with_retransmits(
1442                time,
1443                retransmit_schedule_during_acquisition(rng.get_rng()).take(NUM_REQUEST_RETRANSMITS),
1444                message.as_ref(),
1445                &socket,
1446                Mac::BROADCAST,
1447                *debug_log_prefix,
1448                messaging
1449            )
1450            .fuse()
1451        );
1452
1453        let mut recv_buf = [0u8; BUFFER_SIZE];
1454
1455        let mut ack_or_nak_stream = pin!(
1456            recv_stream(
1457                &socket,
1458                &mut recv_buf,
1459                |packet, src_addr| {
1460                    // We don't care about the src addr of incoming messages, because we
1461                    // identify DHCP servers via the Server Identifier option.
1462                    let _: Mac = src_addr;
1463                    let (src_addr, message) =
1464                        match parse_incoming_dhcp_message_from_ip_packet(packet, *debug_log_prefix)
1465                            .inspect_err(|_| {
1466                                messaging.recv_failed_dhcp_parse.increment();
1467                            })? {
1468                            Some(message) => message,
1469                            None => return Ok(None),
1470                        };
1471                    validate_message(discover_options, client_config, &message)
1472                        .inspect_err(|e| e.increment(&messaging))
1473                        .context("invalid DHCP message")?;
1474
1475                    crate::parse::fields_to_retain_from_response_to_request(
1476                        requested_parameters,
1477                        message,
1478                    )
1479                    .inspect_err(|e| recv_error.increment(e))
1480                    .context("error extracting needed fields from DHCP message during Requesting")
1481                    .map(|(fields, soft_errors)| {
1482                        messaging.increment_soft_errors(soft_errors);
1483                        Some((src_addr, fields))
1484                    })
1485                },
1486                *debug_log_prefix,
1487                messaging
1488            )
1489            .try_filter_map(|parse_result| {
1490                futures::future::ok(match parse_result {
1491                    Ok(msg) => msg,
1492                    Err(error) => {
1493                        log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
1494                        None
1495                    }
1496                })
1497            })
1498            .fuse()
1499        );
1500
1501        let (src_addr, fields_to_retain) = select_biased! {
1502            fields_to_retain_result = ack_or_nak_stream.select_next_some() => {
1503                fields_to_retain_result?
1504            },
1505            () = stop_receiver.select_next_some() => {
1506                return Ok(RequestingOutcome::GracefulShutdown)
1507            },
1508            send_requests_result = send_fut => {
1509                send_requests_result?;
1510                messaging.recv_time_out.increment();
1511                return Ok(RequestingOutcome::RanOutOfRetransmits)
1512            }
1513        };
1514
1515        match fields_to_retain {
1516            crate::parse::IncomingResponseToRequest::Ack(ack) => {
1517                log_ack(&debug_log_prefix, &src_addr, &ack);
1518                let FieldsToRetainFromAck {
1519                    yiaddr,
1520                    server_identifier,
1521                    ip_address_lease_time_secs,
1522                    renewal_time_value_secs,
1523                    rebinding_time_value_secs,
1524                    parameters,
1525                } = ack;
1526                let server_identifier = server_identifier.unwrap_or({
1527                    let crate::parse::FieldsFromOfferToUseInRequest {
1528                        server_identifier,
1529                        ip_address_lease_time_secs: _,
1530                        ip_address_to_request: _,
1531                    } = fields_from_offer_to_use_in_request;
1532                    *server_identifier
1533                });
1534
1535                let ip_address_lease_time = match ip_address_lease_time_secs {
1536                    Some(lease_time) => Duration::from_secs(lease_time.get().into()),
1537                    None => {
1538                        // The DHCP Server did not provide an IP Address Lease
1539                        // Time in the DHCPACK. This is in violation of the RFC,
1540                        // but we accept the ACK regardless for compatibility.
1541                        //
1542                        // Prefer the lease length from the offer, if one was
1543                        // included, otherwise fall back to the default.
1544                        messaging.recv_ack_no_addr_lease_time.increment();
1545                        match fields_from_offer_to_use_in_request.ip_address_lease_time_secs {
1546                            Some(lease_time) => {
1547                                let lease_time = Duration::from_secs(lease_time.get().into());
1548                                log::warn!(
1549                                    "{debug_log_prefix} accepting DHCPACK from {src_addr} that did \
1550                                    not provide an IP Address Lease Time. Falling back to the \
1551                                    lease time from the DHCPOFFER: {lease_time:?}."
1552                                );
1553                                lease_time
1554                            }
1555                            None => {
1556                                log::warn!(
1557                                    "{debug_log_prefix} accepting DHCPACK from {src_addr} that did \
1558                                    not provide an IP Address Lease Time. Falling back to the \
1559                                    default: {DEFAULT_LEASE_TIME:?}."
1560                                );
1561                                DEFAULT_LEASE_TIME
1562                            }
1563                        }
1564                    }
1565                };
1566
1567                Ok(RequestingOutcome::Bound(
1568                    LeaseState {
1569                        discover_options: discover_options.clone(),
1570                        yiaddr,
1571                        server_identifier,
1572                        ip_address_lease_time,
1573                        renewal_time: renewal_time_value_secs
1574                            .map(u64::from)
1575                            .map(Duration::from_secs),
1576                        rebinding_time: rebinding_time_value_secs
1577                            .map(u64::from)
1578                            .map(Duration::from_secs),
1579                        start_time: *start_time,
1580                    },
1581                    parameters,
1582                ))
1583            }
1584            crate::parse::IncomingResponseToRequest::Nak(nak) => {
1585                log_nak(&debug_log_prefix, &src_addr, &nak);
1586                recv_nak.increment();
1587                Ok(RequestingOutcome::Nak(nak))
1588            }
1589        }
1590    }
1591
1592    fn record(&self, inspector: &mut impl Inspector) {
1593        let Self { discover_options, start_time, fields_from_offer_to_use_in_request } = self;
1594        inspector.record_instant(diagnostics_traits::instant_property_name!("Start"), start_time);
1595        discover_options.record(inspector);
1596        fields_from_offer_to_use_in_request.record(inspector);
1597    }
1598}
1599
1600fn build_request_during_address_acquisition(
1601    client_config: &ClientConfig,
1602    discover_options: &DiscoverOptions,
1603    crate::parse::FieldsFromOfferToUseInRequest {
1604        server_identifier,
1605        ip_address_lease_time_secs,
1606        ip_address_to_request,
1607    }: &crate::parse::FieldsFromOfferToUseInRequest,
1608) -> dhcp_protocol::Message {
1609    let ClientConfig {
1610        client_hardware_address: _,
1611        client_identifier: _,
1612        requested_parameters: _,
1613        preferred_lease_time_secs,
1614        requested_ip_address: _,
1615        debug_log_prefix: _,
1616    } = client_config;
1617
1618    // Per the table in RFC 2131 section 4.4.1:
1619    //
1620    // 'ciaddr'                 0 or client's network address (currently 0)
1621    // Requested IP Address     MUST (in SELECTING)
1622    // IP address lease time    MAY
1623    // DHCP message type        DHCPREQUEST
1624    // Server Identifier        MUST (after SELECTING)
1625    // Parameter Request List   MAY
1626    build_outgoing_message(
1627        client_config,
1628        discover_options,
1629        OutgoingOptions {
1630            ciaddr: None,
1631            requested_ip_address: Some(*ip_address_to_request),
1632            ip_address_lease_time_secs: ip_address_lease_time_secs.or(*preferred_lease_time_secs),
1633            message_type: dhcp_protocol::MessageType::DHCPREQUEST,
1634            server_identifier: Some(*server_identifier),
1635            include_parameter_request_list: true,
1636        },
1637    )
1638}
1639
1640/// A newly-acquired DHCP lease.
1641#[derive(Debug, PartialEq)]
1642pub struct NewlyAcquiredLease<I> {
1643    /// The IP address acquired.
1644    pub ip_address: SpecifiedAddr<net_types::ip::Ipv4Addr>,
1645    /// The start time of the lease.
1646    pub start_time: I,
1647    /// The length of the lease.
1648    pub lease_time: Duration,
1649    /// Configuration parameters acquired from the server. Guaranteed to be a
1650    /// subset of the parameters requested in the `parameter_request_list` in
1651    /// `ClientConfig`.
1652    pub parameters: Vec<dhcp_protocol::DhcpOption>,
1653}
1654
1655#[derive(Debug, PartialEq)]
1656pub(crate) enum BoundOutcome<I, R> {
1657    GracefulShutdown,
1658    Renewing(Renewing<I>),
1659    Restart(Init),
1660    AddressRemoved(R),
1661    AddressRejected(WaitingToRestart<I>),
1662    Assigned(Bound<I>),
1663}
1664
1665/// The Bound state as depicted in the state-transition diagram in [RFC 2131].
1666///
1667/// [RFC 2131]: https://datatracker.ietf.org/doc/html/rfc2131#section-4.4
1668#[derive(Debug, PartialEq, Clone, Copy)]
1669pub enum Bound<I> {
1670    /// The address is undergoing Duplicate Address Detection and is not yet
1671    /// assigned.
1672    AwaitingAssignment {
1673        /// State about the lease.
1674        lease_state: LeaseState<I>,
1675    },
1676    /// The address has finished Duplicate Address Detection and is now
1677    /// available for use.
1678    Assigned {
1679        /// State about the lease.
1680        lease_state: LeaseState<I>,
1681    },
1682}
1683
1684/// The state for an acquired lease held by the DHCP state machine.
1685#[derive(Debug, PartialEq, Clone, Copy)]
1686pub struct LeaseState<I> {
1687    discover_options: DiscoverOptions,
1688    yiaddr: SpecifiedAddr<net_types::ip::Ipv4Addr>,
1689    server_identifier: SpecifiedAddr<net_types::ip::Ipv4Addr>,
1690    ip_address_lease_time: Duration,
1691    start_time: I,
1692    renewal_time: Option<Duration>,
1693    rebinding_time: Option<Duration>,
1694}
1695
1696impl<I: deps::Instant> Bound<I> {
1697    async fn do_bound<C: deps::Clock<Instant = I>, R>(
1698        &self,
1699        client_config: &ClientConfig,
1700        time: &C,
1701        stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1702        packet_socket_provider: &impl deps::PacketSocketProvider,
1703        address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
1704        // The Bound state currently doesn't need to increment any counters,
1705        // because all it does is wait.
1706        _counters: &Counters,
1707    ) -> Result<BoundOutcome<I, R>, Error> {
1708        match self {
1709            Bound::AwaitingAssignment { lease_state } => {
1710                do_bound_awaiting_assignment(
1711                    lease_state,
1712                    client_config,
1713                    time,
1714                    stop_receiver,
1715                    packet_socket_provider,
1716                    address_event_receiver,
1717                )
1718                .await
1719            }
1720            Bound::Assigned { lease_state } => {
1721                do_bound_assigned(
1722                    lease_state,
1723                    client_config,
1724                    time,
1725                    stop_receiver,
1726                    packet_socket_provider,
1727                    address_event_receiver,
1728                )
1729                .await
1730            }
1731        }
1732    }
1733}
1734
1735async fn do_bound_awaiting_assignment<I: deps::Instant, C: deps::Clock<Instant = I>, R>(
1736    lease_state: &LeaseState<I>,
1737    client_config: &ClientConfig,
1738    time: &C,
1739    stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1740    packet_socket_provider: &impl deps::PacketSocketProvider,
1741    address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
1742) -> Result<BoundOutcome<I, R>, Error> {
1743    let LeaseState {
1744        discover_options: _,
1745        yiaddr,
1746        server_identifier: _,
1747        ip_address_lease_time,
1748        start_time,
1749        renewal_time: _,
1750        rebinding_time: _,
1751    } = lease_state;
1752
1753    // Wait for the duration of the lease for the address to become assigned.
1754    //
1755    // Note: depending on how long it takes for the address to
1756    // become assigned, we may only exit `Bound::AwaitingAssigned`
1757    // after T1 (renewing time) and T2 (rebinding time). Because all
1758    // of the timeout calculations in this mod are based off of
1759    // `start_time`, we'll simply wait for times in the past, which
1760    // will allow the client to proceed to the renewing/rebinding
1761    // phases without waiting.
1762    let lease_timeout_fut = time.wait_until(start_time.add(*ip_address_lease_time)).fuse();
1763    let mut lease_timeout_fut = pin!(lease_timeout_fut);
1764
1765    let mut address_removed_or_assigned = pin!(address_event_receiver.filter_map(async |event| {
1766        match event {
1767            AddressEvent::Rejected => {
1768                handle_address_rejection(client_config, lease_state, packet_socket_provider, time)
1769                    .map(|result| Some(result.map(BoundOutcome::AddressRejected)))
1770                    .await
1771            }
1772            AddressEvent::Removed(reason) => Some(Ok(BoundOutcome::AddressRemoved(reason))),
1773            AddressEvent::AssignmentStateChanged(new_state) => {
1774                match new_state {
1775                    AddressAssignmentState::Assigned => {
1776                        Some(Ok(BoundOutcome::Assigned(Bound::Assigned {
1777                            lease_state: lease_state.clone(),
1778                        })))
1779                    }
1780                    // TODO(https://fxbug.dev/421941195): Handle addresses
1781                    // becoming unavailable.
1782                    s @ AddressAssignmentState::Tentative
1783                    | s @ AddressAssignmentState::Unavailable => {
1784                        log::warn!(
1785                            "{yiaddr} address state became {s:?} during \
1786                            Bound::AwaitingAssignment; ignoring"
1787                        );
1788                        None
1789                    }
1790                }
1791            }
1792        }
1793    }));
1794
1795    let debug_log_prefix = &client_config.debug_log_prefix;
1796    select! {
1797        // Note: We won't be sending a DHCPDECLINE, and therefore
1798        // can restart immediately (rather than waiting for
1799        // `WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION`).
1800        () = lease_timeout_fut => {
1801            log::info!(
1802                "{debug_log_prefix} Returning to Init due to failing to observe address \
1803                assignment for {yiaddr} before the lease ({ip_address_lease_time:?}) \
1804                expired."
1805            );
1806            Ok(BoundOutcome::Restart(Init::default()))
1807        },
1808        () = stop_receiver.select_next_some() => Ok(BoundOutcome::GracefulShutdown),
1809        result = address_removed_or_assigned.next() => {
1810            result.unwrap_or(Err(Error::AddressEventReceiverEnded))
1811        }
1812    }
1813}
1814
1815async fn do_bound_assigned<I: deps::Instant, C: deps::Clock<Instant = I>, R>(
1816    lease_state: &LeaseState<I>,
1817    client_config: &ClientConfig,
1818    time: &C,
1819    stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1820    packet_socket_provider: &impl deps::PacketSocketProvider,
1821    address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
1822) -> Result<BoundOutcome<I, R>, Error> {
1823    let LeaseState {
1824        discover_options: _,
1825        yiaddr,
1826        server_identifier,
1827        ip_address_lease_time,
1828        start_time,
1829        renewal_time,
1830        rebinding_time: _,
1831    } = lease_state;
1832
1833    // Per RFC 2131 section 4.4.5, "T1 defaults to
1834    // (0.5 * duration_of_lease)". (T1 is how the RFC refers to the
1835    // time at which we transition to Renewing.)
1836    let renewal_time = renewal_time.unwrap_or(*ip_address_lease_time / 2);
1837
1838    let debug_log_prefix = &client_config.debug_log_prefix;
1839    log::info!(
1840        "{debug_log_prefix} In Bound state; ip_address_lease_time = {}, \
1841        renewal_time = {}, server_identifier = {server_identifier}",
1842        ip_address_lease_time.as_secs(),
1843        renewal_time.as_secs(),
1844    );
1845
1846    let renewal_timeout_fut = time.wait_until(start_time.add(renewal_time)).fuse();
1847    let mut renewal_timeout_fut = pin!(renewal_timeout_fut);
1848
1849    let mut address_removed = pin!(address_event_receiver.filter_map(async |event| match event {
1850        AddressEvent::Rejected => {
1851            handle_address_rejection(client_config, lease_state, packet_socket_provider, time)
1852                .map(|result| Some(result.map(BoundOutcome::AddressRejected)))
1853                .await
1854        }
1855        AddressEvent::Removed(reason) => Some(Ok(BoundOutcome::AddressRemoved(reason))),
1856        AddressEvent::AssignmentStateChanged(new_state) => {
1857            // TODO(https://fxbug.dev/421941195): Handle addresses
1858            // becoming unavailable.
1859            log::warn!(
1860                "{yiaddr} address state became {new_state:?} during Bound::Assigned; ignoring"
1861            );
1862            None
1863        }
1864    }));
1865
1866    select! {
1867        () = renewal_timeout_fut => Ok(BoundOutcome::Renewing(Renewing {
1868                lease_state: lease_state.clone()
1869            })),
1870
1871        () = stop_receiver.select_next_some() => Ok(BoundOutcome::GracefulShutdown),
1872        result = address_removed.next() => result.unwrap_or(Err(Error::AddressEventReceiverEnded)),
1873    }
1874}
1875
1876impl<I: deps::Instant> LeaseState<I> {
1877    fn record(&self, inspector: &mut impl Inspector) {
1878        let Self {
1879            discover_options,
1880            yiaddr,
1881            server_identifier,
1882            ip_address_lease_time,
1883            start_time,
1884            renewal_time,
1885            rebinding_time,
1886        } = self;
1887        inspector.record_instant(diagnostics_traits::instant_property_name!("Start"), start_time);
1888        discover_options.record(inspector);
1889        inspector.record_ip_addr("Yiaddr", **yiaddr);
1890        inspector.record_ip_addr("ServerIdentifier", **server_identifier);
1891        inspector.record_uint("IpAddressLeaseTimeSecs", ip_address_lease_time.as_secs());
1892        record_optional_duration_secs(inspector, "RenewalTimeSecs", *renewal_time);
1893        record_optional_duration_secs(inspector, "RebindingTimeSecs", *rebinding_time);
1894    }
1895}
1896
1897#[derive(Debug, PartialEq)]
1898pub(crate) enum RenewingOutcome<I, R> {
1899    GracefulShutdown,
1900    Renewed(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
1901    // It might be surprising to see that it's possible to yield a _new_ address
1902    // while renewing, but per RFC 2131 section 4.4.5, "if the client is given a
1903    // new network address, it MUST NOT continue using the previous network
1904    // address and SHOULD notify the local users of the problem." This suggests
1905    // that we should be prepared for a DHCP server to send us a different
1906    // address from the one we asked for while renewing.
1907    NewAddress(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
1908    Nak(FieldsToRetainFromNak),
1909    Rebinding(Rebinding<I>),
1910    AddressRemoved(R),
1911    AddressRejected(WaitingToRestart<I>),
1912}
1913
1914/// The Renewing state as depicted in the state-transition diagram in [RFC 2131].
1915///
1916/// [RFC 2131]: https://datatracker.ietf.org/doc/html/rfc2131#section-4.4
1917#[derive(Debug, PartialEq, Clone, Copy)]
1918pub struct Renewing<I> {
1919    lease_state: LeaseState<I>,
1920}
1921
1922// Per RFC 2131 section 4.4.5: "In both RENEWING and REBINDING states,
1923// if the client receives no response to its DHCPREQUEST message, the
1924// client SHOULD wait one-half of the remaining time until T2 (in
1925// RENEWING state) and one-half of the remaining lease time (in
1926// REBINDING state), down to a minimum of 60 seconds, before
1927// retransmitting the DHCPREQUEST message."
1928const RENEW_RETRANSMIT_MINIMUM_DELAY: Duration = Duration::from_secs(60);
1929
1930impl<I: deps::Instant> Renewing<I> {
1931    async fn do_renewing<C: deps::Clock<Instant = I>, R>(
1932        &self,
1933        client_config: &ClientConfig,
1934        // TODO(https://fxbug.dev/42075580): avoid dropping/recreating the packet
1935        // socket unnecessarily by taking an `&impl
1936        // deps::Socket<std::net::SocketAddr>` here instead.
1937        udp_socket_provider: &impl deps::UdpSocketProvider,
1938        packet_socket_provider: &impl deps::PacketSocketProvider,
1939        time: &C,
1940        stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1941        address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
1942        counters: &Counters,
1943    ) -> Result<RenewingOutcome<I, R>, Error> {
1944        let Counters { renewing: RenewingCounters { messaging, recv_error, recv_nak, .. }, .. } =
1945            counters;
1946        let renewal_start_time = time.now();
1947        let debug_log_prefix = client_config.debug_log_prefix;
1948
1949        let Self {
1950            lease_state:
1951                lease_state @ LeaseState {
1952                    discover_options,
1953                    yiaddr,
1954                    server_identifier,
1955                    ip_address_lease_time,
1956                    start_time,
1957                    renewal_time: _,
1958                    rebinding_time,
1959                },
1960        } = self;
1961        let rebinding_time = rebinding_time.unwrap_or(*ip_address_lease_time / 8 * 7);
1962        let mut address_removed =
1963            pin!(address_event_receiver.filter_map(async |event| match event {
1964                AddressEvent::Rejected => {
1965                    handle_address_rejection(
1966                        client_config,
1967                        lease_state,
1968                        packet_socket_provider,
1969                        time,
1970                    )
1971                    .map(|result| Some(result.map(RenewingOutcome::AddressRejected)))
1972                    .await
1973                }
1974                AddressEvent::Removed(reason) => {
1975                    Some(Ok(RenewingOutcome::AddressRemoved(reason)))
1976                }
1977                AddressEvent::AssignmentStateChanged(new_state) => {
1978                    // TODO(https://fxbug.dev/421941195): Handle addresses
1979                    // becoming unavailable.
1980                    log::warn!(
1981                        "{yiaddr} address state became {new_state:?} during Renewing; ignoring"
1982                    );
1983                    None
1984                }
1985            }));
1986
1987        let socket = match udp_socket_provider
1988            .bind_new_udp_socket(std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
1989                yiaddr.get().into(),
1990                CLIENT_PORT.get(),
1991            )))
1992            .await
1993        {
1994            Ok(s) => s,
1995            Err(e @ deps::SocketError::AddrNotAvailable) => {
1996                // Do a best-effort check by polling the address event receiver. The event
1997                // is not guaranteed to be available, so just return the socket error if
1998                // it isn't.
1999                if let Some(Some(result)) = address_removed.next().now_or_never() {
2000                    return result;
2001                }
2002                return Err(Error::Socket(e));
2003            }
2004            Err(e) => return Err(Error::Socket(e)),
2005        };
2006
2007        // Per the table in RFC 2131 section 4.4.1:
2008        //
2009        // 'ciaddr'                 client's network address (BOUND/RENEW/REBIND)
2010        // Requested IP Address     MUST NOT (in BOUND or RENEWING)
2011        // IP address lease time    MAY
2012        // DHCP message type        DHCPREQUEST
2013        // Server Identifier        MUST NOT (after INIT-REBOOT, BOUND, RENEWING or REBINDING)
2014        // Parameter Request List   MAY
2015        let message = build_outgoing_message(
2016            client_config,
2017            discover_options,
2018            OutgoingOptions {
2019                ciaddr: Some(*yiaddr),
2020                requested_ip_address: None,
2021                ip_address_lease_time_secs: client_config.preferred_lease_time_secs,
2022                message_type: dhcp_protocol::MessageType::DHCPREQUEST,
2023                server_identifier: None,
2024                include_parameter_request_list: true,
2025            },
2026        );
2027        let message_bytes = message.serialize();
2028
2029        let t2 = start_time.add(rebinding_time);
2030        let server_sockaddr = std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
2031            server_identifier.get().into(),
2032            SERVER_PORT.get(),
2033        ));
2034        let mut send_fut = pin!(
2035            send_with_retransmits_at_instants(
2036                time,
2037                std::iter::from_fn(|| {
2038                    let now = time.now();
2039                    let half_time_until_t2 = now.average(t2);
2040                    Some(half_time_until_t2.max(now.add(RENEW_RETRANSMIT_MINIMUM_DELAY)))
2041                }),
2042                message_bytes.as_ref(),
2043                &socket,
2044                server_sockaddr,
2045                debug_log_prefix,
2046                messaging
2047            )
2048            .fuse()
2049        );
2050
2051        let mut recv_buf = [0u8; BUFFER_SIZE];
2052        let mut responses_stream = pin!(
2053            recv_stream(
2054                &socket,
2055                &mut recv_buf,
2056                |packet, addr| {
2057                    if addr != server_sockaddr {
2058                        return Err(anyhow::Error::from(crate::parse::ParseError::WrongSource(
2059                            addr,
2060                        )));
2061                    }
2062                    let message = dhcp_protocol::Message::from_buffer(packet)
2063                        .map_err(crate::parse::ParseError::Dhcp)
2064                        .context("error while parsing DHCP message from UDP datagram")
2065                        .inspect_err(|_| messaging.recv_failed_dhcp_parse.increment())?;
2066                    validate_message(discover_options, client_config, &message)
2067                        .inspect_err(|e| e.increment(&messaging))
2068                        .context("invalid DHCP message")?;
2069                    crate::parse::fields_to_retain_from_response_to_request(
2070                        &client_config.requested_parameters,
2071                        message,
2072                    )
2073                    .inspect_err(|e| recv_error.increment(e))
2074                    .context("error extracting needed fields from DHCP message during Renewing")
2075                    .map(|(fields, soft_errors)| {
2076                        messaging.increment_soft_errors(soft_errors);
2077                        (addr, fields)
2078                    })
2079                },
2080                debug_log_prefix,
2081                messaging
2082            )
2083            .try_filter_map(|parse_result| {
2084                futures::future::ok(match parse_result {
2085                    Ok(msg) => Some(msg),
2086                    Err(error) => {
2087                        log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
2088                        None
2089                    }
2090                })
2091            })
2092            .fuse()
2093        );
2094
2095        let mut timeout_fut = pin!(time.wait_until(t2).fuse());
2096
2097        let (src_addr, response) = select_biased! {
2098            response = responses_stream.select_next_some() => {
2099                response?
2100            },
2101            () = stop_receiver.select_next_some() => return Ok(RenewingOutcome::GracefulShutdown),
2102            result = address_removed.next() => {
2103                return result.unwrap_or(Err(Error::AddressEventReceiverEnded))
2104            }
2105            send_result = send_fut => {
2106                return Err(send_result.expect_err("send_fut should never complete without error"))
2107            },
2108            () = timeout_fut => {
2109                messaging.recv_time_out.increment();
2110                return Ok(RenewingOutcome::Rebinding(
2111                    Rebinding { lease_state: lease_state.clone() }
2112                ))
2113            }
2114        };
2115
2116        match response {
2117            crate::parse::IncomingResponseToRequest::Ack(ack) => {
2118                log_ack(&debug_log_prefix, &src_addr, &ack);
2119                let FieldsToRetainFromAck {
2120                    yiaddr: new_yiaddr,
2121                    server_identifier: _,
2122                    ip_address_lease_time_secs,
2123                    renewal_time_value_secs,
2124                    rebinding_time_value_secs,
2125                    parameters,
2126                } = ack;
2127                let variant = if new_yiaddr == *yiaddr {
2128                    RenewingOutcome::Renewed
2129                } else {
2130                    log::info!(
2131                        "{debug_log_prefix} obtained different address from renewal: {}",
2132                        new_yiaddr
2133                    );
2134                    RenewingOutcome::NewAddress
2135                };
2136
2137                let ip_address_lease_time = match ip_address_lease_time_secs {
2138                    Some(lease_time) => Duration::from_secs(lease_time.get().into()),
2139                    None => {
2140                        // The DHCP Server did not provide an IP Address Lease
2141                        // Time in the DHCPACK. This is in violation of the RFC,
2142                        // but we accept the ACK regardless for compatibility.
2143                        //
2144                        // Reuse the previously established lease length.
2145                        messaging.recv_ack_no_addr_lease_time.increment();
2146                        log::warn!(
2147                            "{debug_log_prefix} accepting DHCPACK from that did not provide an \
2148                            IP Address Lease Time. Falling back to the original lease time: \
2149                            {ip_address_lease_time:?}."
2150                        );
2151                        *ip_address_lease_time
2152                    }
2153                };
2154
2155                Ok(variant(
2156                    LeaseState {
2157                        discover_options: discover_options.clone(),
2158                        yiaddr: new_yiaddr,
2159                        server_identifier: *server_identifier,
2160                        ip_address_lease_time,
2161                        renewal_time: renewal_time_value_secs
2162                            .map(u64::from)
2163                            .map(Duration::from_secs),
2164                        rebinding_time: rebinding_time_value_secs
2165                            .map(u64::from)
2166                            .map(Duration::from_secs),
2167                        start_time: renewal_start_time,
2168                    },
2169                    parameters,
2170                ))
2171            }
2172            crate::parse::IncomingResponseToRequest::Nak(nak) => {
2173                log_nak(&debug_log_prefix, &src_addr, &nak);
2174                recv_nak.increment();
2175                Ok(RenewingOutcome::Nak(nak))
2176            }
2177        }
2178    }
2179}
2180
2181/// A renewal of a DHCP lease.
2182#[derive(Debug, PartialEq)]
2183pub struct LeaseRenewal<I> {
2184    /// The start time of the lease after renewal.
2185    pub start_time: I,
2186    /// The length of the lease after renewal.
2187    pub lease_time: Duration,
2188    /// Configuration parameters acquired from the server. Guaranteed to be a
2189    /// subset of the parameters requested in the `parameter_request_list` in
2190    /// `ClientConfig`.
2191    pub parameters: Vec<dhcp_protocol::DhcpOption>,
2192}
2193
2194/// The Rebinding state as depicted in the state-transition diagram in
2195/// [RFC 2131].
2196///
2197/// [RFC 2131]: https://datatracker.ietf.org/doc/html/rfc2131#section-4.4
2198#[derive(Debug, PartialEq, Clone, Copy)]
2199pub struct Rebinding<I> {
2200    lease_state: LeaseState<I>,
2201}
2202
2203#[derive(Debug, PartialEq)]
2204pub(crate) enum RebindingOutcome<I, R> {
2205    GracefulShutdown,
2206    Renewed(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
2207    // It might be surprising to see that it's possible to yield a _new_ address
2208    // while rebinding, but per RFC 2131 section 4.4.5, "if the client is given a
2209    // new network address, it MUST NOT continue using the previous network
2210    // address and SHOULD notify the local users of the problem." This suggests
2211    // that we should be prepared for a DHCP server to send us a different
2212    // address from the one we asked for while rebinding.
2213    NewAddress(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
2214    Nak(FieldsToRetainFromNak),
2215    TimedOut,
2216    AddressRemoved(R),
2217    AddressRejected(WaitingToRestart<I>),
2218}
2219
2220impl<I: deps::Instant> Rebinding<I> {
2221    async fn do_rebinding<C: deps::Clock<Instant = I>, R>(
2222        &self,
2223        client_config: &ClientConfig,
2224        // TODO(https://fxbug.dev/42075580): avoid dropping/recreating the packet
2225        // socket unnecessarily by taking an `&impl
2226        // deps::Socket<std::net::SocketAddr>` here instead.
2227        udp_socket_provider: &impl deps::UdpSocketProvider,
2228        packet_socket_provider: &impl deps::PacketSocketProvider,
2229        time: &C,
2230        stop_receiver: &mut mpsc::UnboundedReceiver<()>,
2231        address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
2232        counters: &Counters,
2233    ) -> Result<RebindingOutcome<I, R>, Error> {
2234        let Counters {
2235            rebinding: RebindingCounters { messaging, recv_error, recv_nak, .. }, ..
2236        } = counters;
2237        let rebinding_start_time = time.now();
2238        let debug_log_prefix = client_config.debug_log_prefix;
2239
2240        let Self {
2241            lease_state:
2242                lease_state @ LeaseState {
2243                    discover_options,
2244                    yiaddr,
2245                    server_identifier: _,
2246                    ip_address_lease_time,
2247                    start_time,
2248                    renewal_time: _,
2249                    rebinding_time: _,
2250                },
2251        } = self;
2252        let mut address_removed =
2253            pin!(address_event_receiver.filter_map(async |event| match event {
2254                AddressEvent::Rejected => {
2255                    handle_address_rejection(
2256                        client_config,
2257                        lease_state,
2258                        packet_socket_provider,
2259                        time,
2260                    )
2261                    .map(|result| Some(result.map(RebindingOutcome::AddressRejected)))
2262                    .await
2263                }
2264                AddressEvent::Removed(reason) => {
2265                    Some(Ok(RebindingOutcome::AddressRemoved(reason)))
2266                }
2267                AddressEvent::AssignmentStateChanged(new_state) => {
2268                    // TODO(https://fxbug.dev/421941195): Handle addresses
2269                    // becoming unavailable.
2270                    log::warn!(
2271                        "{yiaddr} address state became {new_state:?} during Rebinding; ignoring"
2272                    );
2273                    None
2274                }
2275            }));
2276
2277        let socket = match udp_socket_provider
2278            .bind_new_udp_socket(std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
2279                yiaddr.get().into(),
2280                CLIENT_PORT.get(),
2281            )))
2282            .await
2283        {
2284            Ok(s) => s,
2285            Err(e @ deps::SocketError::AddrNotAvailable) => {
2286                // Do a best-effort check by polling the address event receiver. The event
2287                // is not guaranteed to be available, so just return the socket error if
2288                // it isn't.
2289                if let Some(Some(result)) = address_removed.next().now_or_never() {
2290                    return result;
2291                }
2292                return Err(Error::Socket(e));
2293            }
2294            Err(e) => return Err(Error::Socket(e)),
2295        };
2296
2297        // Per the table in RFC 2131 section 4.4.1:
2298        //
2299        // 'ciaddr'                 client's network address (BOUND/RENEW/REBIND)
2300        // Requested IP Address     MUST NOT (in BOUND or RENEWING)
2301        // IP address lease time    MAY
2302        // DHCP message type        DHCPREQUEST
2303        // Server Identifier        MUST NOT (after INIT-REBOOT, BOUND, RENEWING or REBINDING)
2304        // Parameter Request List   MAY
2305        let message = build_outgoing_message(
2306            client_config,
2307            discover_options,
2308            OutgoingOptions {
2309                ciaddr: Some(*yiaddr),
2310                requested_ip_address: None,
2311                ip_address_lease_time_secs: client_config.preferred_lease_time_secs,
2312                message_type: dhcp_protocol::MessageType::DHCPREQUEST,
2313                server_identifier: None,
2314                include_parameter_request_list: true,
2315            },
2316        );
2317        let message_bytes = message.serialize();
2318
2319        let lease_expiry = start_time.add(*ip_address_lease_time);
2320        let server_sockaddr = std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
2321            Ipv4Addr::BROADCAST,
2322            SERVER_PORT.get(),
2323        ));
2324        let mut send_fut = pin!(
2325            send_with_retransmits_at_instants(
2326                time,
2327                std::iter::from_fn(|| {
2328                    let now = time.now();
2329                    let half_time_until_lease_expiry = now.average(lease_expiry);
2330                    Some(half_time_until_lease_expiry.max(now.add(RENEW_RETRANSMIT_MINIMUM_DELAY)))
2331                }),
2332                message_bytes.as_ref(),
2333                &socket,
2334                server_sockaddr,
2335                debug_log_prefix,
2336                messaging
2337            )
2338            .fuse()
2339        );
2340
2341        let mut recv_buf = [0u8; BUFFER_SIZE];
2342        let mut responses_stream = pin!(
2343            recv_stream(
2344                &socket,
2345                &mut recv_buf,
2346                |packet, addr| {
2347                    let message = dhcp_protocol::Message::from_buffer(packet)
2348                        .map_err(crate::parse::ParseError::Dhcp)
2349                        .context("error while parsing DHCP message from UDP datagram")
2350                        .inspect_err(|_| messaging.recv_failed_dhcp_parse.increment())?;
2351                    validate_message(discover_options, client_config, &message)
2352                        .inspect_err(|e| e.increment(&messaging))
2353                        .context("invalid DHCP message")?;
2354                    crate::parse::fields_to_retain_from_response_to_request(
2355                        &client_config.requested_parameters,
2356                        message,
2357                    )
2358                    .map(|(response, soft_errors)| {
2359                        messaging.increment_soft_errors(soft_errors);
2360                        response
2361                    })
2362                    .and_then(|response| match response {
2363                        crate::parse::IncomingResponseToRequest::Ack(ack) => {
2364                            // We need to enforce that DHCPACKs in REBINDING include
2365                            // a server identifier, as otherwise we won't know which
2366                            // server to send future renewal requests to.
2367                            Ok(crate::parse::IncomingResponseToRequest::Ack(
2368                                ack.map_server_identifier(|server_identifier| {
2369                                    server_identifier.ok_or(
2370                                crate::parse::IncomingResponseToRequestError::NoServerIdentifier,
2371                            )
2372                                })?,
2373                            ))
2374                        }
2375                        crate::parse::IncomingResponseToRequest::Nak(nak) => {
2376                            Ok(crate::parse::IncomingResponseToRequest::Nak(nak))
2377                        }
2378                    })
2379                    .inspect_err(|e| {
2380                        recv_error.increment(e);
2381                    })
2382                    .context("error extracting needed fields from DHCP message during Rebinding")
2383                    .map(|fields_to_retain| (addr, fields_to_retain))
2384                },
2385                debug_log_prefix,
2386                messaging
2387            )
2388            .try_filter_map(|parse_result| {
2389                futures::future::ok(match parse_result {
2390                    Ok(msg) => Some(msg),
2391                    Err(error) => {
2392                        log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
2393                        None
2394                    }
2395                })
2396            })
2397            .fuse()
2398        );
2399
2400        let mut timeout_fut = pin!(time.wait_until(lease_expiry).fuse());
2401
2402        let (src_addr, response) = select_biased! {
2403            response = responses_stream.select_next_some() => {
2404                response?
2405            },
2406            () = stop_receiver.select_next_some() => return Ok(RebindingOutcome::GracefulShutdown),
2407            result = address_removed.next() => {
2408                return result.unwrap_or(Err(Error::AddressEventReceiverEnded))
2409            }
2410            send_result = send_fut => {
2411                return Err(send_result.expect_err("send_fut should never complete without error"))
2412            },
2413            () = timeout_fut => {
2414                messaging.recv_time_out.increment();
2415                return Ok(RebindingOutcome::TimedOut)
2416            }
2417        };
2418
2419        match response {
2420            crate::parse::IncomingResponseToRequest::Ack(ack) => {
2421                log_ack(&debug_log_prefix, &src_addr, &ack);
2422                let FieldsToRetainFromAck {
2423                    yiaddr: new_yiaddr,
2424                    server_identifier,
2425                    ip_address_lease_time_secs,
2426                    renewal_time_value_secs,
2427                    rebinding_time_value_secs,
2428                    parameters,
2429                } = ack;
2430                let variant = if new_yiaddr == *yiaddr {
2431                    RebindingOutcome::Renewed
2432                } else {
2433                    log::info!(
2434                        "{debug_log_prefix} obtained different address from rebinding: {}",
2435                        new_yiaddr
2436                    );
2437                    RebindingOutcome::NewAddress
2438                };
2439
2440                let ip_address_lease_time = match ip_address_lease_time_secs {
2441                    Some(lease_time) => Duration::from_secs(lease_time.get().into()),
2442                    None => {
2443                        // The DHCP Server did not provide an IP Address Lease
2444                        // Time in the DHCPACK. This is in violation of the RFC,
2445                        // but we accept the ACK regardless for compatibility.
2446                        //
2447                        // Reuse the previously established lease length.
2448                        messaging.recv_ack_no_addr_lease_time.increment();
2449                        log::warn!(
2450                            "{debug_log_prefix} accepting DHCPACK from that did not provide an \
2451                            IP Address Lease Time. Falling back to the original lease time: \
2452                            {ip_address_lease_time:?}."
2453                        );
2454                        *ip_address_lease_time
2455                    }
2456                };
2457
2458                Ok(variant(
2459                    LeaseState {
2460                        discover_options: discover_options.clone(),
2461                        yiaddr: new_yiaddr,
2462                        server_identifier,
2463                        ip_address_lease_time,
2464                        renewal_time: renewal_time_value_secs
2465                            .map(u64::from)
2466                            .map(Duration::from_secs),
2467                        rebinding_time: rebinding_time_value_secs
2468                            .map(u64::from)
2469                            .map(Duration::from_secs),
2470                        start_time: rebinding_start_time,
2471                    },
2472                    parameters,
2473                ))
2474            }
2475            crate::parse::IncomingResponseToRequest::Nak(nak) => {
2476                log_nak(&debug_log_prefix, &src_addr, &nak);
2477                recv_nak.increment();
2478                Ok(RebindingOutcome::Nak(nak))
2479            }
2480        }
2481    }
2482}
2483
2484fn log_offer(
2485    prefix: &DebugLogPrefix,
2486    src_addr: &net_types::ip::Ipv4Addr,
2487    offer_fields: &FieldsFromOfferToUseInRequest,
2488) {
2489    let FieldsFromOfferToUseInRequest {
2490        server_identifier,
2491        ip_address_lease_time_secs: lease_time,
2492        ip_address_to_request: yiaddr,
2493    } = offer_fields;
2494    log::info!(
2495        "{prefix} received DHCPOFFER from {src_addr}: \
2496        yiaddr={yiaddr}, lease_time={lease_time:?}, server_identifier={server_identifier}"
2497    );
2498}
2499
2500fn log_ack<A: Display, S: Debug>(
2501    prefix: &DebugLogPrefix,
2502    src_addr: &A,
2503    ack_fields: &FieldsToRetainFromAck<S>,
2504) {
2505    let FieldsToRetainFromAck {
2506        yiaddr,
2507        server_identifier,
2508        ip_address_lease_time_secs: lease_time,
2509        renewal_time_value_secs: renew_time,
2510        rebinding_time_value_secs: rebinding_time,
2511        parameters,
2512    } = ack_fields;
2513    // NB: Don't log the raw parameters, they may contain unstructured PII.
2514    // Instead extract the parameters that are known to be useful for debugging.
2515    let (prefix_len, routers, dns_servers): (
2516        Option<u8>,
2517        Option<Vec<Ipv4Addr>>,
2518        Option<Vec<Ipv4Addr>>,
2519    ) = parameters.iter().fold(
2520        (None, None, None),
2521        |(mut prefix_len, mut routers, mut dns_servers), option| {
2522            match option {
2523                dhcp_protocol::DhcpOption::SubnetMask(prefix_length) => {
2524                    prefix_len = Some(prefix_length.get());
2525                }
2526                dhcp_protocol::DhcpOption::Router(at_least) => {
2527                    routers.get_or_insert_default().extend(at_least.deref());
2528                }
2529                dhcp_protocol::DhcpOption::DomainNameServer(at_least) => {
2530                    dns_servers.get_or_insert_default().extend(at_least.deref());
2531                }
2532                _ => {}
2533            }
2534            (prefix_len, routers, dns_servers)
2535        },
2536    );
2537    log::info!(
2538        "{prefix} received DHCPACK from {src_addr}: \
2539        yiaddr={yiaddr}, lease_time={lease_time:?}, renewal_time={renew_time:?}, \
2540        rebinding_time={rebinding_time:?}, server_identifier={server_identifier:?}, \
2541        prefix_len={prefix_len:?}, routers={routers:?}, dns_servers={dns_servers:?}"
2542    );
2543}
2544
2545fn log_nak<A: Display>(prefix: &DebugLogPrefix, src_addr: &A, nak_fields: &FieldsToRetainFromNak) {
2546    let FieldsToRetainFromNak { server_identifier, message, client_identifier: _ } = nak_fields;
2547    log::info!(
2548        "{prefix} received DHCPNAK from {src_addr}: \
2549        server_identifier={server_identifier}, message={message:?}"
2550    );
2551}
2552
2553#[cfg(test)]
2554mod test {
2555    use super::*;
2556    use crate::deps::Clock as _;
2557    use crate::deps::testutil::{
2558        FakeRngProvider, FakeSocket, FakeSocketProvider, FakeTimeController, TestInstant, advance,
2559        run_until_next_timers_fire,
2560    };
2561    use assert_matches::assert_matches;
2562    use fuchsia_async as fasync;
2563    use futures::{Future, join};
2564    use itertools::Itertools as _;
2565    use net_declare::net::prefix_length_v4;
2566    use net_declare::{net_mac, std_ip_v4};
2567    use net_types::ip::{Ipv4, PrefixLength};
2568    use simplelog::{Config, LevelFilter, WriteLogger};
2569    use std::cell::RefCell;
2570    use std::rc::Rc;
2571    use test_case::test_case;
2572
2573    fn initialize_logging() {
2574        WriteLogger::init(LevelFilter::Info, Config::default(), std::io::stderr()).unwrap();
2575    }
2576
2577    const TEST_MAC_ADDRESS: Mac = net_mac!("01:01:01:01:01:01");
2578    const TEST_SERVER_MAC_ADDRESS: Mac = net_mac!("02:02:02:02:02:02");
2579    const OTHER_MAC_ADDRESS: Mac = net_mac!("03:03:03:03:03:03");
2580
2581    const SERVER_IP: Ipv4Addr = std_ip_v4!("192.168.1.1");
2582    const OTHER_SERVER_IP: Ipv4Addr = std_ip_v4!("192.168.1.11");
2583    const YIADDR: Ipv4Addr = std_ip_v4!("198.168.1.5");
2584    const OTHER_ADDR: Ipv4Addr = std_ip_v4!("198.168.1.6");
2585    const DEFAULT_LEASE_LENGTH_SECONDS: u32 = 100;
2586    const MAX_LEASE_LENGTH_SECONDS: u32 = 200;
2587    const TEST_PREFIX_LENGTH: PrefixLength<Ipv4> = prefix_length_v4!(24);
2588
2589    fn test_requested_parameters() -> OptionCodeMap<OptionRequested> {
2590        use dhcp_protocol::OptionCode;
2591        [
2592            (OptionCode::SubnetMask, OptionRequested::Required),
2593            (OptionCode::Router, OptionRequested::Optional),
2594            (OptionCode::DomainNameServer, OptionRequested::Optional),
2595        ]
2596        .into_iter()
2597        .collect::<OptionCodeMap<_>>()
2598    }
2599
2600    fn test_parameter_values_excluding_subnet_mask() -> [dhcp_protocol::DhcpOption; 2] {
2601        [
2602            dhcp_protocol::DhcpOption::Router([SERVER_IP].into()),
2603            dhcp_protocol::DhcpOption::DomainNameServer([SERVER_IP, std_ip_v4!("8.8.8.8")].into()),
2604        ]
2605    }
2606
2607    fn test_parameter_values() -> impl IntoIterator<Item = dhcp_protocol::DhcpOption> {
2608        std::iter::once(dhcp_protocol::DhcpOption::SubnetMask(TEST_PREFIX_LENGTH))
2609            .chain(test_parameter_values_excluding_subnet_mask())
2610    }
2611
2612    fn test_client_config() -> ClientConfig {
2613        ClientConfig {
2614            client_hardware_address: TEST_MAC_ADDRESS,
2615            client_identifier: None,
2616            requested_parameters: test_requested_parameters(),
2617            preferred_lease_time_secs: None,
2618            requested_ip_address: None,
2619            debug_log_prefix: DebugLogPrefix { interface_id: NonZeroU64::new(2).unwrap() },
2620        }
2621    }
2622
2623    // A fake address removal reason for tests.
2624    #[derive(Debug, PartialEq)]
2625    struct FakeRemovedReason;
2626
2627    #[test]
2628    fn do_init_uses_rng() {
2629        let mut rng = FakeRngProvider::new(0);
2630        let time = FakeTimeController::new();
2631        let arbitrary_start_time = std::time::Duration::from_secs(42);
2632        advance(&time, arbitrary_start_time);
2633
2634        let Selecting {
2635            discover_options: DiscoverOptions { xid: xid_a },
2636            start_time: start_time_a,
2637        } = Init.do_init(&mut rng, &time);
2638        let Selecting {
2639            discover_options: DiscoverOptions { xid: xid_b },
2640            start_time: start_time_b,
2641        } = Init.do_init(&mut rng, &time);
2642        assert_ne!(xid_a, xid_b);
2643        assert_eq!(start_time_a, TestInstant(arbitrary_start_time));
2644        assert_eq!(start_time_b, TestInstant(arbitrary_start_time));
2645    }
2646
2647    fn run_with_accelerated_time<F>(
2648        executor: &mut fasync::TestExecutor,
2649        time: &Rc<RefCell<FakeTimeController>>,
2650        main_future: &mut F,
2651    ) -> F::Output
2652    where
2653        F: Future + Unpin,
2654    {
2655        loop {
2656            match run_until_next_timers_fire(executor, time, main_future) {
2657                std::task::Poll::Ready(result) => break result,
2658                std::task::Poll::Pending => (),
2659            }
2660        }
2661    }
2662
2663    fn build_test_selecting_state() -> Selecting<TestInstant> {
2664        Selecting {
2665            discover_options: DiscoverOptions { xid: TransactionId(NonZeroU32::new(1).unwrap()) },
2666            start_time: TestInstant(std::time::Duration::from_secs(0)),
2667        }
2668    }
2669
2670    #[test]
2671    fn do_selecting_obeys_graceful_shutdown() {
2672        initialize_logging();
2673        let counters = Counters::default();
2674
2675        let mut executor = fasync::TestExecutor::new();
2676        let time = FakeTimeController::new();
2677
2678        let selecting = build_test_selecting_state();
2679        let mut rng = FakeRngProvider::new(0);
2680
2681        let (_server_end, client_end) = FakeSocket::new_pair();
2682        let test_socket_provider = FakeSocketProvider::new(client_end);
2683
2684        let client_config = test_client_config();
2685
2686        let (stop_sender, mut stop_receiver) = mpsc::unbounded();
2687
2688        let mut selecting_fut = pin!(
2689            selecting
2690                .do_selecting(
2691                    &client_config,
2692                    &test_socket_provider,
2693                    &mut rng,
2694                    &time,
2695                    &mut stop_receiver,
2696                    &counters,
2697                )
2698                .fuse()
2699        );
2700
2701        let time = &time;
2702
2703        let mut wait_fut = pin!(
2704            async {
2705                // Wait some arbitrary amount of time to ensure `do_selecting` is waiting on a reply.
2706                // Note that this is fake time, not 30 actual seconds.
2707                time.wait_until(TestInstant(std::time::Duration::from_secs(30))).await;
2708            }
2709            .fuse()
2710        );
2711
2712        {
2713            let main_future = async {
2714                select! {
2715                    _ = selecting_fut => unreachable!("should keep retransmitting DHCPDISCOVER forever"),
2716                    () = wait_fut => (),
2717                }
2718            };
2719            let mut main_future = pin!(main_future);
2720
2721            run_with_accelerated_time(&mut executor, time, &mut main_future);
2722        }
2723
2724        stop_sender.unbounded_send(()).expect("sending stop signal should succeed");
2725
2726        let selecting_result = selecting_fut.now_or_never().expect(
2727            "selecting_fut should complete after single poll after stop signal has been sent",
2728        );
2729
2730        assert_matches!(selecting_result, Ok(SelectingOutcome::GracefulShutdown));
2731    }
2732
2733    struct VaryingOutgoingMessageFields {
2734        xid: u32,
2735        options: Vec<dhcp_protocol::DhcpOption>,
2736    }
2737
2738    #[track_caller]
2739    fn assert_outgoing_message_when_not_assigned_address(
2740        got_message: &dhcp_protocol::Message,
2741        fields: VaryingOutgoingMessageFields,
2742    ) {
2743        let VaryingOutgoingMessageFields { xid, options } = fields;
2744        let want_message = dhcp_protocol::Message {
2745            op: dhcp_protocol::OpCode::BOOTREQUEST,
2746            xid,
2747            secs: 0,
2748            bdcast_flag: false,
2749            ciaddr: Ipv4Addr::UNSPECIFIED,
2750            yiaddr: Ipv4Addr::UNSPECIFIED,
2751            siaddr: Ipv4Addr::UNSPECIFIED,
2752            giaddr: Ipv4Addr::UNSPECIFIED,
2753            chaddr: TEST_MAC_ADDRESS,
2754            sname: BString::default(),
2755            file: BString::default(),
2756            options,
2757        };
2758        assert_eq!(got_message, &want_message);
2759    }
2760
2761    #[test]
2762    fn do_selecting_sends_discover() {
2763        initialize_logging();
2764        let counters = Counters::default();
2765
2766        let mut executor = fasync::TestExecutor::new();
2767        let time = FakeTimeController::new();
2768
2769        let selecting = Selecting {
2770            discover_options: DiscoverOptions { xid: TransactionId(NonZeroU32::new(1).unwrap()) },
2771            start_time: TestInstant(std::time::Duration::from_secs(0)),
2772        };
2773        let mut rng = FakeRngProvider::new(0);
2774
2775        let (server_end, client_end) = FakeSocket::new_pair();
2776        let test_socket_provider = FakeSocketProvider::new(client_end);
2777
2778        let client_config = test_client_config();
2779
2780        let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
2781
2782        let mut selecting_fut = pin!(
2783            selecting
2784                .do_selecting(
2785                    &client_config,
2786                    &test_socket_provider,
2787                    &mut rng,
2788                    &time,
2789                    &mut stop_receiver,
2790                    &counters,
2791                )
2792                .fuse()
2793        );
2794
2795        let time = &time;
2796
2797        // These are the time ranges in which we expect to see messages from the
2798        // DHCP client. They are ranges in order to account for randomized
2799        // delays.
2800        const EXPECTED_RANGES: [(u64, u64); 7] =
2801            [(0, 0), (3, 5), (7, 9), (15, 17), (31, 33), (63, 65), (63, 65)];
2802
2803        let mut receive_fut =
2804            pin!(
2805                async {
2806                    let mut previous_time = std::time::Duration::from_secs(0);
2807
2808                    for (start, end) in EXPECTED_RANGES {
2809                        let mut recv_buf = [0u8; BUFFER_SIZE];
2810                        let DatagramInfo { length, address } = server_end
2811                            .recv_from(&mut recv_buf)
2812                            .await
2813                            .expect("recv_from on test socket should succeed");
2814
2815                        assert_eq!(address, Mac::BROADCAST);
2816
2817                        let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
2818                            &recv_buf[..length],
2819                            dhcp_protocol::SERVER_PORT,
2820                        )
2821                        .expect("received packet should parse as DHCP message");
2822
2823                        assert_outgoing_message_when_not_assigned_address(
2824                            &msg,
2825                            VaryingOutgoingMessageFields {
2826                                xid: msg.xid,
2827                                options: vec![
2828                            dhcp_protocol::DhcpOption::DhcpMessageType(
2829                                dhcp_protocol::MessageType::DHCPDISCOVER,
2830                            ),
2831                            dhcp_protocol::DhcpOption::ParameterRequestList(
2832                                test_requested_parameters()
2833                                    .iter_keys()
2834                                    .collect::<Vec<_>>()
2835                                    .try_into()
2836                                    .expect("should fit parameter request list size constraints"),
2837                            ),
2838                        ],
2839                            },
2840                        );
2841
2842                        let TestInstant(received_time) = time.now();
2843
2844                        let duration_range = std::time::Duration::from_secs(start)
2845                            ..=std::time::Duration::from_secs(end);
2846                        assert!(duration_range.contains(&(received_time - previous_time)));
2847
2848                        previous_time = received_time;
2849                    }
2850                }
2851                .fuse()
2852            );
2853
2854        let main_future = async {
2855            select! {
2856                _ = selecting_fut => unreachable!("should keep retransmitting DHCPDISCOVER forever"),
2857                () = receive_fut => (),
2858            }
2859        };
2860        let mut main_future = pin!(main_future);
2861
2862        run_with_accelerated_time(&mut executor, time, &mut main_future);
2863        assert_eq!(counters.selecting.messaging.send_message.load(), EXPECTED_RANGES.len());
2864        assert_eq!(counters.selecting.messaging.recv_time_out.load(), EXPECTED_RANGES.len() - 1);
2865    }
2866
2867    const XID: NonZeroU32 = NonZeroU32::new(1).unwrap();
2868    #[test_case(u32::from(XID), TEST_MAC_ADDRESS => Ok(()) ; "accepts good reply")]
2869    #[test_case(u32::from(XID), TEST_SERVER_MAC_ADDRESS => Err(
2870        ValidateMessageError::WrongChaddr {
2871            expected: TEST_MAC_ADDRESS,
2872            actual: TEST_SERVER_MAC_ADDRESS,
2873        }) ; "rejects wrong chaddr")]
2874    #[test_case(u32::from(XID).wrapping_add(1), TEST_MAC_ADDRESS => Err(
2875        ValidateMessageError::WrongXid {
2876            expected: u32::from(XID),
2877            actual: u32::from(XID).wrapping_add(1),
2878        }) ; "rejects wrong xid")]
2879    fn test_validate_message(
2880        message_xid: u32,
2881        message_chaddr: Mac,
2882    ) -> Result<(), ValidateMessageError> {
2883        let discover_options = DiscoverOptions { xid: TransactionId(XID) };
2884        let client_config = ClientConfig {
2885            client_hardware_address: TEST_MAC_ADDRESS,
2886            client_identifier: None,
2887            requested_parameters: test_requested_parameters(),
2888            preferred_lease_time_secs: None,
2889            requested_ip_address: None,
2890            debug_log_prefix: DebugLogPrefix { interface_id: NonZeroU64::new(2).unwrap() },
2891        };
2892
2893        let reply = dhcp_protocol::Message {
2894            op: dhcp_protocol::OpCode::BOOTREPLY,
2895            xid: message_xid,
2896            secs: 0,
2897            bdcast_flag: false,
2898            ciaddr: Ipv4Addr::UNSPECIFIED,
2899            yiaddr: Ipv4Addr::UNSPECIFIED,
2900            siaddr: Ipv4Addr::UNSPECIFIED,
2901            giaddr: Ipv4Addr::UNSPECIFIED,
2902            chaddr: message_chaddr,
2903            sname: BString::default(),
2904            file: BString::default(),
2905            options: Vec::new(),
2906        };
2907
2908        validate_message(&discover_options, &client_config, &reply)
2909    }
2910
2911    #[allow(clippy::unused_unit)]
2912    #[test_case(false ; "with no garbage traffic on link")]
2913    #[test_case(true ; "ignoring garbage replies to discover")]
2914    fn do_selecting_good_offer(reply_to_discover_with_garbage: bool) {
2915        initialize_logging();
2916        let counters = Counters::default();
2917
2918        let mut rng = FakeRngProvider::new(0);
2919        let time = FakeTimeController::new();
2920
2921        let arbitrary_start_time = std::time::Duration::from_secs(42);
2922        advance(&time, arbitrary_start_time);
2923
2924        let selecting = Init.do_init(&mut rng, &time);
2925        let TransactionId(xid) = selecting.discover_options.xid;
2926
2927        let (server_end, client_end) = FakeSocket::<Mac>::new_pair();
2928        let test_socket_provider = FakeSocketProvider::new(client_end);
2929
2930        let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
2931
2932        let client_config = test_client_config();
2933
2934        let selecting_fut = pin!(
2935            selecting
2936                .do_selecting(
2937                    &client_config,
2938                    &test_socket_provider,
2939                    &mut rng,
2940                    &time,
2941                    &mut stop_receiver,
2942                    &counters,
2943                )
2944                .fuse()
2945        );
2946
2947        let server_fut = pin!(
2948            async {
2949                let mut recv_buf = [0u8; BUFFER_SIZE];
2950
2951                if reply_to_discover_with_garbage {
2952                    let DatagramInfo { length: _, address: dst_addr } = server_end
2953                        .recv_from(&mut recv_buf)
2954                        .await
2955                        .expect("recv_from on test socket should succeed");
2956                    assert_eq!(dst_addr, Mac::BROADCAST);
2957
2958                    server_end
2959                        .send_to(b"hello", OTHER_MAC_ADDRESS)
2960                        .await
2961                        .expect("send_to with garbage data should succeed");
2962                }
2963
2964                let DatagramInfo { length, address } = server_end
2965                    .recv_from(&mut recv_buf)
2966                    .await
2967                    .expect("recv_from on test socket should succeed");
2968                assert_eq!(address, Mac::BROADCAST);
2969
2970                // `dhcp_protocol::Message` intentionally doesn't implement `Clone`,
2971                // so we re-parse instead for testing purposes.
2972                let parse_msg = || {
2973                    let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
2974                        &recv_buf[..length],
2975                        dhcp_protocol::SERVER_PORT,
2976                    )
2977                    .expect("received packet on test socket should parse as DHCP message");
2978                    msg
2979                };
2980
2981                let msg = parse_msg();
2982                assert_outgoing_message_when_not_assigned_address(
2983                    &parse_msg(),
2984                    VaryingOutgoingMessageFields {
2985                        xid: msg.xid,
2986                        options: vec![
2987                            dhcp_protocol::DhcpOption::DhcpMessageType(
2988                                dhcp_protocol::MessageType::DHCPDISCOVER,
2989                            ),
2990                            dhcp_protocol::DhcpOption::ParameterRequestList(
2991                                test_requested_parameters()
2992                                    .iter_keys()
2993                                    .collect::<Vec<_>>()
2994                                    .try_into()
2995                                    .expect("should fit parameter request list size constraints"),
2996                            ),
2997                        ],
2998                    },
2999                );
3000
3001                let build_reply = || {
3002                    dhcpv4::server::build_offer(
3003                        parse_msg(),
3004                        dhcpv4::server::OfferOptions {
3005                            offered_ip: YIADDR,
3006                            server_ip: SERVER_IP,
3007                            lease_length_config: dhcpv4::configuration::LeaseLength {
3008                                default_seconds: DEFAULT_LEASE_LENGTH_SECONDS,
3009                                max_seconds: MAX_LEASE_LENGTH_SECONDS,
3010                            },
3011                            // The following fields don't matter for this test, as the
3012                            // client will read them from the DHCPACK rather than
3013                            // remembering them from the DHCPOFFER.
3014                            renewal_time_value: Some(20),
3015                            rebinding_time_value: Some(30),
3016                            subnet_mask: TEST_PREFIX_LENGTH,
3017                        },
3018                        &dhcpv4::server::options_repo(test_parameter_values()),
3019                    )
3020                    .expect("dhcp server crate error building offer")
3021                };
3022
3023                let reply_with_wrong_xid = dhcp_protocol::Message {
3024                    xid: (u32::from(xid).wrapping_add(1)),
3025                    // Provide a different yiaddr in order to distinguish whether
3026                    // the client correctly discarded this one, since we check which
3027                    // `yiaddr` the client uses as its requested IP address later.
3028                    yiaddr: OTHER_ADDR,
3029                    ..build_reply()
3030                };
3031
3032                let reply_without_subnet_mask = {
3033                    let mut reply = build_reply();
3034                    let options = std::mem::take(&mut reply.options);
3035                    let (subnet_masks, other_options): (Vec<_>, Vec<_>) =
3036                        options.into_iter().partition_map(|option| match option {
3037                            dhcp_protocol::DhcpOption::SubnetMask(_) => {
3038                                itertools::Either::Left(option)
3039                            }
3040                            _ => itertools::Either::Right(option),
3041                        });
3042                    assert_matches!(
3043                        &subnet_masks[..],
3044                        &[dhcp_protocol::DhcpOption::SubnetMask(TEST_PREFIX_LENGTH)]
3045                    );
3046                    reply.options = other_options;
3047
3048                    // Provide a different yiaddr in order to distinguish whether
3049                    // the client correctly discarded this one, since we check which
3050                    // `yiaddr` the client uses as its requested IP address later.
3051                    reply.yiaddr = OTHER_ADDR;
3052                    reply
3053                };
3054
3055                let good_reply = build_reply();
3056
3057                let send_reply = |reply: dhcp_protocol::Message| async {
3058                    let dst_ip = reply.yiaddr;
3059                    server_end
3060                        .send_to(
3061                            crate::parse::serialize_dhcp_message_to_ip_packet(
3062                                reply,
3063                                SERVER_IP,
3064                                SERVER_PORT,
3065                                dst_ip,
3066                                CLIENT_PORT,
3067                            )
3068                            .as_ref(),
3069                            // Note that this is the address the client under test
3070                            // observes in `recv_from`.
3071                            TEST_SERVER_MAC_ADDRESS,
3072                        )
3073                        .await
3074                        .expect("send_to on test socket should succeed");
3075                };
3076
3077                // The DHCP client should ignore the reply with an incorrect xid.
3078                send_reply(reply_with_wrong_xid).await;
3079
3080                // The DHCP client should ignore the reply without a subnet mask.
3081                send_reply(reply_without_subnet_mask).await;
3082
3083                send_reply(good_reply).await;
3084            }
3085            .fuse()
3086        );
3087
3088        let main_future = async move {
3089            let (selecting_result, ()) = join!(selecting_fut, server_fut);
3090            selecting_result
3091        }
3092        .fuse();
3093        let mut main_future = pin!(main_future);
3094        let mut executor = fasync::TestExecutor::new();
3095        let selecting_result = run_with_accelerated_time(&mut executor, &time, &mut main_future);
3096
3097        let requesting = assert_matches!(
3098            selecting_result,
3099            Ok(SelectingOutcome::Requesting(requesting)) => requesting,
3100            "should have successfully transitioned to Requesting"
3101        );
3102
3103        assert_eq!(
3104            requesting,
3105            Requesting {
3106                discover_options: DiscoverOptions { xid: requesting.discover_options.xid },
3107                fields_from_offer_to_use_in_request: crate::parse::FieldsFromOfferToUseInRequest {
3108                    server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3109                        .try_into()
3110                        .expect("should be specified"),
3111                    ip_address_lease_time_secs: Some(
3112                        NonZeroU32::new(DEFAULT_LEASE_LENGTH_SECONDS).unwrap()
3113                    ),
3114                    ip_address_to_request: net_types::ip::Ipv4Addr::from(YIADDR)
3115                        .try_into()
3116                        .expect("should be specified"),
3117                },
3118                start_time: TestInstant(arbitrary_start_time),
3119            }
3120        );
3121        assert_eq!(
3122            counters.selecting.messaging.send_message.load(),
3123            if reply_to_discover_with_garbage { 2 } else { 1 }
3124        );
3125        assert_eq!(
3126            counters.selecting.messaging.recv_time_out.load(),
3127            if reply_to_discover_with_garbage { 1 } else { 0 }
3128        );
3129        assert_eq!(
3130            counters.selecting.messaging.recv_failed_dhcp_parse.load(),
3131            if reply_to_discover_with_garbage { 1 } else { 0 }
3132        );
3133        assert_eq!(counters.selecting.messaging.recv_wrong_xid.load(), 1);
3134        assert_eq!(counters.selecting.messaging.recv_wrong_chaddr.load(), 0);
3135        assert_eq!(counters.selecting.recv_error.missing_required_option.load(), 1);
3136    }
3137
3138    const TEST_XID: TransactionId = TransactionId(NonZeroU32::new(1).unwrap());
3139    const TEST_DISCOVER_OPTIONS: DiscoverOptions = DiscoverOptions { xid: TEST_XID };
3140
3141    fn build_test_requesting_state() -> Requesting<TestInstant> {
3142        Requesting {
3143            discover_options: TEST_DISCOVER_OPTIONS,
3144            start_time: TestInstant(std::time::Duration::from_secs(0)),
3145            fields_from_offer_to_use_in_request: crate::parse::FieldsFromOfferToUseInRequest {
3146                server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3147                    .try_into()
3148                    .expect("should be specified"),
3149                ip_address_lease_time_secs: Some(
3150                    NonZeroU32::new(DEFAULT_LEASE_LENGTH_SECONDS).unwrap(),
3151                ),
3152                ip_address_to_request: net_types::ip::Ipv4Addr::from(YIADDR)
3153                    .try_into()
3154                    .expect("should be specified"),
3155            },
3156        }
3157    }
3158
3159    #[test]
3160    fn do_requesting_obeys_graceful_shutdown() {
3161        initialize_logging();
3162        let counters = Counters::default();
3163
3164        let time = FakeTimeController::new();
3165
3166        let requesting = build_test_requesting_state();
3167        let mut rng = FakeRngProvider::new(0);
3168
3169        let (_server_end, client_end) = FakeSocket::new_pair();
3170        let test_socket_provider = FakeSocketProvider::new(client_end);
3171
3172        let client_config = test_client_config();
3173
3174        let (stop_sender, mut stop_receiver) = mpsc::unbounded();
3175
3176        let requesting_fut = requesting
3177            .do_requesting(
3178                &client_config,
3179                &test_socket_provider,
3180                &mut rng,
3181                &time,
3182                &mut stop_receiver,
3183                &counters,
3184            )
3185            .fuse();
3186        let mut requesting_fut = pin!(requesting_fut);
3187
3188        let mut executor = fasync::TestExecutor::new();
3189        assert_matches!(executor.run_until_stalled(&mut requesting_fut), std::task::Poll::Pending);
3190
3191        stop_sender.unbounded_send(()).expect("sending stop signal should succeed");
3192
3193        let requesting_result = requesting_fut.now_or_never().expect(
3194            "requesting_fut should complete after single poll after stop signal has been sent",
3195        );
3196
3197        assert_matches!(requesting_result, Ok(RequestingOutcome::GracefulShutdown));
3198    }
3199
3200    #[test]
3201    fn do_requesting_sends_requests() {
3202        initialize_logging();
3203        let counters = Counters::default();
3204
3205        let mut executor = fasync::TestExecutor::new();
3206        let time = FakeTimeController::new();
3207
3208        let requesting = build_test_requesting_state();
3209        let mut rng = FakeRngProvider::new(0);
3210
3211        let (server_end, client_end) = FakeSocket::new_pair();
3212        let test_socket_provider = FakeSocketProvider::new(client_end);
3213
3214        let client_config = test_client_config();
3215
3216        let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3217
3218        let requesting_fut = pin!(
3219            requesting
3220                .do_requesting(
3221                    &client_config,
3222                    &test_socket_provider,
3223                    &mut rng,
3224                    &time,
3225                    &mut stop_receiver,
3226                    &counters,
3227                )
3228                .fuse()
3229        );
3230
3231        let time = &time;
3232
3233        // These are the time ranges in which we expect to see messages from the
3234        // DHCP client. They are ranges in order to account for randomized
3235        // delays.
3236        const EXPECTED_RANGES: [(u64, u64); NUM_REQUEST_RETRANSMITS + 1] =
3237            [(0, 0), (3, 5), (7, 9), (15, 17), (31, 33)];
3238
3239        let receive_fut = pin!(
3240            async {
3241                let mut previous_time = std::time::Duration::from_secs(0);
3242
3243                for (start, end) in EXPECTED_RANGES {
3244                    let mut recv_buf = [0u8; BUFFER_SIZE];
3245                    let DatagramInfo { length, address } = server_end
3246                        .recv_from(&mut recv_buf)
3247                        .await
3248                        .expect("recv_from on test socket should succeed");
3249
3250                    assert_eq!(address, Mac::BROADCAST);
3251
3252                    let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
3253                        &recv_buf[..length],
3254                        dhcp_protocol::SERVER_PORT,
3255                    )
3256                    .expect("received packet should parse as DHCP message");
3257
3258                    assert_outgoing_message_when_not_assigned_address(
3259                        &msg,
3260                        VaryingOutgoingMessageFields {
3261                            xid: msg.xid,
3262                            options: vec![
3263                                dhcp_protocol::DhcpOption::DhcpMessageType(
3264                                    dhcp_protocol::MessageType::DHCPREQUEST,
3265                                ),
3266                                dhcp_protocol::DhcpOption::ParameterRequestList(
3267                                    test_requested_parameters()
3268                                        .iter_keys()
3269                                        .collect::<Vec<_>>()
3270                                        .try_into()
3271                                        .expect(
3272                                            "should fit parameter request list size constraints",
3273                                        ),
3274                                ),
3275                                dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3276                                dhcp_protocol::DhcpOption::RequestedIpAddress(YIADDR),
3277                                dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3278                                    DEFAULT_LEASE_LENGTH_SECONDS,
3279                                ),
3280                            ],
3281                        },
3282                    );
3283
3284                    let TestInstant(received_time) = time.now();
3285
3286                    let duration_range =
3287                        std::time::Duration::from_secs(start)..=std::time::Duration::from_secs(end);
3288                    assert!(duration_range.contains(&(received_time - previous_time)));
3289
3290                    previous_time = received_time;
3291                }
3292            }
3293            .fuse()
3294        );
3295
3296        let main_future = async { join!(requesting_fut, receive_fut) };
3297        let mut main_future = pin!(main_future);
3298
3299        let (requesting_result, ()) =
3300            run_with_accelerated_time(&mut executor, time, &mut main_future);
3301
3302        assert_matches!(requesting_result, Ok(RequestingOutcome::RanOutOfRetransmits));
3303        assert_eq!(counters.requesting.messaging.send_message.load(), EXPECTED_RANGES.len());
3304        assert_eq!(counters.requesting.messaging.recv_time_out.load(), EXPECTED_RANGES.len());
3305    }
3306
3307    struct VaryingIncomingMessageFields {
3308        yiaddr: Ipv4Addr,
3309        options: Vec<dhcp_protocol::DhcpOption>,
3310    }
3311
3312    fn build_incoming_message(
3313        xid: u32,
3314        fields: VaryingIncomingMessageFields,
3315    ) -> dhcp_protocol::Message {
3316        let VaryingIncomingMessageFields { yiaddr, options } = fields;
3317
3318        dhcp_protocol::Message {
3319            op: dhcp_protocol::OpCode::BOOTREPLY,
3320            xid,
3321            secs: 0,
3322            bdcast_flag: false,
3323            ciaddr: Ipv4Addr::UNSPECIFIED,
3324            yiaddr,
3325            siaddr: Ipv4Addr::UNSPECIFIED,
3326            giaddr: Ipv4Addr::UNSPECIFIED,
3327            chaddr: TEST_MAC_ADDRESS,
3328            sname: BString::default(),
3329            file: BString::default(),
3330            options,
3331        }
3332    }
3333
3334    const NAK_MESSAGE: &str = "something went wrong";
3335
3336    #[derive(PartialEq, Debug)]
3337    struct RequestingTestResult {
3338        outcome: RequestingOutcome<TestInstant>,
3339        counters: RequestingTestCounters,
3340    }
3341
3342    #[derive(PartialEq, Eq, Debug, Default)]
3343    struct RequestingTestCounters {
3344        send_message: usize,
3345        recv_time_out: usize,
3346        recv_failed_dhcp_parse: usize,
3347        recv_wrong_xid: usize,
3348        recv_wrong_chaddr: usize,
3349        recv_message: usize,
3350        recv_nak: usize,
3351        recv_missing_option: usize,
3352    }
3353
3354    #[test_case(VaryingIncomingMessageFields {
3355        yiaddr: YIADDR,
3356        options: [
3357            dhcp_protocol::DhcpOption::DhcpMessageType(
3358                dhcp_protocol::MessageType::DHCPACK,
3359            ),
3360            dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3361            dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3362                DEFAULT_LEASE_LENGTH_SECONDS,
3363            ),
3364        ]
3365        .into_iter()
3366        .chain(test_parameter_values())
3367        .collect(),
3368    } => RequestingTestResult {
3369        outcome: RequestingOutcome::Bound(LeaseState {
3370            discover_options: TEST_DISCOVER_OPTIONS,
3371            yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
3372                .try_into()
3373                .expect("should be specified"),
3374            server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3375                .try_into()
3376                .expect("should be specified"),
3377            ip_address_lease_time: std::time::Duration::from_secs(
3378                DEFAULT_LEASE_LENGTH_SECONDS.into()
3379            ),
3380            renewal_time: None,
3381            rebinding_time: None,
3382            start_time: TestInstant(std::time::Duration::from_secs(0)),
3383        }, test_parameter_values().into_iter().collect()),
3384        counters: RequestingTestCounters {
3385            send_message: 1,
3386            recv_message: 1,
3387            ..Default::default()
3388        }
3389     } ; "transitions to Bound after receiving DHCPACK")]
3390    #[test_case(VaryingIncomingMessageFields {
3391        yiaddr: YIADDR,
3392        options: [
3393            dhcp_protocol::DhcpOption::DhcpMessageType(
3394                dhcp_protocol::MessageType::DHCPACK,
3395            ),
3396            dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3397        ]
3398        .into_iter()
3399        .chain(test_parameter_values())
3400        .collect(),
3401    } => RequestingTestResult {
3402        outcome: RequestingOutcome::Bound(LeaseState {
3403            discover_options: TEST_DISCOVER_OPTIONS,
3404            yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
3405                .try_into()
3406                .expect("should be specified"),
3407            server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3408                .try_into()
3409                .expect("should be specified"),
3410            ip_address_lease_time: std::time::Duration::from_secs(
3411                DEFAULT_LEASE_LENGTH_SECONDS.into()
3412            ),
3413            renewal_time: None,
3414            rebinding_time: None,
3415            start_time: TestInstant(std::time::Duration::from_secs(0)),
3416        }, test_parameter_values().into_iter().collect()),
3417        counters: RequestingTestCounters {
3418            send_message: 1,
3419            recv_message: 1,
3420            ..Default::default()
3421        }
3422     } ; "transitions to Bound after receiving DHCPACK without lease time")]
3423    #[test_case(VaryingIncomingMessageFields {
3424        yiaddr: YIADDR,
3425        options: [
3426            dhcp_protocol::DhcpOption::DhcpMessageType(
3427                dhcp_protocol::MessageType::DHCPACK,
3428            ),
3429            dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3430            dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3431                DEFAULT_LEASE_LENGTH_SECONDS,
3432            ),
3433        ]
3434        .into_iter()
3435        .chain(test_parameter_values_excluding_subnet_mask())
3436        .collect(),
3437    } => RequestingTestResult {
3438        outcome: RequestingOutcome::RanOutOfRetransmits,
3439        counters: RequestingTestCounters {
3440            send_message: 5,
3441            recv_time_out: 5,
3442            recv_message: 1,
3443            recv_missing_option: 1,
3444            ..Default::default()
3445        },
3446    }; "ignores replies lacking required option SubnetMask")]
3447    #[test_case(VaryingIncomingMessageFields {
3448        yiaddr: Ipv4Addr::UNSPECIFIED,
3449        options: [
3450            dhcp_protocol::DhcpOption::DhcpMessageType(
3451                dhcp_protocol::MessageType::DHCPNAK,
3452            ),
3453            dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3454            dhcp_protocol::DhcpOption::Message(NAK_MESSAGE.to_owned()),
3455        ]
3456        .into_iter()
3457        .chain(test_parameter_values())
3458        .collect(),
3459    } => RequestingTestResult {
3460        outcome: RequestingOutcome::Nak(crate::parse::FieldsToRetainFromNak {
3461            server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3462                .try_into()
3463                .expect("should be specified"),
3464            message: Some(NAK_MESSAGE.to_owned()),
3465            client_identifier: None,
3466        }),
3467        counters: RequestingTestCounters {
3468            send_message: 1,
3469            recv_message: 1,
3470            recv_nak: 1,
3471            ..Default::default()
3472        },
3473    }; "transitions to Init after receiving DHCPNAK")]
3474    fn do_requesting_transitions_on_reply(
3475        incoming_message: VaryingIncomingMessageFields,
3476    ) -> RequestingTestResult {
3477        initialize_logging();
3478        let counters = Counters::default();
3479
3480        let time = &FakeTimeController::new();
3481
3482        let requesting = build_test_requesting_state();
3483        let mut rng = FakeRngProvider::new(0);
3484
3485        let (server_end, client_end) = FakeSocket::new_pair();
3486        let test_socket_provider = FakeSocketProvider::new(client_end);
3487
3488        let client_config = test_client_config();
3489
3490        let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3491
3492        let requesting_fut = pin!(
3493            requesting
3494                .do_requesting(
3495                    &client_config,
3496                    &test_socket_provider,
3497                    &mut rng,
3498                    time,
3499                    &mut stop_receiver,
3500                    &counters,
3501                )
3502                .fuse()
3503        );
3504
3505        let server_fut = pin!(
3506            async {
3507                let mut recv_buf = [0u8; BUFFER_SIZE];
3508
3509                let DatagramInfo { length, address } = server_end
3510                    .recv_from(&mut recv_buf)
3511                    .await
3512                    .expect("recv_from on test socket should succeed");
3513                assert_eq!(address, Mac::BROADCAST);
3514
3515                let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
3516                    &recv_buf[..length],
3517                    dhcp_protocol::SERVER_PORT,
3518                )
3519                .expect("received packet on test socket should parse as DHCP message");
3520
3521                assert_outgoing_message_when_not_assigned_address(
3522                    &msg,
3523                    VaryingOutgoingMessageFields {
3524                        xid: msg.xid,
3525                        options: vec![
3526                            dhcp_protocol::DhcpOption::DhcpMessageType(
3527                                dhcp_protocol::MessageType::DHCPREQUEST,
3528                            ),
3529                            dhcp_protocol::DhcpOption::ParameterRequestList(
3530                                test_requested_parameters()
3531                                    .iter_keys()
3532                                    .collect::<Vec<_>>()
3533                                    .try_into()
3534                                    .expect("should fit parameter request list size constraints"),
3535                            ),
3536                            dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3537                            dhcp_protocol::DhcpOption::RequestedIpAddress(YIADDR),
3538                            dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3539                                DEFAULT_LEASE_LENGTH_SECONDS,
3540                            ),
3541                        ],
3542                    },
3543                );
3544
3545                let reply = build_incoming_message(msg.xid, incoming_message);
3546
3547                server_end
3548                    .send_to(
3549                        crate::parse::serialize_dhcp_message_to_ip_packet(
3550                            reply,
3551                            SERVER_IP,
3552                            SERVER_PORT,
3553                            YIADDR,
3554                            CLIENT_PORT,
3555                        )
3556                        .as_ref(),
3557                        // Note that this is the address the client under test
3558                        // observes in `recv_from`.
3559                        TEST_SERVER_MAC_ADDRESS,
3560                    )
3561                    .await
3562                    .expect("send_to on test socket should succeed");
3563            }
3564            .fuse()
3565        );
3566
3567        let main_future = async move {
3568            let (requesting_result, ()) = join!(requesting_fut, server_fut);
3569            requesting_result
3570        }
3571        .fuse();
3572
3573        let mut main_future = pin!(main_future);
3574
3575        let mut executor = fasync::TestExecutor::new();
3576        let requesting_result = run_with_accelerated_time(&mut executor, time, &mut main_future);
3577
3578        let outcome = assert_matches!(requesting_result, Ok(outcome) => outcome);
3579        let counters = RequestingTestCounters {
3580            send_message: counters.requesting.messaging.send_message.load(),
3581            recv_time_out: counters.requesting.messaging.recv_time_out.load(),
3582            recv_failed_dhcp_parse: counters.requesting.messaging.recv_failed_dhcp_parse.load(),
3583            recv_wrong_xid: counters.requesting.messaging.recv_wrong_xid.load(),
3584            recv_wrong_chaddr: counters.requesting.messaging.recv_wrong_chaddr.load(),
3585            recv_message: counters.requesting.messaging.recv_message.load(),
3586            recv_nak: counters.requesting.recv_nak.load(),
3587            recv_missing_option: counters.requesting.recv_error.missing_required_option.load(),
3588        };
3589        RequestingTestResult { outcome, counters }
3590    }
3591
3592    fn build_test_lease_state() -> LeaseState<TestInstant> {
3593        build_test_lease_state_with_times(
3594            Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3595            None,
3596            None,
3597        )
3598    }
3599
3600    fn build_test_lease_state_with_times(
3601        lease_length: Duration,
3602        renewal_time: Option<Duration>,
3603        rebinding_time: Option<Duration>,
3604    ) -> LeaseState<TestInstant> {
3605        LeaseState {
3606            discover_options: TEST_DISCOVER_OPTIONS,
3607            yiaddr: net_types::ip::Ipv4Addr::from(YIADDR).try_into().expect("should be specified"),
3608            server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3609                .try_into()
3610                .expect("should be specified"),
3611            ip_address_lease_time: lease_length,
3612            start_time: TestInstant(std::time::Duration::from_secs(0)),
3613            renewal_time,
3614            rebinding_time,
3615        }
3616    }
3617
3618    fn build_test_newly_acquired_lease() -> NewlyAcquiredLease<TestInstant> {
3619        NewlyAcquiredLease {
3620            ip_address: net_types::ip::Ipv4Addr::from(YIADDR)
3621                .try_into()
3622                .expect("should be specified"),
3623            start_time: TestInstant(std::time::Duration::from_secs(0)),
3624            lease_time: Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3625            parameters: Vec::new(),
3626        }
3627    }
3628
3629    #[test_case(
3630        (
3631            State::Init(Init::default()),
3632            Transition::BoundWithNewLease(
3633                Bound::AwaitingAssignment{lease_state: build_test_lease_state()},
3634                build_test_newly_acquired_lease()
3635            )
3636        ) => matches Some(TransitionEffect::HandleNewLease(_));
3637        "yields newly-acquired lease effect"
3638    )]
3639    #[test_case(
3640        (
3641            State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3642            Transition::Init(Init),
3643        ) => matches Some(TransitionEffect::DropLease {address_rejected: false});
3644        "recognizes loss of lease"
3645    )]
3646    #[test_case(
3647        (
3648            State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3649            Transition::WaitingToRestart(WaitingToRestart {
3650                waiting_until: TestInstant(WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION)
3651            }),
3652        ) => matches Some(TransitionEffect::DropLease {address_rejected: true});
3653        "recognizes address rejection"
3654    )]
3655
3656    fn apply_transition(
3657        (state, transition): (State<TestInstant>, Transition<TestInstant>),
3658    ) -> Option<TransitionEffect<TestInstant>> {
3659        let (_next_state, effect) = state.apply(&test_client_config(), transition);
3660        effect
3661    }
3662
3663    enum AddrRemovedExpect {
3664        Decline,
3665        Exit,
3666        Ignore,
3667    }
3668
3669    #[test_case(
3670        State::Selecting(build_test_selecting_state()),
3671        AddrRemovedExpect::Ignore;
3672        "selecting_ignore"
3673    )]
3674    #[test_case(
3675        State::Requesting(build_test_requesting_state()),
3676        AddrRemovedExpect::Ignore;
3677        "requesting_ignore"
3678    )]
3679    #[test_case(
3680        State::Bound(Bound::AwaitingAssignment{lease_state: build_test_lease_state()}),
3681        AddrRemovedExpect::Exit;
3682        "bound_awaiting_assignment_exit"
3683    )]
3684    #[test_case(
3685        State::Bound(Bound::AwaitingAssignment{lease_state: build_test_lease_state()}),
3686        AddrRemovedExpect::Decline;
3687        "bound_awaiting_assignment_decline"
3688    )]
3689    #[test_case(
3690        State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3691        AddrRemovedExpect::Exit;
3692        "bound_assigned_exit"
3693    )]
3694    #[test_case(
3695        State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3696        AddrRemovedExpect::Decline;
3697        "bound_assigned_decline"
3698    )]
3699    #[test_case(
3700        State::Renewing(Renewing{lease_state: build_test_lease_state()}),
3701        AddrRemovedExpect::Exit;
3702        "renewing_exit"
3703    )]
3704    #[test_case(
3705        State::Renewing(Renewing{lease_state: build_test_lease_state()}),
3706        AddrRemovedExpect::Decline;
3707        "renewing_decline"
3708    )]
3709    #[test_case(
3710        State::Rebinding(Rebinding{lease_state: build_test_lease_state()}),
3711        AddrRemovedExpect::Exit;
3712        "rebinding_exit"
3713    )]
3714    #[test_case(
3715        State::Rebinding(Rebinding{lease_state: build_test_lease_state()}),
3716        AddrRemovedExpect::Decline;
3717        "rebinding_decline"
3718    )]
3719    #[test_case(
3720        State::WaitingToRestart(WaitingToRestart {
3721            waiting_until: TestInstant(WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION)
3722        }),
3723        AddrRemovedExpect::Ignore;
3724        "waiting_to_restart_ignore"
3725    )]
3726    fn on_address_removed(state: State<TestInstant>, expect: AddrRemovedExpect) {
3727        let config = &test_client_config();
3728        let time = &FakeTimeController::new();
3729        let mut rng = FakeRngProvider::new(0);
3730        let (server_end, packet_client_end) = FakeSocket::new_pair();
3731        let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
3732        let (_server_end, udp_client_end) = FakeSocket::new_pair();
3733        let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
3734        let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3735        let counters = Counters::default();
3736
3737        let address_event_receiver = futures::stream::once(async {
3738            match expect {
3739                AddrRemovedExpect::Decline => AddressEvent::Rejected,
3740                AddrRemovedExpect::Exit => AddressEvent::Removed(FakeRemovedReason),
3741                AddrRemovedExpect::Ignore => panic!("address_event_receiver should not be polled"),
3742            }
3743        })
3744        .chain(futures::stream::pending());
3745
3746        let run_result = state
3747            .run(
3748                config,
3749                packet_socket_provider,
3750                udp_socket_provider,
3751                &mut rng,
3752                time,
3753                &mut stop_receiver,
3754                address_event_receiver,
3755                &counters,
3756            )
3757            .now_or_never();
3758
3759        match expect {
3760            AddrRemovedExpect::Decline => {
3761                let run_result = run_result.expect("fut should finish when declining");
3762                let WaitingToRestart { waiting_until } = assert_matches!(
3763                    run_result,
3764                    Ok(Step::NextState(Transition::WaitingToRestart(waiting))) => waiting
3765                );
3766                assert_eq!(waiting_until, TestInstant(Duration::from_secs(10)));
3767
3768                let mut buf = [0u8; BUFFER_SIZE];
3769                let DatagramInfo { length, address } = server_end
3770                    .recv_from(&mut buf)
3771                    .now_or_never()
3772                    .expect("should be ready")
3773                    .expect("should succeed");
3774                assert_eq!(address, Mac::BROADCAST);
3775
3776                let (_src_addr, message) =
3777                    crate::parse::parse_dhcp_message_from_ip_packet(&buf[..length], SERVER_PORT)
3778                        .expect("should succeed");
3779
3780                use dhcp_protocol::DhcpOption;
3781                assert_outgoing_message_when_not_assigned_address(
3782                    &message,
3783                    VaryingOutgoingMessageFields {
3784                        xid: message.xid,
3785                        options: vec![
3786                            DhcpOption::DhcpMessageType(dhcp_protocol::MessageType::DHCPDECLINE),
3787                            DhcpOption::ServerIdentifier(SERVER_IP),
3788                            DhcpOption::RequestedIpAddress(YIADDR),
3789                        ],
3790                    },
3791                );
3792            }
3793            AddrRemovedExpect::Exit => {
3794                let run_result = run_result.expect("fut should finish when exiting");
3795                assert_matches!(
3796                    run_result,
3797                    Ok(Step::Exit(ExitReason::AddressRemoved(FakeRemovedReason)))
3798                );
3799            }
3800            AddrRemovedExpect::Ignore => {
3801                assert_matches!(run_result, None, "fut should not finish when ignored.");
3802            }
3803        }
3804    }
3805
3806    enum AddrAssignmentChangeExpect {
3807        Ignore,
3808        EnterAssigned,
3809    }
3810
3811    #[test_case(
3812        State::Selecting(build_test_selecting_state()),
3813        AddressAssignmentState::Assigned,
3814        AddrAssignmentChangeExpect::Ignore;
3815        "selecting_ignores"
3816    )]
3817    #[test_case(
3818        State::Requesting(build_test_requesting_state()),
3819        AddressAssignmentState::Assigned,
3820        AddrAssignmentChangeExpect::Ignore;
3821        "requesting_ignores"
3822    )]
3823    #[test_case(
3824        State::Bound(Bound::AwaitingAssignment{lease_state: build_test_lease_state()}),
3825        AddressAssignmentState::Tentative,
3826        AddrAssignmentChangeExpect::Ignore;
3827        "bound_awaiting_assignment_ignores_tentative"
3828    )]
3829    #[test_case(
3830        State::Bound(Bound::AwaitingAssignment{lease_state: build_test_lease_state()}),
3831        AddressAssignmentState::Assigned,
3832        AddrAssignmentChangeExpect::EnterAssigned;
3833        "bound_awaiting_assignment_enter_assigned"
3834    )]
3835    #[test_case(
3836        State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3837        AddressAssignmentState::Tentative,
3838        AddrAssignmentChangeExpect::Ignore;
3839        "bound_assigned_ignores_tentative"
3840    )]
3841    #[test_case(
3842        State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3843        AddressAssignmentState::Assigned,
3844        AddrAssignmentChangeExpect::Ignore;
3845        "bound_assigned_ignores_assigned"
3846    )]
3847    #[test_case(
3848        State::Renewing(Renewing{lease_state: build_test_lease_state()}),
3849        AddressAssignmentState::Tentative,
3850        AddrAssignmentChangeExpect::Ignore;
3851        "renewing_ignores_tentative"
3852    )]
3853    #[test_case(
3854        State::Renewing(Renewing{lease_state: build_test_lease_state()}),
3855        AddressAssignmentState::Assigned,
3856        AddrAssignmentChangeExpect::Ignore;
3857        "renewing_ignores_assigned"
3858    )]
3859    #[test_case(
3860        State::Rebinding(Rebinding{lease_state: build_test_lease_state()}),
3861        AddressAssignmentState::Tentative,
3862        AddrAssignmentChangeExpect::Ignore;
3863        "rebinding_ignores_tentative"
3864    )]
3865    #[test_case(
3866        State::Rebinding(Rebinding{lease_state: build_test_lease_state()}),
3867        AddressAssignmentState::Assigned,
3868        AddrAssignmentChangeExpect::Ignore;
3869        "rebinding_ignores_assigned"
3870    )]
3871    #[test_case(
3872        State::WaitingToRestart(WaitingToRestart {
3873            waiting_until: TestInstant(WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION)
3874        }),
3875        AddressAssignmentState::Assigned,
3876        AddrAssignmentChangeExpect::Ignore;
3877        "waiting_to_restart_ignores"
3878    )]
3879    fn on_address_assignment_change(
3880        state: State<TestInstant>,
3881        change: AddressAssignmentState,
3882        expect: AddrAssignmentChangeExpect,
3883    ) {
3884        let config = &test_client_config();
3885        let time = &FakeTimeController::new();
3886        let mut rng = FakeRngProvider::new(0);
3887        let (_server_end, packet_client_end) = FakeSocket::new_pair();
3888        let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
3889        let (_server_end, udp_client_end) = FakeSocket::new_pair();
3890        let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
3891        let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3892        let counters = Counters::default();
3893
3894        let address_event_receiver = futures::stream::once(futures::future::ready(
3895            AddressEvent::<FakeRemovedReason>::AssignmentStateChanged(change),
3896        ))
3897        .chain(futures::stream::pending());
3898        let run_result = state
3899            .run(
3900                config,
3901                packet_socket_provider,
3902                udp_socket_provider,
3903                &mut rng,
3904                time,
3905                &mut stop_receiver,
3906                address_event_receiver,
3907                &counters,
3908            )
3909            .now_or_never();
3910
3911        match expect {
3912            AddrAssignmentChangeExpect::EnterAssigned => {
3913                let run_result = run_result.expect("fut should finish when exiting");
3914                assert_matches!(
3915                    run_result,
3916                    Ok(Step::NextState(Transition::BoundAssigned(Bound::Assigned { .. })))
3917                );
3918            }
3919            AddrAssignmentChangeExpect::Ignore => {
3920                assert_matches!(run_result, None, "fut should not finish when ignored.");
3921            }
3922        }
3923    }
3924
3925    #[test]
3926    fn waiting_to_restart() {
3927        let time = &FakeTimeController::new();
3928
3929        const WAITING_UNTIL: TestInstant = TestInstant(Duration::from_secs(10));
3930
3931        // Set the start time to some arbitrary time below WAITING_UNTIL to show
3932        // that `WaitingToRestart` waits until an absolute time rather than for
3933        // a particular duration.
3934        advance(time, Duration::from_secs(3));
3935
3936        let waiting = WaitingToRestart { waiting_until: WAITING_UNTIL };
3937        let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3938        let main_fut = waiting.do_waiting_to_restart(time, &mut stop_receiver).fuse();
3939        let mut main_fut = pin!(main_fut);
3940        let mut executor = fasync::TestExecutor::new();
3941        let outcome = run_with_accelerated_time(&mut executor, time, &mut main_fut);
3942        assert_eq!(outcome, WaitingToRestartOutcome::Init(Init));
3943
3944        assert_eq!(time.now(), WAITING_UNTIL);
3945    }
3946
3947    #[test]
3948    fn bound_awaiting_assignment_times_out() {
3949        let time = &FakeTimeController::new();
3950        let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3951        let (_server_end, packet_client_end) = FakeSocket::new_pair();
3952        let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
3953        let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
3954        let config = &test_client_config();
3955        let counters = Counters::default();
3956        let bound = Bound::AwaitingAssignment { lease_state: build_test_lease_state() };
3957        let main_fut = bound
3958            .do_bound(
3959                config,
3960                time,
3961                &mut stop_receiver,
3962                packet_socket_provider,
3963                address_event_receiver,
3964                &counters,
3965            )
3966            .fuse();
3967        let mut main_fut = pin!(main_fut);
3968        let mut executor = fasync::TestExecutor::new();
3969        let outcome = run_with_accelerated_time(&mut executor, time, &mut main_fut)
3970            .expect("do_bound should succeed");
3971        assert_eq!(outcome, BoundOutcome::Restart(Init::default()));
3972        assert_eq!(
3973            time.now(),
3974            TestInstant(Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()))
3975        );
3976    }
3977
3978    #[test_case(
3979        build_test_lease_state() =>
3980        TestInstant(Duration::from_secs(u64::from(DEFAULT_LEASE_LENGTH_SECONDS) / 2));
3981        "waits default renewal time when not specified")]
3982    #[test_case(
3983        LeaseState {
3984            renewal_time: Some(Duration::from_secs(10)),
3985            ..build_test_lease_state()
3986        } => TestInstant(Duration::from_secs(10));
3987        "waits specified renewal time")]
3988    fn bound_assigned_waits_for_renewal_time(lease_state: LeaseState<TestInstant>) -> TestInstant {
3989        let time = &FakeTimeController::new();
3990        let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3991        let (_server_end, packet_client_end) = FakeSocket::new_pair();
3992        let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
3993        let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
3994        let config = &test_client_config();
3995        let counters = Counters::default();
3996        let bound = Bound::Assigned { lease_state };
3997        let main_fut = bound
3998            .do_bound(
3999                config,
4000                time,
4001                &mut stop_receiver,
4002                packet_socket_provider,
4003                address_event_receiver,
4004                &counters,
4005            )
4006            .fuse();
4007        let mut main_fut = pin!(main_fut);
4008        let mut executor = fasync::TestExecutor::new();
4009        let outcome = run_with_accelerated_time(&mut executor, time, &mut main_fut)
4010            .expect("do_bound should succeed");
4011        assert_eq!(outcome, BoundOutcome::Renewing(Renewing { lease_state: lease_state.clone() }));
4012        time.now()
4013    }
4014
4015    #[test_case(Bound::Assigned {lease_state: build_test_lease_state()}; "assigned")]
4016    #[test_case(Bound::AwaitingAssignment {lease_state: build_test_lease_state()};
4017        "awaiting_assignment")]
4018    fn bound_obeys_graceful_shutdown(bound: Bound<TestInstant>) {
4019        let time = &FakeTimeController::new();
4020        let (stop_sender, mut stop_receiver) = mpsc::unbounded();
4021        let (_server_end, packet_client_end) = FakeSocket::new_pair();
4022        let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4023        let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4024        let config = &test_client_config();
4025        let counters = Counters::default();
4026        let bound_fut = bound
4027            .do_bound(
4028                &config,
4029                time,
4030                &mut stop_receiver,
4031                packet_socket_provider,
4032                address_event_receiver,
4033                &counters,
4034            )
4035            .fuse();
4036
4037        stop_sender.unbounded_send(()).expect("send should succeed");
4038        assert_eq!(
4039            bound_fut
4040                .now_or_never()
4041                .expect("should have completed")
4042                .expect("do_bound should succeed"),
4043            BoundOutcome::GracefulShutdown
4044        );
4045    }
4046
4047    fn build_test_renewing_state(
4048        lease_length: Duration,
4049        renewal_time: Option<Duration>,
4050        rebinding_time: Option<Duration>,
4051    ) -> Renewing<TestInstant> {
4052        Renewing {
4053            lease_state: build_test_lease_state_with_times(
4054                lease_length,
4055                renewal_time,
4056                rebinding_time,
4057            ),
4058        }
4059    }
4060
4061    #[test]
4062    fn do_renewing_obeys_graceful_shutdown() {
4063        initialize_logging();
4064        let counters = Counters::default();
4065
4066        let renewing = build_test_renewing_state(
4067            Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4068            None,
4069            None,
4070        );
4071        let client_config = &test_client_config();
4072
4073        let (_server_end, packet_client_end) = FakeSocket::new_pair();
4074        let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4075        let (_server_end, udp_client_end) = FakeSocket::new_pair();
4076        let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
4077        let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4078        let (stop_sender, mut stop_receiver) = mpsc::unbounded();
4079        let time = &FakeTimeController::new();
4080
4081        let renewing_fut = renewing
4082            .do_renewing(
4083                client_config,
4084                udp_socket_provider,
4085                packet_socket_provider,
4086                time,
4087                &mut stop_receiver,
4088                address_event_receiver,
4089                &counters,
4090            )
4091            .fuse();
4092        let mut renewing_fut = pin!(renewing_fut);
4093
4094        let mut executor = fasync::TestExecutor::new();
4095        assert_matches!(executor.run_until_stalled(&mut renewing_fut), std::task::Poll::Pending);
4096
4097        stop_sender.unbounded_send(()).expect("sending stop signal should succeed");
4098
4099        let renewing_result = renewing_fut.now_or_never().expect(
4100            "renewing_fut should complete after single poll after stop signal has been sent",
4101        );
4102
4103        assert_matches!(renewing_result, Ok(RenewingOutcome::GracefulShutdown));
4104    }
4105
4106    #[track_caller]
4107    fn assert_outgoing_message_when_assigned_address(
4108        got_message: &dhcp_protocol::Message,
4109        fields: VaryingOutgoingMessageFields,
4110    ) {
4111        let VaryingOutgoingMessageFields { xid, options } = fields;
4112        let want_message = dhcp_protocol::Message {
4113            op: dhcp_protocol::OpCode::BOOTREQUEST,
4114            xid,
4115            secs: 0,
4116            bdcast_flag: false,
4117            ciaddr: YIADDR,
4118            yiaddr: Ipv4Addr::UNSPECIFIED,
4119            siaddr: Ipv4Addr::UNSPECIFIED,
4120            giaddr: Ipv4Addr::UNSPECIFIED,
4121            chaddr: TEST_MAC_ADDRESS,
4122            sname: BString::default(),
4123            file: BString::default(),
4124            options,
4125        };
4126        assert_eq!(got_message, &want_message);
4127    }
4128
4129    #[test]
4130    fn do_renewing_sends_requests() {
4131        initialize_logging();
4132
4133        // Just needs to be larger than rebinding time.
4134        const LEASE_LENGTH: Duration = Duration::from_secs(100000);
4135
4136        // Set to have timestamps be conveniently derivable from powers of 2.
4137        const RENEWAL_TIME: Duration = Duration::from_secs(0);
4138        const REBINDING_TIME: Duration = Duration::from_secs(1024);
4139
4140        let renewing =
4141            build_test_renewing_state(LEASE_LENGTH, Some(RENEWAL_TIME), Some(REBINDING_TIME));
4142        let client_config = &test_client_config();
4143
4144        let (_server_end, packet_client_end) = FakeSocket::new_pair();
4145        let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4146        let (udp_server_end, udp_client_end) = FakeSocket::new_pair();
4147        let (binds_sender, mut binds_receiver) = mpsc::unbounded();
4148        let udp_socket_provider =
4149            &FakeSocketProvider::new_with_events(udp_client_end, binds_sender);
4150        let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4151        let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4152        let time = &FakeTimeController::new();
4153        let counters = Counters::default();
4154        let renewing_fut = pin!(
4155            renewing
4156                .do_renewing(
4157                    client_config,
4158                    udp_socket_provider,
4159                    packet_socket_provider,
4160                    time,
4161                    &mut stop_receiver,
4162                    address_event_receiver,
4163                    &counters
4164                )
4165                .fuse()
4166        );
4167
4168        // Observe the "64, 4" instead of "64, 32" due to the 60 second minimum
4169        // retransmission delay.
4170        let expected_times_requests_are_sent =
4171            [1024, 512, 256, 128, 64, 4].map(|time_remaining_when_request_is_sent| {
4172                Duration::from_secs(1024 - time_remaining_when_request_is_sent)
4173            });
4174
4175        let receive_fut =
4176            pin!(
4177                async {
4178                    for expected_time in expected_times_requests_are_sent {
4179                        let mut recv_buf = [0u8; BUFFER_SIZE];
4180                        let DatagramInfo { length, address } = udp_server_end
4181                            .recv_from(&mut recv_buf)
4182                            .await
4183                            .expect("recv_from on test socket should succeed");
4184
4185                        assert_eq!(
4186                            address,
4187                            std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
4188                                SERVER_IP,
4189                                SERVER_PORT.get()
4190                            ))
4191                        );
4192                        assert_eq!(time.now(), TestInstant(expected_time));
4193                        let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
4194                            .expect("received packet should parse as DHCP message");
4195
4196                        assert_outgoing_message_when_assigned_address(
4197                            &msg,
4198                            VaryingOutgoingMessageFields {
4199                                xid: msg.xid,
4200                                options: vec![
4201                            dhcp_protocol::DhcpOption::DhcpMessageType(
4202                                dhcp_protocol::MessageType::DHCPREQUEST,
4203                            ),
4204                            dhcp_protocol::DhcpOption::ParameterRequestList(
4205                                test_requested_parameters()
4206                                    .iter_keys()
4207                                    .collect::<Vec<_>>()
4208                                    .try_into()
4209                                    .expect("should fit parameter request list size constraints"),
4210                            ),
4211                        ],
4212                            },
4213                        );
4214                    }
4215                }
4216                .fuse()
4217            );
4218
4219        let main_future = async { join!(renewing_fut, receive_fut) };
4220        let mut main_future = pin!(main_future);
4221
4222        let mut executor = fasync::TestExecutor::new();
4223        let (requesting_result, ()) =
4224            run_with_accelerated_time(&mut executor, time, &mut main_future);
4225
4226        assert_matches!(requesting_result, Ok(RenewingOutcome::Rebinding(_)));
4227        assert_matches!(udp_server_end.recv_from(&mut []).now_or_never(), None);
4228
4229        let bound_socket_addr = binds_receiver
4230            .next()
4231            .now_or_never()
4232            .expect("should have completed")
4233            .expect("should be present");
4234        assert_eq!(
4235            bound_socket_addr,
4236            std::net::SocketAddr::V4(std::net::SocketAddrV4::new(YIADDR, CLIENT_PORT.into()))
4237        );
4238        assert_eq!(
4239            counters.renewing.messaging.send_message.load(),
4240            expected_times_requests_are_sent.len()
4241        );
4242        assert_eq!(
4243            counters.renewing.messaging.recv_time_out.load(),
4244            expected_times_requests_are_sent.len()
4245        );
4246    }
4247
4248    #[derive(PartialEq, Debug)]
4249    struct RenewingTestResult {
4250        outcome: RenewingOutcome<TestInstant, FakeRemovedReason>,
4251        counters: RenewingTestCounters,
4252    }
4253
4254    #[derive(PartialEq, Eq, Debug, Default)]
4255    struct RenewingTestCounters {
4256        send_message: usize,
4257        recv_time_out: usize,
4258        recv_failed_dhcp_parse: usize,
4259        recv_wrong_xid: usize,
4260        recv_wrong_chaddr: usize,
4261        recv_message: usize,
4262        recv_nak: usize,
4263        recv_missing_option: usize,
4264    }
4265
4266    #[test_case(VaryingIncomingMessageFields {
4267        yiaddr: YIADDR,
4268        options: [
4269            dhcp_protocol::DhcpOption::DhcpMessageType(
4270                dhcp_protocol::MessageType::DHCPACK,
4271            ),
4272            dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4273            dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4274                DEFAULT_LEASE_LENGTH_SECONDS,
4275            ),
4276        ]
4277        .into_iter()
4278        .chain(test_parameter_values())
4279        .collect(),
4280    } => RenewingTestResult {
4281        outcome: RenewingOutcome::Renewed(LeaseState {
4282            discover_options: TEST_DISCOVER_OPTIONS,
4283            yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
4284                .try_into()
4285                .expect("should be specified"),
4286            server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
4287                .try_into()
4288                .expect("should be specified"),
4289            ip_address_lease_time: std::time::Duration::from_secs(
4290                DEFAULT_LEASE_LENGTH_SECONDS.into()
4291            ),
4292            renewal_time: None,
4293            rebinding_time: None,
4294            start_time: TestInstant(std::time::Duration::from_secs(0)),
4295        }, test_parameter_values().into_iter().collect()),
4296        counters: RenewingTestCounters {
4297            send_message: 1,
4298            recv_message: 1,
4299            ..Default::default()
4300        }
4301    }; "successfully renews after receiving DHCPACK")]
4302    #[test_case(VaryingIncomingMessageFields {
4303        yiaddr: YIADDR,
4304        options: [
4305            dhcp_protocol::DhcpOption::DhcpMessageType(
4306                dhcp_protocol::MessageType::DHCPACK,
4307            ),
4308            dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4309        ]
4310        .into_iter()
4311        .chain(test_parameter_values())
4312        .collect(),
4313    } => RenewingTestResult {
4314        outcome: RenewingOutcome::Renewed(LeaseState {
4315            discover_options: TEST_DISCOVER_OPTIONS,
4316            yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
4317                .try_into()
4318                .expect("should be specified"),
4319            server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
4320                .try_into()
4321                .expect("should be specified"),
4322            ip_address_lease_time: std::time::Duration::from_secs(
4323                DEFAULT_LEASE_LENGTH_SECONDS.into()
4324            ),
4325            renewal_time: None,
4326            rebinding_time: None,
4327            start_time: TestInstant(std::time::Duration::from_secs(0)),
4328        }, test_parameter_values().into_iter().collect()),
4329        counters: RenewingTestCounters {
4330            send_message: 1,
4331            recv_message: 1,
4332            ..Default::default()
4333        }
4334    }; "successfully renews after receiving DHCPACK without lease time")]
4335    #[test_case(VaryingIncomingMessageFields {
4336        yiaddr: OTHER_ADDR,
4337        options: [
4338            dhcp_protocol::DhcpOption::DhcpMessageType(
4339                dhcp_protocol::MessageType::DHCPACK,
4340            ),
4341            dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4342            dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4343                DEFAULT_LEASE_LENGTH_SECONDS,
4344            ),
4345        ]
4346        .into_iter()
4347        .chain(test_parameter_values())
4348        .collect(),
4349    } => RenewingTestResult {
4350        outcome: RenewingOutcome::NewAddress(LeaseState {
4351            discover_options: TEST_DISCOVER_OPTIONS,
4352            yiaddr: net_types::ip::Ipv4Addr::from(OTHER_ADDR)
4353                .try_into()
4354                .expect("should be specified"),
4355            server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
4356                .try_into()
4357                .expect("should be specified"),
4358            ip_address_lease_time: std::time::Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4359            renewal_time: None,
4360            rebinding_time: None,
4361            start_time: TestInstant(std::time::Duration::from_secs(0)),
4362        }, test_parameter_values().into_iter().collect()),
4363        counters: RenewingTestCounters {
4364            send_message: 1,
4365            recv_message: 1,
4366            ..Default::default()
4367        }
4368    }; "observes new address from DHCPACK")]
4369    #[test_case(VaryingIncomingMessageFields {
4370        yiaddr: YIADDR,
4371        options: [
4372            dhcp_protocol::DhcpOption::DhcpMessageType(
4373                dhcp_protocol::MessageType::DHCPACK,
4374            ),
4375            dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4376            dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4377                DEFAULT_LEASE_LENGTH_SECONDS,
4378            ),
4379        ]
4380        .into_iter()
4381        .chain(test_parameter_values_excluding_subnet_mask())
4382        .collect(),
4383    } => RenewingTestResult {
4384        outcome: RenewingOutcome::Rebinding(
4385            Rebinding {
4386                lease_state: build_test_lease_state()
4387        }),
4388        counters: RenewingTestCounters {
4389            send_message: 2,
4390            recv_time_out: 2,
4391            recv_message: 1,
4392            recv_missing_option: 1,
4393            ..Default::default()
4394        },
4395    }; "ignores replies lacking required option SubnetMask")]
4396    #[test_case(VaryingIncomingMessageFields {
4397        yiaddr: Ipv4Addr::UNSPECIFIED,
4398        options: [
4399            dhcp_protocol::DhcpOption::DhcpMessageType(
4400                dhcp_protocol::MessageType::DHCPNAK,
4401            ),
4402            dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4403            dhcp_protocol::DhcpOption::Message(NAK_MESSAGE.to_owned()),
4404        ]
4405        .into_iter()
4406        .chain(test_parameter_values())
4407        .collect(),
4408    } => RenewingTestResult {
4409        outcome: RenewingOutcome::Nak(crate::parse::FieldsToRetainFromNak {
4410            server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
4411                .try_into()
4412                .expect("should be specified"),
4413            message: Some(NAK_MESSAGE.to_owned()),
4414            client_identifier: None,
4415        }),
4416        counters: RenewingTestCounters {
4417            send_message: 1,
4418            recv_message: 1,
4419            recv_nak: 1,
4420            ..Default::default()
4421        },
4422    }; "transitions to Init after receiving DHCPNAK")]
4423    fn do_renewing_transitions_on_reply(
4424        incoming_message: VaryingIncomingMessageFields,
4425    ) -> RenewingTestResult {
4426        initialize_logging();
4427
4428        let renewing = build_test_renewing_state(
4429            Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4430            None,
4431            None,
4432        );
4433        let client_config = &test_client_config();
4434
4435        let (_server_end, packet_client_end) = FakeSocket::new_pair();
4436        let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4437        let (udp_server_end, udp_client_end) = FakeSocket::new_pair();
4438        let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
4439        let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4440        let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4441        let time = &FakeTimeController::new();
4442        let counters = Counters::default();
4443        let renewing_fut = pin!(
4444            renewing
4445                .do_renewing(
4446                    client_config,
4447                    udp_socket_provider,
4448                    packet_socket_provider,
4449                    time,
4450                    &mut stop_receiver,
4451                    address_event_receiver,
4452                    &counters
4453                )
4454                .fuse()
4455        );
4456        let renewing_fut = pin!(renewing_fut);
4457
4458        let server_socket_addr =
4459            std::net::SocketAddr::V4(std::net::SocketAddrV4::new(SERVER_IP, SERVER_PORT.into()));
4460
4461        let server_fut = pin!(
4462            async {
4463                let mut recv_buf = [0u8; BUFFER_SIZE];
4464
4465                let DatagramInfo { length, address } = udp_server_end
4466                    .recv_from(&mut recv_buf)
4467                    .await
4468                    .expect("recv_from on test socket should succeed");
4469                assert_eq!(address, server_socket_addr);
4470
4471                let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
4472                    .expect("received packet on test socket should parse as DHCP message");
4473
4474                assert_outgoing_message_when_assigned_address(
4475                    &msg,
4476                    VaryingOutgoingMessageFields {
4477                        xid: msg.xid,
4478                        options: vec![
4479                            dhcp_protocol::DhcpOption::DhcpMessageType(
4480                                dhcp_protocol::MessageType::DHCPREQUEST,
4481                            ),
4482                            dhcp_protocol::DhcpOption::ParameterRequestList(
4483                                test_requested_parameters()
4484                                    .iter_keys()
4485                                    .collect::<Vec<_>>()
4486                                    .try_into()
4487                                    .expect("should fit parameter request list size constraints"),
4488                            ),
4489                        ],
4490                    },
4491                );
4492
4493                let reply = build_incoming_message(msg.xid, incoming_message);
4494
4495                udp_server_end
4496                    .send_to(
4497                        &reply.serialize(),
4498                        // Note that this is the address the client under test
4499                        // observes in `recv_from`.
4500                        server_socket_addr,
4501                    )
4502                    .await
4503                    .expect("send_to on test socket should succeed");
4504            }
4505            .fuse()
4506        );
4507
4508        let main_future = async move {
4509            let (renewing_result, ()) = join!(renewing_fut, server_fut);
4510            renewing_result
4511        }
4512        .fuse();
4513
4514        let mut main_future = pin!(main_future);
4515
4516        let mut executor = fasync::TestExecutor::new();
4517        let renewing_result = run_with_accelerated_time(&mut executor, time, &mut main_future);
4518
4519        let outcome = assert_matches!(renewing_result, Ok(outcome) => outcome);
4520        let counters = RenewingTestCounters {
4521            send_message: counters.renewing.messaging.send_message.load(),
4522            recv_time_out: counters.renewing.messaging.recv_time_out.load(),
4523            recv_failed_dhcp_parse: counters.renewing.messaging.recv_failed_dhcp_parse.load(),
4524            recv_wrong_xid: counters.renewing.messaging.recv_wrong_xid.load(),
4525            recv_wrong_chaddr: counters.renewing.messaging.recv_wrong_chaddr.load(),
4526            recv_message: counters.renewing.messaging.recv_message.load(),
4527            recv_nak: counters.renewing.recv_nak.load(),
4528            recv_missing_option: counters.renewing.recv_error.missing_required_option.load(),
4529        };
4530        RenewingTestResult { outcome, counters }
4531    }
4532
4533    fn build_test_rebinding_state(
4534        lease_length: Duration,
4535        renewal_time: Option<Duration>,
4536        rebinding_time: Option<Duration>,
4537    ) -> Rebinding<TestInstant> {
4538        Rebinding {
4539            lease_state: build_test_lease_state_with_times(
4540                lease_length,
4541                renewal_time,
4542                rebinding_time,
4543            ),
4544        }
4545    }
4546
4547    #[test]
4548    fn do_rebinding_sends_requests() {
4549        initialize_logging();
4550
4551        // Set to have timestamps be conveniently derivable from powers of 2.
4552        const RENEWAL_TIME: Duration = Duration::from_secs(0);
4553        const REBINDING_TIME: Duration = Duration::from_secs(0);
4554        const LEASE_LENGTH: Duration = Duration::from_secs(1024);
4555
4556        let rebinding =
4557            build_test_rebinding_state(LEASE_LENGTH, Some(RENEWAL_TIME), Some(REBINDING_TIME));
4558        let client_config = &test_client_config();
4559
4560        let (_server_end, packet_client_end) = FakeSocket::new_pair();
4561        let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4562        let (udp_server_end, udp_client_end) = FakeSocket::new_pair();
4563        let (binds_sender, mut binds_receiver) = mpsc::unbounded();
4564        let udp_socket_provider =
4565            &FakeSocketProvider::new_with_events(udp_client_end, binds_sender);
4566        let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4567        let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4568        let time = &FakeTimeController::new();
4569        let counters = Counters::default();
4570        let rebinding_fut = pin!(
4571            rebinding
4572                .do_rebinding(
4573                    client_config,
4574                    udp_socket_provider,
4575                    packet_socket_provider,
4576                    time,
4577                    &mut stop_receiver,
4578                    address_event_receiver,
4579                    &counters
4580                )
4581                .fuse()
4582        );
4583
4584        // Observe the "64, 4" instead of "64, 32" due to the 60 second minimum
4585        // retransmission delay.
4586        let expected_times_requests_are_sent =
4587            [1024, 512, 256, 128, 64, 4].map(|time_remaining_when_request_is_sent| {
4588                Duration::from_secs(1024 - time_remaining_when_request_is_sent)
4589            });
4590
4591        let receive_fut =
4592            pin!(
4593                async {
4594                    for expected_time in expected_times_requests_are_sent {
4595                        let mut recv_buf = [0u8; BUFFER_SIZE];
4596                        let DatagramInfo { length, address } = udp_server_end
4597                            .recv_from(&mut recv_buf)
4598                            .await
4599                            .expect("recv_from on test socket should succeed");
4600
4601                        assert_eq!(
4602                            address,
4603                            std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
4604                                std::net::Ipv4Addr::BROADCAST,
4605                                SERVER_PORT.get()
4606                            ))
4607                        );
4608                        assert_eq!(time.now(), TestInstant(expected_time));
4609                        let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
4610                            .expect("received packet should parse as DHCP message");
4611
4612                        assert_outgoing_message_when_assigned_address(
4613                            &msg,
4614                            VaryingOutgoingMessageFields {
4615                                xid: msg.xid,
4616                                options: vec![
4617                            dhcp_protocol::DhcpOption::DhcpMessageType(
4618                                dhcp_protocol::MessageType::DHCPREQUEST,
4619                            ),
4620                            dhcp_protocol::DhcpOption::ParameterRequestList(
4621                                test_requested_parameters()
4622                                    .iter_keys()
4623                                    .collect::<Vec<_>>()
4624                                    .try_into()
4625                                    .expect("should fit parameter request list size constraints"),
4626                            ),
4627                        ],
4628                            },
4629                        );
4630                    }
4631                }
4632                .fuse()
4633            );
4634
4635        let main_future = async { join!(rebinding_fut, receive_fut) };
4636        let mut main_future = pin!(main_future);
4637
4638        let mut executor = fasync::TestExecutor::new();
4639        let (requesting_result, ()) =
4640            run_with_accelerated_time(&mut executor, time, &mut main_future);
4641
4642        assert_matches!(requesting_result, Ok(RebindingOutcome::TimedOut));
4643        assert_matches!(udp_server_end.recv_from(&mut []).now_or_never(), None);
4644
4645        let bound_socket_addr = binds_receiver
4646            .next()
4647            .now_or_never()
4648            .expect("should have completed")
4649            .expect("should be present");
4650        assert_eq!(
4651            bound_socket_addr,
4652            std::net::SocketAddr::V4(std::net::SocketAddrV4::new(YIADDR, CLIENT_PORT.into()))
4653        );
4654    }
4655
4656    #[derive(PartialEq, Debug)]
4657    struct RebindingTestResult {
4658        outcome: RebindingOutcome<TestInstant, FakeRemovedReason>,
4659        counters: RebindingTestCounters,
4660    }
4661
4662    #[derive(PartialEq, Eq, Debug, Default)]
4663    struct RebindingTestCounters {
4664        send_message: usize,
4665        recv_time_out: usize,
4666        recv_failed_dhcp_parse: usize,
4667        recv_wrong_xid: usize,
4668        recv_wrong_chaddr: usize,
4669        recv_message: usize,
4670        recv_nak: usize,
4671        recv_missing_option: usize,
4672    }
4673
4674    #[test_case(VaryingIncomingMessageFields {
4675        yiaddr: YIADDR,
4676        options: [
4677            dhcp_protocol::DhcpOption::DhcpMessageType(
4678                dhcp_protocol::MessageType::DHCPACK,
4679            ),
4680            dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4681            dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4682                DEFAULT_LEASE_LENGTH_SECONDS,
4683            ),
4684        ]
4685        .into_iter()
4686        .chain(test_parameter_values())
4687        .collect(),
4688    } => RebindingTestResult {
4689        outcome: RebindingOutcome::Renewed(LeaseState {
4690            discover_options: TEST_DISCOVER_OPTIONS,
4691            yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
4692                .try_into()
4693                .expect("should be specified"),
4694            server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
4695                .try_into()
4696                .expect("should be specified"),
4697            ip_address_lease_time: std::time::Duration::from_secs(
4698                DEFAULT_LEASE_LENGTH_SECONDS.into()
4699            ),
4700            renewal_time: None,
4701            rebinding_time: None,
4702            start_time: TestInstant(std::time::Duration::from_secs(0)),
4703        }, test_parameter_values().into_iter().collect()),
4704        counters: RebindingTestCounters {
4705            send_message: 1,
4706            recv_message: 1,
4707            ..Default::default()
4708        }
4709    }; "successfully rebinds after receiving DHCPACK")]
4710    #[test_case(VaryingIncomingMessageFields {
4711        yiaddr: YIADDR,
4712        options: [
4713            dhcp_protocol::DhcpOption::DhcpMessageType(
4714                dhcp_protocol::MessageType::DHCPACK,
4715            ),
4716            dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4717        ]
4718        .into_iter()
4719        .chain(test_parameter_values())
4720        .collect(),
4721    } => RebindingTestResult {
4722        outcome: RebindingOutcome::Renewed(LeaseState {
4723            discover_options: TEST_DISCOVER_OPTIONS,
4724            yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
4725                .try_into()
4726                .expect("should be specified"),
4727            server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
4728                .try_into()
4729                .expect("should be specified"),
4730            ip_address_lease_time: std::time::Duration::from_secs(
4731                DEFAULT_LEASE_LENGTH_SECONDS.into()
4732            ),
4733            renewal_time: None,
4734            rebinding_time: None,
4735            start_time: TestInstant(std::time::Duration::from_secs(0)),
4736        }, test_parameter_values().into_iter().collect()),
4737        counters: RebindingTestCounters {
4738            send_message: 1,
4739            recv_message: 1,
4740            ..Default::default()
4741        }
4742    }; "successfully rebinds after receiving DHCPACK without lease time")]
4743    #[test_case(VaryingIncomingMessageFields {
4744        yiaddr: OTHER_ADDR,
4745        options: [
4746            dhcp_protocol::DhcpOption::DhcpMessageType(
4747                dhcp_protocol::MessageType::DHCPACK,
4748            ),
4749            dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4750            dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4751                DEFAULT_LEASE_LENGTH_SECONDS,
4752            ),
4753        ]
4754        .into_iter()
4755        .chain(test_parameter_values())
4756        .collect(),
4757    } => RebindingTestResult {
4758        outcome: RebindingOutcome::NewAddress(LeaseState {
4759        discover_options: TEST_DISCOVER_OPTIONS,
4760        yiaddr: net_types::ip::Ipv4Addr::from(OTHER_ADDR)
4761            .try_into()
4762            .expect("should be specified"),
4763        server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
4764            .try_into()
4765            .expect("should be specified"),
4766        ip_address_lease_time: std::time::Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4767        renewal_time: None,
4768        rebinding_time: None,
4769        start_time: TestInstant(std::time::Duration::from_secs(0)),
4770    }, test_parameter_values().into_iter().collect()),
4771    counters: RebindingTestCounters {
4772        send_message: 1,
4773        recv_message: 1,
4774        ..Default::default()
4775    }
4776 } ; "observes new address from DHCPACK")]
4777    #[test_case(VaryingIncomingMessageFields {
4778        yiaddr: YIADDR,
4779        options: [
4780            dhcp_protocol::DhcpOption::DhcpMessageType(
4781                dhcp_protocol::MessageType::DHCPACK,
4782            ),
4783            dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4784            dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4785                DEFAULT_LEASE_LENGTH_SECONDS,
4786            ),
4787        ]
4788        .into_iter()
4789        .chain(test_parameter_values_excluding_subnet_mask())
4790        .collect(),
4791    } => RebindingTestResult {
4792        outcome: RebindingOutcome::TimedOut,
4793        counters: RebindingTestCounters {
4794            send_message: 2,
4795            recv_time_out: 2,
4796            recv_message: 1,
4797            recv_missing_option: 1,
4798            ..Default::default()
4799        }
4800    } ; "ignores replies lacking required option SubnetMask")]
4801    #[test_case(VaryingIncomingMessageFields {
4802        yiaddr: Ipv4Addr::UNSPECIFIED,
4803        options: [
4804            dhcp_protocol::DhcpOption::DhcpMessageType(
4805                dhcp_protocol::MessageType::DHCPNAK,
4806            ),
4807            dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4808            dhcp_protocol::DhcpOption::Message(NAK_MESSAGE.to_owned()),
4809        ]
4810        .into_iter()
4811        .chain(test_parameter_values())
4812        .collect(),
4813    } => RebindingTestResult {
4814        outcome: RebindingOutcome::Nak(crate::parse::FieldsToRetainFromNak {
4815            server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
4816                .try_into()
4817                .expect("should be specified"),
4818            message: Some(NAK_MESSAGE.to_owned()),
4819            client_identifier: None,
4820        }),
4821        counters: RebindingTestCounters {
4822            send_message: 1,
4823            recv_message: 1,
4824            recv_nak: 1,
4825            ..Default::default()
4826        },
4827    } ; "transitions to Init after receiving DHCPNAK")]
4828    fn do_rebinding_transitions_on_reply(
4829        incoming_message: VaryingIncomingMessageFields,
4830    ) -> RebindingTestResult {
4831        initialize_logging();
4832
4833        let rebinding = build_test_rebinding_state(
4834            Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4835            None,
4836            None,
4837        );
4838        let client_config = &test_client_config();
4839
4840        let (_server_end, packet_client_end) = FakeSocket::new_pair();
4841        let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4842        let (udp_server_end, udp_client_end) = FakeSocket::new_pair();
4843        let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
4844        let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4845        let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4846        let time = &FakeTimeController::new();
4847        let counters = Counters::default();
4848        let rebinding_fut = pin!(
4849            rebinding
4850                .do_rebinding(
4851                    client_config,
4852                    udp_socket_provider,
4853                    packet_socket_provider,
4854                    time,
4855                    &mut stop_receiver,
4856                    address_event_receiver,
4857                    &counters
4858                )
4859                .fuse()
4860        );
4861
4862        let server_socket_addr = std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
4863            OTHER_SERVER_IP,
4864            SERVER_PORT.into(),
4865        ));
4866
4867        let server_fut = pin!(
4868            async {
4869                let mut recv_buf = [0u8; BUFFER_SIZE];
4870
4871                let DatagramInfo { length, address } = udp_server_end
4872                    .recv_from(&mut recv_buf)
4873                    .await
4874                    .expect("recv_from on test socket should succeed");
4875                assert_eq!(
4876                    address,
4877                    std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
4878                        std::net::Ipv4Addr::BROADCAST,
4879                        SERVER_PORT.into()
4880                    ))
4881                );
4882
4883                let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
4884                    .expect("received packet on test socket should parse as DHCP message");
4885
4886                assert_outgoing_message_when_assigned_address(
4887                    &msg,
4888                    VaryingOutgoingMessageFields {
4889                        xid: msg.xid,
4890                        options: vec![
4891                            dhcp_protocol::DhcpOption::DhcpMessageType(
4892                                dhcp_protocol::MessageType::DHCPREQUEST,
4893                            ),
4894                            dhcp_protocol::DhcpOption::ParameterRequestList(
4895                                test_requested_parameters()
4896                                    .iter_keys()
4897                                    .collect::<Vec<_>>()
4898                                    .try_into()
4899                                    .expect("should fit parameter request list size constraints"),
4900                            ),
4901                        ],
4902                    },
4903                );
4904
4905                let reply = build_incoming_message(msg.xid, incoming_message);
4906
4907                udp_server_end
4908                    .send_to(
4909                        &reply.serialize(),
4910                        // Note that this is the address the client under test
4911                        // observes in `recv_from`.
4912                        server_socket_addr,
4913                    )
4914                    .await
4915                    .expect("send_to on test socket should succeed");
4916            }
4917            .fuse()
4918        );
4919
4920        let main_future = async move {
4921            let (rebinding_result, ()) = join!(rebinding_fut, server_fut);
4922            rebinding_result
4923        }
4924        .fuse();
4925
4926        let mut main_future = pin!(main_future);
4927
4928        let mut executor = fasync::TestExecutor::new();
4929        let rebinding_result = run_with_accelerated_time(&mut executor, time, &mut main_future);
4930
4931        let outcome = assert_matches!(rebinding_result, Ok(outcome) => outcome);
4932        let counters = RebindingTestCounters {
4933            send_message: counters.rebinding.messaging.send_message.load(),
4934            recv_time_out: counters.rebinding.messaging.recv_time_out.load(),
4935            recv_failed_dhcp_parse: counters.rebinding.messaging.recv_failed_dhcp_parse.load(),
4936            recv_wrong_xid: counters.rebinding.messaging.recv_wrong_xid.load(),
4937            recv_wrong_chaddr: counters.rebinding.messaging.recv_wrong_chaddr.load(),
4938            recv_message: counters.rebinding.messaging.recv_message.load(),
4939            recv_nak: counters.rebinding.recv_nak.load(),
4940            recv_missing_option: counters.rebinding.recv_error.missing_required_option.load(),
4941        };
4942        RebindingTestResult { outcome, counters }
4943    }
4944
4945    struct FailingUdpSocketProvider;
4946
4947    impl deps::UdpSocketProvider for FailingUdpSocketProvider {
4948        type Sock = Rc<FakeSocket<std::net::SocketAddr>>;
4949        async fn bind_new_udp_socket(
4950            &self,
4951            _bound_addr: std::net::SocketAddr,
4952        ) -> Result<Self::Sock, deps::SocketError> {
4953            Err(deps::SocketError::AddrNotAvailable)
4954        }
4955    }
4956
4957    #[test_case(
4958        State::Renewing(build_test_renewing_state(Duration::from_secs(100), None, None));
4959        "renewing"
4960    )]
4961    #[test_case(
4962        State::Rebinding(build_test_rebinding_state(Duration::from_secs(100), None, None));
4963        "rebinding"
4964    )]
4965    fn return_address_removed_on_udp_socket_addr_not_available(state: State<TestInstant>) {
4966        initialize_logging();
4967        let counters = Counters::default();
4968        let client_config = &test_client_config();
4969        let (_server_end, packet_client_end) = FakeSocket::new_pair();
4970        let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4971        let udp_socket_provider = &FailingUdpSocketProvider;
4972        let address_event_receiver =
4973            futures::stream::once(async { AddressEvent::Removed(FakeRemovedReason) }).fuse();
4974        let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4975        let time = &FakeTimeController::new();
4976        let mut rng = FakeRngProvider::new(0);
4977
4978        let mut executor = fasync::TestExecutor::new();
4979        let mut fut = pin!(state.run(
4980            client_config,
4981            packet_socket_provider,
4982            udp_socket_provider,
4983            &mut rng,
4984            time,
4985            &mut stop_receiver,
4986            address_event_receiver,
4987            &counters,
4988        ));
4989
4990        let result = executor.run_until_stalled(&mut fut);
4991        let result = assert_matches!(result, std::task::Poll::Ready(r) => r);
4992        let step = assert_matches!(result, Ok(s) => s);
4993        assert_matches!(step, Step::Exit(ExitReason::AddressRemoved(FakeRemovedReason)));
4994    }
4995}