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