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