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