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