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
893 | deps::SocketError::AddrNotAvailable => return Err(Error::Socket(e)),
894 deps::SocketError::HostUnreachable => {
899 log::warn!("{debug_log_prefix} destination host unreachable: {:?}", dest);
900 }
901 deps::SocketError::Other(_) => {
904 log::error!(
905 "{debug_log_prefix} socket error while sending to {:?}: {:?}",
906 dest,
907 e
908 );
909 }
910 },
911 }
912 }
913 Ok(())
914}
915
916fn retransmit_schedule_during_acquisition(
917 rng: &mut (impl rand::Rng + ?Sized),
918) -> impl Iterator<Item = Duration> + '_ {
919 const MILLISECONDS_PER_SECOND: i32 = 1000;
920 [4i32, 8, 16, 32]
921 .into_iter()
922 .chain(std::iter::repeat(64))
923 .zip(std::iter::from_fn(|| {
931 Some(rng.random_range((-MILLISECONDS_PER_SECOND)..=MILLISECONDS_PER_SECOND))
932 }))
933 .map(|(base_seconds, jitter_millis)| {
934 let millis = u64::try_from(base_seconds * MILLISECONDS_PER_SECOND + jitter_millis)
935 .expect("retransmit wait is never negative");
936 Duration::from_millis(millis)
937 })
938}
939
940const BUFFER_SIZE: usize = 1500;
943
944fn recv_stream<'a, T: 'a, U: Send>(
945 socket: &'a impl deps::Socket<U>,
946 recv_buf: &'a mut [u8],
947 parser: impl Fn(&[u8], U) -> T + 'a,
948 debug_log_prefix: DebugLogPrefix,
949 counters: &'a MessagingRelatedCounters,
950) -> impl Stream<Item = Result<T, Error>> + 'a {
951 let MessagingRelatedCounters {
952 recv_message,
953 recv_message_fatal_socket_error,
954 recv_message_non_fatal_socket_error,
955 ..
956 } = counters;
957 futures::stream::try_unfold((recv_buf, parser), move |(recv_buf, parser)| async move {
958 let result = socket.recv_from(recv_buf).await;
959 let DatagramInfo { length, address } = match result {
960 Ok(datagram_info) => {
961 recv_message.increment();
962 datagram_info
963 }
964 Err(e) => match e {
965 deps::SocketError::FailedToOpen(_)
968 | deps::SocketError::NoInterface
969 | deps::SocketError::NetworkUnreachable
970 | deps::SocketError::UnsupportedHardwareType
971 | deps::SocketError::AddrNotAvailable => {
972 recv_message_fatal_socket_error.increment();
973 return Err(Error::Socket(e));
974 }
975 deps::SocketError::HostUnreachable => {
984 log::warn!("{debug_log_prefix} EHOSTUNREACH from recv_from");
985 recv_message_non_fatal_socket_error.increment();
986 return Ok(Some((None, (recv_buf, parser))));
987 }
988 deps::SocketError::Other(_) => {
991 log::error!("{debug_log_prefix} socket error while receiving: {:?}", e);
992 recv_message_non_fatal_socket_error.increment();
993 return Ok(Some((None, (recv_buf, parser))));
994 }
995 },
996 };
997 let raw_msg = &recv_buf[..length];
998 let parsed = parser(raw_msg, address);
999 Ok(Some((Some(parsed), (recv_buf, parser))))
1000 })
1001 .try_filter_map(|item| futures::future::ok(item))
1002}
1003
1004struct OutgoingOptions {
1005 ciaddr: Option<SpecifiedAddr<net_types::ip::Ipv4Addr>>,
1006 requested_ip_address: Option<SpecifiedAddr<net_types::ip::Ipv4Addr>>,
1007 ip_address_lease_time_secs: Option<NonZeroU32>,
1008 message_type: dhcp_protocol::MessageType,
1009 server_identifier: Option<SpecifiedAddr<net_types::ip::Ipv4Addr>>,
1010 include_parameter_request_list: bool,
1011}
1012
1013fn build_outgoing_message(
1014 ClientConfig {
1015 client_hardware_address,
1016 client_identifier,
1017 requested_parameters,
1018 preferred_lease_time_secs: _,
1019 requested_ip_address: _,
1020 debug_log_prefix: _,
1021 }: &ClientConfig,
1022 DiscoverOptions { xid: TransactionId(xid) }: &DiscoverOptions,
1023 OutgoingOptions {
1024 ciaddr,
1025 requested_ip_address,
1026 ip_address_lease_time_secs,
1027 message_type,
1028 server_identifier,
1029 include_parameter_request_list,
1030 }: OutgoingOptions,
1031) -> dhcp_protocol::Message {
1032 use dhcp_protocol::DhcpOption;
1033
1034 dhcp_protocol::Message {
1035 op: dhcp_protocol::OpCode::BOOTREQUEST,
1036 xid: xid.get(),
1037 secs: 0,
1038 bdcast_flag: false,
1039 ciaddr: ciaddr.map(|ip| ip.get().into()).unwrap_or(Ipv4Addr::UNSPECIFIED),
1040 yiaddr: Ipv4Addr::UNSPECIFIED,
1041 siaddr: Ipv4Addr::UNSPECIFIED,
1042 giaddr: Ipv4Addr::UNSPECIFIED,
1043 chaddr: *client_hardware_address,
1044 sname: BString::default(),
1045 file: BString::default(),
1046 options: [
1060 Some(DhcpOption::DhcpMessageType(message_type)),
1061 include_parameter_request_list
1062 .then(|| requested_parameters.try_to_parameter_request_list())
1063 .flatten()
1064 .map(DhcpOption::ParameterRequestList),
1065 server_identifier.map(|ip| DhcpOption::ServerIdentifier(ip.get().into())),
1066 requested_ip_address.map(|ip| DhcpOption::RequestedIpAddress(ip.get().into())),
1067 ip_address_lease_time_secs.map(|time| DhcpOption::IpAddressLeaseTime(time.get())),
1068 client_identifier.clone().map(DhcpOption::ClientIdentifier),
1069 ]
1070 .into_iter()
1071 .flatten()
1072 .collect(),
1073 }
1074}
1075
1076fn build_discover(
1077 client_config: &ClientConfig,
1078 discover_options: &DiscoverOptions,
1079) -> dhcp_protocol::Message {
1080 let ClientConfig {
1081 client_hardware_address: _,
1082 client_identifier: _,
1083 requested_parameters: _,
1084 preferred_lease_time_secs,
1085 requested_ip_address,
1086 debug_log_prefix: _,
1087 } = client_config;
1088
1089 build_outgoing_message(
1098 client_config,
1099 discover_options,
1100 OutgoingOptions {
1101 ciaddr: None,
1102 requested_ip_address: *requested_ip_address,
1103 ip_address_lease_time_secs: *preferred_lease_time_secs,
1104 message_type: dhcp_protocol::MessageType::DHCPDISCOVER,
1105 server_identifier: None,
1106 include_parameter_request_list: true,
1107 },
1108 )
1109}
1110
1111fn parse_incoming_dhcp_message_from_ip_packet(
1115 packet: &[u8],
1116 debug_log_prefix: DebugLogPrefix,
1117) -> Result<Option<(net_types::ip::Ipv4Addr, dhcp_protocol::Message)>, anyhow::Error> {
1118 match crate::parse::parse_dhcp_message_from_ip_packet(packet, CLIENT_PORT) {
1119 Ok(message) => Ok(Some(message)),
1120 Err(err) => match err {
1121 crate::parse::ParseError::NotUdp => {
1122 log::debug!("{debug_log_prefix} ignoring non-UDP incoming packet");
1123 return Ok(None);
1124 }
1125 crate::parse::ParseError::WrongPort(port) => {
1126 log::debug!(
1127 "{debug_log_prefix} ignoring incoming UDP packet \
1128 to non-DHCP-client port {port}"
1129 );
1130 return Ok(None);
1131 }
1132 err @ (crate::parse::ParseError::Ipv4(_)
1133 | crate::parse::ParseError::Udp(_)
1134 | crate::parse::ParseError::WrongSource(_)
1135 | crate::parse::ParseError::Dhcp(_)) => {
1136 return Err(err).context("error while parsing DHCP message from IP packet");
1137 }
1138 },
1139 }
1140}
1141
1142#[derive(Debug)]
1143pub(crate) enum SelectingOutcome<I> {
1144 GracefulShutdown,
1145 Requesting(Requesting<I>),
1146}
1147
1148#[derive(Debug, Clone, Copy)]
1152pub struct Selecting<I> {
1153 discover_options: DiscoverOptions,
1154 start_time: I,
1157}
1158
1159impl<I: deps::Instant> Selecting<I> {
1160 async fn do_selecting<C: deps::Clock<Instant = I>>(
1166 &self,
1167 client_config: &ClientConfig,
1168 packet_socket_provider: &impl deps::PacketSocketProvider,
1169 rng: &mut impl deps::RngProvider,
1170 time: &C,
1171 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1172 counters: &Counters,
1173 ) -> Result<SelectingOutcome<I>, Error> {
1174 let Counters { selecting: SelectingCounters { messaging, recv_error, .. }, .. } = counters;
1175 let socket = packet_socket_provider.get_packet_socket().await.map_err(Error::Socket)?;
1179 let Selecting { discover_options, start_time } = self;
1180 let message = build_discover(client_config, discover_options);
1181
1182 let ClientConfig {
1183 client_hardware_address: _,
1184 client_identifier: _,
1185 requested_parameters,
1186 preferred_lease_time_secs: _,
1187 requested_ip_address: _,
1188 debug_log_prefix,
1189 } = client_config;
1190
1191 let message = crate::parse::serialize_dhcp_message_to_ip_packet(
1192 message,
1193 Ipv4Addr::UNSPECIFIED, CLIENT_PORT,
1195 Ipv4Addr::BROADCAST, SERVER_PORT,
1197 );
1198
1199 let mut send_fut = pin!(
1200 send_with_retransmits(
1201 time,
1202 retransmit_schedule_during_acquisition(rng.get_rng()),
1203 message.as_ref(),
1204 &socket,
1205 Mac::BROADCAST,
1206 *debug_log_prefix,
1207 messaging
1208 )
1209 .fuse()
1210 );
1211
1212 let mut recv_buf = [0u8; BUFFER_SIZE];
1213 let mut offer_fields_stream = pin!(
1214 recv_stream(
1215 &socket,
1216 &mut recv_buf,
1217 |packet, src_addr| {
1218 let _: Mac = src_addr;
1221
1222 let (src_addr, message) =
1223 match parse_incoming_dhcp_message_from_ip_packet(packet, *debug_log_prefix)
1224 .inspect_err(|_| messaging.recv_failed_dhcp_parse.increment())?
1225 {
1226 Some(message) => message,
1227 None => return Ok(None),
1228 };
1229 validate_message(discover_options, client_config, &message)
1230 .inspect_err(|e| e.increment(&messaging))
1231 .context("invalid DHCP message")?;
1232 crate::parse::fields_to_retain_from_selecting(requested_parameters, message)
1233 .inspect_err(|e| recv_error.increment(&e))
1234 .map(|(fields, soft_errors)| {
1235 messaging.increment_soft_errors(soft_errors);
1236 Some((src_addr, fields))
1237 })
1238 .context(
1239 "error while retrieving fields to use in DHCPREQUEST from DHCP message",
1240 )
1241 },
1242 *debug_log_prefix,
1243 messaging
1244 )
1245 .try_filter_map(|parse_result| {
1246 futures::future::ok(match parse_result {
1247 Ok(fields) => fields,
1248 Err(error) => {
1249 log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
1250 None
1251 }
1252 })
1253 })
1254 .fuse()
1255 );
1256
1257 select_biased! {
1258 fields_to_use_in_request_result = offer_fields_stream.select_next_some() => {
1259 let (src_addr, fields_from_offer_to_use_in_request) =
1260 fields_to_use_in_request_result?;
1261 log_offer(&debug_log_prefix, &src_addr, &fields_from_offer_to_use_in_request);
1262 Ok(SelectingOutcome::Requesting(Requesting {
1265 discover_options: discover_options.clone(),
1266 fields_from_offer_to_use_in_request,
1267 start_time: *start_time,
1268 }))
1269 },
1270 () = stop_receiver.select_next_some() => {
1271 Ok(SelectingOutcome::GracefulShutdown)
1272 },
1273 send_discovers_result = send_fut => {
1274 send_discovers_result?;
1275 unreachable!("should never stop retransmitting DHCPDISCOVER unless we hit an error");
1276 }
1277 }
1278 }
1279}
1280
1281impl<I: deps::Instant> Selecting<I> {
1282 fn record(&self, inspector: &mut impl Inspector) {
1283 let Self { discover_options, start_time } = self;
1284 inspector.record_instant(diagnostics_traits::instant_property_name!("Start"), start_time);
1285 discover_options.record(inspector);
1286 }
1287}
1288
1289#[derive(thiserror::Error, Debug, PartialEq)]
1290enum ValidateMessageError {
1291 #[error("xid {actual} doesn't match expected xid {expected}")]
1292 WrongXid { expected: u32, actual: u32 },
1293 #[error("chaddr {actual} doesn't match expected chaddr {expected}")]
1294 WrongChaddr { expected: Mac, actual: Mac },
1295}
1296
1297impl ValidateMessageError {
1298 fn increment(&self, counters: &MessagingRelatedCounters) {
1299 match self {
1300 ValidateMessageError::WrongXid { .. } => counters.recv_wrong_xid.increment(),
1301 ValidateMessageError::WrongChaddr { .. } => counters.recv_wrong_chaddr.increment(),
1302 }
1303 }
1304}
1305
1306fn validate_message(
1307 DiscoverOptions { xid: TransactionId(my_xid) }: &DiscoverOptions,
1308 ClientConfig {
1309 client_hardware_address: my_chaddr,
1310 client_identifier: _,
1311 requested_parameters: _,
1312 preferred_lease_time_secs: _,
1313 requested_ip_address: _,
1314 debug_log_prefix: _,
1315 }: &ClientConfig,
1316 dhcp_protocol::Message {
1317 op: _,
1318 xid: msg_xid,
1319 secs: _,
1320 bdcast_flag: _,
1321 ciaddr: _,
1322 yiaddr: _,
1323 siaddr: _,
1324 giaddr: _,
1325 chaddr: msg_chaddr,
1326 sname: _,
1327 file: _,
1328 options: _,
1329 }: &dhcp_protocol::Message,
1330) -> Result<(), ValidateMessageError> {
1331 if *msg_xid != u32::from(*my_xid) {
1332 return Err(ValidateMessageError::WrongXid { expected: my_xid.get(), actual: *msg_xid });
1333 }
1334
1335 if msg_chaddr != my_chaddr {
1336 return Err(ValidateMessageError::WrongChaddr {
1337 expected: *my_chaddr,
1338 actual: *msg_chaddr,
1339 });
1340 }
1341 Ok(())
1342}
1343
1344#[derive(Debug, PartialEq)]
1345pub(crate) enum RequestingOutcome<I> {
1346 RanOutOfRetransmits,
1347 GracefulShutdown,
1348 Bound(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
1349 Nak(FieldsToRetainFromNak),
1350}
1351
1352#[derive(Debug, PartialEq, Clone, Copy)]
1356pub struct Requesting<I> {
1357 discover_options: DiscoverOptions,
1358 fields_from_offer_to_use_in_request: crate::parse::FieldsFromOfferToUseInRequest,
1359 start_time: I,
1360}
1361
1362const NUM_REQUEST_RETRANSMITS: usize = 4;
1366
1367const DEFAULT_LEASE_TIME: Duration = Duration::from_hours(12);
1392
1393impl<I: deps::Instant> Requesting<I> {
1394 async fn do_requesting<C: deps::Clock<Instant = I>>(
1400 &self,
1401 client_config: &ClientConfig,
1402 packet_socket_provider: &impl deps::PacketSocketProvider,
1403 rng: &mut impl deps::RngProvider,
1404 time: &C,
1405 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1406 counters: &Counters,
1407 ) -> Result<RequestingOutcome<I>, Error> {
1408 let Counters {
1409 requesting: RequestingCounters { messaging, recv_error, recv_nak, .. }, ..
1410 } = counters;
1411 let socket = packet_socket_provider.get_packet_socket().await.map_err(Error::Socket)?;
1412 let Requesting { discover_options, fields_from_offer_to_use_in_request, start_time } = self;
1413 let message = build_request_during_address_acquisition(
1414 client_config,
1415 discover_options,
1416 fields_from_offer_to_use_in_request,
1417 );
1418
1419 let message = crate::parse::serialize_dhcp_message_to_ip_packet(
1420 message,
1421 Ipv4Addr::UNSPECIFIED, CLIENT_PORT,
1423 Ipv4Addr::BROADCAST, SERVER_PORT,
1425 );
1426
1427 let ClientConfig {
1428 client_hardware_address: _,
1429 client_identifier: _,
1430 requested_parameters,
1431 preferred_lease_time_secs: _,
1432 requested_ip_address: _,
1433 debug_log_prefix,
1434 } = client_config;
1435
1436 let mut send_fut = pin!(
1437 send_with_retransmits(
1438 time,
1439 retransmit_schedule_during_acquisition(rng.get_rng()).take(NUM_REQUEST_RETRANSMITS),
1440 message.as_ref(),
1441 &socket,
1442 Mac::BROADCAST,
1443 *debug_log_prefix,
1444 messaging
1445 )
1446 .fuse()
1447 );
1448
1449 let mut recv_buf = [0u8; BUFFER_SIZE];
1450
1451 let mut ack_or_nak_stream = pin!(
1452 recv_stream(
1453 &socket,
1454 &mut recv_buf,
1455 |packet, src_addr| {
1456 let _: Mac = src_addr;
1459 let (src_addr, message) =
1460 match parse_incoming_dhcp_message_from_ip_packet(packet, *debug_log_prefix)
1461 .inspect_err(|_| {
1462 messaging.recv_failed_dhcp_parse.increment();
1463 })? {
1464 Some(message) => message,
1465 None => return Ok(None),
1466 };
1467 validate_message(discover_options, client_config, &message)
1468 .inspect_err(|e| e.increment(&messaging))
1469 .context("invalid DHCP message")?;
1470
1471 crate::parse::fields_to_retain_from_response_to_request(
1472 requested_parameters,
1473 message,
1474 )
1475 .inspect_err(|e| recv_error.increment(e))
1476 .context("error extracting needed fields from DHCP message during Requesting")
1477 .map(|(fields, soft_errors)| {
1478 messaging.increment_soft_errors(soft_errors);
1479 Some((src_addr, fields))
1480 })
1481 },
1482 *debug_log_prefix,
1483 messaging
1484 )
1485 .try_filter_map(|parse_result| {
1486 futures::future::ok(match parse_result {
1487 Ok(msg) => msg,
1488 Err(error) => {
1489 log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
1490 None
1491 }
1492 })
1493 })
1494 .fuse()
1495 );
1496
1497 let (src_addr, fields_to_retain) = select_biased! {
1498 fields_to_retain_result = ack_or_nak_stream.select_next_some() => {
1499 fields_to_retain_result?
1500 },
1501 () = stop_receiver.select_next_some() => {
1502 return Ok(RequestingOutcome::GracefulShutdown)
1503 },
1504 send_requests_result = send_fut => {
1505 send_requests_result?;
1506 messaging.recv_time_out.increment();
1507 return Ok(RequestingOutcome::RanOutOfRetransmits)
1508 }
1509 };
1510
1511 match fields_to_retain {
1512 crate::parse::IncomingResponseToRequest::Ack(ack) => {
1513 log_ack(&debug_log_prefix, &src_addr, &ack);
1514 let FieldsToRetainFromAck {
1515 yiaddr,
1516 server_identifier,
1517 ip_address_lease_time_secs,
1518 renewal_time_value_secs,
1519 rebinding_time_value_secs,
1520 parameters,
1521 } = ack;
1522 let server_identifier = server_identifier.unwrap_or({
1523 let crate::parse::FieldsFromOfferToUseInRequest {
1524 server_identifier,
1525 ip_address_lease_time_secs: _,
1526 ip_address_to_request: _,
1527 } = fields_from_offer_to_use_in_request;
1528 *server_identifier
1529 });
1530
1531 let ip_address_lease_time = match ip_address_lease_time_secs {
1532 Some(lease_time) => Duration::from_secs(lease_time.get().into()),
1533 None => {
1534 messaging.recv_ack_no_addr_lease_time.increment();
1541 match fields_from_offer_to_use_in_request.ip_address_lease_time_secs {
1542 Some(lease_time) => {
1543 let lease_time = Duration::from_secs(lease_time.get().into());
1544 log::warn!(
1545 "{debug_log_prefix} accepting DHCPACK from {src_addr} that did \
1546 not provide an IP Address Lease Time. Falling back to the \
1547 lease time from the DHCPOFFER: {lease_time:?}."
1548 );
1549 lease_time
1550 }
1551 None => {
1552 log::warn!(
1553 "{debug_log_prefix} accepting DHCPACK from {src_addr} that did \
1554 not provide an IP Address Lease Time. Falling back to the \
1555 default: {DEFAULT_LEASE_TIME:?}."
1556 );
1557 DEFAULT_LEASE_TIME
1558 }
1559 }
1560 }
1561 };
1562
1563 Ok(RequestingOutcome::Bound(
1564 LeaseState {
1565 discover_options: discover_options.clone(),
1566 yiaddr,
1567 server_identifier,
1568 ip_address_lease_time,
1569 renewal_time: renewal_time_value_secs
1570 .map(u64::from)
1571 .map(Duration::from_secs),
1572 rebinding_time: rebinding_time_value_secs
1573 .map(u64::from)
1574 .map(Duration::from_secs),
1575 start_time: *start_time,
1576 },
1577 parameters,
1578 ))
1579 }
1580 crate::parse::IncomingResponseToRequest::Nak(nak) => {
1581 log_nak(&debug_log_prefix, &src_addr, &nak);
1582 recv_nak.increment();
1583 Ok(RequestingOutcome::Nak(nak))
1584 }
1585 }
1586 }
1587
1588 fn record(&self, inspector: &mut impl Inspector) {
1589 let Self { discover_options, start_time, fields_from_offer_to_use_in_request } = self;
1590 inspector.record_instant(diagnostics_traits::instant_property_name!("Start"), start_time);
1591 discover_options.record(inspector);
1592 fields_from_offer_to_use_in_request.record(inspector);
1593 }
1594}
1595
1596fn build_request_during_address_acquisition(
1597 client_config: &ClientConfig,
1598 discover_options: &DiscoverOptions,
1599 crate::parse::FieldsFromOfferToUseInRequest {
1600 server_identifier,
1601 ip_address_lease_time_secs,
1602 ip_address_to_request,
1603 }: &crate::parse::FieldsFromOfferToUseInRequest,
1604) -> dhcp_protocol::Message {
1605 let ClientConfig {
1606 client_hardware_address: _,
1607 client_identifier: _,
1608 requested_parameters: _,
1609 preferred_lease_time_secs,
1610 requested_ip_address: _,
1611 debug_log_prefix: _,
1612 } = client_config;
1613
1614 build_outgoing_message(
1623 client_config,
1624 discover_options,
1625 OutgoingOptions {
1626 ciaddr: None,
1627 requested_ip_address: Some(*ip_address_to_request),
1628 ip_address_lease_time_secs: ip_address_lease_time_secs.or(*preferred_lease_time_secs),
1629 message_type: dhcp_protocol::MessageType::DHCPREQUEST,
1630 server_identifier: Some(*server_identifier),
1631 include_parameter_request_list: true,
1632 },
1633 )
1634}
1635
1636#[derive(Debug, PartialEq)]
1638pub struct NewlyAcquiredLease<I> {
1639 pub ip_address: SpecifiedAddr<net_types::ip::Ipv4Addr>,
1641 pub start_time: I,
1643 pub lease_time: Duration,
1645 pub parameters: Vec<dhcp_protocol::DhcpOption>,
1649}
1650
1651#[derive(Debug, PartialEq)]
1652pub(crate) enum BoundOutcome<I, R> {
1653 GracefulShutdown,
1654 Renewing(Renewing<I>),
1655 Restart(Init),
1656 AddressRemoved(R),
1657 AddressRejected(WaitingToRestart<I>),
1658 Assigned(Bound<I>),
1659}
1660
1661#[derive(Debug, PartialEq, Clone, Copy)]
1665pub enum Bound<I> {
1666 AwaitingAssignment {
1669 lease_state: LeaseState<I>,
1671 },
1672 Assigned {
1675 lease_state: LeaseState<I>,
1677 },
1678}
1679
1680#[derive(Debug, PartialEq, Clone, Copy)]
1682pub struct LeaseState<I> {
1683 discover_options: DiscoverOptions,
1684 yiaddr: SpecifiedAddr<net_types::ip::Ipv4Addr>,
1685 server_identifier: SpecifiedAddr<net_types::ip::Ipv4Addr>,
1686 ip_address_lease_time: Duration,
1687 start_time: I,
1688 renewal_time: Option<Duration>,
1689 rebinding_time: Option<Duration>,
1690}
1691
1692impl<I: deps::Instant> Bound<I> {
1693 async fn do_bound<C: deps::Clock<Instant = I>, R>(
1694 &self,
1695 client_config: &ClientConfig,
1696 time: &C,
1697 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1698 packet_socket_provider: &impl deps::PacketSocketProvider,
1699 address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
1700 _counters: &Counters,
1703 ) -> Result<BoundOutcome<I, R>, Error> {
1704 match self {
1705 Bound::AwaitingAssignment { lease_state } => {
1706 do_bound_awaiting_assignment(
1707 lease_state,
1708 client_config,
1709 time,
1710 stop_receiver,
1711 packet_socket_provider,
1712 address_event_receiver,
1713 )
1714 .await
1715 }
1716 Bound::Assigned { lease_state } => {
1717 do_bound_assigned(
1718 lease_state,
1719 client_config,
1720 time,
1721 stop_receiver,
1722 packet_socket_provider,
1723 address_event_receiver,
1724 )
1725 .await
1726 }
1727 }
1728 }
1729}
1730
1731async fn do_bound_awaiting_assignment<I: deps::Instant, C: deps::Clock<Instant = I>, R>(
1732 lease_state: &LeaseState<I>,
1733 client_config: &ClientConfig,
1734 time: &C,
1735 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1736 packet_socket_provider: &impl deps::PacketSocketProvider,
1737 address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
1738) -> Result<BoundOutcome<I, R>, Error> {
1739 let LeaseState {
1740 discover_options: _,
1741 yiaddr,
1742 server_identifier: _,
1743 ip_address_lease_time,
1744 start_time,
1745 renewal_time: _,
1746 rebinding_time: _,
1747 } = lease_state;
1748
1749 let lease_timeout_fut = time.wait_until(start_time.add(*ip_address_lease_time)).fuse();
1759 let mut lease_timeout_fut = pin!(lease_timeout_fut);
1760
1761 let mut address_removed_or_assigned = pin!(address_event_receiver.filter_map(async |event| {
1762 match event {
1763 AddressEvent::Rejected => {
1764 handle_address_rejection(client_config, lease_state, packet_socket_provider, time)
1765 .map(|result| Some(result.map(BoundOutcome::AddressRejected)))
1766 .await
1767 }
1768 AddressEvent::Removed(reason) => Some(Ok(BoundOutcome::AddressRemoved(reason))),
1769 AddressEvent::AssignmentStateChanged(new_state) => {
1770 match new_state {
1771 AddressAssignmentState::Assigned => {
1772 Some(Ok(BoundOutcome::Assigned(Bound::Assigned {
1773 lease_state: lease_state.clone(),
1774 })))
1775 }
1776 s @ AddressAssignmentState::Tentative
1779 | s @ AddressAssignmentState::Unavailable => {
1780 log::warn!(
1781 "{yiaddr} address state became {s:?} during \
1782 Bound::AwaitingAssignment; ignoring"
1783 );
1784 None
1785 }
1786 }
1787 }
1788 }
1789 }));
1790
1791 let debug_log_prefix = &client_config.debug_log_prefix;
1792 select! {
1793 () = lease_timeout_fut => {
1797 log::info!(
1798 "{debug_log_prefix} Returning to Init due to failing to observe address \
1799 assignment for {yiaddr} before the lease ({ip_address_lease_time:?}) \
1800 expired."
1801 );
1802 Ok(BoundOutcome::Restart(Init::default()))
1803 },
1804 () = stop_receiver.select_next_some() => Ok(BoundOutcome::GracefulShutdown),
1805 result = address_removed_or_assigned.next() => {
1806 result.unwrap_or(Err(Error::AddressEventReceiverEnded))
1807 }
1808 }
1809}
1810
1811async fn do_bound_assigned<I: deps::Instant, C: deps::Clock<Instant = I>, R>(
1812 lease_state: &LeaseState<I>,
1813 client_config: &ClientConfig,
1814 time: &C,
1815 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1816 packet_socket_provider: &impl deps::PacketSocketProvider,
1817 address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
1818) -> Result<BoundOutcome<I, R>, Error> {
1819 let LeaseState {
1820 discover_options: _,
1821 yiaddr,
1822 server_identifier,
1823 ip_address_lease_time,
1824 start_time,
1825 renewal_time,
1826 rebinding_time: _,
1827 } = lease_state;
1828
1829 let renewal_time = renewal_time.unwrap_or(*ip_address_lease_time / 2);
1833
1834 let debug_log_prefix = &client_config.debug_log_prefix;
1835 log::info!(
1836 "{debug_log_prefix} In Bound state; ip_address_lease_time = {}, \
1837 renewal_time = {}, server_identifier = {server_identifier}",
1838 ip_address_lease_time.as_secs(),
1839 renewal_time.as_secs(),
1840 );
1841
1842 let renewal_timeout_fut = time.wait_until(start_time.add(renewal_time)).fuse();
1843 let mut renewal_timeout_fut = pin!(renewal_timeout_fut);
1844
1845 let mut address_removed = pin!(address_event_receiver.filter_map(async |event| match event {
1846 AddressEvent::Rejected => {
1847 handle_address_rejection(client_config, lease_state, packet_socket_provider, time)
1848 .map(|result| Some(result.map(BoundOutcome::AddressRejected)))
1849 .await
1850 }
1851 AddressEvent::Removed(reason) => Some(Ok(BoundOutcome::AddressRemoved(reason))),
1852 AddressEvent::AssignmentStateChanged(new_state) => {
1853 log::warn!(
1856 "{yiaddr} address state became {new_state:?} during Bound::Assigned; ignoring"
1857 );
1858 None
1859 }
1860 }));
1861
1862 select! {
1863 () = renewal_timeout_fut => Ok(BoundOutcome::Renewing(Renewing {
1864 lease_state: lease_state.clone()
1865 })),
1866
1867 () = stop_receiver.select_next_some() => Ok(BoundOutcome::GracefulShutdown),
1868 result = address_removed.next() => result.unwrap_or(Err(Error::AddressEventReceiverEnded)),
1869 }
1870}
1871
1872impl<I: deps::Instant> LeaseState<I> {
1873 fn record(&self, inspector: &mut impl Inspector) {
1874 let Self {
1875 discover_options,
1876 yiaddr,
1877 server_identifier,
1878 ip_address_lease_time,
1879 start_time,
1880 renewal_time,
1881 rebinding_time,
1882 } = self;
1883 inspector.record_instant(diagnostics_traits::instant_property_name!("Start"), start_time);
1884 discover_options.record(inspector);
1885 inspector.record_ip_addr("Yiaddr", **yiaddr);
1886 inspector.record_ip_addr("ServerIdentifier", **server_identifier);
1887 inspector.record_uint("IpAddressLeaseTimeSecs", ip_address_lease_time.as_secs());
1888 record_optional_duration_secs(inspector, "RenewalTimeSecs", *renewal_time);
1889 record_optional_duration_secs(inspector, "RebindingTimeSecs", *rebinding_time);
1890 }
1891}
1892
1893#[derive(Debug, PartialEq)]
1894pub(crate) enum RenewingOutcome<I, R> {
1895 GracefulShutdown,
1896 Renewed(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
1897 NewAddress(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
1904 Nak(FieldsToRetainFromNak),
1905 Rebinding(Rebinding<I>),
1906 AddressRemoved(R),
1907 AddressRejected(WaitingToRestart<I>),
1908}
1909
1910#[derive(Debug, PartialEq, Clone, Copy)]
1914pub struct Renewing<I> {
1915 lease_state: LeaseState<I>,
1916}
1917
1918const RENEW_RETRANSMIT_MINIMUM_DELAY: Duration = Duration::from_secs(60);
1925
1926impl<I: deps::Instant> Renewing<I> {
1927 async fn do_renewing<C: deps::Clock<Instant = I>, R>(
1928 &self,
1929 client_config: &ClientConfig,
1930 udp_socket_provider: &impl deps::UdpSocketProvider,
1934 packet_socket_provider: &impl deps::PacketSocketProvider,
1935 time: &C,
1936 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1937 address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
1938 counters: &Counters,
1939 ) -> Result<RenewingOutcome<I, R>, Error> {
1940 let Counters { renewing: RenewingCounters { messaging, recv_error, recv_nak, .. }, .. } =
1941 counters;
1942 let renewal_start_time = time.now();
1943 let debug_log_prefix = client_config.debug_log_prefix;
1944
1945 let Self {
1946 lease_state:
1947 lease_state @ LeaseState {
1948 discover_options,
1949 yiaddr,
1950 server_identifier,
1951 ip_address_lease_time,
1952 start_time,
1953 renewal_time: _,
1954 rebinding_time,
1955 },
1956 } = self;
1957 let rebinding_time = rebinding_time.unwrap_or(*ip_address_lease_time / 8 * 7);
1958 let mut address_removed =
1959 pin!(address_event_receiver.filter_map(async |event| match event {
1960 AddressEvent::Rejected => {
1961 handle_address_rejection(
1962 client_config,
1963 lease_state,
1964 packet_socket_provider,
1965 time,
1966 )
1967 .map(|result| Some(result.map(RenewingOutcome::AddressRejected)))
1968 .await
1969 }
1970 AddressEvent::Removed(reason) => {
1971 Some(Ok(RenewingOutcome::AddressRemoved(reason)))
1972 }
1973 AddressEvent::AssignmentStateChanged(new_state) => {
1974 log::warn!(
1977 "{yiaddr} address state became {new_state:?} during Renewing; ignoring"
1978 );
1979 None
1980 }
1981 }));
1982
1983 let socket = match udp_socket_provider
1984 .bind_new_udp_socket(std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
1985 yiaddr.get().into(),
1986 CLIENT_PORT.get(),
1987 )))
1988 .await
1989 {
1990 Ok(s) => s,
1991 Err(e @ deps::SocketError::AddrNotAvailable) => {
1992 if let Some(Some(result)) = address_removed.next().now_or_never() {
1996 return result;
1997 }
1998 return Err(Error::Socket(e));
1999 }
2000 Err(e) => return Err(Error::Socket(e)),
2001 };
2002
2003 let message = build_outgoing_message(
2012 client_config,
2013 discover_options,
2014 OutgoingOptions {
2015 ciaddr: Some(*yiaddr),
2016 requested_ip_address: None,
2017 ip_address_lease_time_secs: client_config.preferred_lease_time_secs,
2018 message_type: dhcp_protocol::MessageType::DHCPREQUEST,
2019 server_identifier: None,
2020 include_parameter_request_list: true,
2021 },
2022 );
2023 let message_bytes = message.serialize();
2024
2025 let t2 = start_time.add(rebinding_time);
2026 let server_sockaddr = std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
2027 server_identifier.get().into(),
2028 SERVER_PORT.get(),
2029 ));
2030 let mut send_fut = pin!(
2031 send_with_retransmits_at_instants(
2032 time,
2033 std::iter::from_fn(|| {
2034 let now = time.now();
2035 let half_time_until_t2 = now.average(t2);
2036 Some(half_time_until_t2.max(now.add(RENEW_RETRANSMIT_MINIMUM_DELAY)))
2037 }),
2038 message_bytes.as_ref(),
2039 &socket,
2040 server_sockaddr,
2041 debug_log_prefix,
2042 messaging
2043 )
2044 .fuse()
2045 );
2046
2047 let mut recv_buf = [0u8; BUFFER_SIZE];
2048 let mut responses_stream = pin!(
2049 recv_stream(
2050 &socket,
2051 &mut recv_buf,
2052 |packet, addr| {
2053 if addr != server_sockaddr {
2054 return Err(anyhow::Error::from(crate::parse::ParseError::WrongSource(
2055 addr,
2056 )));
2057 }
2058 let message = dhcp_protocol::Message::from_buffer(packet)
2059 .map_err(crate::parse::ParseError::Dhcp)
2060 .context("error while parsing DHCP message from UDP datagram")
2061 .inspect_err(|_| messaging.recv_failed_dhcp_parse.increment())?;
2062 validate_message(discover_options, client_config, &message)
2063 .inspect_err(|e| e.increment(&messaging))
2064 .context("invalid DHCP message")?;
2065 crate::parse::fields_to_retain_from_response_to_request(
2066 &client_config.requested_parameters,
2067 message,
2068 )
2069 .inspect_err(|e| recv_error.increment(e))
2070 .context("error extracting needed fields from DHCP message during Renewing")
2071 .map(|(fields, soft_errors)| {
2072 messaging.increment_soft_errors(soft_errors);
2073 (addr, fields)
2074 })
2075 },
2076 debug_log_prefix,
2077 messaging
2078 )
2079 .try_filter_map(|parse_result| {
2080 futures::future::ok(match parse_result {
2081 Ok(msg) => Some(msg),
2082 Err(error) => {
2083 log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
2084 None
2085 }
2086 })
2087 })
2088 .fuse()
2089 );
2090
2091 let mut timeout_fut = pin!(time.wait_until(t2).fuse());
2092
2093 let (src_addr, response) = select_biased! {
2094 response = responses_stream.select_next_some() => {
2095 response?
2096 },
2097 () = stop_receiver.select_next_some() => return Ok(RenewingOutcome::GracefulShutdown),
2098 result = address_removed.next() => {
2099 return result.unwrap_or(Err(Error::AddressEventReceiverEnded))
2100 }
2101 send_result = send_fut => {
2102 return Err(send_result.expect_err("send_fut should never complete without error"))
2103 },
2104 () = timeout_fut => {
2105 messaging.recv_time_out.increment();
2106 return Ok(RenewingOutcome::Rebinding(
2107 Rebinding { lease_state: lease_state.clone() }
2108 ))
2109 }
2110 };
2111
2112 match response {
2113 crate::parse::IncomingResponseToRequest::Ack(ack) => {
2114 log_ack(&debug_log_prefix, &src_addr, &ack);
2115 let FieldsToRetainFromAck {
2116 yiaddr: new_yiaddr,
2117 server_identifier: _,
2118 ip_address_lease_time_secs,
2119 renewal_time_value_secs,
2120 rebinding_time_value_secs,
2121 parameters,
2122 } = ack;
2123 let variant = if new_yiaddr == *yiaddr {
2124 RenewingOutcome::Renewed
2125 } else {
2126 log::info!(
2127 "{debug_log_prefix} obtained different address from renewal: {}",
2128 new_yiaddr
2129 );
2130 RenewingOutcome::NewAddress
2131 };
2132
2133 let ip_address_lease_time = match ip_address_lease_time_secs {
2134 Some(lease_time) => Duration::from_secs(lease_time.get().into()),
2135 None => {
2136 messaging.recv_ack_no_addr_lease_time.increment();
2142 log::warn!(
2143 "{debug_log_prefix} accepting DHCPACK from that did not provide an \
2144 IP Address Lease Time. Falling back to the original lease time: \
2145 {ip_address_lease_time:?}."
2146 );
2147 *ip_address_lease_time
2148 }
2149 };
2150
2151 Ok(variant(
2152 LeaseState {
2153 discover_options: discover_options.clone(),
2154 yiaddr: new_yiaddr,
2155 server_identifier: *server_identifier,
2156 ip_address_lease_time,
2157 renewal_time: renewal_time_value_secs
2158 .map(u64::from)
2159 .map(Duration::from_secs),
2160 rebinding_time: rebinding_time_value_secs
2161 .map(u64::from)
2162 .map(Duration::from_secs),
2163 start_time: renewal_start_time,
2164 },
2165 parameters,
2166 ))
2167 }
2168 crate::parse::IncomingResponseToRequest::Nak(nak) => {
2169 log_nak(&debug_log_prefix, &src_addr, &nak);
2170 recv_nak.increment();
2171 Ok(RenewingOutcome::Nak(nak))
2172 }
2173 }
2174 }
2175}
2176
2177#[derive(Debug, PartialEq)]
2179pub struct LeaseRenewal<I> {
2180 pub start_time: I,
2182 pub lease_time: Duration,
2184 pub parameters: Vec<dhcp_protocol::DhcpOption>,
2188}
2189
2190#[derive(Debug, PartialEq, Clone, Copy)]
2195pub struct Rebinding<I> {
2196 lease_state: LeaseState<I>,
2197}
2198
2199#[derive(Debug, PartialEq)]
2200pub(crate) enum RebindingOutcome<I, R> {
2201 GracefulShutdown,
2202 Renewed(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
2203 NewAddress(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
2210 Nak(FieldsToRetainFromNak),
2211 TimedOut,
2212 AddressRemoved(R),
2213 AddressRejected(WaitingToRestart<I>),
2214}
2215
2216impl<I: deps::Instant> Rebinding<I> {
2217 async fn do_rebinding<C: deps::Clock<Instant = I>, R>(
2218 &self,
2219 client_config: &ClientConfig,
2220 udp_socket_provider: &impl deps::UdpSocketProvider,
2224 packet_socket_provider: &impl deps::PacketSocketProvider,
2225 time: &C,
2226 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
2227 address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
2228 counters: &Counters,
2229 ) -> Result<RebindingOutcome<I, R>, Error> {
2230 let Counters {
2231 rebinding: RebindingCounters { messaging, recv_error, recv_nak, .. }, ..
2232 } = counters;
2233 let rebinding_start_time = time.now();
2234 let debug_log_prefix = client_config.debug_log_prefix;
2235
2236 let Self {
2237 lease_state:
2238 lease_state @ LeaseState {
2239 discover_options,
2240 yiaddr,
2241 server_identifier: _,
2242 ip_address_lease_time,
2243 start_time,
2244 renewal_time: _,
2245 rebinding_time: _,
2246 },
2247 } = self;
2248 let mut address_removed =
2249 pin!(address_event_receiver.filter_map(async |event| match event {
2250 AddressEvent::Rejected => {
2251 handle_address_rejection(
2252 client_config,
2253 lease_state,
2254 packet_socket_provider,
2255 time,
2256 )
2257 .map(|result| Some(result.map(RebindingOutcome::AddressRejected)))
2258 .await
2259 }
2260 AddressEvent::Removed(reason) => {
2261 Some(Ok(RebindingOutcome::AddressRemoved(reason)))
2262 }
2263 AddressEvent::AssignmentStateChanged(new_state) => {
2264 log::warn!(
2267 "{yiaddr} address state became {new_state:?} during Rebinding; ignoring"
2268 );
2269 None
2270 }
2271 }));
2272
2273 let socket = match udp_socket_provider
2274 .bind_new_udp_socket(std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
2275 yiaddr.get().into(),
2276 CLIENT_PORT.get(),
2277 )))
2278 .await
2279 {
2280 Ok(s) => s,
2281 Err(e @ deps::SocketError::AddrNotAvailable) => {
2282 if let Some(Some(result)) = address_removed.next().now_or_never() {
2286 return result;
2287 }
2288 return Err(Error::Socket(e));
2289 }
2290 Err(e) => return Err(Error::Socket(e)),
2291 };
2292
2293 let message = build_outgoing_message(
2302 client_config,
2303 discover_options,
2304 OutgoingOptions {
2305 ciaddr: Some(*yiaddr),
2306 requested_ip_address: None,
2307 ip_address_lease_time_secs: client_config.preferred_lease_time_secs,
2308 message_type: dhcp_protocol::MessageType::DHCPREQUEST,
2309 server_identifier: None,
2310 include_parameter_request_list: true,
2311 },
2312 );
2313 let message_bytes = message.serialize();
2314
2315 let lease_expiry = start_time.add(*ip_address_lease_time);
2316 let server_sockaddr = std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
2317 Ipv4Addr::BROADCAST,
2318 SERVER_PORT.get(),
2319 ));
2320 let mut send_fut = pin!(
2321 send_with_retransmits_at_instants(
2322 time,
2323 std::iter::from_fn(|| {
2324 let now = time.now();
2325 let half_time_until_lease_expiry = now.average(lease_expiry);
2326 Some(half_time_until_lease_expiry.max(now.add(RENEW_RETRANSMIT_MINIMUM_DELAY)))
2327 }),
2328 message_bytes.as_ref(),
2329 &socket,
2330 server_sockaddr,
2331 debug_log_prefix,
2332 messaging
2333 )
2334 .fuse()
2335 );
2336
2337 let mut recv_buf = [0u8; BUFFER_SIZE];
2338 let mut responses_stream = pin!(
2339 recv_stream(
2340 &socket,
2341 &mut recv_buf,
2342 |packet, addr| {
2343 let message = dhcp_protocol::Message::from_buffer(packet)
2344 .map_err(crate::parse::ParseError::Dhcp)
2345 .context("error while parsing DHCP message from UDP datagram")
2346 .inspect_err(|_| messaging.recv_failed_dhcp_parse.increment())?;
2347 validate_message(discover_options, client_config, &message)
2348 .inspect_err(|e| e.increment(&messaging))
2349 .context("invalid DHCP message")?;
2350 crate::parse::fields_to_retain_from_response_to_request(
2351 &client_config.requested_parameters,
2352 message,
2353 )
2354 .map(|(response, soft_errors)| {
2355 messaging.increment_soft_errors(soft_errors);
2356 response
2357 })
2358 .and_then(|response| match response {
2359 crate::parse::IncomingResponseToRequest::Ack(ack) => {
2360 Ok(crate::parse::IncomingResponseToRequest::Ack(
2364 ack.map_server_identifier(|server_identifier| {
2365 server_identifier.ok_or(
2366 crate::parse::IncomingResponseToRequestError::NoServerIdentifier,
2367 )
2368 })?,
2369 ))
2370 }
2371 crate::parse::IncomingResponseToRequest::Nak(nak) => {
2372 Ok(crate::parse::IncomingResponseToRequest::Nak(nak))
2373 }
2374 })
2375 .inspect_err(|e| {
2376 recv_error.increment(e);
2377 })
2378 .context("error extracting needed fields from DHCP message during Rebinding")
2379 .map(|fields_to_retain| (addr, fields_to_retain))
2380 },
2381 debug_log_prefix,
2382 messaging
2383 )
2384 .try_filter_map(|parse_result| {
2385 futures::future::ok(match parse_result {
2386 Ok(msg) => Some(msg),
2387 Err(error) => {
2388 log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
2389 None
2390 }
2391 })
2392 })
2393 .fuse()
2394 );
2395
2396 let mut timeout_fut = pin!(time.wait_until(lease_expiry).fuse());
2397
2398 let (src_addr, response) = select_biased! {
2399 response = responses_stream.select_next_some() => {
2400 response?
2401 },
2402 () = stop_receiver.select_next_some() => return Ok(RebindingOutcome::GracefulShutdown),
2403 result = address_removed.next() => {
2404 return result.unwrap_or(Err(Error::AddressEventReceiverEnded))
2405 }
2406 send_result = send_fut => {
2407 return Err(send_result.expect_err("send_fut should never complete without error"))
2408 },
2409 () = timeout_fut => {
2410 messaging.recv_time_out.increment();
2411 return Ok(RebindingOutcome::TimedOut)
2412 }
2413 };
2414
2415 match response {
2416 crate::parse::IncomingResponseToRequest::Ack(ack) => {
2417 log_ack(&debug_log_prefix, &src_addr, &ack);
2418 let FieldsToRetainFromAck {
2419 yiaddr: new_yiaddr,
2420 server_identifier,
2421 ip_address_lease_time_secs,
2422 renewal_time_value_secs,
2423 rebinding_time_value_secs,
2424 parameters,
2425 } = ack;
2426 let variant = if new_yiaddr == *yiaddr {
2427 RebindingOutcome::Renewed
2428 } else {
2429 log::info!(
2430 "{debug_log_prefix} obtained different address from rebinding: {}",
2431 new_yiaddr
2432 );
2433 RebindingOutcome::NewAddress
2434 };
2435
2436 let ip_address_lease_time = match ip_address_lease_time_secs {
2437 Some(lease_time) => Duration::from_secs(lease_time.get().into()),
2438 None => {
2439 messaging.recv_ack_no_addr_lease_time.increment();
2445 log::warn!(
2446 "{debug_log_prefix} accepting DHCPACK from that did not provide an \
2447 IP Address Lease Time. Falling back to the original lease time: \
2448 {ip_address_lease_time:?}."
2449 );
2450 *ip_address_lease_time
2451 }
2452 };
2453
2454 Ok(variant(
2455 LeaseState {
2456 discover_options: discover_options.clone(),
2457 yiaddr: new_yiaddr,
2458 server_identifier,
2459 ip_address_lease_time,
2460 renewal_time: renewal_time_value_secs
2461 .map(u64::from)
2462 .map(Duration::from_secs),
2463 rebinding_time: rebinding_time_value_secs
2464 .map(u64::from)
2465 .map(Duration::from_secs),
2466 start_time: rebinding_start_time,
2467 },
2468 parameters,
2469 ))
2470 }
2471 crate::parse::IncomingResponseToRequest::Nak(nak) => {
2472 log_nak(&debug_log_prefix, &src_addr, &nak);
2473 recv_nak.increment();
2474 Ok(RebindingOutcome::Nak(nak))
2475 }
2476 }
2477 }
2478}
2479
2480fn log_offer(
2481 prefix: &DebugLogPrefix,
2482 src_addr: &net_types::ip::Ipv4Addr,
2483 offer_fields: &FieldsFromOfferToUseInRequest,
2484) {
2485 let FieldsFromOfferToUseInRequest {
2486 server_identifier,
2487 ip_address_lease_time_secs: lease_time,
2488 ip_address_to_request: yiaddr,
2489 } = offer_fields;
2490 log::info!(
2491 "{prefix} received DHCPOFFER from {src_addr}: \
2492 yiaddr={yiaddr}, lease_time={lease_time:?}, server_identifier={server_identifier}"
2493 );
2494}
2495
2496fn log_ack<A: Display, S: Debug>(
2497 prefix: &DebugLogPrefix,
2498 src_addr: &A,
2499 ack_fields: &FieldsToRetainFromAck<S>,
2500) {
2501 let FieldsToRetainFromAck {
2502 yiaddr,
2503 server_identifier,
2504 ip_address_lease_time_secs: lease_time,
2505 renewal_time_value_secs: renew_time,
2506 rebinding_time_value_secs: rebinding_time,
2507 parameters,
2508 } = ack_fields;
2509 let (prefix_len, routers, dns_servers): (
2512 Option<u8>,
2513 Option<Vec<Ipv4Addr>>,
2514 Option<Vec<Ipv4Addr>>,
2515 ) = parameters.iter().fold(
2516 (None, None, None),
2517 |(mut prefix_len, mut routers, mut dns_servers), option| {
2518 match option {
2519 dhcp_protocol::DhcpOption::SubnetMask(prefix_length) => {
2520 prefix_len = Some(prefix_length.get());
2521 }
2522 dhcp_protocol::DhcpOption::Router(at_least) => {
2523 routers.get_or_insert_default().extend(at_least.deref());
2524 }
2525 dhcp_protocol::DhcpOption::DomainNameServer(at_least) => {
2526 dns_servers.get_or_insert_default().extend(at_least.deref());
2527 }
2528 _ => {}
2529 }
2530 (prefix_len, routers, dns_servers)
2531 },
2532 );
2533 log::info!(
2534 "{prefix} received DHCPACK from {src_addr}: \
2535 yiaddr={yiaddr}, lease_time={lease_time:?}, renewal_time={renew_time:?}, \
2536 rebinding_time={rebinding_time:?}, server_identifier={server_identifier:?}, \
2537 prefix_len={prefix_len:?}, routers={routers:?}, dns_servers={dns_servers:?}"
2538 );
2539}
2540
2541fn log_nak<A: Display>(prefix: &DebugLogPrefix, src_addr: &A, nak_fields: &FieldsToRetainFromNak) {
2542 let FieldsToRetainFromNak { server_identifier, message, client_identifier: _ } = nak_fields;
2543 log::info!(
2544 "{prefix} received DHCPNAK from {src_addr}: \
2545 server_identifier={server_identifier}, message={message:?}"
2546 );
2547}
2548
2549#[cfg(test)]
2550mod test {
2551 use super::*;
2552 use crate::deps::Clock as _;
2553 use crate::deps::testutil::{
2554 FakeRngProvider, FakeSocket, FakeSocketProvider, FakeTimeController, TestInstant, advance,
2555 run_until_next_timers_fire,
2556 };
2557 use assert_matches::assert_matches;
2558 use fuchsia_async as fasync;
2559 use futures::{Future, join};
2560 use itertools::Itertools as _;
2561 use net_declare::net::prefix_length_v4;
2562 use net_declare::{net_mac, std_ip_v4};
2563 use net_types::ip::{Ipv4, PrefixLength};
2564 use simplelog::{Config, LevelFilter, WriteLogger};
2565 use std::cell::RefCell;
2566 use std::rc::Rc;
2567 use test_case::test_case;
2568
2569 fn initialize_logging() {
2570 WriteLogger::init(LevelFilter::Info, Config::default(), std::io::stderr()).unwrap();
2571 }
2572
2573 const TEST_MAC_ADDRESS: Mac = net_mac!("01:01:01:01:01:01");
2574 const TEST_SERVER_MAC_ADDRESS: Mac = net_mac!("02:02:02:02:02:02");
2575 const OTHER_MAC_ADDRESS: Mac = net_mac!("03:03:03:03:03:03");
2576
2577 const SERVER_IP: Ipv4Addr = std_ip_v4!("192.168.1.1");
2578 const OTHER_SERVER_IP: Ipv4Addr = std_ip_v4!("192.168.1.11");
2579 const YIADDR: Ipv4Addr = std_ip_v4!("198.168.1.5");
2580 const OTHER_ADDR: Ipv4Addr = std_ip_v4!("198.168.1.6");
2581 const DEFAULT_LEASE_LENGTH_SECONDS: u32 = 100;
2582 const MAX_LEASE_LENGTH_SECONDS: u32 = 200;
2583 const TEST_PREFIX_LENGTH: PrefixLength<Ipv4> = prefix_length_v4!(24);
2584
2585 fn test_requested_parameters() -> OptionCodeMap<OptionRequested> {
2586 use dhcp_protocol::OptionCode;
2587 [
2588 (OptionCode::SubnetMask, OptionRequested::Required),
2589 (OptionCode::Router, OptionRequested::Optional),
2590 (OptionCode::DomainNameServer, OptionRequested::Optional),
2591 ]
2592 .into_iter()
2593 .collect::<OptionCodeMap<_>>()
2594 }
2595
2596 fn test_parameter_values_excluding_subnet_mask() -> [dhcp_protocol::DhcpOption; 2] {
2597 [
2598 dhcp_protocol::DhcpOption::Router([SERVER_IP].into()),
2599 dhcp_protocol::DhcpOption::DomainNameServer([SERVER_IP, std_ip_v4!("8.8.8.8")].into()),
2600 ]
2601 }
2602
2603 fn test_parameter_values() -> impl IntoIterator<Item = dhcp_protocol::DhcpOption> {
2604 std::iter::once(dhcp_protocol::DhcpOption::SubnetMask(TEST_PREFIX_LENGTH))
2605 .chain(test_parameter_values_excluding_subnet_mask())
2606 }
2607
2608 fn test_client_config() -> ClientConfig {
2609 ClientConfig {
2610 client_hardware_address: TEST_MAC_ADDRESS,
2611 client_identifier: None,
2612 requested_parameters: test_requested_parameters(),
2613 preferred_lease_time_secs: None,
2614 requested_ip_address: None,
2615 debug_log_prefix: DebugLogPrefix { interface_id: NonZeroU64::new(2).unwrap() },
2616 }
2617 }
2618
2619 #[derive(Debug, PartialEq)]
2621 struct FakeRemovedReason;
2622
2623 #[test]
2624 fn do_init_uses_rng() {
2625 let mut rng = FakeRngProvider::new(0);
2626 let time = FakeTimeController::new();
2627 let arbitrary_start_time = std::time::Duration::from_secs(42);
2628 advance(&time, arbitrary_start_time);
2629
2630 let Selecting {
2631 discover_options: DiscoverOptions { xid: xid_a },
2632 start_time: start_time_a,
2633 } = Init.do_init(&mut rng, &time);
2634 let Selecting {
2635 discover_options: DiscoverOptions { xid: xid_b },
2636 start_time: start_time_b,
2637 } = Init.do_init(&mut rng, &time);
2638 assert_ne!(xid_a, xid_b);
2639 assert_eq!(start_time_a, TestInstant(arbitrary_start_time));
2640 assert_eq!(start_time_b, TestInstant(arbitrary_start_time));
2641 }
2642
2643 fn run_with_accelerated_time<F>(
2644 executor: &mut fasync::TestExecutor,
2645 time: &Rc<RefCell<FakeTimeController>>,
2646 main_future: &mut F,
2647 ) -> F::Output
2648 where
2649 F: Future + Unpin,
2650 {
2651 loop {
2652 match run_until_next_timers_fire(executor, time, main_future) {
2653 std::task::Poll::Ready(result) => break result,
2654 std::task::Poll::Pending => (),
2655 }
2656 }
2657 }
2658
2659 fn build_test_selecting_state() -> Selecting<TestInstant> {
2660 Selecting {
2661 discover_options: DiscoverOptions { xid: TransactionId(NonZeroU32::new(1).unwrap()) },
2662 start_time: TestInstant(std::time::Duration::from_secs(0)),
2663 }
2664 }
2665
2666 #[test]
2667 fn do_selecting_obeys_graceful_shutdown() {
2668 initialize_logging();
2669 let counters = Counters::default();
2670
2671 let mut executor = fasync::TestExecutor::new();
2672 let time = FakeTimeController::new();
2673
2674 let selecting = build_test_selecting_state();
2675 let mut rng = FakeRngProvider::new(0);
2676
2677 let (_server_end, client_end) = FakeSocket::new_pair();
2678 let test_socket_provider = FakeSocketProvider::new(client_end);
2679
2680 let client_config = test_client_config();
2681
2682 let (stop_sender, mut stop_receiver) = mpsc::unbounded();
2683
2684 let mut selecting_fut = pin!(
2685 selecting
2686 .do_selecting(
2687 &client_config,
2688 &test_socket_provider,
2689 &mut rng,
2690 &time,
2691 &mut stop_receiver,
2692 &counters,
2693 )
2694 .fuse()
2695 );
2696
2697 let time = &time;
2698
2699 let mut wait_fut = pin!(
2700 async {
2701 time.wait_until(TestInstant(std::time::Duration::from_secs(30))).await;
2704 }
2705 .fuse()
2706 );
2707
2708 {
2709 let main_future = async {
2710 select! {
2711 _ = selecting_fut => unreachable!("should keep retransmitting DHCPDISCOVER forever"),
2712 () = wait_fut => (),
2713 }
2714 };
2715 let mut main_future = pin!(main_future);
2716
2717 run_with_accelerated_time(&mut executor, time, &mut main_future);
2718 }
2719
2720 stop_sender.unbounded_send(()).expect("sending stop signal should succeed");
2721
2722 let selecting_result = selecting_fut.now_or_never().expect(
2723 "selecting_fut should complete after single poll after stop signal has been sent",
2724 );
2725
2726 assert_matches!(selecting_result, Ok(SelectingOutcome::GracefulShutdown));
2727 }
2728
2729 struct VaryingOutgoingMessageFields {
2730 xid: u32,
2731 options: Vec<dhcp_protocol::DhcpOption>,
2732 }
2733
2734 #[track_caller]
2735 fn assert_outgoing_message_when_not_assigned_address(
2736 got_message: &dhcp_protocol::Message,
2737 fields: VaryingOutgoingMessageFields,
2738 ) {
2739 let VaryingOutgoingMessageFields { xid, options } = fields;
2740 let want_message = dhcp_protocol::Message {
2741 op: dhcp_protocol::OpCode::BOOTREQUEST,
2742 xid,
2743 secs: 0,
2744 bdcast_flag: false,
2745 ciaddr: Ipv4Addr::UNSPECIFIED,
2746 yiaddr: Ipv4Addr::UNSPECIFIED,
2747 siaddr: Ipv4Addr::UNSPECIFIED,
2748 giaddr: Ipv4Addr::UNSPECIFIED,
2749 chaddr: TEST_MAC_ADDRESS,
2750 sname: BString::default(),
2751 file: BString::default(),
2752 options,
2753 };
2754 assert_eq!(got_message, &want_message);
2755 }
2756
2757 #[test]
2758 fn do_selecting_sends_discover() {
2759 initialize_logging();
2760 let counters = Counters::default();
2761
2762 let mut executor = fasync::TestExecutor::new();
2763 let time = FakeTimeController::new();
2764
2765 let selecting = Selecting {
2766 discover_options: DiscoverOptions { xid: TransactionId(NonZeroU32::new(1).unwrap()) },
2767 start_time: TestInstant(std::time::Duration::from_secs(0)),
2768 };
2769 let mut rng = FakeRngProvider::new(0);
2770
2771 let (server_end, client_end) = FakeSocket::new_pair();
2772 let test_socket_provider = FakeSocketProvider::new(client_end);
2773
2774 let client_config = test_client_config();
2775
2776 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
2777
2778 let mut selecting_fut = pin!(
2779 selecting
2780 .do_selecting(
2781 &client_config,
2782 &test_socket_provider,
2783 &mut rng,
2784 &time,
2785 &mut stop_receiver,
2786 &counters,
2787 )
2788 .fuse()
2789 );
2790
2791 let time = &time;
2792
2793 const EXPECTED_RANGES: [(u64, u64); 7] =
2797 [(0, 0), (3, 5), (7, 9), (15, 17), (31, 33), (63, 65), (63, 65)];
2798
2799 let mut receive_fut =
2800 pin!(
2801 async {
2802 let mut previous_time = std::time::Duration::from_secs(0);
2803
2804 for (start, end) in EXPECTED_RANGES {
2805 let mut recv_buf = [0u8; BUFFER_SIZE];
2806 let DatagramInfo { length, address } = server_end
2807 .recv_from(&mut recv_buf)
2808 .await
2809 .expect("recv_from on test socket should succeed");
2810
2811 assert_eq!(address, Mac::BROADCAST);
2812
2813 let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
2814 &recv_buf[..length],
2815 dhcp_protocol::SERVER_PORT,
2816 )
2817 .expect("received packet should parse as DHCP message");
2818
2819 assert_outgoing_message_when_not_assigned_address(
2820 &msg,
2821 VaryingOutgoingMessageFields {
2822 xid: msg.xid,
2823 options: vec![
2824 dhcp_protocol::DhcpOption::DhcpMessageType(
2825 dhcp_protocol::MessageType::DHCPDISCOVER,
2826 ),
2827 dhcp_protocol::DhcpOption::ParameterRequestList(
2828 test_requested_parameters()
2829 .iter_keys()
2830 .collect::<Vec<_>>()
2831 .try_into()
2832 .expect("should fit parameter request list size constraints"),
2833 ),
2834 ],
2835 },
2836 );
2837
2838 let TestInstant(received_time) = time.now();
2839
2840 let duration_range = std::time::Duration::from_secs(start)
2841 ..=std::time::Duration::from_secs(end);
2842 assert!(duration_range.contains(&(received_time - previous_time)));
2843
2844 previous_time = received_time;
2845 }
2846 }
2847 .fuse()
2848 );
2849
2850 let main_future = async {
2851 select! {
2852 _ = selecting_fut => unreachable!("should keep retransmitting DHCPDISCOVER forever"),
2853 () = receive_fut => (),
2854 }
2855 };
2856 let mut main_future = pin!(main_future);
2857
2858 run_with_accelerated_time(&mut executor, time, &mut main_future);
2859 assert_eq!(counters.selecting.messaging.send_message.load(), EXPECTED_RANGES.len());
2860 assert_eq!(counters.selecting.messaging.recv_time_out.load(), EXPECTED_RANGES.len() - 1);
2861 }
2862
2863 const XID: NonZeroU32 = NonZeroU32::new(1).unwrap();
2864 #[test_case(u32::from(XID), TEST_MAC_ADDRESS => Ok(()) ; "accepts good reply")]
2865 #[test_case(u32::from(XID), TEST_SERVER_MAC_ADDRESS => Err(
2866 ValidateMessageError::WrongChaddr {
2867 expected: TEST_MAC_ADDRESS,
2868 actual: TEST_SERVER_MAC_ADDRESS,
2869 }) ; "rejects wrong chaddr")]
2870 #[test_case(u32::from(XID).wrapping_add(1), TEST_MAC_ADDRESS => Err(
2871 ValidateMessageError::WrongXid {
2872 expected: u32::from(XID),
2873 actual: u32::from(XID).wrapping_add(1),
2874 }) ; "rejects wrong xid")]
2875 fn test_validate_message(
2876 message_xid: u32,
2877 message_chaddr: Mac,
2878 ) -> Result<(), ValidateMessageError> {
2879 let discover_options = DiscoverOptions { xid: TransactionId(XID) };
2880 let client_config = ClientConfig {
2881 client_hardware_address: TEST_MAC_ADDRESS,
2882 client_identifier: None,
2883 requested_parameters: test_requested_parameters(),
2884 preferred_lease_time_secs: None,
2885 requested_ip_address: None,
2886 debug_log_prefix: DebugLogPrefix { interface_id: NonZeroU64::new(2).unwrap() },
2887 };
2888
2889 let reply = dhcp_protocol::Message {
2890 op: dhcp_protocol::OpCode::BOOTREPLY,
2891 xid: message_xid,
2892 secs: 0,
2893 bdcast_flag: false,
2894 ciaddr: Ipv4Addr::UNSPECIFIED,
2895 yiaddr: Ipv4Addr::UNSPECIFIED,
2896 siaddr: Ipv4Addr::UNSPECIFIED,
2897 giaddr: Ipv4Addr::UNSPECIFIED,
2898 chaddr: message_chaddr,
2899 sname: BString::default(),
2900 file: BString::default(),
2901 options: Vec::new(),
2902 };
2903
2904 validate_message(&discover_options, &client_config, &reply)
2905 }
2906
2907 #[allow(clippy::unused_unit)]
2908 #[test_case(false ; "with no garbage traffic on link")]
2909 #[test_case(true ; "ignoring garbage replies to discover")]
2910 fn do_selecting_good_offer(reply_to_discover_with_garbage: bool) {
2911 initialize_logging();
2912 let counters = Counters::default();
2913
2914 let mut rng = FakeRngProvider::new(0);
2915 let time = FakeTimeController::new();
2916
2917 let arbitrary_start_time = std::time::Duration::from_secs(42);
2918 advance(&time, arbitrary_start_time);
2919
2920 let selecting = Init.do_init(&mut rng, &time);
2921 let TransactionId(xid) = selecting.discover_options.xid;
2922
2923 let (server_end, client_end) = FakeSocket::<Mac>::new_pair();
2924 let test_socket_provider = FakeSocketProvider::new(client_end);
2925
2926 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
2927
2928 let client_config = test_client_config();
2929
2930 let selecting_fut = pin!(
2931 selecting
2932 .do_selecting(
2933 &client_config,
2934 &test_socket_provider,
2935 &mut rng,
2936 &time,
2937 &mut stop_receiver,
2938 &counters,
2939 )
2940 .fuse()
2941 );
2942
2943 let server_fut = pin!(
2944 async {
2945 let mut recv_buf = [0u8; BUFFER_SIZE];
2946
2947 if reply_to_discover_with_garbage {
2948 let DatagramInfo { length: _, address: dst_addr } = server_end
2949 .recv_from(&mut recv_buf)
2950 .await
2951 .expect("recv_from on test socket should succeed");
2952 assert_eq!(dst_addr, Mac::BROADCAST);
2953
2954 server_end
2955 .send_to(b"hello", OTHER_MAC_ADDRESS)
2956 .await
2957 .expect("send_to with garbage data should succeed");
2958 }
2959
2960 let DatagramInfo { length, address } = server_end
2961 .recv_from(&mut recv_buf)
2962 .await
2963 .expect("recv_from on test socket should succeed");
2964 assert_eq!(address, Mac::BROADCAST);
2965
2966 let parse_msg = || {
2969 let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
2970 &recv_buf[..length],
2971 dhcp_protocol::SERVER_PORT,
2972 )
2973 .expect("received packet on test socket should parse as DHCP message");
2974 msg
2975 };
2976
2977 let msg = parse_msg();
2978 assert_outgoing_message_when_not_assigned_address(
2979 &parse_msg(),
2980 VaryingOutgoingMessageFields {
2981 xid: msg.xid,
2982 options: vec![
2983 dhcp_protocol::DhcpOption::DhcpMessageType(
2984 dhcp_protocol::MessageType::DHCPDISCOVER,
2985 ),
2986 dhcp_protocol::DhcpOption::ParameterRequestList(
2987 test_requested_parameters()
2988 .iter_keys()
2989 .collect::<Vec<_>>()
2990 .try_into()
2991 .expect("should fit parameter request list size constraints"),
2992 ),
2993 ],
2994 },
2995 );
2996
2997 let build_reply = || {
2998 dhcpv4::server::build_offer(
2999 parse_msg(),
3000 dhcpv4::server::OfferOptions {
3001 offered_ip: YIADDR,
3002 server_ip: SERVER_IP,
3003 lease_length_config: dhcpv4::configuration::LeaseLength {
3004 default_seconds: DEFAULT_LEASE_LENGTH_SECONDS,
3005 max_seconds: MAX_LEASE_LENGTH_SECONDS,
3006 },
3007 renewal_time_value: Some(20),
3011 rebinding_time_value: Some(30),
3012 subnet_mask: TEST_PREFIX_LENGTH,
3013 },
3014 &dhcpv4::server::options_repo(test_parameter_values()),
3015 )
3016 .expect("dhcp server crate error building offer")
3017 };
3018
3019 let reply_with_wrong_xid = dhcp_protocol::Message {
3020 xid: (u32::from(xid).wrapping_add(1)),
3021 yiaddr: OTHER_ADDR,
3025 ..build_reply()
3026 };
3027
3028 let reply_without_subnet_mask = {
3029 let mut reply = build_reply();
3030 let options = std::mem::take(&mut reply.options);
3031 let (subnet_masks, other_options): (Vec<_>, Vec<_>) =
3032 options.into_iter().partition_map(|option| match option {
3033 dhcp_protocol::DhcpOption::SubnetMask(_) => {
3034 itertools::Either::Left(option)
3035 }
3036 _ => itertools::Either::Right(option),
3037 });
3038 assert_matches!(
3039 &subnet_masks[..],
3040 &[dhcp_protocol::DhcpOption::SubnetMask(TEST_PREFIX_LENGTH)]
3041 );
3042 reply.options = other_options;
3043
3044 reply.yiaddr = OTHER_ADDR;
3048 reply
3049 };
3050
3051 let good_reply = build_reply();
3052
3053 let send_reply = |reply: dhcp_protocol::Message| async {
3054 let dst_ip = reply.yiaddr;
3055 server_end
3056 .send_to(
3057 crate::parse::serialize_dhcp_message_to_ip_packet(
3058 reply,
3059 SERVER_IP,
3060 SERVER_PORT,
3061 dst_ip,
3062 CLIENT_PORT,
3063 )
3064 .as_ref(),
3065 TEST_SERVER_MAC_ADDRESS,
3068 )
3069 .await
3070 .expect("send_to on test socket should succeed");
3071 };
3072
3073 send_reply(reply_with_wrong_xid).await;
3075
3076 send_reply(reply_without_subnet_mask).await;
3078
3079 send_reply(good_reply).await;
3080 }
3081 .fuse()
3082 );
3083
3084 let main_future = async move {
3085 let (selecting_result, ()) = join!(selecting_fut, server_fut);
3086 selecting_result
3087 }
3088 .fuse();
3089 let mut main_future = pin!(main_future);
3090 let mut executor = fasync::TestExecutor::new();
3091 let selecting_result = run_with_accelerated_time(&mut executor, &time, &mut main_future);
3092
3093 let requesting = assert_matches!(
3094 selecting_result,
3095 Ok(SelectingOutcome::Requesting(requesting)) => requesting,
3096 "should have successfully transitioned to Requesting"
3097 );
3098
3099 assert_eq!(
3100 requesting,
3101 Requesting {
3102 discover_options: DiscoverOptions { xid: requesting.discover_options.xid },
3103 fields_from_offer_to_use_in_request: crate::parse::FieldsFromOfferToUseInRequest {
3104 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3105 .try_into()
3106 .expect("should be specified"),
3107 ip_address_lease_time_secs: Some(
3108 NonZeroU32::new(DEFAULT_LEASE_LENGTH_SECONDS).unwrap()
3109 ),
3110 ip_address_to_request: net_types::ip::Ipv4Addr::from(YIADDR)
3111 .try_into()
3112 .expect("should be specified"),
3113 },
3114 start_time: TestInstant(arbitrary_start_time),
3115 }
3116 );
3117 assert_eq!(
3118 counters.selecting.messaging.send_message.load(),
3119 if reply_to_discover_with_garbage { 2 } else { 1 }
3120 );
3121 assert_eq!(
3122 counters.selecting.messaging.recv_time_out.load(),
3123 if reply_to_discover_with_garbage { 1 } else { 0 }
3124 );
3125 assert_eq!(
3126 counters.selecting.messaging.recv_failed_dhcp_parse.load(),
3127 if reply_to_discover_with_garbage { 1 } else { 0 }
3128 );
3129 assert_eq!(counters.selecting.messaging.recv_wrong_xid.load(), 1);
3130 assert_eq!(counters.selecting.messaging.recv_wrong_chaddr.load(), 0);
3131 assert_eq!(counters.selecting.recv_error.missing_required_option.load(), 1);
3132 }
3133
3134 const TEST_XID: TransactionId = TransactionId(NonZeroU32::new(1).unwrap());
3135 const TEST_DISCOVER_OPTIONS: DiscoverOptions = DiscoverOptions { xid: TEST_XID };
3136
3137 fn build_test_requesting_state() -> Requesting<TestInstant> {
3138 Requesting {
3139 discover_options: TEST_DISCOVER_OPTIONS,
3140 start_time: TestInstant(std::time::Duration::from_secs(0)),
3141 fields_from_offer_to_use_in_request: crate::parse::FieldsFromOfferToUseInRequest {
3142 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3143 .try_into()
3144 .expect("should be specified"),
3145 ip_address_lease_time_secs: Some(
3146 NonZeroU32::new(DEFAULT_LEASE_LENGTH_SECONDS).unwrap(),
3147 ),
3148 ip_address_to_request: net_types::ip::Ipv4Addr::from(YIADDR)
3149 .try_into()
3150 .expect("should be specified"),
3151 },
3152 }
3153 }
3154
3155 #[test]
3156 fn do_requesting_obeys_graceful_shutdown() {
3157 initialize_logging();
3158 let counters = Counters::default();
3159
3160 let time = FakeTimeController::new();
3161
3162 let requesting = build_test_requesting_state();
3163 let mut rng = FakeRngProvider::new(0);
3164
3165 let (_server_end, client_end) = FakeSocket::new_pair();
3166 let test_socket_provider = FakeSocketProvider::new(client_end);
3167
3168 let client_config = test_client_config();
3169
3170 let (stop_sender, mut stop_receiver) = mpsc::unbounded();
3171
3172 let requesting_fut = requesting
3173 .do_requesting(
3174 &client_config,
3175 &test_socket_provider,
3176 &mut rng,
3177 &time,
3178 &mut stop_receiver,
3179 &counters,
3180 )
3181 .fuse();
3182 let mut requesting_fut = pin!(requesting_fut);
3183
3184 let mut executor = fasync::TestExecutor::new();
3185 assert_matches!(executor.run_until_stalled(&mut requesting_fut), std::task::Poll::Pending);
3186
3187 stop_sender.unbounded_send(()).expect("sending stop signal should succeed");
3188
3189 let requesting_result = requesting_fut.now_or_never().expect(
3190 "requesting_fut should complete after single poll after stop signal has been sent",
3191 );
3192
3193 assert_matches!(requesting_result, Ok(RequestingOutcome::GracefulShutdown));
3194 }
3195
3196 #[test]
3197 fn do_requesting_sends_requests() {
3198 initialize_logging();
3199 let counters = Counters::default();
3200
3201 let mut executor = fasync::TestExecutor::new();
3202 let time = FakeTimeController::new();
3203
3204 let requesting = build_test_requesting_state();
3205 let mut rng = FakeRngProvider::new(0);
3206
3207 let (server_end, client_end) = FakeSocket::new_pair();
3208 let test_socket_provider = FakeSocketProvider::new(client_end);
3209
3210 let client_config = test_client_config();
3211
3212 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3213
3214 let requesting_fut = pin!(
3215 requesting
3216 .do_requesting(
3217 &client_config,
3218 &test_socket_provider,
3219 &mut rng,
3220 &time,
3221 &mut stop_receiver,
3222 &counters,
3223 )
3224 .fuse()
3225 );
3226
3227 let time = &time;
3228
3229 const EXPECTED_RANGES: [(u64, u64); NUM_REQUEST_RETRANSMITS + 1] =
3233 [(0, 0), (3, 5), (7, 9), (15, 17), (31, 33)];
3234
3235 let receive_fut = pin!(
3236 async {
3237 let mut previous_time = std::time::Duration::from_secs(0);
3238
3239 for (start, end) in EXPECTED_RANGES {
3240 let mut recv_buf = [0u8; BUFFER_SIZE];
3241 let DatagramInfo { length, address } = server_end
3242 .recv_from(&mut recv_buf)
3243 .await
3244 .expect("recv_from on test socket should succeed");
3245
3246 assert_eq!(address, Mac::BROADCAST);
3247
3248 let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
3249 &recv_buf[..length],
3250 dhcp_protocol::SERVER_PORT,
3251 )
3252 .expect("received packet should parse as DHCP message");
3253
3254 assert_outgoing_message_when_not_assigned_address(
3255 &msg,
3256 VaryingOutgoingMessageFields {
3257 xid: msg.xid,
3258 options: vec![
3259 dhcp_protocol::DhcpOption::DhcpMessageType(
3260 dhcp_protocol::MessageType::DHCPREQUEST,
3261 ),
3262 dhcp_protocol::DhcpOption::ParameterRequestList(
3263 test_requested_parameters()
3264 .iter_keys()
3265 .collect::<Vec<_>>()
3266 .try_into()
3267 .expect(
3268 "should fit parameter request list size constraints",
3269 ),
3270 ),
3271 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3272 dhcp_protocol::DhcpOption::RequestedIpAddress(YIADDR),
3273 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3274 DEFAULT_LEASE_LENGTH_SECONDS,
3275 ),
3276 ],
3277 },
3278 );
3279
3280 let TestInstant(received_time) = time.now();
3281
3282 let duration_range =
3283 std::time::Duration::from_secs(start)..=std::time::Duration::from_secs(end);
3284 assert!(duration_range.contains(&(received_time - previous_time)));
3285
3286 previous_time = received_time;
3287 }
3288 }
3289 .fuse()
3290 );
3291
3292 let main_future = async { join!(requesting_fut, receive_fut) };
3293 let mut main_future = pin!(main_future);
3294
3295 let (requesting_result, ()) =
3296 run_with_accelerated_time(&mut executor, time, &mut main_future);
3297
3298 assert_matches!(requesting_result, Ok(RequestingOutcome::RanOutOfRetransmits));
3299 assert_eq!(counters.requesting.messaging.send_message.load(), EXPECTED_RANGES.len());
3300 assert_eq!(counters.requesting.messaging.recv_time_out.load(), EXPECTED_RANGES.len());
3301 }
3302
3303 struct VaryingIncomingMessageFields {
3304 yiaddr: Ipv4Addr,
3305 options: Vec<dhcp_protocol::DhcpOption>,
3306 }
3307
3308 fn build_incoming_message(
3309 xid: u32,
3310 fields: VaryingIncomingMessageFields,
3311 ) -> dhcp_protocol::Message {
3312 let VaryingIncomingMessageFields { yiaddr, options } = fields;
3313
3314 dhcp_protocol::Message {
3315 op: dhcp_protocol::OpCode::BOOTREPLY,
3316 xid,
3317 secs: 0,
3318 bdcast_flag: false,
3319 ciaddr: Ipv4Addr::UNSPECIFIED,
3320 yiaddr,
3321 siaddr: Ipv4Addr::UNSPECIFIED,
3322 giaddr: Ipv4Addr::UNSPECIFIED,
3323 chaddr: TEST_MAC_ADDRESS,
3324 sname: BString::default(),
3325 file: BString::default(),
3326 options,
3327 }
3328 }
3329
3330 const NAK_MESSAGE: &str = "something went wrong";
3331
3332 #[derive(PartialEq, Debug)]
3333 struct RequestingTestResult {
3334 outcome: RequestingOutcome<TestInstant>,
3335 counters: RequestingTestCounters,
3336 }
3337
3338 #[derive(PartialEq, Eq, Debug, Default)]
3339 struct RequestingTestCounters {
3340 send_message: usize,
3341 recv_time_out: usize,
3342 recv_failed_dhcp_parse: usize,
3343 recv_wrong_xid: usize,
3344 recv_wrong_chaddr: usize,
3345 recv_message: usize,
3346 recv_nak: usize,
3347 recv_missing_option: usize,
3348 }
3349
3350 #[test_case(VaryingIncomingMessageFields {
3351 yiaddr: YIADDR,
3352 options: [
3353 dhcp_protocol::DhcpOption::DhcpMessageType(
3354 dhcp_protocol::MessageType::DHCPACK,
3355 ),
3356 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3357 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3358 DEFAULT_LEASE_LENGTH_SECONDS,
3359 ),
3360 ]
3361 .into_iter()
3362 .chain(test_parameter_values())
3363 .collect(),
3364 } => RequestingTestResult {
3365 outcome: RequestingOutcome::Bound(LeaseState {
3366 discover_options: TEST_DISCOVER_OPTIONS,
3367 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
3368 .try_into()
3369 .expect("should be specified"),
3370 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3371 .try_into()
3372 .expect("should be specified"),
3373 ip_address_lease_time: std::time::Duration::from_secs(
3374 DEFAULT_LEASE_LENGTH_SECONDS.into()
3375 ),
3376 renewal_time: None,
3377 rebinding_time: None,
3378 start_time: TestInstant(std::time::Duration::from_secs(0)),
3379 }, test_parameter_values().into_iter().collect()),
3380 counters: RequestingTestCounters {
3381 send_message: 1,
3382 recv_message: 1,
3383 ..Default::default()
3384 }
3385 } ; "transitions to Bound after receiving DHCPACK")]
3386 #[test_case(VaryingIncomingMessageFields {
3387 yiaddr: YIADDR,
3388 options: [
3389 dhcp_protocol::DhcpOption::DhcpMessageType(
3390 dhcp_protocol::MessageType::DHCPACK,
3391 ),
3392 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3393 ]
3394 .into_iter()
3395 .chain(test_parameter_values())
3396 .collect(),
3397 } => RequestingTestResult {
3398 outcome: RequestingOutcome::Bound(LeaseState {
3399 discover_options: TEST_DISCOVER_OPTIONS,
3400 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
3401 .try_into()
3402 .expect("should be specified"),
3403 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3404 .try_into()
3405 .expect("should be specified"),
3406 ip_address_lease_time: std::time::Duration::from_secs(
3407 DEFAULT_LEASE_LENGTH_SECONDS.into()
3408 ),
3409 renewal_time: None,
3410 rebinding_time: None,
3411 start_time: TestInstant(std::time::Duration::from_secs(0)),
3412 }, test_parameter_values().into_iter().collect()),
3413 counters: RequestingTestCounters {
3414 send_message: 1,
3415 recv_message: 1,
3416 ..Default::default()
3417 }
3418 } ; "transitions to Bound after receiving DHCPACK without lease time")]
3419 #[test_case(VaryingIncomingMessageFields {
3420 yiaddr: YIADDR,
3421 options: [
3422 dhcp_protocol::DhcpOption::DhcpMessageType(
3423 dhcp_protocol::MessageType::DHCPACK,
3424 ),
3425 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3426 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3427 DEFAULT_LEASE_LENGTH_SECONDS,
3428 ),
3429 ]
3430 .into_iter()
3431 .chain(test_parameter_values_excluding_subnet_mask())
3432 .collect(),
3433 } => RequestingTestResult {
3434 outcome: RequestingOutcome::RanOutOfRetransmits,
3435 counters: RequestingTestCounters {
3436 send_message: 5,
3437 recv_time_out: 5,
3438 recv_message: 1,
3439 recv_missing_option: 1,
3440 ..Default::default()
3441 },
3442 }; "ignores replies lacking required option SubnetMask")]
3443 #[test_case(VaryingIncomingMessageFields {
3444 yiaddr: Ipv4Addr::UNSPECIFIED,
3445 options: [
3446 dhcp_protocol::DhcpOption::DhcpMessageType(
3447 dhcp_protocol::MessageType::DHCPNAK,
3448 ),
3449 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3450 dhcp_protocol::DhcpOption::Message(NAK_MESSAGE.to_owned()),
3451 ]
3452 .into_iter()
3453 .chain(test_parameter_values())
3454 .collect(),
3455 } => RequestingTestResult {
3456 outcome: RequestingOutcome::Nak(crate::parse::FieldsToRetainFromNak {
3457 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3458 .try_into()
3459 .expect("should be specified"),
3460 message: Some(NAK_MESSAGE.to_owned()),
3461 client_identifier: None,
3462 }),
3463 counters: RequestingTestCounters {
3464 send_message: 1,
3465 recv_message: 1,
3466 recv_nak: 1,
3467 ..Default::default()
3468 },
3469 }; "transitions to Init after receiving DHCPNAK")]
3470 fn do_requesting_transitions_on_reply(
3471 incoming_message: VaryingIncomingMessageFields,
3472 ) -> RequestingTestResult {
3473 initialize_logging();
3474 let counters = Counters::default();
3475
3476 let time = &FakeTimeController::new();
3477
3478 let requesting = build_test_requesting_state();
3479 let mut rng = FakeRngProvider::new(0);
3480
3481 let (server_end, client_end) = FakeSocket::new_pair();
3482 let test_socket_provider = FakeSocketProvider::new(client_end);
3483
3484 let client_config = test_client_config();
3485
3486 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3487
3488 let requesting_fut = pin!(
3489 requesting
3490 .do_requesting(
3491 &client_config,
3492 &test_socket_provider,
3493 &mut rng,
3494 time,
3495 &mut stop_receiver,
3496 &counters,
3497 )
3498 .fuse()
3499 );
3500
3501 let server_fut = pin!(
3502 async {
3503 let mut recv_buf = [0u8; BUFFER_SIZE];
3504
3505 let DatagramInfo { length, address } = server_end
3506 .recv_from(&mut recv_buf)
3507 .await
3508 .expect("recv_from on test socket should succeed");
3509 assert_eq!(address, Mac::BROADCAST);
3510
3511 let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
3512 &recv_buf[..length],
3513 dhcp_protocol::SERVER_PORT,
3514 )
3515 .expect("received packet on test socket should parse as DHCP message");
3516
3517 assert_outgoing_message_when_not_assigned_address(
3518 &msg,
3519 VaryingOutgoingMessageFields {
3520 xid: msg.xid,
3521 options: vec![
3522 dhcp_protocol::DhcpOption::DhcpMessageType(
3523 dhcp_protocol::MessageType::DHCPREQUEST,
3524 ),
3525 dhcp_protocol::DhcpOption::ParameterRequestList(
3526 test_requested_parameters()
3527 .iter_keys()
3528 .collect::<Vec<_>>()
3529 .try_into()
3530 .expect("should fit parameter request list size constraints"),
3531 ),
3532 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3533 dhcp_protocol::DhcpOption::RequestedIpAddress(YIADDR),
3534 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3535 DEFAULT_LEASE_LENGTH_SECONDS,
3536 ),
3537 ],
3538 },
3539 );
3540
3541 let reply = build_incoming_message(msg.xid, incoming_message);
3542
3543 server_end
3544 .send_to(
3545 crate::parse::serialize_dhcp_message_to_ip_packet(
3546 reply,
3547 SERVER_IP,
3548 SERVER_PORT,
3549 YIADDR,
3550 CLIENT_PORT,
3551 )
3552 .as_ref(),
3553 TEST_SERVER_MAC_ADDRESS,
3556 )
3557 .await
3558 .expect("send_to on test socket should succeed");
3559 }
3560 .fuse()
3561 );
3562
3563 let main_future = async move {
3564 let (requesting_result, ()) = join!(requesting_fut, server_fut);
3565 requesting_result
3566 }
3567 .fuse();
3568
3569 let mut main_future = pin!(main_future);
3570
3571 let mut executor = fasync::TestExecutor::new();
3572 let requesting_result = run_with_accelerated_time(&mut executor, time, &mut main_future);
3573
3574 let outcome = assert_matches!(requesting_result, Ok(outcome) => outcome);
3575 let counters = RequestingTestCounters {
3576 send_message: counters.requesting.messaging.send_message.load(),
3577 recv_time_out: counters.requesting.messaging.recv_time_out.load(),
3578 recv_failed_dhcp_parse: counters.requesting.messaging.recv_failed_dhcp_parse.load(),
3579 recv_wrong_xid: counters.requesting.messaging.recv_wrong_xid.load(),
3580 recv_wrong_chaddr: counters.requesting.messaging.recv_wrong_chaddr.load(),
3581 recv_message: counters.requesting.messaging.recv_message.load(),
3582 recv_nak: counters.requesting.recv_nak.load(),
3583 recv_missing_option: counters.requesting.recv_error.missing_required_option.load(),
3584 };
3585 RequestingTestResult { outcome, counters }
3586 }
3587
3588 fn build_test_lease_state() -> LeaseState<TestInstant> {
3589 build_test_lease_state_with_times(
3590 Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3591 None,
3592 None,
3593 )
3594 }
3595
3596 fn build_test_lease_state_with_times(
3597 lease_length: Duration,
3598 renewal_time: Option<Duration>,
3599 rebinding_time: Option<Duration>,
3600 ) -> LeaseState<TestInstant> {
3601 LeaseState {
3602 discover_options: TEST_DISCOVER_OPTIONS,
3603 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR).try_into().expect("should be specified"),
3604 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3605 .try_into()
3606 .expect("should be specified"),
3607 ip_address_lease_time: lease_length,
3608 start_time: TestInstant(std::time::Duration::from_secs(0)),
3609 renewal_time,
3610 rebinding_time,
3611 }
3612 }
3613
3614 fn build_test_newly_acquired_lease() -> NewlyAcquiredLease<TestInstant> {
3615 NewlyAcquiredLease {
3616 ip_address: net_types::ip::Ipv4Addr::from(YIADDR)
3617 .try_into()
3618 .expect("should be specified"),
3619 start_time: TestInstant(std::time::Duration::from_secs(0)),
3620 lease_time: Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3621 parameters: Vec::new(),
3622 }
3623 }
3624
3625 #[test_case(
3626 (
3627 State::Init(Init::default()),
3628 Transition::BoundWithNewLease(
3629 Bound::AwaitingAssignment{lease_state: build_test_lease_state()},
3630 build_test_newly_acquired_lease()
3631 )
3632 ) => matches Some(TransitionEffect::HandleNewLease(_));
3633 "yields newly-acquired lease effect"
3634 )]
3635 #[test_case(
3636 (
3637 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3638 Transition::Init(Init),
3639 ) => matches Some(TransitionEffect::DropLease {address_rejected: false});
3640 "recognizes loss of lease"
3641 )]
3642 #[test_case(
3643 (
3644 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3645 Transition::WaitingToRestart(WaitingToRestart {
3646 waiting_until: TestInstant(WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION)
3647 }),
3648 ) => matches Some(TransitionEffect::DropLease {address_rejected: true});
3649 "recognizes address rejection"
3650 )]
3651
3652 fn apply_transition(
3653 (state, transition): (State<TestInstant>, Transition<TestInstant>),
3654 ) -> Option<TransitionEffect<TestInstant>> {
3655 let (_next_state, effect) = state.apply(&test_client_config(), transition);
3656 effect
3657 }
3658
3659 enum AddrRemovedExpect {
3660 Decline,
3661 Exit,
3662 Ignore,
3663 }
3664
3665 #[test_case(
3666 State::Selecting(build_test_selecting_state()),
3667 AddrRemovedExpect::Ignore;
3668 "selecting_ignore"
3669 )]
3670 #[test_case(
3671 State::Requesting(build_test_requesting_state()),
3672 AddrRemovedExpect::Ignore;
3673 "requesting_ignore"
3674 )]
3675 #[test_case(
3676 State::Bound(Bound::AwaitingAssignment{lease_state: build_test_lease_state()}),
3677 AddrRemovedExpect::Exit;
3678 "bound_awaiting_assignment_exit"
3679 )]
3680 #[test_case(
3681 State::Bound(Bound::AwaitingAssignment{lease_state: build_test_lease_state()}),
3682 AddrRemovedExpect::Decline;
3683 "bound_awaiting_assignment_decline"
3684 )]
3685 #[test_case(
3686 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3687 AddrRemovedExpect::Exit;
3688 "bound_assigned_exit"
3689 )]
3690 #[test_case(
3691 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3692 AddrRemovedExpect::Decline;
3693 "bound_assigned_decline"
3694 )]
3695 #[test_case(
3696 State::Renewing(Renewing{lease_state: build_test_lease_state()}),
3697 AddrRemovedExpect::Exit;
3698 "renewing_exit"
3699 )]
3700 #[test_case(
3701 State::Renewing(Renewing{lease_state: build_test_lease_state()}),
3702 AddrRemovedExpect::Decline;
3703 "renewing_decline"
3704 )]
3705 #[test_case(
3706 State::Rebinding(Rebinding{lease_state: build_test_lease_state()}),
3707 AddrRemovedExpect::Exit;
3708 "rebinding_exit"
3709 )]
3710 #[test_case(
3711 State::Rebinding(Rebinding{lease_state: build_test_lease_state()}),
3712 AddrRemovedExpect::Decline;
3713 "rebinding_decline"
3714 )]
3715 #[test_case(
3716 State::WaitingToRestart(WaitingToRestart {
3717 waiting_until: TestInstant(WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION)
3718 }),
3719 AddrRemovedExpect::Ignore;
3720 "waiting_to_restart_ignore"
3721 )]
3722 fn on_address_removed(state: State<TestInstant>, expect: AddrRemovedExpect) {
3723 let config = &test_client_config();
3724 let time = &FakeTimeController::new();
3725 let mut rng = FakeRngProvider::new(0);
3726 let (server_end, packet_client_end) = FakeSocket::new_pair();
3727 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
3728 let (_server_end, udp_client_end) = FakeSocket::new_pair();
3729 let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
3730 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3731 let counters = Counters::default();
3732
3733 let address_event_receiver = futures::stream::once(async {
3734 match expect {
3735 AddrRemovedExpect::Decline => AddressEvent::Rejected,
3736 AddrRemovedExpect::Exit => AddressEvent::Removed(FakeRemovedReason),
3737 AddrRemovedExpect::Ignore => panic!("address_event_receiver should not be polled"),
3738 }
3739 })
3740 .chain(futures::stream::pending());
3741
3742 let run_result = state
3743 .run(
3744 config,
3745 packet_socket_provider,
3746 udp_socket_provider,
3747 &mut rng,
3748 time,
3749 &mut stop_receiver,
3750 address_event_receiver,
3751 &counters,
3752 )
3753 .now_or_never();
3754
3755 match expect {
3756 AddrRemovedExpect::Decline => {
3757 let run_result = run_result.expect("fut should finish when declining");
3758 let WaitingToRestart { waiting_until } = assert_matches!(
3759 run_result,
3760 Ok(Step::NextState(Transition::WaitingToRestart(waiting))) => waiting
3761 );
3762 assert_eq!(waiting_until, TestInstant(Duration::from_secs(10)));
3763
3764 let mut buf = [0u8; BUFFER_SIZE];
3765 let DatagramInfo { length, address } = server_end
3766 .recv_from(&mut buf)
3767 .now_or_never()
3768 .expect("should be ready")
3769 .expect("should succeed");
3770 assert_eq!(address, Mac::BROADCAST);
3771
3772 let (_src_addr, message) =
3773 crate::parse::parse_dhcp_message_from_ip_packet(&buf[..length], SERVER_PORT)
3774 .expect("should succeed");
3775
3776 use dhcp_protocol::DhcpOption;
3777 assert_outgoing_message_when_not_assigned_address(
3778 &message,
3779 VaryingOutgoingMessageFields {
3780 xid: message.xid,
3781 options: vec![
3782 DhcpOption::DhcpMessageType(dhcp_protocol::MessageType::DHCPDECLINE),
3783 DhcpOption::ServerIdentifier(SERVER_IP),
3784 DhcpOption::RequestedIpAddress(YIADDR),
3785 ],
3786 },
3787 );
3788 }
3789 AddrRemovedExpect::Exit => {
3790 let run_result = run_result.expect("fut should finish when exiting");
3791 assert_matches!(
3792 run_result,
3793 Ok(Step::Exit(ExitReason::AddressRemoved(FakeRemovedReason)))
3794 );
3795 }
3796 AddrRemovedExpect::Ignore => {
3797 assert_matches!(run_result, None, "fut should not finish when ignored.");
3798 }
3799 }
3800 }
3801
3802 enum AddrAssignmentChangeExpect {
3803 Ignore,
3804 EnterAssigned,
3805 }
3806
3807 #[test_case(
3808 State::Selecting(build_test_selecting_state()),
3809 AddressAssignmentState::Assigned,
3810 AddrAssignmentChangeExpect::Ignore;
3811 "selecting_ignores"
3812 )]
3813 #[test_case(
3814 State::Requesting(build_test_requesting_state()),
3815 AddressAssignmentState::Assigned,
3816 AddrAssignmentChangeExpect::Ignore;
3817 "requesting_ignores"
3818 )]
3819 #[test_case(
3820 State::Bound(Bound::AwaitingAssignment{lease_state: build_test_lease_state()}),
3821 AddressAssignmentState::Tentative,
3822 AddrAssignmentChangeExpect::Ignore;
3823 "bound_awaiting_assignment_ignores_tentative"
3824 )]
3825 #[test_case(
3826 State::Bound(Bound::AwaitingAssignment{lease_state: build_test_lease_state()}),
3827 AddressAssignmentState::Assigned,
3828 AddrAssignmentChangeExpect::EnterAssigned;
3829 "bound_awaiting_assignment_enter_assigned"
3830 )]
3831 #[test_case(
3832 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3833 AddressAssignmentState::Tentative,
3834 AddrAssignmentChangeExpect::Ignore;
3835 "bound_assigned_ignores_tentative"
3836 )]
3837 #[test_case(
3838 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3839 AddressAssignmentState::Assigned,
3840 AddrAssignmentChangeExpect::Ignore;
3841 "bound_assigned_ignores_assigned"
3842 )]
3843 #[test_case(
3844 State::Renewing(Renewing{lease_state: build_test_lease_state()}),
3845 AddressAssignmentState::Tentative,
3846 AddrAssignmentChangeExpect::Ignore;
3847 "renewing_ignores_tentative"
3848 )]
3849 #[test_case(
3850 State::Renewing(Renewing{lease_state: build_test_lease_state()}),
3851 AddressAssignmentState::Assigned,
3852 AddrAssignmentChangeExpect::Ignore;
3853 "renewing_ignores_assigned"
3854 )]
3855 #[test_case(
3856 State::Rebinding(Rebinding{lease_state: build_test_lease_state()}),
3857 AddressAssignmentState::Tentative,
3858 AddrAssignmentChangeExpect::Ignore;
3859 "rebinding_ignores_tentative"
3860 )]
3861 #[test_case(
3862 State::Rebinding(Rebinding{lease_state: build_test_lease_state()}),
3863 AddressAssignmentState::Assigned,
3864 AddrAssignmentChangeExpect::Ignore;
3865 "rebinding_ignores_assigned"
3866 )]
3867 #[test_case(
3868 State::WaitingToRestart(WaitingToRestart {
3869 waiting_until: TestInstant(WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION)
3870 }),
3871 AddressAssignmentState::Assigned,
3872 AddrAssignmentChangeExpect::Ignore;
3873 "waiting_to_restart_ignores"
3874 )]
3875 fn on_address_assignment_change(
3876 state: State<TestInstant>,
3877 change: AddressAssignmentState,
3878 expect: AddrAssignmentChangeExpect,
3879 ) {
3880 let config = &test_client_config();
3881 let time = &FakeTimeController::new();
3882 let mut rng = FakeRngProvider::new(0);
3883 let (_server_end, packet_client_end) = FakeSocket::new_pair();
3884 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
3885 let (_server_end, udp_client_end) = FakeSocket::new_pair();
3886 let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
3887 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3888 let counters = Counters::default();
3889
3890 let address_event_receiver = futures::stream::once(futures::future::ready(
3891 AddressEvent::<FakeRemovedReason>::AssignmentStateChanged(change),
3892 ))
3893 .chain(futures::stream::pending());
3894 let run_result = state
3895 .run(
3896 config,
3897 packet_socket_provider,
3898 udp_socket_provider,
3899 &mut rng,
3900 time,
3901 &mut stop_receiver,
3902 address_event_receiver,
3903 &counters,
3904 )
3905 .now_or_never();
3906
3907 match expect {
3908 AddrAssignmentChangeExpect::EnterAssigned => {
3909 let run_result = run_result.expect("fut should finish when exiting");
3910 assert_matches!(
3911 run_result,
3912 Ok(Step::NextState(Transition::BoundAssigned(Bound::Assigned { .. })))
3913 );
3914 }
3915 AddrAssignmentChangeExpect::Ignore => {
3916 assert_matches!(run_result, None, "fut should not finish when ignored.");
3917 }
3918 }
3919 }
3920
3921 #[test]
3922 fn waiting_to_restart() {
3923 let time = &FakeTimeController::new();
3924
3925 const WAITING_UNTIL: TestInstant = TestInstant(Duration::from_secs(10));
3926
3927 advance(time, Duration::from_secs(3));
3931
3932 let waiting = WaitingToRestart { waiting_until: WAITING_UNTIL };
3933 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3934 let main_fut = waiting.do_waiting_to_restart(time, &mut stop_receiver).fuse();
3935 let mut main_fut = pin!(main_fut);
3936 let mut executor = fasync::TestExecutor::new();
3937 let outcome = run_with_accelerated_time(&mut executor, time, &mut main_fut);
3938 assert_eq!(outcome, WaitingToRestartOutcome::Init(Init));
3939
3940 assert_eq!(time.now(), WAITING_UNTIL);
3941 }
3942
3943 #[test]
3944 fn bound_awaiting_assignment_times_out() {
3945 let time = &FakeTimeController::new();
3946 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3947 let (_server_end, packet_client_end) = FakeSocket::new_pair();
3948 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
3949 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
3950 let config = &test_client_config();
3951 let counters = Counters::default();
3952 let bound = Bound::AwaitingAssignment { lease_state: build_test_lease_state() };
3953 let main_fut = bound
3954 .do_bound(
3955 config,
3956 time,
3957 &mut stop_receiver,
3958 packet_socket_provider,
3959 address_event_receiver,
3960 &counters,
3961 )
3962 .fuse();
3963 let mut main_fut = pin!(main_fut);
3964 let mut executor = fasync::TestExecutor::new();
3965 let outcome = run_with_accelerated_time(&mut executor, time, &mut main_fut)
3966 .expect("do_bound should succeed");
3967 assert_eq!(outcome, BoundOutcome::Restart(Init::default()));
3968 assert_eq!(
3969 time.now(),
3970 TestInstant(Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()))
3971 );
3972 }
3973
3974 #[test_case(
3975 build_test_lease_state() =>
3976 TestInstant(Duration::from_secs(u64::from(DEFAULT_LEASE_LENGTH_SECONDS) / 2));
3977 "waits default renewal time when not specified")]
3978 #[test_case(
3979 LeaseState {
3980 renewal_time: Some(Duration::from_secs(10)),
3981 ..build_test_lease_state()
3982 } => TestInstant(Duration::from_secs(10));
3983 "waits specified renewal time")]
3984 fn bound_assigned_waits_for_renewal_time(lease_state: LeaseState<TestInstant>) -> TestInstant {
3985 let time = &FakeTimeController::new();
3986 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3987 let (_server_end, packet_client_end) = FakeSocket::new_pair();
3988 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
3989 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
3990 let config = &test_client_config();
3991 let counters = Counters::default();
3992 let bound = Bound::Assigned { lease_state };
3993 let main_fut = bound
3994 .do_bound(
3995 config,
3996 time,
3997 &mut stop_receiver,
3998 packet_socket_provider,
3999 address_event_receiver,
4000 &counters,
4001 )
4002 .fuse();
4003 let mut main_fut = pin!(main_fut);
4004 let mut executor = fasync::TestExecutor::new();
4005 let outcome = run_with_accelerated_time(&mut executor, time, &mut main_fut)
4006 .expect("do_bound should succeed");
4007 assert_eq!(outcome, BoundOutcome::Renewing(Renewing { lease_state: lease_state.clone() }));
4008 time.now()
4009 }
4010
4011 #[test_case(Bound::Assigned {lease_state: build_test_lease_state()}; "assigned")]
4012 #[test_case(Bound::AwaitingAssignment {lease_state: build_test_lease_state()};
4013 "awaiting_assignment")]
4014 fn bound_obeys_graceful_shutdown(bound: Bound<TestInstant>) {
4015 let time = &FakeTimeController::new();
4016 let (stop_sender, mut stop_receiver) = mpsc::unbounded();
4017 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4018 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4019 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4020 let config = &test_client_config();
4021 let counters = Counters::default();
4022 let bound_fut = bound
4023 .do_bound(
4024 &config,
4025 time,
4026 &mut stop_receiver,
4027 packet_socket_provider,
4028 address_event_receiver,
4029 &counters,
4030 )
4031 .fuse();
4032
4033 stop_sender.unbounded_send(()).expect("send should succeed");
4034 assert_eq!(
4035 bound_fut
4036 .now_or_never()
4037 .expect("should have completed")
4038 .expect("do_bound should succeed"),
4039 BoundOutcome::GracefulShutdown
4040 );
4041 }
4042
4043 fn build_test_renewing_state(
4044 lease_length: Duration,
4045 renewal_time: Option<Duration>,
4046 rebinding_time: Option<Duration>,
4047 ) -> Renewing<TestInstant> {
4048 Renewing {
4049 lease_state: build_test_lease_state_with_times(
4050 lease_length,
4051 renewal_time,
4052 rebinding_time,
4053 ),
4054 }
4055 }
4056
4057 #[test]
4058 fn do_renewing_obeys_graceful_shutdown() {
4059 initialize_logging();
4060 let counters = Counters::default();
4061
4062 let renewing = build_test_renewing_state(
4063 Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4064 None,
4065 None,
4066 );
4067 let client_config = &test_client_config();
4068
4069 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4070 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4071 let (_server_end, udp_client_end) = FakeSocket::new_pair();
4072 let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
4073 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4074 let (stop_sender, mut stop_receiver) = mpsc::unbounded();
4075 let time = &FakeTimeController::new();
4076
4077 let renewing_fut = renewing
4078 .do_renewing(
4079 client_config,
4080 udp_socket_provider,
4081 packet_socket_provider,
4082 time,
4083 &mut stop_receiver,
4084 address_event_receiver,
4085 &counters,
4086 )
4087 .fuse();
4088 let mut renewing_fut = pin!(renewing_fut);
4089
4090 let mut executor = fasync::TestExecutor::new();
4091 assert_matches!(executor.run_until_stalled(&mut renewing_fut), std::task::Poll::Pending);
4092
4093 stop_sender.unbounded_send(()).expect("sending stop signal should succeed");
4094
4095 let renewing_result = renewing_fut.now_or_never().expect(
4096 "renewing_fut should complete after single poll after stop signal has been sent",
4097 );
4098
4099 assert_matches!(renewing_result, Ok(RenewingOutcome::GracefulShutdown));
4100 }
4101
4102 #[track_caller]
4103 fn assert_outgoing_message_when_assigned_address(
4104 got_message: &dhcp_protocol::Message,
4105 fields: VaryingOutgoingMessageFields,
4106 ) {
4107 let VaryingOutgoingMessageFields { xid, options } = fields;
4108 let want_message = dhcp_protocol::Message {
4109 op: dhcp_protocol::OpCode::BOOTREQUEST,
4110 xid,
4111 secs: 0,
4112 bdcast_flag: false,
4113 ciaddr: YIADDR,
4114 yiaddr: Ipv4Addr::UNSPECIFIED,
4115 siaddr: Ipv4Addr::UNSPECIFIED,
4116 giaddr: Ipv4Addr::UNSPECIFIED,
4117 chaddr: TEST_MAC_ADDRESS,
4118 sname: BString::default(),
4119 file: BString::default(),
4120 options,
4121 };
4122 assert_eq!(got_message, &want_message);
4123 }
4124
4125 #[test]
4126 fn do_renewing_sends_requests() {
4127 initialize_logging();
4128
4129 const LEASE_LENGTH: Duration = Duration::from_secs(100000);
4131
4132 const RENEWAL_TIME: Duration = Duration::from_secs(0);
4134 const REBINDING_TIME: Duration = Duration::from_secs(1024);
4135
4136 let renewing =
4137 build_test_renewing_state(LEASE_LENGTH, Some(RENEWAL_TIME), Some(REBINDING_TIME));
4138 let client_config = &test_client_config();
4139
4140 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4141 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4142 let (udp_server_end, udp_client_end) = FakeSocket::new_pair();
4143 let (binds_sender, mut binds_receiver) = mpsc::unbounded();
4144 let udp_socket_provider =
4145 &FakeSocketProvider::new_with_events(udp_client_end, binds_sender);
4146 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4147 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4148 let time = &FakeTimeController::new();
4149 let counters = Counters::default();
4150 let renewing_fut = pin!(
4151 renewing
4152 .do_renewing(
4153 client_config,
4154 udp_socket_provider,
4155 packet_socket_provider,
4156 time,
4157 &mut stop_receiver,
4158 address_event_receiver,
4159 &counters
4160 )
4161 .fuse()
4162 );
4163
4164 let expected_times_requests_are_sent =
4167 [1024, 512, 256, 128, 64, 4].map(|time_remaining_when_request_is_sent| {
4168 Duration::from_secs(1024 - time_remaining_when_request_is_sent)
4169 });
4170
4171 let receive_fut =
4172 pin!(
4173 async {
4174 for expected_time in expected_times_requests_are_sent {
4175 let mut recv_buf = [0u8; BUFFER_SIZE];
4176 let DatagramInfo { length, address } = udp_server_end
4177 .recv_from(&mut recv_buf)
4178 .await
4179 .expect("recv_from on test socket should succeed");
4180
4181 assert_eq!(
4182 address,
4183 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
4184 SERVER_IP,
4185 SERVER_PORT.get()
4186 ))
4187 );
4188 assert_eq!(time.now(), TestInstant(expected_time));
4189 let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
4190 .expect("received packet should parse as DHCP message");
4191
4192 assert_outgoing_message_when_assigned_address(
4193 &msg,
4194 VaryingOutgoingMessageFields {
4195 xid: msg.xid,
4196 options: vec![
4197 dhcp_protocol::DhcpOption::DhcpMessageType(
4198 dhcp_protocol::MessageType::DHCPREQUEST,
4199 ),
4200 dhcp_protocol::DhcpOption::ParameterRequestList(
4201 test_requested_parameters()
4202 .iter_keys()
4203 .collect::<Vec<_>>()
4204 .try_into()
4205 .expect("should fit parameter request list size constraints"),
4206 ),
4207 ],
4208 },
4209 );
4210 }
4211 }
4212 .fuse()
4213 );
4214
4215 let main_future = async { join!(renewing_fut, receive_fut) };
4216 let mut main_future = pin!(main_future);
4217
4218 let mut executor = fasync::TestExecutor::new();
4219 let (requesting_result, ()) =
4220 run_with_accelerated_time(&mut executor, time, &mut main_future);
4221
4222 assert_matches!(requesting_result, Ok(RenewingOutcome::Rebinding(_)));
4223 assert_matches!(udp_server_end.recv_from(&mut []).now_or_never(), None);
4224
4225 let bound_socket_addr = binds_receiver
4226 .next()
4227 .now_or_never()
4228 .expect("should have completed")
4229 .expect("should be present");
4230 assert_eq!(
4231 bound_socket_addr,
4232 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(YIADDR, CLIENT_PORT.into()))
4233 );
4234 assert_eq!(
4235 counters.renewing.messaging.send_message.load(),
4236 expected_times_requests_are_sent.len()
4237 );
4238 assert_eq!(
4239 counters.renewing.messaging.recv_time_out.load(),
4240 expected_times_requests_are_sent.len()
4241 );
4242 }
4243
4244 #[derive(PartialEq, Debug)]
4245 struct RenewingTestResult {
4246 outcome: RenewingOutcome<TestInstant, FakeRemovedReason>,
4247 counters: RenewingTestCounters,
4248 }
4249
4250 #[derive(PartialEq, Eq, Debug, Default)]
4251 struct RenewingTestCounters {
4252 send_message: usize,
4253 recv_time_out: usize,
4254 recv_failed_dhcp_parse: usize,
4255 recv_wrong_xid: usize,
4256 recv_wrong_chaddr: usize,
4257 recv_message: usize,
4258 recv_nak: usize,
4259 recv_missing_option: usize,
4260 }
4261
4262 #[test_case(VaryingIncomingMessageFields {
4263 yiaddr: YIADDR,
4264 options: [
4265 dhcp_protocol::DhcpOption::DhcpMessageType(
4266 dhcp_protocol::MessageType::DHCPACK,
4267 ),
4268 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4269 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4270 DEFAULT_LEASE_LENGTH_SECONDS,
4271 ),
4272 ]
4273 .into_iter()
4274 .chain(test_parameter_values())
4275 .collect(),
4276 } => RenewingTestResult {
4277 outcome: RenewingOutcome::Renewed(LeaseState {
4278 discover_options: TEST_DISCOVER_OPTIONS,
4279 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
4280 .try_into()
4281 .expect("should be specified"),
4282 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
4283 .try_into()
4284 .expect("should be specified"),
4285 ip_address_lease_time: std::time::Duration::from_secs(
4286 DEFAULT_LEASE_LENGTH_SECONDS.into()
4287 ),
4288 renewal_time: None,
4289 rebinding_time: None,
4290 start_time: TestInstant(std::time::Duration::from_secs(0)),
4291 }, test_parameter_values().into_iter().collect()),
4292 counters: RenewingTestCounters {
4293 send_message: 1,
4294 recv_message: 1,
4295 ..Default::default()
4296 }
4297 }; "successfully renews after receiving DHCPACK")]
4298 #[test_case(VaryingIncomingMessageFields {
4299 yiaddr: YIADDR,
4300 options: [
4301 dhcp_protocol::DhcpOption::DhcpMessageType(
4302 dhcp_protocol::MessageType::DHCPACK,
4303 ),
4304 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4305 ]
4306 .into_iter()
4307 .chain(test_parameter_values())
4308 .collect(),
4309 } => RenewingTestResult {
4310 outcome: RenewingOutcome::Renewed(LeaseState {
4311 discover_options: TEST_DISCOVER_OPTIONS,
4312 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
4313 .try_into()
4314 .expect("should be specified"),
4315 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
4316 .try_into()
4317 .expect("should be specified"),
4318 ip_address_lease_time: std::time::Duration::from_secs(
4319 DEFAULT_LEASE_LENGTH_SECONDS.into()
4320 ),
4321 renewal_time: None,
4322 rebinding_time: None,
4323 start_time: TestInstant(std::time::Duration::from_secs(0)),
4324 }, test_parameter_values().into_iter().collect()),
4325 counters: RenewingTestCounters {
4326 send_message: 1,
4327 recv_message: 1,
4328 ..Default::default()
4329 }
4330 }; "successfully renews after receiving DHCPACK without lease time")]
4331 #[test_case(VaryingIncomingMessageFields {
4332 yiaddr: OTHER_ADDR,
4333 options: [
4334 dhcp_protocol::DhcpOption::DhcpMessageType(
4335 dhcp_protocol::MessageType::DHCPACK,
4336 ),
4337 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4338 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4339 DEFAULT_LEASE_LENGTH_SECONDS,
4340 ),
4341 ]
4342 .into_iter()
4343 .chain(test_parameter_values())
4344 .collect(),
4345 } => RenewingTestResult {
4346 outcome: RenewingOutcome::NewAddress(LeaseState {
4347 discover_options: TEST_DISCOVER_OPTIONS,
4348 yiaddr: net_types::ip::Ipv4Addr::from(OTHER_ADDR)
4349 .try_into()
4350 .expect("should be specified"),
4351 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
4352 .try_into()
4353 .expect("should be specified"),
4354 ip_address_lease_time: std::time::Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4355 renewal_time: None,
4356 rebinding_time: None,
4357 start_time: TestInstant(std::time::Duration::from_secs(0)),
4358 }, test_parameter_values().into_iter().collect()),
4359 counters: RenewingTestCounters {
4360 send_message: 1,
4361 recv_message: 1,
4362 ..Default::default()
4363 }
4364 }; "observes new address from DHCPACK")]
4365 #[test_case(VaryingIncomingMessageFields {
4366 yiaddr: YIADDR,
4367 options: [
4368 dhcp_protocol::DhcpOption::DhcpMessageType(
4369 dhcp_protocol::MessageType::DHCPACK,
4370 ),
4371 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4372 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4373 DEFAULT_LEASE_LENGTH_SECONDS,
4374 ),
4375 ]
4376 .into_iter()
4377 .chain(test_parameter_values_excluding_subnet_mask())
4378 .collect(),
4379 } => RenewingTestResult {
4380 outcome: RenewingOutcome::Rebinding(
4381 Rebinding {
4382 lease_state: build_test_lease_state()
4383 }),
4384 counters: RenewingTestCounters {
4385 send_message: 2,
4386 recv_time_out: 2,
4387 recv_message: 1,
4388 recv_missing_option: 1,
4389 ..Default::default()
4390 },
4391 }; "ignores replies lacking required option SubnetMask")]
4392 #[test_case(VaryingIncomingMessageFields {
4393 yiaddr: Ipv4Addr::UNSPECIFIED,
4394 options: [
4395 dhcp_protocol::DhcpOption::DhcpMessageType(
4396 dhcp_protocol::MessageType::DHCPNAK,
4397 ),
4398 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4399 dhcp_protocol::DhcpOption::Message(NAK_MESSAGE.to_owned()),
4400 ]
4401 .into_iter()
4402 .chain(test_parameter_values())
4403 .collect(),
4404 } => RenewingTestResult {
4405 outcome: RenewingOutcome::Nak(crate::parse::FieldsToRetainFromNak {
4406 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
4407 .try_into()
4408 .expect("should be specified"),
4409 message: Some(NAK_MESSAGE.to_owned()),
4410 client_identifier: None,
4411 }),
4412 counters: RenewingTestCounters {
4413 send_message: 1,
4414 recv_message: 1,
4415 recv_nak: 1,
4416 ..Default::default()
4417 },
4418 }; "transitions to Init after receiving DHCPNAK")]
4419 fn do_renewing_transitions_on_reply(
4420 incoming_message: VaryingIncomingMessageFields,
4421 ) -> RenewingTestResult {
4422 initialize_logging();
4423
4424 let renewing = build_test_renewing_state(
4425 Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4426 None,
4427 None,
4428 );
4429 let client_config = &test_client_config();
4430
4431 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4432 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4433 let (udp_server_end, udp_client_end) = FakeSocket::new_pair();
4434 let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
4435 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4436 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4437 let time = &FakeTimeController::new();
4438 let counters = Counters::default();
4439 let renewing_fut = pin!(
4440 renewing
4441 .do_renewing(
4442 client_config,
4443 udp_socket_provider,
4444 packet_socket_provider,
4445 time,
4446 &mut stop_receiver,
4447 address_event_receiver,
4448 &counters
4449 )
4450 .fuse()
4451 );
4452 let renewing_fut = pin!(renewing_fut);
4453
4454 let server_socket_addr =
4455 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(SERVER_IP, SERVER_PORT.into()));
4456
4457 let server_fut = pin!(
4458 async {
4459 let mut recv_buf = [0u8; BUFFER_SIZE];
4460
4461 let DatagramInfo { length, address } = udp_server_end
4462 .recv_from(&mut recv_buf)
4463 .await
4464 .expect("recv_from on test socket should succeed");
4465 assert_eq!(address, server_socket_addr);
4466
4467 let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
4468 .expect("received packet on test socket should parse as DHCP message");
4469
4470 assert_outgoing_message_when_assigned_address(
4471 &msg,
4472 VaryingOutgoingMessageFields {
4473 xid: msg.xid,
4474 options: vec![
4475 dhcp_protocol::DhcpOption::DhcpMessageType(
4476 dhcp_protocol::MessageType::DHCPREQUEST,
4477 ),
4478 dhcp_protocol::DhcpOption::ParameterRequestList(
4479 test_requested_parameters()
4480 .iter_keys()
4481 .collect::<Vec<_>>()
4482 .try_into()
4483 .expect("should fit parameter request list size constraints"),
4484 ),
4485 ],
4486 },
4487 );
4488
4489 let reply = build_incoming_message(msg.xid, incoming_message);
4490
4491 udp_server_end
4492 .send_to(
4493 &reply.serialize(),
4494 server_socket_addr,
4497 )
4498 .await
4499 .expect("send_to on test socket should succeed");
4500 }
4501 .fuse()
4502 );
4503
4504 let main_future = async move {
4505 let (renewing_result, ()) = join!(renewing_fut, server_fut);
4506 renewing_result
4507 }
4508 .fuse();
4509
4510 let mut main_future = pin!(main_future);
4511
4512 let mut executor = fasync::TestExecutor::new();
4513 let renewing_result = run_with_accelerated_time(&mut executor, time, &mut main_future);
4514
4515 let outcome = assert_matches!(renewing_result, Ok(outcome) => outcome);
4516 let counters = RenewingTestCounters {
4517 send_message: counters.renewing.messaging.send_message.load(),
4518 recv_time_out: counters.renewing.messaging.recv_time_out.load(),
4519 recv_failed_dhcp_parse: counters.renewing.messaging.recv_failed_dhcp_parse.load(),
4520 recv_wrong_xid: counters.renewing.messaging.recv_wrong_xid.load(),
4521 recv_wrong_chaddr: counters.renewing.messaging.recv_wrong_chaddr.load(),
4522 recv_message: counters.renewing.messaging.recv_message.load(),
4523 recv_nak: counters.renewing.recv_nak.load(),
4524 recv_missing_option: counters.renewing.recv_error.missing_required_option.load(),
4525 };
4526 RenewingTestResult { outcome, counters }
4527 }
4528
4529 fn build_test_rebinding_state(
4530 lease_length: Duration,
4531 renewal_time: Option<Duration>,
4532 rebinding_time: Option<Duration>,
4533 ) -> Rebinding<TestInstant> {
4534 Rebinding {
4535 lease_state: build_test_lease_state_with_times(
4536 lease_length,
4537 renewal_time,
4538 rebinding_time,
4539 ),
4540 }
4541 }
4542
4543 #[test]
4544 fn do_rebinding_sends_requests() {
4545 initialize_logging();
4546
4547 const RENEWAL_TIME: Duration = Duration::from_secs(0);
4549 const REBINDING_TIME: Duration = Duration::from_secs(0);
4550 const LEASE_LENGTH: Duration = Duration::from_secs(1024);
4551
4552 let rebinding =
4553 build_test_rebinding_state(LEASE_LENGTH, Some(RENEWAL_TIME), Some(REBINDING_TIME));
4554 let client_config = &test_client_config();
4555
4556 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4557 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4558 let (udp_server_end, udp_client_end) = FakeSocket::new_pair();
4559 let (binds_sender, mut binds_receiver) = mpsc::unbounded();
4560 let udp_socket_provider =
4561 &FakeSocketProvider::new_with_events(udp_client_end, binds_sender);
4562 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4563 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4564 let time = &FakeTimeController::new();
4565 let counters = Counters::default();
4566 let rebinding_fut = pin!(
4567 rebinding
4568 .do_rebinding(
4569 client_config,
4570 udp_socket_provider,
4571 packet_socket_provider,
4572 time,
4573 &mut stop_receiver,
4574 address_event_receiver,
4575 &counters
4576 )
4577 .fuse()
4578 );
4579
4580 let expected_times_requests_are_sent =
4583 [1024, 512, 256, 128, 64, 4].map(|time_remaining_when_request_is_sent| {
4584 Duration::from_secs(1024 - time_remaining_when_request_is_sent)
4585 });
4586
4587 let receive_fut =
4588 pin!(
4589 async {
4590 for expected_time in expected_times_requests_are_sent {
4591 let mut recv_buf = [0u8; BUFFER_SIZE];
4592 let DatagramInfo { length, address } = udp_server_end
4593 .recv_from(&mut recv_buf)
4594 .await
4595 .expect("recv_from on test socket should succeed");
4596
4597 assert_eq!(
4598 address,
4599 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
4600 std::net::Ipv4Addr::BROADCAST,
4601 SERVER_PORT.get()
4602 ))
4603 );
4604 assert_eq!(time.now(), TestInstant(expected_time));
4605 let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
4606 .expect("received packet should parse as DHCP message");
4607
4608 assert_outgoing_message_when_assigned_address(
4609 &msg,
4610 VaryingOutgoingMessageFields {
4611 xid: msg.xid,
4612 options: vec![
4613 dhcp_protocol::DhcpOption::DhcpMessageType(
4614 dhcp_protocol::MessageType::DHCPREQUEST,
4615 ),
4616 dhcp_protocol::DhcpOption::ParameterRequestList(
4617 test_requested_parameters()
4618 .iter_keys()
4619 .collect::<Vec<_>>()
4620 .try_into()
4621 .expect("should fit parameter request list size constraints"),
4622 ),
4623 ],
4624 },
4625 );
4626 }
4627 }
4628 .fuse()
4629 );
4630
4631 let main_future = async { join!(rebinding_fut, receive_fut) };
4632 let mut main_future = pin!(main_future);
4633
4634 let mut executor = fasync::TestExecutor::new();
4635 let (requesting_result, ()) =
4636 run_with_accelerated_time(&mut executor, time, &mut main_future);
4637
4638 assert_matches!(requesting_result, Ok(RebindingOutcome::TimedOut));
4639 assert_matches!(udp_server_end.recv_from(&mut []).now_or_never(), None);
4640
4641 let bound_socket_addr = binds_receiver
4642 .next()
4643 .now_or_never()
4644 .expect("should have completed")
4645 .expect("should be present");
4646 assert_eq!(
4647 bound_socket_addr,
4648 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(YIADDR, CLIENT_PORT.into()))
4649 );
4650 }
4651
4652 #[derive(PartialEq, Debug)]
4653 struct RebindingTestResult {
4654 outcome: RebindingOutcome<TestInstant, FakeRemovedReason>,
4655 counters: RebindingTestCounters,
4656 }
4657
4658 #[derive(PartialEq, Eq, Debug, Default)]
4659 struct RebindingTestCounters {
4660 send_message: usize,
4661 recv_time_out: usize,
4662 recv_failed_dhcp_parse: usize,
4663 recv_wrong_xid: usize,
4664 recv_wrong_chaddr: usize,
4665 recv_message: usize,
4666 recv_nak: usize,
4667 recv_missing_option: usize,
4668 }
4669
4670 #[test_case(VaryingIncomingMessageFields {
4671 yiaddr: YIADDR,
4672 options: [
4673 dhcp_protocol::DhcpOption::DhcpMessageType(
4674 dhcp_protocol::MessageType::DHCPACK,
4675 ),
4676 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4677 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4678 DEFAULT_LEASE_LENGTH_SECONDS,
4679 ),
4680 ]
4681 .into_iter()
4682 .chain(test_parameter_values())
4683 .collect(),
4684 } => RebindingTestResult {
4685 outcome: RebindingOutcome::Renewed(LeaseState {
4686 discover_options: TEST_DISCOVER_OPTIONS,
4687 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
4688 .try_into()
4689 .expect("should be specified"),
4690 server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
4691 .try_into()
4692 .expect("should be specified"),
4693 ip_address_lease_time: std::time::Duration::from_secs(
4694 DEFAULT_LEASE_LENGTH_SECONDS.into()
4695 ),
4696 renewal_time: None,
4697 rebinding_time: None,
4698 start_time: TestInstant(std::time::Duration::from_secs(0)),
4699 }, test_parameter_values().into_iter().collect()),
4700 counters: RebindingTestCounters {
4701 send_message: 1,
4702 recv_message: 1,
4703 ..Default::default()
4704 }
4705 }; "successfully rebinds after receiving DHCPACK")]
4706 #[test_case(VaryingIncomingMessageFields {
4707 yiaddr: YIADDR,
4708 options: [
4709 dhcp_protocol::DhcpOption::DhcpMessageType(
4710 dhcp_protocol::MessageType::DHCPACK,
4711 ),
4712 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4713 ]
4714 .into_iter()
4715 .chain(test_parameter_values())
4716 .collect(),
4717 } => RebindingTestResult {
4718 outcome: RebindingOutcome::Renewed(LeaseState {
4719 discover_options: TEST_DISCOVER_OPTIONS,
4720 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
4721 .try_into()
4722 .expect("should be specified"),
4723 server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
4724 .try_into()
4725 .expect("should be specified"),
4726 ip_address_lease_time: std::time::Duration::from_secs(
4727 DEFAULT_LEASE_LENGTH_SECONDS.into()
4728 ),
4729 renewal_time: None,
4730 rebinding_time: None,
4731 start_time: TestInstant(std::time::Duration::from_secs(0)),
4732 }, test_parameter_values().into_iter().collect()),
4733 counters: RebindingTestCounters {
4734 send_message: 1,
4735 recv_message: 1,
4736 ..Default::default()
4737 }
4738 }; "successfully rebinds after receiving DHCPACK without lease time")]
4739 #[test_case(VaryingIncomingMessageFields {
4740 yiaddr: OTHER_ADDR,
4741 options: [
4742 dhcp_protocol::DhcpOption::DhcpMessageType(
4743 dhcp_protocol::MessageType::DHCPACK,
4744 ),
4745 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4746 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4747 DEFAULT_LEASE_LENGTH_SECONDS,
4748 ),
4749 ]
4750 .into_iter()
4751 .chain(test_parameter_values())
4752 .collect(),
4753 } => RebindingTestResult {
4754 outcome: RebindingOutcome::NewAddress(LeaseState {
4755 discover_options: TEST_DISCOVER_OPTIONS,
4756 yiaddr: net_types::ip::Ipv4Addr::from(OTHER_ADDR)
4757 .try_into()
4758 .expect("should be specified"),
4759 server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
4760 .try_into()
4761 .expect("should be specified"),
4762 ip_address_lease_time: std::time::Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4763 renewal_time: None,
4764 rebinding_time: None,
4765 start_time: TestInstant(std::time::Duration::from_secs(0)),
4766 }, test_parameter_values().into_iter().collect()),
4767 counters: RebindingTestCounters {
4768 send_message: 1,
4769 recv_message: 1,
4770 ..Default::default()
4771 }
4772 } ; "observes new address from DHCPACK")]
4773 #[test_case(VaryingIncomingMessageFields {
4774 yiaddr: YIADDR,
4775 options: [
4776 dhcp_protocol::DhcpOption::DhcpMessageType(
4777 dhcp_protocol::MessageType::DHCPACK,
4778 ),
4779 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4780 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4781 DEFAULT_LEASE_LENGTH_SECONDS,
4782 ),
4783 ]
4784 .into_iter()
4785 .chain(test_parameter_values_excluding_subnet_mask())
4786 .collect(),
4787 } => RebindingTestResult {
4788 outcome: RebindingOutcome::TimedOut,
4789 counters: RebindingTestCounters {
4790 send_message: 2,
4791 recv_time_out: 2,
4792 recv_message: 1,
4793 recv_missing_option: 1,
4794 ..Default::default()
4795 }
4796 } ; "ignores replies lacking required option SubnetMask")]
4797 #[test_case(VaryingIncomingMessageFields {
4798 yiaddr: Ipv4Addr::UNSPECIFIED,
4799 options: [
4800 dhcp_protocol::DhcpOption::DhcpMessageType(
4801 dhcp_protocol::MessageType::DHCPNAK,
4802 ),
4803 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4804 dhcp_protocol::DhcpOption::Message(NAK_MESSAGE.to_owned()),
4805 ]
4806 .into_iter()
4807 .chain(test_parameter_values())
4808 .collect(),
4809 } => RebindingTestResult {
4810 outcome: RebindingOutcome::Nak(crate::parse::FieldsToRetainFromNak {
4811 server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
4812 .try_into()
4813 .expect("should be specified"),
4814 message: Some(NAK_MESSAGE.to_owned()),
4815 client_identifier: None,
4816 }),
4817 counters: RebindingTestCounters {
4818 send_message: 1,
4819 recv_message: 1,
4820 recv_nak: 1,
4821 ..Default::default()
4822 },
4823 } ; "transitions to Init after receiving DHCPNAK")]
4824 fn do_rebinding_transitions_on_reply(
4825 incoming_message: VaryingIncomingMessageFields,
4826 ) -> RebindingTestResult {
4827 initialize_logging();
4828
4829 let rebinding = build_test_rebinding_state(
4830 Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4831 None,
4832 None,
4833 );
4834 let client_config = &test_client_config();
4835
4836 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4837 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4838 let (udp_server_end, udp_client_end) = FakeSocket::new_pair();
4839 let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
4840 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4841 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4842 let time = &FakeTimeController::new();
4843 let counters = Counters::default();
4844 let rebinding_fut = pin!(
4845 rebinding
4846 .do_rebinding(
4847 client_config,
4848 udp_socket_provider,
4849 packet_socket_provider,
4850 time,
4851 &mut stop_receiver,
4852 address_event_receiver,
4853 &counters
4854 )
4855 .fuse()
4856 );
4857
4858 let server_socket_addr = std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
4859 OTHER_SERVER_IP,
4860 SERVER_PORT.into(),
4861 ));
4862
4863 let server_fut = pin!(
4864 async {
4865 let mut recv_buf = [0u8; BUFFER_SIZE];
4866
4867 let DatagramInfo { length, address } = udp_server_end
4868 .recv_from(&mut recv_buf)
4869 .await
4870 .expect("recv_from on test socket should succeed");
4871 assert_eq!(
4872 address,
4873 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
4874 std::net::Ipv4Addr::BROADCAST,
4875 SERVER_PORT.into()
4876 ))
4877 );
4878
4879 let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
4880 .expect("received packet on test socket should parse as DHCP message");
4881
4882 assert_outgoing_message_when_assigned_address(
4883 &msg,
4884 VaryingOutgoingMessageFields {
4885 xid: msg.xid,
4886 options: vec![
4887 dhcp_protocol::DhcpOption::DhcpMessageType(
4888 dhcp_protocol::MessageType::DHCPREQUEST,
4889 ),
4890 dhcp_protocol::DhcpOption::ParameterRequestList(
4891 test_requested_parameters()
4892 .iter_keys()
4893 .collect::<Vec<_>>()
4894 .try_into()
4895 .expect("should fit parameter request list size constraints"),
4896 ),
4897 ],
4898 },
4899 );
4900
4901 let reply = build_incoming_message(msg.xid, incoming_message);
4902
4903 udp_server_end
4904 .send_to(
4905 &reply.serialize(),
4906 server_socket_addr,
4909 )
4910 .await
4911 .expect("send_to on test socket should succeed");
4912 }
4913 .fuse()
4914 );
4915
4916 let main_future = async move {
4917 let (rebinding_result, ()) = join!(rebinding_fut, server_fut);
4918 rebinding_result
4919 }
4920 .fuse();
4921
4922 let mut main_future = pin!(main_future);
4923
4924 let mut executor = fasync::TestExecutor::new();
4925 let rebinding_result = run_with_accelerated_time(&mut executor, time, &mut main_future);
4926
4927 let outcome = assert_matches!(rebinding_result, Ok(outcome) => outcome);
4928 let counters = RebindingTestCounters {
4929 send_message: counters.rebinding.messaging.send_message.load(),
4930 recv_time_out: counters.rebinding.messaging.recv_time_out.load(),
4931 recv_failed_dhcp_parse: counters.rebinding.messaging.recv_failed_dhcp_parse.load(),
4932 recv_wrong_xid: counters.rebinding.messaging.recv_wrong_xid.load(),
4933 recv_wrong_chaddr: counters.rebinding.messaging.recv_wrong_chaddr.load(),
4934 recv_message: counters.rebinding.messaging.recv_message.load(),
4935 recv_nak: counters.rebinding.recv_nak.load(),
4936 recv_missing_option: counters.rebinding.recv_error.missing_required_option.load(),
4937 };
4938 RebindingTestResult { outcome, counters }
4939 }
4940
4941 struct FailingUdpSocketProvider;
4942
4943 impl deps::UdpSocketProvider for FailingUdpSocketProvider {
4944 type Sock = Rc<FakeSocket<std::net::SocketAddr>>;
4945 async fn bind_new_udp_socket(
4946 &self,
4947 _bound_addr: std::net::SocketAddr,
4948 ) -> Result<Self::Sock, deps::SocketError> {
4949 Err(deps::SocketError::AddrNotAvailable)
4950 }
4951 }
4952
4953 #[test_case(
4954 State::Renewing(build_test_renewing_state(Duration::from_secs(100), None, None));
4955 "renewing"
4956 )]
4957 #[test_case(
4958 State::Rebinding(build_test_rebinding_state(Duration::from_secs(100), None, None));
4959 "rebinding"
4960 )]
4961 fn return_address_removed_on_udp_socket_addr_not_available(state: State<TestInstant>) {
4962 initialize_logging();
4963 let counters = Counters::default();
4964 let client_config = &test_client_config();
4965 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4966 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4967 let udp_socket_provider = &FailingUdpSocketProvider;
4968 let address_event_receiver =
4969 futures::stream::once(async { AddressEvent::Removed(FakeRemovedReason) }).fuse();
4970 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4971 let time = &FakeTimeController::new();
4972 let mut rng = FakeRngProvider::new(0);
4973
4974 let mut executor = fasync::TestExecutor::new();
4975 let mut fut = pin!(state.run(
4976 client_config,
4977 packet_socket_provider,
4978 udp_socket_provider,
4979 &mut rng,
4980 time,
4981 &mut stop_receiver,
4982 address_event_receiver,
4983 &counters,
4984 ));
4985
4986 let result = executor.run_until_stalled(&mut fut);
4987 let result = assert_matches!(result, std::task::Poll::Ready(r) => r);
4988 let step = assert_matches!(result, Ok(s) => s);
4989 assert_matches!(step, Step::Exit(ExitReason::AddressRemoved(FakeRemovedReason)));
4990 }
4991}