dhcp_client_core/
client.rs

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