dhcp_client_core/
client.rs

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