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