Skip to main content

dhcp_client_core/
client.rs

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