Skip to main content

dhcp_client_core/
client.rs

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