1use crate::deps::{self, DatagramInfo, Instant as _, Socket as _};
8use crate::inspect::{
9 Counters, MessagingRelatedCounters, RebindingCounters, RenewingCounters, RequestingCounters,
10 SelectingCounters,
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.as_ref(),
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.as_ref(),
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.as_ref(),
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.as_ref(),
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.as_ref(),
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 fields
1484 })
1485 .map(|fields| {
1486 let fallback_lease_time = fields_from_offer_to_use_in_request
1493 .ip_address_lease_time_secs
1494 .map(|t| Duration::from_secs(t.get().into()));
1495 let (validated, soft_errors) =
1496 validate_response_to_request(fields, fallback_lease_time);
1497 soft_errors.increment(&messaging);
1498
1499 Some((src_addr, validated))
1500 })
1501 },
1502 *debug_log_prefix,
1503 messaging
1504 )
1505 .try_filter_map(|parse_result| {
1506 futures::future::ok(match parse_result {
1507 Ok(msg) => msg,
1508 Err(error) => {
1509 log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
1510 None
1511 }
1512 })
1513 })
1514 .fuse()
1515 );
1516
1517 let (src_addr, validated) = select_biased! {
1518 validated_result = ack_or_nak_stream.select_next_some() => {
1519 validated_result?
1520 },
1521 () = stop_receiver.select_next_some() => {
1522 return Ok(RequestingOutcome::GracefulShutdown)
1523 },
1524 send_requests_result = send_fut => {
1525 send_requests_result?;
1526 messaging.recv_time_out.increment();
1527 return Ok(RequestingOutcome::RanOutOfRetransmits)
1528 }
1529 };
1530
1531 match validated {
1532 ValidatedResponse::Ack { ack, lease_time, renewal_time, rebinding_time } => {
1533 log_ack(&debug_log_prefix, &src_addr, &ack);
1534 let FieldsToRetainFromAck {
1535 yiaddr,
1536 server_identifier,
1537 ip_address_lease_time_secs: _,
1538 renewal_time_value_secs: _,
1539 rebinding_time_value_secs: _,
1540 parameters,
1541 } = ack;
1542 let server_identifier = server_identifier.unwrap_or({
1543 let crate::parse::FieldsFromOfferToUseInRequest {
1544 server_identifier,
1545 ip_address_lease_time_secs: _,
1546 ip_address_to_request: _,
1547 } = fields_from_offer_to_use_in_request;
1548 *server_identifier
1549 });
1550
1551 match lease_time {
1552 RelativeTime::Explicit(_) => {}
1553 r @ (RelativeTime::Previous(_) | RelativeTime::Default(_)) => {
1554 messaging.recv_ack_no_addr_lease_time.increment();
1555 log::warn!(
1556 "{debug_log_prefix} accepting DHCPACK from {src_addr} that did \
1557 not provide an IP Address Lease Time. Using {r:?} instead."
1558 );
1559 }
1560 }
1561
1562 Ok(RequestingOutcome::Bound(
1563 LeaseState {
1564 discover_options: discover_options.clone(),
1565 yiaddr,
1566 server_identifier,
1567 ip_address_lease_time: lease_time,
1568 renewal_time,
1569 rebinding_time,
1570 start_time: *start_time,
1571 },
1572 parameters,
1573 ))
1574 }
1575 ValidatedResponse::Nak(nak) => {
1576 log_nak(&debug_log_prefix, &src_addr, &nak);
1577 recv_nak.increment();
1578 Ok(RequestingOutcome::Nak(nak))
1579 }
1580 }
1581 }
1582
1583 fn record(&self, inspector: &mut impl Inspector) {
1584 let Self { discover_options, start_time, fields_from_offer_to_use_in_request } = self;
1585 inspector.record_instant(diagnostics_traits::instant_property_name!("Start"), start_time);
1586 discover_options.record(inspector);
1587 fields_from_offer_to_use_in_request.record(inspector);
1588 }
1589}
1590
1591fn build_request_during_address_acquisition(
1592 client_config: &ClientConfig,
1593 discover_options: &DiscoverOptions,
1594 crate::parse::FieldsFromOfferToUseInRequest {
1595 server_identifier,
1596 ip_address_lease_time_secs,
1597 ip_address_to_request,
1598 }: &crate::parse::FieldsFromOfferToUseInRequest,
1599) -> dhcp_protocol::Message {
1600 let ClientConfig {
1601 client_hardware_address: _,
1602 client_identifier: _,
1603 requested_parameters: _,
1604 preferred_lease_time_secs,
1605 requested_ip_address: _,
1606 debug_log_prefix: _,
1607 } = client_config;
1608
1609 build_outgoing_message(
1618 client_config,
1619 discover_options,
1620 OutgoingOptions {
1621 ciaddr: None,
1622 requested_ip_address: Some(*ip_address_to_request),
1623 ip_address_lease_time_secs: ip_address_lease_time_secs.or(*preferred_lease_time_secs),
1624 message_type: dhcp_protocol::MessageType::DHCPREQUEST,
1625 server_identifier: Some(*server_identifier),
1626 include_parameter_request_list: true,
1627 },
1628 )
1629}
1630
1631#[derive(Debug, PartialEq)]
1633pub struct NewlyAcquiredLease<I> {
1634 pub ip_address: SpecifiedAddr<net_types::ip::Ipv4Addr>,
1636 pub start_time: I,
1638 pub lease_time: Duration,
1640 pub parameters: Vec<dhcp_protocol::DhcpOption>,
1644}
1645
1646#[derive(Debug, PartialEq)]
1647pub(crate) enum BoundOutcome<I, R> {
1648 GracefulShutdown,
1649 Renewing(Renewing<I>),
1650 Restart(Init),
1651 AddressRemoved(R),
1652 AddressRejected(WaitingToRestart<I>),
1653 Assigned(Bound<I>),
1654}
1655
1656#[derive(Debug, PartialEq, Clone, Copy)]
1660pub enum Bound<I> {
1661 AwaitingAssignment {
1664 lease_state: LeaseState<I>,
1666 },
1667 Assigned {
1670 lease_state: LeaseState<I>,
1672 },
1673}
1674
1675#[derive(Debug, PartialEq, Clone, Copy)]
1677pub struct LeaseState<I> {
1678 discover_options: DiscoverOptions,
1679 yiaddr: SpecifiedAddr<net_types::ip::Ipv4Addr>,
1680 server_identifier: SpecifiedAddr<net_types::ip::Ipv4Addr>,
1681 ip_address_lease_time: RelativeTime,
1682 start_time: I,
1683 renewal_time: RelativeTime,
1684 rebinding_time: RelativeTime,
1685}
1686
1687impl<I: deps::Instant> Bound<I> {
1688 async fn do_bound<C: deps::Clock<Instant = I>, R>(
1689 &self,
1690 client_config: &ClientConfig,
1691 time: &C,
1692 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1693 packet_socket_provider: &impl deps::PacketSocketProvider,
1694 address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
1695 _counters: &Counters,
1698 ) -> Result<BoundOutcome<I, R>, Error> {
1699 match self {
1700 Bound::AwaitingAssignment { lease_state } => {
1701 do_bound_awaiting_assignment(
1702 lease_state,
1703 client_config,
1704 time,
1705 stop_receiver,
1706 packet_socket_provider,
1707 address_event_receiver,
1708 )
1709 .await
1710 }
1711 Bound::Assigned { lease_state } => {
1712 do_bound_assigned(
1713 lease_state,
1714 client_config,
1715 time,
1716 stop_receiver,
1717 packet_socket_provider,
1718 address_event_receiver,
1719 )
1720 .await
1721 }
1722 }
1723 }
1724}
1725
1726async fn do_bound_awaiting_assignment<I: deps::Instant, C: deps::Clock<Instant = I>, R>(
1727 lease_state: &LeaseState<I>,
1728 client_config: &ClientConfig,
1729 time: &C,
1730 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1731 packet_socket_provider: &impl deps::PacketSocketProvider,
1732 address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
1733) -> Result<BoundOutcome<I, R>, Error> {
1734 let LeaseState {
1735 discover_options: _,
1736 yiaddr,
1737 server_identifier: _,
1738 ip_address_lease_time,
1739 start_time,
1740 renewal_time: _,
1741 rebinding_time: _,
1742 } = lease_state;
1743
1744 let lease_timeout_fut = time.wait_until(start_time.add(*ip_address_lease_time.as_ref())).fuse();
1754 let mut lease_timeout_fut = pin!(lease_timeout_fut);
1755
1756 let mut address_removed_or_assigned = pin!(address_event_receiver.filter_map(async |event| {
1757 match event {
1758 AddressEvent::Rejected => {
1759 handle_address_rejection(client_config, lease_state, packet_socket_provider, time)
1760 .map(|result| Some(result.map(BoundOutcome::AddressRejected)))
1761 .await
1762 }
1763 AddressEvent::Removed(reason) => Some(Ok(BoundOutcome::AddressRemoved(reason))),
1764 AddressEvent::AssignmentStateChanged(new_state) => {
1765 match new_state {
1766 AddressAssignmentState::Assigned => {
1767 Some(Ok(BoundOutcome::Assigned(Bound::Assigned {
1768 lease_state: lease_state.clone(),
1769 })))
1770 }
1771 s @ AddressAssignmentState::Tentative
1774 | s @ AddressAssignmentState::Unavailable => {
1775 log::warn!(
1776 "{yiaddr} address state became {s:?} during \
1777 Bound::AwaitingAssignment; ignoring"
1778 );
1779 None
1780 }
1781 }
1782 }
1783 }
1784 }));
1785
1786 let debug_log_prefix = &client_config.debug_log_prefix;
1787 select! {
1788 () = lease_timeout_fut => {
1792 log::info!(
1793 "{debug_log_prefix} Returning to Init due to failing to observe address \
1794 assignment for {yiaddr} before the lease ({ip_address_lease_time:?}) \
1795 expired."
1796 );
1797 Ok(BoundOutcome::Restart(Init::default()))
1798 },
1799 () = stop_receiver.select_next_some() => Ok(BoundOutcome::GracefulShutdown),
1800 result = address_removed_or_assigned.next() => {
1801 result.unwrap_or(Err(Error::AddressEventReceiverEnded))
1802 }
1803 }
1804}
1805
1806async fn do_bound_assigned<I: deps::Instant, C: deps::Clock<Instant = I>, R>(
1807 lease_state: &LeaseState<I>,
1808 client_config: &ClientConfig,
1809 time: &C,
1810 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1811 packet_socket_provider: &impl deps::PacketSocketProvider,
1812 address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
1813) -> Result<BoundOutcome<I, R>, Error> {
1814 let LeaseState {
1815 discover_options: _,
1816 yiaddr,
1817 server_identifier,
1818 ip_address_lease_time,
1819 start_time,
1820 renewal_time,
1821 rebinding_time: _,
1822 } = lease_state;
1823
1824 let renewal_time = *renewal_time.as_ref();
1825
1826 let debug_log_prefix = &client_config.debug_log_prefix;
1827 log::info!(
1828 "{debug_log_prefix} In Bound state; ip_address_lease_time = {}, \
1829 renewal_time = {}, server_identifier = {server_identifier}",
1830 ip_address_lease_time.as_ref().as_secs(),
1831 renewal_time.as_secs(),
1832 );
1833
1834 let renewal_timeout_fut = time.wait_until(start_time.add(renewal_time)).fuse();
1835 let mut renewal_timeout_fut = pin!(renewal_timeout_fut);
1836
1837 let mut address_removed = pin!(address_event_receiver.filter_map(async |event| match event {
1838 AddressEvent::Rejected => {
1839 handle_address_rejection(client_config, lease_state, packet_socket_provider, time)
1840 .map(|result| Some(result.map(BoundOutcome::AddressRejected)))
1841 .await
1842 }
1843 AddressEvent::Removed(reason) => Some(Ok(BoundOutcome::AddressRemoved(reason))),
1844 AddressEvent::AssignmentStateChanged(new_state) => {
1845 log::warn!(
1848 "{yiaddr} address state became {new_state:?} during Bound::Assigned; ignoring"
1849 );
1850 None
1851 }
1852 }));
1853
1854 select! {
1855 () = renewal_timeout_fut => Ok(BoundOutcome::Renewing(Renewing {
1856 lease_state: lease_state.clone()
1857 })),
1858
1859 () = stop_receiver.select_next_some() => Ok(BoundOutcome::GracefulShutdown),
1860 result = address_removed.next() => result.unwrap_or(Err(Error::AddressEventReceiverEnded)),
1861 }
1862}
1863
1864impl<I: deps::Instant> LeaseState<I> {
1865 fn record(&self, inspector: &mut impl Inspector) {
1866 let Self {
1867 discover_options,
1868 yiaddr,
1869 server_identifier,
1870 ip_address_lease_time,
1871 start_time,
1872 renewal_time,
1873 rebinding_time,
1874 } = self;
1875 inspector.record_instant(diagnostics_traits::instant_property_name!("Start"), start_time);
1876 discover_options.record(inspector);
1877 inspector.record_ip_addr("Yiaddr", **yiaddr);
1878 inspector.record_ip_addr("ServerIdentifier", **server_identifier);
1879 ip_address_lease_time.record(
1880 inspector,
1881 "IpAddressLeaseTimeSecs",
1882 "IpAddressLeaseTimeSource",
1883 );
1884 renewal_time.record(inspector, "RenewalTimeSecs", "RenewalTimeSource");
1885 rebinding_time.record(inspector, "RebindingTimeSecs", "RebindingTimeSource");
1886 }
1887}
1888
1889#[derive(Debug, PartialEq)]
1890pub(crate) enum RenewingOutcome<I, R> {
1891 GracefulShutdown,
1892 Renewed(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
1893 NewAddress(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
1900 Nak(FieldsToRetainFromNak),
1901 Rebinding(Rebinding<I>),
1902 AddressRemoved(R),
1903 AddressRejected(WaitingToRestart<I>),
1904}
1905
1906#[derive(Debug, PartialEq, Clone, Copy)]
1910pub struct Renewing<I> {
1911 lease_state: LeaseState<I>,
1912}
1913
1914const RENEW_RETRANSMIT_MINIMUM_DELAY: Duration = Duration::from_secs(60);
1921
1922impl<I: deps::Instant> Renewing<I> {
1923 async fn do_renewing<C: deps::Clock<Instant = I>, R>(
1924 &self,
1925 client_config: &ClientConfig,
1926 udp_socket_provider: &impl deps::UdpSocketProvider,
1930 packet_socket_provider: &impl deps::PacketSocketProvider,
1931 time: &C,
1932 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1933 address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
1934 counters: &Counters,
1935 ) -> Result<RenewingOutcome<I, R>, Error> {
1936 let Counters { renewing: RenewingCounters { messaging, recv_error, recv_nak, .. }, .. } =
1937 counters;
1938 let renewal_start_time = time.now();
1939 let debug_log_prefix = client_config.debug_log_prefix;
1940
1941 let Self {
1942 lease_state:
1943 lease_state @ LeaseState {
1944 discover_options,
1945 yiaddr,
1946 server_identifier,
1947 ip_address_lease_time,
1948 start_time,
1949 renewal_time: _,
1950 rebinding_time,
1951 },
1952 } = self;
1953 let rebinding_time = *rebinding_time.as_ref();
1954 let mut address_removed =
1955 pin!(address_event_receiver.filter_map(async |event| match event {
1956 AddressEvent::Rejected => {
1957 handle_address_rejection(
1958 client_config,
1959 lease_state,
1960 packet_socket_provider,
1961 time,
1962 )
1963 .map(|result| Some(result.map(RenewingOutcome::AddressRejected)))
1964 .await
1965 }
1966 AddressEvent::Removed(reason) => {
1967 Some(Ok(RenewingOutcome::AddressRemoved(reason)))
1968 }
1969 AddressEvent::AssignmentStateChanged(new_state) => {
1970 log::warn!(
1973 "{yiaddr} address state became {new_state:?} during Renewing; ignoring"
1974 );
1975 None
1976 }
1977 }));
1978
1979 let socket = match udp_socket_provider
1980 .bind_new_udp_socket(std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
1981 yiaddr.get().into(),
1982 CLIENT_PORT.get(),
1983 )))
1984 .await
1985 {
1986 Ok(s) => s,
1987 Err(e @ deps::SocketError::AddrNotAvailable) => {
1988 if let Some(Some(result)) = address_removed.next().now_or_never() {
1992 return result;
1993 }
1994 return Err(Error::Socket(e));
1995 }
1996 Err(e) => return Err(Error::Socket(e)),
1997 };
1998
1999 let message = build_outgoing_message(
2008 client_config,
2009 discover_options,
2010 OutgoingOptions {
2011 ciaddr: Some(*yiaddr),
2012 requested_ip_address: None,
2013 ip_address_lease_time_secs: client_config.preferred_lease_time_secs,
2014 message_type: dhcp_protocol::MessageType::DHCPREQUEST,
2015 server_identifier: None,
2016 include_parameter_request_list: true,
2017 },
2018 );
2019 let message_bytes = message.serialize();
2020
2021 let t2 = start_time.add(rebinding_time);
2022 let server_sockaddr = std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
2023 server_identifier.get().into(),
2024 SERVER_PORT.get(),
2025 ));
2026 let mut send_fut = pin!(
2027 send_with_retransmits_at_instants(
2028 time,
2029 std::iter::from_fn(|| {
2030 let now = time.now();
2031 let half_time_until_t2 = now.average(t2);
2032 Some(half_time_until_t2.max(now.add(RENEW_RETRANSMIT_MINIMUM_DELAY)))
2033 }),
2034 message_bytes.as_ref(),
2035 &socket,
2036 server_sockaddr,
2037 debug_log_prefix,
2038 messaging
2039 )
2040 .fuse()
2041 );
2042
2043 let mut recv_buf = [0u8; BUFFER_SIZE];
2044 let mut responses_stream = pin!(
2045 recv_stream(
2046 &socket,
2047 &mut recv_buf,
2048 |packet, addr| {
2049 if addr != server_sockaddr {
2050 return Err(anyhow::Error::from(crate::parse::ParseError::WrongSource(
2051 addr,
2052 )));
2053 }
2054 let message = dhcp_protocol::Message::from_buffer(packet)
2055 .map_err(crate::parse::ParseError::Dhcp)
2056 .context("error while parsing DHCP message from UDP datagram")
2057 .inspect_err(|_| messaging.recv_failed_dhcp_parse.increment())?;
2058 validate_message(discover_options, client_config, &message)
2059 .inspect_err(|e| e.increment(&messaging))
2060 .context("invalid DHCP message")?;
2061 crate::parse::fields_to_retain_from_response_to_request(
2062 &client_config.requested_parameters,
2063 message,
2064 )
2065 .inspect_err(|e| recv_error.increment(e))
2066 .context("error extracting needed fields from DHCP message during Renewing")
2067 .map(|(fields, soft_errors)| {
2068 messaging.increment_soft_errors(soft_errors);
2069 fields
2070 })
2071 .map(|fields| {
2072 let (validated, soft_errors) = validate_response_to_request(
2073 fields,
2074 Some(*ip_address_lease_time.as_ref()),
2080 );
2081 soft_errors.increment(&messaging);
2082
2083 (addr, validated)
2084 })
2085 },
2086 debug_log_prefix,
2087 messaging
2088 )
2089 .try_filter_map(|parse_result| {
2090 futures::future::ok(match parse_result {
2091 Ok(msg) => Some(msg),
2092 Err(error) => {
2093 log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
2094 None
2095 }
2096 })
2097 })
2098 .fuse()
2099 );
2100
2101 let mut timeout_fut = pin!(time.wait_until(t2).fuse());
2102
2103 let (src_addr, validated) = select_biased! {
2104 response = responses_stream.select_next_some() => {
2105 response?
2106 },
2107 () = stop_receiver.select_next_some() => return Ok(RenewingOutcome::GracefulShutdown),
2108 result = address_removed.next() => {
2109 return result.unwrap_or(Err(Error::AddressEventReceiverEnded))
2110 }
2111 send_result = send_fut => {
2112 return Err(send_result.expect_err("send_fut should never complete without error"))
2113 },
2114 () = timeout_fut => {
2115 messaging.recv_time_out.increment();
2116 return Ok(RenewingOutcome::Rebinding(
2117 Rebinding { lease_state: lease_state.clone() }
2118 ))
2119 }
2120 };
2121
2122 match validated {
2123 ValidatedResponse::Ack { ack, lease_time, renewal_time, rebinding_time } => {
2124 log_ack(&debug_log_prefix, &src_addr, &ack);
2125 let FieldsToRetainFromAck {
2126 yiaddr: new_yiaddr,
2127 server_identifier: _,
2128 ip_address_lease_time_secs: _,
2129 renewal_time_value_secs: _,
2130 rebinding_time_value_secs: _,
2131 parameters,
2132 } = ack;
2133 let variant = if new_yiaddr == *yiaddr {
2134 RenewingOutcome::Renewed
2135 } else {
2136 log::info!(
2137 "{debug_log_prefix} obtained different address from renewal: {}",
2138 new_yiaddr
2139 );
2140 RenewingOutcome::NewAddress
2141 };
2142
2143 match lease_time {
2144 RelativeTime::Explicit(_) => {}
2145 r @ (RelativeTime::Previous(_) | RelativeTime::Default(_)) => {
2146 messaging.recv_ack_no_addr_lease_time.increment();
2147 log::warn!(
2148 "{debug_log_prefix} accepting DHCPACK from {src_addr} that did \
2149 not provide an IP Address Lease Time. Using {r:?} instead."
2150 );
2151 }
2152 }
2153
2154 Ok(variant(
2155 LeaseState {
2156 discover_options: discover_options.clone(),
2157 yiaddr: new_yiaddr,
2158 server_identifier: *server_identifier,
2159 ip_address_lease_time: lease_time,
2160 renewal_time,
2161 rebinding_time,
2162 start_time: renewal_start_time,
2163 },
2164 parameters,
2165 ))
2166 }
2167 ValidatedResponse::Nak(nak) => {
2168 log_nak(&debug_log_prefix, &src_addr, &nak);
2169 recv_nak.increment();
2170 Ok(RenewingOutcome::Nak(nak))
2171 }
2172 }
2173 }
2174}
2175
2176#[derive(Debug, PartialEq)]
2178pub struct LeaseRenewal<I> {
2179 pub start_time: I,
2181 pub lease_time: Duration,
2183 pub parameters: Vec<dhcp_protocol::DhcpOption>,
2187}
2188
2189#[derive(Debug, PartialEq, Clone, Copy)]
2194pub struct Rebinding<I> {
2195 lease_state: LeaseState<I>,
2196}
2197
2198#[derive(Debug, PartialEq)]
2199pub(crate) enum RebindingOutcome<I, R> {
2200 GracefulShutdown,
2201 Renewed(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
2202 NewAddress(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
2209 Nak(FieldsToRetainFromNak),
2210 TimedOut,
2211 AddressRemoved(R),
2212 AddressRejected(WaitingToRestart<I>),
2213}
2214
2215impl<I: deps::Instant> Rebinding<I> {
2216 async fn do_rebinding<C: deps::Clock<Instant = I>, R>(
2217 &self,
2218 client_config: &ClientConfig,
2219 udp_socket_provider: &impl deps::UdpSocketProvider,
2223 packet_socket_provider: &impl deps::PacketSocketProvider,
2224 time: &C,
2225 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
2226 address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
2227 counters: &Counters,
2228 ) -> Result<RebindingOutcome<I, R>, Error> {
2229 let Counters {
2230 rebinding: RebindingCounters { messaging, recv_error, recv_nak, .. }, ..
2231 } = counters;
2232 let rebinding_start_time = time.now();
2233 let debug_log_prefix = client_config.debug_log_prefix;
2234
2235 let Self {
2236 lease_state:
2237 lease_state @ LeaseState {
2238 discover_options,
2239 yiaddr,
2240 server_identifier: _,
2241 ip_address_lease_time,
2242 start_time,
2243 renewal_time: _,
2244 rebinding_time: _,
2245 },
2246 } = self;
2247 let mut address_removed =
2248 pin!(address_event_receiver.filter_map(async |event| match event {
2249 AddressEvent::Rejected => {
2250 handle_address_rejection(
2251 client_config,
2252 lease_state,
2253 packet_socket_provider,
2254 time,
2255 )
2256 .map(|result| Some(result.map(RebindingOutcome::AddressRejected)))
2257 .await
2258 }
2259 AddressEvent::Removed(reason) => {
2260 Some(Ok(RebindingOutcome::AddressRemoved(reason)))
2261 }
2262 AddressEvent::AssignmentStateChanged(new_state) => {
2263 log::warn!(
2266 "{yiaddr} address state became {new_state:?} during Rebinding; ignoring"
2267 );
2268 None
2269 }
2270 }));
2271
2272 let socket = match udp_socket_provider
2273 .bind_new_udp_socket(std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
2274 yiaddr.get().into(),
2275 CLIENT_PORT.get(),
2276 )))
2277 .await
2278 {
2279 Ok(s) => s,
2280 Err(e @ deps::SocketError::AddrNotAvailable) => {
2281 if let Some(Some(result)) = address_removed.next().now_or_never() {
2285 return result;
2286 }
2287 return Err(Error::Socket(e));
2288 }
2289 Err(e) => return Err(Error::Socket(e)),
2290 };
2291
2292 let message = build_outgoing_message(
2301 client_config,
2302 discover_options,
2303 OutgoingOptions {
2304 ciaddr: Some(*yiaddr),
2305 requested_ip_address: None,
2306 ip_address_lease_time_secs: client_config.preferred_lease_time_secs,
2307 message_type: dhcp_protocol::MessageType::DHCPREQUEST,
2308 server_identifier: None,
2309 include_parameter_request_list: true,
2310 },
2311 );
2312 let message_bytes = message.serialize();
2313
2314 let lease_expiry = start_time.add(*ip_address_lease_time.as_ref());
2315 let server_sockaddr = std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
2316 Ipv4Addr::BROADCAST,
2317 SERVER_PORT.get(),
2318 ));
2319 let mut send_fut = pin!(
2320 send_with_retransmits_at_instants(
2321 time,
2322 std::iter::from_fn(|| {
2323 let now = time.now();
2324 let half_time_until_lease_expiry = now.average(lease_expiry);
2325 Some(half_time_until_lease_expiry.max(now.add(RENEW_RETRANSMIT_MINIMUM_DELAY)))
2326 }),
2327 message_bytes.as_ref(),
2328 &socket,
2329 server_sockaddr,
2330 debug_log_prefix,
2331 messaging
2332 )
2333 .fuse()
2334 );
2335
2336 let mut recv_buf = [0u8; BUFFER_SIZE];
2337 let mut responses_stream = pin!(
2338 recv_stream(
2339 &socket,
2340 &mut recv_buf,
2341 |packet, addr| {
2342 let message = dhcp_protocol::Message::from_buffer(packet)
2343 .map_err(crate::parse::ParseError::Dhcp)
2344 .context("error while parsing DHCP message from UDP datagram")
2345 .inspect_err(|_| messaging.recv_failed_dhcp_parse.increment())?;
2346 validate_message(discover_options, client_config, &message)
2347 .inspect_err(|e| e.increment(&messaging))
2348 .context("invalid DHCP message")?;
2349 crate::parse::fields_to_retain_from_response_to_request(
2350 &client_config.requested_parameters,
2351 message,
2352 )
2353 .map(|(response, soft_errors)| {
2354 messaging.increment_soft_errors(soft_errors);
2355 response
2356 })
2357 .and_then(|response| match response {
2358 crate::parse::IncomingResponseToRequest::Ack(ack) => {
2359 Ok(crate::parse::IncomingResponseToRequest::Ack(
2363 ack.map_server_identifier(|server_identifier| {
2364 server_identifier.ok_or(
2365 crate::parse::IncomingResponseToRequestError::NoServerIdentifier,
2366 )
2367 })?,
2368 ))
2369 }
2370 crate::parse::IncomingResponseToRequest::Nak(nak) => {
2371 Ok(crate::parse::IncomingResponseToRequest::Nak(nak))
2372 }
2373 })
2374 .inspect_err(|e| recv_error.increment(e))
2375 .context("error extracting needed fields from DHCP message during Rebinding")
2376 .map(|response| {
2377 let (validated, soft_errors) = validate_response_to_request(
2378 response,
2379 Some(*ip_address_lease_time.as_ref()),
2385 );
2386 soft_errors.increment(&messaging);
2387 (addr, validated)
2388 })
2389 },
2390 debug_log_prefix,
2391 messaging
2392 )
2393 .try_filter_map(|parse_result| {
2394 futures::future::ok(match parse_result {
2395 Ok(msg) => Some(msg),
2396 Err(error) => {
2397 log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
2398 None
2399 }
2400 })
2401 })
2402 .fuse()
2403 );
2404
2405 let mut timeout_fut = pin!(time.wait_until(lease_expiry).fuse());
2406
2407 let (src_addr, validated) = select_biased! {
2408 response = responses_stream.select_next_some() => {
2409 response?
2410 },
2411 () = stop_receiver.select_next_some() => return Ok(RebindingOutcome::GracefulShutdown),
2412 result = address_removed.next() => {
2413 return result.unwrap_or(Err(Error::AddressEventReceiverEnded))
2414 }
2415 send_result = send_fut => {
2416 return Err(send_result.expect_err("send_fut should never complete without error"))
2417 },
2418 () = timeout_fut => {
2419 messaging.recv_time_out.increment();
2420 return Ok(RebindingOutcome::TimedOut)
2421 }
2422 };
2423
2424 match validated {
2425 ValidatedResponse::Ack { ack, lease_time, renewal_time, rebinding_time } => {
2426 log_ack(&debug_log_prefix, &src_addr, &ack);
2427 let FieldsToRetainFromAck {
2428 yiaddr: new_yiaddr,
2429 server_identifier,
2430 ip_address_lease_time_secs: _,
2431 renewal_time_value_secs: _,
2432 rebinding_time_value_secs: _,
2433 parameters,
2434 } = ack;
2435 let variant = if new_yiaddr == *yiaddr {
2436 RebindingOutcome::Renewed
2437 } else {
2438 log::info!(
2439 "{debug_log_prefix} obtained different address from rebinding: {}",
2440 new_yiaddr
2441 );
2442 RebindingOutcome::NewAddress
2443 };
2444
2445 match lease_time {
2446 RelativeTime::Explicit(_) => {}
2447 r @ (RelativeTime::Previous(_) | RelativeTime::Default(_)) => {
2448 messaging.recv_ack_no_addr_lease_time.increment();
2449 log::warn!(
2450 "{debug_log_prefix} accepting DHCPACK from {src_addr} that did \
2451 not provide an IP Address Lease Time. Using {r:?} instead."
2452 );
2453 }
2454 }
2455
2456 Ok(variant(
2457 LeaseState {
2458 discover_options: discover_options.clone(),
2459 yiaddr: new_yiaddr,
2460 server_identifier,
2461 ip_address_lease_time: lease_time,
2462 renewal_time,
2463 rebinding_time,
2464 start_time: rebinding_start_time,
2465 },
2466 parameters,
2467 ))
2468 }
2469 ValidatedResponse::Nak(nak) => {
2470 log_nak(&debug_log_prefix, &src_addr, &nak);
2471 recv_nak.increment();
2472 Ok(RebindingOutcome::Nak(nak))
2473 }
2474 }
2475 }
2476}
2477
2478#[derive(Debug, PartialEq, Clone, Copy)]
2481pub(crate) enum RelativeTime {
2482 Explicit(Duration),
2484 Previous(Duration),
2486 Default(Duration),
2488}
2489
2490impl AsRef<Duration> for RelativeTime {
2491 fn as_ref(&self) -> &Duration {
2492 match self {
2493 RelativeTime::Explicit(d) | RelativeTime::Previous(d) | RelativeTime::Default(d) => d,
2494 }
2495 }
2496}
2497
2498#[derive(Debug, PartialEq)]
2499pub(crate) enum ValidatedResponse<S> {
2500 Ack {
2501 ack: crate::parse::FieldsToRetainFromAck<S>,
2502 lease_time: RelativeTime,
2503 renewal_time: RelativeTime,
2504 rebinding_time: RelativeTime,
2505 },
2506 Nak(crate::parse::FieldsToRetainFromNak),
2507}
2508
2509impl RelativeTime {
2510 fn record(&self, inspector: &mut impl Inspector, value_key: &str, source_key: &str) {
2511 inspector.record_uint(value_key, self.as_ref().as_secs());
2512 match self {
2513 RelativeTime::Explicit(_) => {}
2514 RelativeTime::Previous(_) => {
2515 inspector.record_str(source_key, "Previous");
2516 }
2517 RelativeTime::Default(_) => {
2518 inspector.record_str(source_key, "Default");
2519 }
2520 }
2521 }
2522}
2523
2524fn default_renewal_time(lease_time: Duration) -> RelativeTime {
2528 RelativeTime::Default(lease_time / 2)
2529}
2530
2531fn default_rebinding_time(lease_time: Duration) -> RelativeTime {
2535 RelativeTime::Default(lease_time / 8 * 7)
2536}
2537
2538#[derive(Default)]
2539struct ResponseValidationSoftErrors {
2540 t1_outlives_t2: bool,
2541 t2_outlives_lease: bool,
2542}
2543
2544impl ResponseValidationSoftErrors {
2545 fn increment(&self, counters: &MessagingRelatedCounters) {
2546 if self.t1_outlives_t2 {
2547 counters.recv_ack_renewal_time_after_rebinding_time.increment();
2548 }
2549 if self.t2_outlives_lease {
2550 counters.recv_ack_rebinding_time_outlives_lease.increment();
2551 }
2552 }
2553}
2554
2555fn validate_response_to_request<S>(
2561 response: crate::parse::IncomingResponseToRequest<S>,
2562 fallback_lease_time: Option<Duration>,
2563) -> (ValidatedResponse<S>, ResponseValidationSoftErrors) {
2564 let mut soft_errors = ResponseValidationSoftErrors::default();
2565 match response {
2566 crate::parse::IncomingResponseToRequest::Ack(ack) => {
2567 let lease_time = match ack.ip_address_lease_time_secs {
2568 Some(t) => RelativeTime::Explicit(Duration::from_secs(t.get().into())),
2569 None => match fallback_lease_time {
2570 Some(t) => RelativeTime::Previous(t),
2571 None => RelativeTime::Default(DEFAULT_LEASE_TIME),
2572 },
2573 };
2574 let mut t1 = ack
2575 .renewal_time_value_secs
2576 .map(|t| RelativeTime::Explicit(Duration::from_secs(t.into())))
2577 .unwrap_or_else(|| default_renewal_time(*lease_time.as_ref()));
2578 let mut t2 = ack
2579 .rebinding_time_value_secs
2580 .map(|t| RelativeTime::Explicit(Duration::from_secs(t.into())))
2581 .unwrap_or_else(|| default_rebinding_time(*lease_time.as_ref()));
2582 if t1.as_ref() >= t2.as_ref() {
2590 soft_errors.t1_outlives_t2 = true;
2591 }
2592 if t2.as_ref() >= lease_time.as_ref() {
2593 soft_errors.t2_outlives_lease = true;
2594 }
2595 if soft_errors.t1_outlives_t2 || soft_errors.t2_outlives_lease {
2596 if !matches!(t1, RelativeTime::Default(_)) {
2597 t1 = default_renewal_time(*lease_time.as_ref());
2598 }
2599 if !matches!(t2, RelativeTime::Default(_)) {
2600 t2 = default_rebinding_time(*lease_time.as_ref());
2601 }
2602 }
2603 (
2604 ValidatedResponse::Ack { ack, lease_time, renewal_time: t1, rebinding_time: t2 },
2605 soft_errors,
2606 )
2607 }
2608 crate::parse::IncomingResponseToRequest::Nak(nak) => {
2609 (ValidatedResponse::Nak(nak), soft_errors)
2610 }
2611 }
2612}
2613
2614fn log_offer(
2615 prefix: &DebugLogPrefix,
2616 src_addr: &net_types::ip::Ipv4Addr,
2617 offer_fields: &FieldsFromOfferToUseInRequest,
2618) {
2619 let FieldsFromOfferToUseInRequest {
2620 server_identifier,
2621 ip_address_lease_time_secs: lease_time,
2622 ip_address_to_request: yiaddr,
2623 } = offer_fields;
2624 log::info!(
2625 "{prefix} received DHCPOFFER from {src_addr}: \
2626 yiaddr={yiaddr}, lease_time={lease_time:?}, server_identifier={server_identifier}"
2627 );
2628}
2629
2630fn log_ack<A: Display, S: Debug>(
2631 prefix: &DebugLogPrefix,
2632 src_addr: &A,
2633 ack_fields: &FieldsToRetainFromAck<S>,
2634) {
2635 let FieldsToRetainFromAck {
2636 yiaddr,
2637 server_identifier,
2638 ip_address_lease_time_secs: lease_time,
2639 renewal_time_value_secs: renew_time,
2640 rebinding_time_value_secs: rebinding_time,
2641 parameters,
2642 } = ack_fields;
2643 let (prefix_len, routers, dns_servers): (
2646 Option<u8>,
2647 Option<Vec<Ipv4Addr>>,
2648 Option<Vec<Ipv4Addr>>,
2649 ) = parameters.iter().fold(
2650 (None, None, None),
2651 |(mut prefix_len, mut routers, mut dns_servers), option| {
2652 match option {
2653 dhcp_protocol::DhcpOption::SubnetMask(prefix_length) => {
2654 prefix_len = Some(prefix_length.get());
2655 }
2656 dhcp_protocol::DhcpOption::Router(at_least) => {
2657 routers.get_or_insert_default().extend(at_least.deref());
2658 }
2659 dhcp_protocol::DhcpOption::DomainNameServer(at_least) => {
2660 dns_servers.get_or_insert_default().extend(at_least.deref());
2661 }
2662 _ => {}
2663 }
2664 (prefix_len, routers, dns_servers)
2665 },
2666 );
2667 log::info!(
2668 "{prefix} received DHCPACK from {src_addr}: \
2669 yiaddr={yiaddr}, lease_time={lease_time:?}, renewal_time={renew_time:?}, \
2670 rebinding_time={rebinding_time:?}, server_identifier={server_identifier:?}, \
2671 prefix_len={prefix_len:?}, routers={routers:?}, dns_servers={dns_servers:?}"
2672 );
2673}
2674
2675fn log_nak<A: Display>(prefix: &DebugLogPrefix, src_addr: &A, nak_fields: &FieldsToRetainFromNak) {
2676 let FieldsToRetainFromNak { server_identifier, message, client_identifier: _ } = nak_fields;
2677 log::info!(
2678 "{prefix} received DHCPNAK from {src_addr}: \
2679 server_identifier={server_identifier}, message={message:?}"
2680 );
2681}
2682
2683#[cfg(test)]
2684mod test {
2685 use super::*;
2686 use crate::deps::Clock as _;
2687 use crate::deps::testutil::{
2688 FakeRngProvider, FakeSocket, FakeSocketProvider, FakeTimeController, TestInstant, advance,
2689 run_until_next_timers_fire,
2690 };
2691 use assert_matches::assert_matches;
2692 use fuchsia_async as fasync;
2693 use futures::{Future, join};
2694 use itertools::Itertools as _;
2695 use net_declare::net::prefix_length_v4;
2696 use net_declare::{net_mac, std_ip_v4};
2697 use net_types::ip::{Ipv4, PrefixLength};
2698 use simplelog::{Config, LevelFilter, WriteLogger};
2699 use std::cell::RefCell;
2700 use std::rc::Rc;
2701 use test_case::test_case;
2702
2703 fn initialize_logging() {
2704 WriteLogger::init(LevelFilter::Info, Config::default(), std::io::stderr()).unwrap();
2705 }
2706
2707 const TEST_MAC_ADDRESS: Mac = net_mac!("01:01:01:01:01:01");
2708 const TEST_SERVER_MAC_ADDRESS: Mac = net_mac!("02:02:02:02:02:02");
2709 const OTHER_MAC_ADDRESS: Mac = net_mac!("03:03:03:03:03:03");
2710
2711 const SERVER_IP: Ipv4Addr = std_ip_v4!("192.168.1.1");
2712 const OTHER_SERVER_IP: Ipv4Addr = std_ip_v4!("192.168.1.11");
2713 const YIADDR: Ipv4Addr = std_ip_v4!("198.168.1.5");
2714 const OTHER_ADDR: Ipv4Addr = std_ip_v4!("198.168.1.6");
2715 const DEFAULT_LEASE_LENGTH_SECONDS: u32 = 100;
2716 const DEFAULT_RENEWAL_TIME: RelativeTime = RelativeTime::Default(Duration::from_secs(50));
2717 const DEFAULT_REBINDING_TIME: RelativeTime =
2718 RelativeTime::Default(Duration::from_millis(87500));
2719 const MAX_LEASE_LENGTH_SECONDS: u32 = 200;
2720 const TEST_PREFIX_LENGTH: PrefixLength<Ipv4> = prefix_length_v4!(24);
2721
2722 fn test_requested_parameters() -> OptionCodeMap<OptionRequested> {
2723 use dhcp_protocol::OptionCode;
2724 [
2725 (OptionCode::SubnetMask, OptionRequested::Required),
2726 (OptionCode::Router, OptionRequested::Optional),
2727 (OptionCode::DomainNameServer, OptionRequested::Optional),
2728 ]
2729 .into_iter()
2730 .collect::<OptionCodeMap<_>>()
2731 }
2732
2733 fn test_parameter_values_excluding_subnet_mask() -> [dhcp_protocol::DhcpOption; 2] {
2734 [
2735 dhcp_protocol::DhcpOption::Router([SERVER_IP].into()),
2736 dhcp_protocol::DhcpOption::DomainNameServer([SERVER_IP, std_ip_v4!("8.8.8.8")].into()),
2737 ]
2738 }
2739
2740 fn test_parameter_values() -> impl IntoIterator<Item = dhcp_protocol::DhcpOption> {
2741 std::iter::once(dhcp_protocol::DhcpOption::SubnetMask(TEST_PREFIX_LENGTH))
2742 .chain(test_parameter_values_excluding_subnet_mask())
2743 }
2744
2745 fn test_client_config() -> ClientConfig {
2746 ClientConfig {
2747 client_hardware_address: TEST_MAC_ADDRESS,
2748 client_identifier: None,
2749 requested_parameters: test_requested_parameters(),
2750 preferred_lease_time_secs: None,
2751 requested_ip_address: None,
2752 debug_log_prefix: DebugLogPrefix { interface_id: NonZeroU64::new(2).unwrap() },
2753 }
2754 }
2755
2756 #[derive(Debug, PartialEq)]
2758 struct FakeRemovedReason;
2759
2760 #[test]
2761 fn do_init_uses_rng() {
2762 let mut rng = FakeRngProvider::new(0);
2763 let time = FakeTimeController::new();
2764 let arbitrary_start_time = std::time::Duration::from_secs(42);
2765 advance(&time, arbitrary_start_time);
2766
2767 let Selecting {
2768 discover_options: DiscoverOptions { xid: xid_a },
2769 start_time: start_time_a,
2770 } = Init.do_init(&mut rng, &time);
2771 let Selecting {
2772 discover_options: DiscoverOptions { xid: xid_b },
2773 start_time: start_time_b,
2774 } = Init.do_init(&mut rng, &time);
2775 assert_ne!(xid_a, xid_b);
2776 assert_eq!(start_time_a, TestInstant(arbitrary_start_time));
2777 assert_eq!(start_time_b, TestInstant(arbitrary_start_time));
2778 }
2779
2780 fn run_with_accelerated_time<F>(
2781 executor: &mut fasync::TestExecutor,
2782 time: &Rc<RefCell<FakeTimeController>>,
2783 main_future: &mut F,
2784 ) -> F::Output
2785 where
2786 F: Future + Unpin,
2787 {
2788 loop {
2789 match run_until_next_timers_fire(executor, time, main_future) {
2790 std::task::Poll::Ready(result) => break result,
2791 std::task::Poll::Pending => (),
2792 }
2793 }
2794 }
2795
2796 fn build_test_selecting_state() -> Selecting<TestInstant> {
2797 Selecting {
2798 discover_options: DiscoverOptions { xid: TransactionId(NonZeroU32::new(1).unwrap()) },
2799 start_time: TestInstant(std::time::Duration::from_secs(0)),
2800 }
2801 }
2802
2803 #[test]
2804 fn do_selecting_obeys_graceful_shutdown() {
2805 initialize_logging();
2806 let counters = Counters::default();
2807
2808 let mut executor = fasync::TestExecutor::new();
2809 let time = FakeTimeController::new();
2810
2811 let selecting = build_test_selecting_state();
2812 let mut rng = FakeRngProvider::new(0);
2813
2814 let (_server_end, client_end) = FakeSocket::new_pair();
2815 let test_socket_provider = FakeSocketProvider::new(client_end);
2816
2817 let client_config = test_client_config();
2818
2819 let (stop_sender, mut stop_receiver) = mpsc::unbounded();
2820
2821 let mut selecting_fut = pin!(
2822 selecting
2823 .do_selecting(
2824 &client_config,
2825 &test_socket_provider,
2826 &mut rng,
2827 &time,
2828 &mut stop_receiver,
2829 &counters,
2830 )
2831 .fuse()
2832 );
2833
2834 let time = &time;
2835
2836 let mut wait_fut = pin!(
2837 async {
2838 time.wait_until(TestInstant(std::time::Duration::from_secs(30))).await;
2841 }
2842 .fuse()
2843 );
2844
2845 {
2846 let main_future = async {
2847 select! {
2848 _ = selecting_fut => unreachable!("should keep retransmitting DHCPDISCOVER forever"),
2849 () = wait_fut => (),
2850 }
2851 };
2852 let mut main_future = pin!(main_future);
2853
2854 run_with_accelerated_time(&mut executor, time, &mut main_future);
2855 }
2856
2857 stop_sender.unbounded_send(()).expect("sending stop signal should succeed");
2858
2859 let selecting_result = selecting_fut.now_or_never().expect(
2860 "selecting_fut should complete after single poll after stop signal has been sent",
2861 );
2862
2863 assert_matches!(selecting_result, Ok(SelectingOutcome::GracefulShutdown));
2864 }
2865
2866 struct VaryingOutgoingMessageFields {
2867 xid: u32,
2868 options: Vec<dhcp_protocol::DhcpOption>,
2869 }
2870
2871 #[track_caller]
2872 fn assert_outgoing_message_when_not_assigned_address(
2873 got_message: &dhcp_protocol::Message,
2874 fields: VaryingOutgoingMessageFields,
2875 ) {
2876 let VaryingOutgoingMessageFields { xid, options } = fields;
2877 let want_message = dhcp_protocol::Message {
2878 op: dhcp_protocol::OpCode::BOOTREQUEST,
2879 xid,
2880 secs: 0,
2881 bdcast_flag: false,
2882 ciaddr: Ipv4Addr::UNSPECIFIED,
2883 yiaddr: Ipv4Addr::UNSPECIFIED,
2884 siaddr: Ipv4Addr::UNSPECIFIED,
2885 giaddr: Ipv4Addr::UNSPECIFIED,
2886 chaddr: TEST_MAC_ADDRESS,
2887 sname: BString::default(),
2888 file: BString::default(),
2889 options,
2890 };
2891 assert_eq!(got_message, &want_message);
2892 }
2893
2894 #[test]
2895 fn do_selecting_sends_discover() {
2896 initialize_logging();
2897 let counters = Counters::default();
2898
2899 let mut executor = fasync::TestExecutor::new();
2900 let time = FakeTimeController::new();
2901
2902 let selecting = Selecting {
2903 discover_options: DiscoverOptions { xid: TransactionId(NonZeroU32::new(1).unwrap()) },
2904 start_time: TestInstant(std::time::Duration::from_secs(0)),
2905 };
2906 let mut rng = FakeRngProvider::new(0);
2907
2908 let (server_end, client_end) = FakeSocket::new_pair();
2909 let test_socket_provider = FakeSocketProvider::new(client_end);
2910
2911 let client_config = test_client_config();
2912
2913 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
2914
2915 let mut selecting_fut = pin!(
2916 selecting
2917 .do_selecting(
2918 &client_config,
2919 &test_socket_provider,
2920 &mut rng,
2921 &time,
2922 &mut stop_receiver,
2923 &counters,
2924 )
2925 .fuse()
2926 );
2927
2928 let time = &time;
2929
2930 const EXPECTED_RANGES: [(u64, u64); 7] =
2934 [(0, 0), (3, 5), (7, 9), (15, 17), (31, 33), (63, 65), (63, 65)];
2935
2936 let mut receive_fut =
2937 pin!(
2938 async {
2939 let mut previous_time = std::time::Duration::from_secs(0);
2940
2941 for (start, end) in EXPECTED_RANGES {
2942 let mut recv_buf = [0u8; BUFFER_SIZE];
2943 let DatagramInfo { length, address } = server_end
2944 .recv_from(&mut recv_buf)
2945 .await
2946 .expect("recv_from on test socket should succeed");
2947
2948 assert_eq!(address, Mac::BROADCAST);
2949
2950 let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
2951 &recv_buf[..length],
2952 dhcp_protocol::SERVER_PORT,
2953 )
2954 .expect("received packet should parse as DHCP message");
2955
2956 assert_outgoing_message_when_not_assigned_address(
2957 &msg,
2958 VaryingOutgoingMessageFields {
2959 xid: msg.xid,
2960 options: vec![
2961 dhcp_protocol::DhcpOption::DhcpMessageType(
2962 dhcp_protocol::MessageType::DHCPDISCOVER,
2963 ),
2964 dhcp_protocol::DhcpOption::ParameterRequestList(
2965 test_requested_parameters()
2966 .iter_keys()
2967 .collect::<Vec<_>>()
2968 .try_into()
2969 .expect("should fit parameter request list size constraints"),
2970 ),
2971 ],
2972 },
2973 );
2974
2975 let TestInstant(received_time) = time.now();
2976
2977 let duration_range = std::time::Duration::from_secs(start)
2978 ..=std::time::Duration::from_secs(end);
2979 assert!(duration_range.contains(&(received_time - previous_time)));
2980
2981 previous_time = received_time;
2982 }
2983 }
2984 .fuse()
2985 );
2986
2987 let main_future = async {
2988 select! {
2989 _ = selecting_fut => unreachable!("should keep retransmitting DHCPDISCOVER forever"),
2990 () = receive_fut => (),
2991 }
2992 };
2993 let mut main_future = pin!(main_future);
2994
2995 run_with_accelerated_time(&mut executor, time, &mut main_future);
2996 assert_eq!(counters.selecting.messaging.send_message.load(), EXPECTED_RANGES.len());
2997 assert_eq!(counters.selecting.messaging.recv_time_out.load(), EXPECTED_RANGES.len() - 1);
2998 }
2999
3000 const XID: NonZeroU32 = NonZeroU32::new(1).unwrap();
3001 #[test_case(u32::from(XID), TEST_MAC_ADDRESS => Ok(()) ; "accepts good reply")]
3002 #[test_case(u32::from(XID), TEST_SERVER_MAC_ADDRESS => Err(
3003 ValidateMessageError::WrongChaddr {
3004 expected: TEST_MAC_ADDRESS,
3005 actual: TEST_SERVER_MAC_ADDRESS,
3006 }) ; "rejects wrong chaddr")]
3007 #[test_case(u32::from(XID).wrapping_add(1), TEST_MAC_ADDRESS => Err(
3008 ValidateMessageError::WrongXid {
3009 expected: u32::from(XID),
3010 actual: u32::from(XID).wrapping_add(1),
3011 }) ; "rejects wrong xid")]
3012 fn test_validate_message(
3013 message_xid: u32,
3014 message_chaddr: Mac,
3015 ) -> Result<(), ValidateMessageError> {
3016 let discover_options = DiscoverOptions { xid: TransactionId(XID) };
3017 let client_config = ClientConfig {
3018 client_hardware_address: TEST_MAC_ADDRESS,
3019 client_identifier: None,
3020 requested_parameters: test_requested_parameters(),
3021 preferred_lease_time_secs: None,
3022 requested_ip_address: None,
3023 debug_log_prefix: DebugLogPrefix { interface_id: NonZeroU64::new(2).unwrap() },
3024 };
3025
3026 let reply = dhcp_protocol::Message {
3027 op: dhcp_protocol::OpCode::BOOTREPLY,
3028 xid: message_xid,
3029 secs: 0,
3030 bdcast_flag: false,
3031 ciaddr: Ipv4Addr::UNSPECIFIED,
3032 yiaddr: Ipv4Addr::UNSPECIFIED,
3033 siaddr: Ipv4Addr::UNSPECIFIED,
3034 giaddr: Ipv4Addr::UNSPECIFIED,
3035 chaddr: message_chaddr,
3036 sname: BString::default(),
3037 file: BString::default(),
3038 options: Vec::new(),
3039 };
3040
3041 validate_message(&discover_options, &client_config, &reply)
3042 }
3043
3044 #[test_case(false ; "with no garbage traffic on link")]
3045 #[test_case(true ; "ignoring garbage replies to discover")]
3046 fn do_selecting_good_offer(reply_to_discover_with_garbage: bool) {
3047 initialize_logging();
3048 let counters = Counters::default();
3049
3050 let mut rng = FakeRngProvider::new(0);
3051 let time = FakeTimeController::new();
3052
3053 let arbitrary_start_time = std::time::Duration::from_secs(42);
3054 advance(&time, arbitrary_start_time);
3055
3056 let selecting = Init.do_init(&mut rng, &time);
3057 let TransactionId(xid) = selecting.discover_options.xid;
3058
3059 let (server_end, client_end) = FakeSocket::<Mac>::new_pair();
3060 let test_socket_provider = FakeSocketProvider::new(client_end);
3061
3062 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3063
3064 let client_config = test_client_config();
3065
3066 let selecting_fut = pin!(
3067 selecting
3068 .do_selecting(
3069 &client_config,
3070 &test_socket_provider,
3071 &mut rng,
3072 &time,
3073 &mut stop_receiver,
3074 &counters,
3075 )
3076 .fuse()
3077 );
3078
3079 let server_fut = pin!(
3080 async {
3081 let mut recv_buf = [0u8; BUFFER_SIZE];
3082
3083 if reply_to_discover_with_garbage {
3084 let DatagramInfo { length: _, address: dst_addr } = server_end
3085 .recv_from(&mut recv_buf)
3086 .await
3087 .expect("recv_from on test socket should succeed");
3088 assert_eq!(dst_addr, Mac::BROADCAST);
3089
3090 server_end
3091 .send_to(b"hello", OTHER_MAC_ADDRESS)
3092 .await
3093 .expect("send_to with garbage data should succeed");
3094 }
3095
3096 let DatagramInfo { length, address } = server_end
3097 .recv_from(&mut recv_buf)
3098 .await
3099 .expect("recv_from on test socket should succeed");
3100 assert_eq!(address, Mac::BROADCAST);
3101
3102 let parse_msg = || {
3105 let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
3106 &recv_buf[..length],
3107 dhcp_protocol::SERVER_PORT,
3108 )
3109 .expect("received packet on test socket should parse as DHCP message");
3110 msg
3111 };
3112
3113 let msg = parse_msg();
3114 assert_outgoing_message_when_not_assigned_address(
3115 &parse_msg(),
3116 VaryingOutgoingMessageFields {
3117 xid: msg.xid,
3118 options: vec![
3119 dhcp_protocol::DhcpOption::DhcpMessageType(
3120 dhcp_protocol::MessageType::DHCPDISCOVER,
3121 ),
3122 dhcp_protocol::DhcpOption::ParameterRequestList(
3123 test_requested_parameters()
3124 .iter_keys()
3125 .collect::<Vec<_>>()
3126 .try_into()
3127 .expect("should fit parameter request list size constraints"),
3128 ),
3129 ],
3130 },
3131 );
3132
3133 let build_reply = || {
3134 dhcpv4::server::build_offer(
3135 parse_msg(),
3136 dhcpv4::server::OfferOptions {
3137 offered_ip: YIADDR,
3138 server_ip: SERVER_IP,
3139 lease_length_config: dhcpv4::configuration::LeaseLength {
3140 default_seconds: DEFAULT_LEASE_LENGTH_SECONDS,
3141 max_seconds: MAX_LEASE_LENGTH_SECONDS,
3142 },
3143 renewal_time_value: Some(20),
3147 rebinding_time_value: Some(30),
3148 subnet_mask: TEST_PREFIX_LENGTH,
3149 },
3150 &dhcpv4::server::options_repo(test_parameter_values()),
3151 )
3152 .expect("dhcp server crate error building offer")
3153 };
3154
3155 let reply_with_wrong_xid = dhcp_protocol::Message {
3156 xid: (u32::from(xid).wrapping_add(1)),
3157 yiaddr: OTHER_ADDR,
3161 ..build_reply()
3162 };
3163
3164 let reply_without_subnet_mask = {
3165 let mut reply = build_reply();
3166 let options = std::mem::take(&mut reply.options);
3167 let (subnet_masks, other_options): (Vec<_>, Vec<_>) =
3168 options.into_iter().partition_map(|option| match option {
3169 dhcp_protocol::DhcpOption::SubnetMask(_) => {
3170 itertools::Either::Left(option)
3171 }
3172 _ => itertools::Either::Right(option),
3173 });
3174 assert_matches!(
3175 &subnet_masks[..],
3176 &[dhcp_protocol::DhcpOption::SubnetMask(TEST_PREFIX_LENGTH)]
3177 );
3178 reply.options = other_options;
3179
3180 reply.yiaddr = OTHER_ADDR;
3184 reply
3185 };
3186
3187 let good_reply = build_reply();
3188
3189 let send_reply = |reply: dhcp_protocol::Message| async {
3190 let dst_ip = reply.yiaddr;
3191 server_end
3192 .send_to(
3193 crate::parse::serialize_dhcp_message_to_ip_packet(
3194 reply,
3195 SERVER_IP,
3196 SERVER_PORT,
3197 dst_ip,
3198 CLIENT_PORT,
3199 )
3200 .as_ref(),
3201 TEST_SERVER_MAC_ADDRESS,
3204 )
3205 .await
3206 .expect("send_to on test socket should succeed");
3207 };
3208
3209 send_reply(reply_with_wrong_xid).await;
3211
3212 send_reply(reply_without_subnet_mask).await;
3214
3215 send_reply(good_reply).await;
3216 }
3217 .fuse()
3218 );
3219
3220 let main_future = async move {
3221 let (selecting_result, ()) = join!(selecting_fut, server_fut);
3222 selecting_result
3223 }
3224 .fuse();
3225 let mut main_future = pin!(main_future);
3226 let mut executor = fasync::TestExecutor::new();
3227 let selecting_result = run_with_accelerated_time(&mut executor, &time, &mut main_future);
3228
3229 let requesting = assert_matches!(
3230 selecting_result,
3231 Ok(SelectingOutcome::Requesting(requesting)) => requesting,
3232 "should have successfully transitioned to Requesting"
3233 );
3234
3235 assert_eq!(
3236 requesting,
3237 Requesting {
3238 discover_options: DiscoverOptions { xid: requesting.discover_options.xid },
3239 fields_from_offer_to_use_in_request: crate::parse::FieldsFromOfferToUseInRequest {
3240 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3241 .try_into()
3242 .expect("should be specified"),
3243 ip_address_lease_time_secs: Some(
3244 NonZeroU32::new(DEFAULT_LEASE_LENGTH_SECONDS).unwrap()
3245 ),
3246 ip_address_to_request: net_types::ip::Ipv4Addr::from(YIADDR)
3247 .try_into()
3248 .expect("should be specified"),
3249 },
3250 start_time: TestInstant(arbitrary_start_time),
3251 }
3252 );
3253 assert_eq!(
3254 counters.selecting.messaging.send_message.load(),
3255 if reply_to_discover_with_garbage { 2 } else { 1 }
3256 );
3257 assert_eq!(
3258 counters.selecting.messaging.recv_time_out.load(),
3259 if reply_to_discover_with_garbage { 1 } else { 0 }
3260 );
3261 assert_eq!(
3262 counters.selecting.messaging.recv_failed_dhcp_parse.load(),
3263 if reply_to_discover_with_garbage { 1 } else { 0 }
3264 );
3265 assert_eq!(counters.selecting.messaging.recv_wrong_xid.load(), 1);
3266 assert_eq!(counters.selecting.messaging.recv_wrong_chaddr.load(), 0);
3267 assert_eq!(counters.selecting.recv_error.missing_required_option.load(), 1);
3268 }
3269
3270 const TEST_XID: TransactionId = TransactionId(NonZeroU32::new(1).unwrap());
3271 const TEST_DISCOVER_OPTIONS: DiscoverOptions = DiscoverOptions { xid: TEST_XID };
3272
3273 fn build_test_requesting_state() -> Requesting<TestInstant> {
3274 Requesting {
3275 discover_options: TEST_DISCOVER_OPTIONS,
3276 start_time: TestInstant(std::time::Duration::from_secs(0)),
3277 fields_from_offer_to_use_in_request: crate::parse::FieldsFromOfferToUseInRequest {
3278 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3279 .try_into()
3280 .expect("should be specified"),
3281 ip_address_lease_time_secs: Some(
3282 NonZeroU32::new(DEFAULT_LEASE_LENGTH_SECONDS).unwrap(),
3283 ),
3284 ip_address_to_request: net_types::ip::Ipv4Addr::from(YIADDR)
3285 .try_into()
3286 .expect("should be specified"),
3287 },
3288 }
3289 }
3290
3291 #[test]
3292 fn do_requesting_obeys_graceful_shutdown() {
3293 initialize_logging();
3294 let counters = Counters::default();
3295
3296 let time = FakeTimeController::new();
3297
3298 let requesting = build_test_requesting_state();
3299 let mut rng = FakeRngProvider::new(0);
3300
3301 let (_server_end, client_end) = FakeSocket::new_pair();
3302 let test_socket_provider = FakeSocketProvider::new(client_end);
3303
3304 let client_config = test_client_config();
3305
3306 let (stop_sender, mut stop_receiver) = mpsc::unbounded();
3307
3308 let requesting_fut = requesting
3309 .do_requesting(
3310 &client_config,
3311 &test_socket_provider,
3312 &mut rng,
3313 &time,
3314 &mut stop_receiver,
3315 &counters,
3316 )
3317 .fuse();
3318 let mut requesting_fut = pin!(requesting_fut);
3319
3320 let mut executor = fasync::TestExecutor::new();
3321 assert_matches!(executor.run_until_stalled(&mut requesting_fut), std::task::Poll::Pending);
3322
3323 stop_sender.unbounded_send(()).expect("sending stop signal should succeed");
3324
3325 let requesting_result = requesting_fut.now_or_never().expect(
3326 "requesting_fut should complete after single poll after stop signal has been sent",
3327 );
3328
3329 assert_matches!(requesting_result, Ok(RequestingOutcome::GracefulShutdown));
3330 }
3331
3332 #[test]
3333 fn do_requesting_sends_requests() {
3334 initialize_logging();
3335 let counters = Counters::default();
3336
3337 let mut executor = fasync::TestExecutor::new();
3338 let time = FakeTimeController::new();
3339
3340 let requesting = build_test_requesting_state();
3341 let mut rng = FakeRngProvider::new(0);
3342
3343 let (server_end, client_end) = FakeSocket::new_pair();
3344 let test_socket_provider = FakeSocketProvider::new(client_end);
3345
3346 let client_config = test_client_config();
3347
3348 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3349
3350 let requesting_fut = pin!(
3351 requesting
3352 .do_requesting(
3353 &client_config,
3354 &test_socket_provider,
3355 &mut rng,
3356 &time,
3357 &mut stop_receiver,
3358 &counters,
3359 )
3360 .fuse()
3361 );
3362
3363 let time = &time;
3364
3365 const EXPECTED_RANGES: [(u64, u64); NUM_REQUEST_RETRANSMITS + 1] =
3369 [(0, 0), (3, 5), (7, 9), (15, 17), (31, 33)];
3370
3371 let receive_fut = pin!(
3372 async {
3373 let mut previous_time = std::time::Duration::from_secs(0);
3374
3375 for (start, end) in EXPECTED_RANGES {
3376 let mut recv_buf = [0u8; BUFFER_SIZE];
3377 let DatagramInfo { length, address } = server_end
3378 .recv_from(&mut recv_buf)
3379 .await
3380 .expect("recv_from on test socket should succeed");
3381
3382 assert_eq!(address, Mac::BROADCAST);
3383
3384 let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
3385 &recv_buf[..length],
3386 dhcp_protocol::SERVER_PORT,
3387 )
3388 .expect("received packet should parse as DHCP message");
3389
3390 assert_outgoing_message_when_not_assigned_address(
3391 &msg,
3392 VaryingOutgoingMessageFields {
3393 xid: msg.xid,
3394 options: vec![
3395 dhcp_protocol::DhcpOption::DhcpMessageType(
3396 dhcp_protocol::MessageType::DHCPREQUEST,
3397 ),
3398 dhcp_protocol::DhcpOption::ParameterRequestList(
3399 test_requested_parameters()
3400 .iter_keys()
3401 .collect::<Vec<_>>()
3402 .try_into()
3403 .expect(
3404 "should fit parameter request list size constraints",
3405 ),
3406 ),
3407 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3408 dhcp_protocol::DhcpOption::RequestedIpAddress(YIADDR),
3409 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3410 DEFAULT_LEASE_LENGTH_SECONDS,
3411 ),
3412 ],
3413 },
3414 );
3415
3416 let TestInstant(received_time) = time.now();
3417
3418 let duration_range =
3419 std::time::Duration::from_secs(start)..=std::time::Duration::from_secs(end);
3420 assert!(duration_range.contains(&(received_time - previous_time)));
3421
3422 previous_time = received_time;
3423 }
3424 }
3425 .fuse()
3426 );
3427
3428 let main_future = async { join!(requesting_fut, receive_fut) };
3429 let mut main_future = pin!(main_future);
3430
3431 let (requesting_result, ()) =
3432 run_with_accelerated_time(&mut executor, time, &mut main_future);
3433
3434 assert_matches!(requesting_result, Ok(RequestingOutcome::RanOutOfRetransmits));
3435 assert_eq!(counters.requesting.messaging.send_message.load(), EXPECTED_RANGES.len());
3436 assert_eq!(counters.requesting.messaging.recv_time_out.load(), EXPECTED_RANGES.len());
3437 }
3438
3439 struct VaryingIncomingMessageFields {
3440 yiaddr: Ipv4Addr,
3441 options: Vec<dhcp_protocol::DhcpOption>,
3442 }
3443
3444 fn build_incoming_message(
3445 xid: u32,
3446 fields: VaryingIncomingMessageFields,
3447 ) -> dhcp_protocol::Message {
3448 let VaryingIncomingMessageFields { yiaddr, options } = fields;
3449
3450 dhcp_protocol::Message {
3451 op: dhcp_protocol::OpCode::BOOTREPLY,
3452 xid,
3453 secs: 0,
3454 bdcast_flag: false,
3455 ciaddr: Ipv4Addr::UNSPECIFIED,
3456 yiaddr,
3457 siaddr: Ipv4Addr::UNSPECIFIED,
3458 giaddr: Ipv4Addr::UNSPECIFIED,
3459 chaddr: TEST_MAC_ADDRESS,
3460 sname: BString::default(),
3461 file: BString::default(),
3462 options,
3463 }
3464 }
3465
3466 const NAK_MESSAGE: &str = "something went wrong";
3467
3468 #[derive(PartialEq, Debug)]
3469 struct RequestingTestResult {
3470 outcome: RequestingOutcome<TestInstant>,
3471 counters: RequestingTestCounters,
3472 }
3473
3474 #[derive(PartialEq, Eq, Debug, Default)]
3475 struct RequestingTestCounters {
3476 send_message: usize,
3477 recv_time_out: usize,
3478 recv_failed_dhcp_parse: usize,
3479 recv_wrong_xid: usize,
3480 recv_wrong_chaddr: usize,
3481 recv_message: usize,
3482 recv_nak: usize,
3483 recv_missing_option: usize,
3484 recv_ack_renewal_time_after_rebinding_time: usize,
3485 recv_ack_rebinding_time_outlives_lease: usize,
3486 }
3487
3488 #[test_case(VaryingIncomingMessageFields {
3489 yiaddr: YIADDR,
3490 options: [
3491 dhcp_protocol::DhcpOption::DhcpMessageType(
3492 dhcp_protocol::MessageType::DHCPACK,
3493 ),
3494 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3495 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3496 DEFAULT_LEASE_LENGTH_SECONDS,
3497 ),
3498 ]
3499 .into_iter()
3500 .chain(test_parameter_values())
3501 .collect(),
3502 } => RequestingTestResult {
3503 outcome: RequestingOutcome::Bound(LeaseState {
3504 discover_options: TEST_DISCOVER_OPTIONS,
3505 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
3506 .try_into()
3507 .expect("should be specified"),
3508 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3509 .try_into()
3510 .expect("should be specified"),
3511 ip_address_lease_time: RelativeTime::Explicit(std::time::Duration::from_secs(
3512 DEFAULT_LEASE_LENGTH_SECONDS.into()
3513 )),
3514 renewal_time: DEFAULT_RENEWAL_TIME,
3515 rebinding_time: DEFAULT_REBINDING_TIME,
3516 start_time: TestInstant(std::time::Duration::from_secs(0)),
3517 }, test_parameter_values().into_iter().collect()),
3518 counters: RequestingTestCounters {
3519 send_message: 1,
3520 recv_message: 1,
3521 ..Default::default()
3522 }
3523 } ; "transitions to Bound after receiving DHCPACK")]
3524 #[test_case(VaryingIncomingMessageFields {
3525 yiaddr: YIADDR,
3526 options: [
3527 dhcp_protocol::DhcpOption::DhcpMessageType(
3528 dhcp_protocol::MessageType::DHCPACK,
3529 ),
3530 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3531 ]
3532 .into_iter()
3533 .chain(test_parameter_values())
3534 .collect(),
3535 } => RequestingTestResult {
3536 outcome: RequestingOutcome::Bound(LeaseState {
3537 discover_options: TEST_DISCOVER_OPTIONS,
3538 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
3539 .try_into()
3540 .expect("should be specified"),
3541 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3542 .try_into()
3543 .expect("should be specified"),
3544 ip_address_lease_time: RelativeTime::Previous(std::time::Duration::from_secs(
3545 DEFAULT_LEASE_LENGTH_SECONDS.into()
3546 )),
3547 renewal_time: DEFAULT_RENEWAL_TIME,
3548 rebinding_time: DEFAULT_REBINDING_TIME,
3549 start_time: TestInstant(std::time::Duration::from_secs(0)),
3550 }, test_parameter_values().into_iter().collect()),
3551 counters: RequestingTestCounters {
3552 send_message: 1,
3553 recv_message: 1,
3554 ..Default::default()
3555 }
3556 } ; "transitions to Bound after receiving DHCPACK without lease time")]
3557 #[test_case(VaryingIncomingMessageFields {
3558 yiaddr: YIADDR,
3559 options: [
3560 dhcp_protocol::DhcpOption::DhcpMessageType(
3561 dhcp_protocol::MessageType::DHCPACK,
3562 ),
3563 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3564 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3565 DEFAULT_LEASE_LENGTH_SECONDS,
3566 ),
3567 ]
3568 .into_iter()
3569 .chain(test_parameter_values_excluding_subnet_mask())
3570 .collect(),
3571 } => RequestingTestResult {
3572 outcome: RequestingOutcome::RanOutOfRetransmits,
3573 counters: RequestingTestCounters {
3574 send_message: 5,
3575 recv_time_out: 5,
3576 recv_message: 1,
3577 recv_missing_option: 1,
3578 ..Default::default()
3579 },
3580 }; "ignores replies lacking required option SubnetMask")]
3581 #[test_case(VaryingIncomingMessageFields {
3582 yiaddr: Ipv4Addr::UNSPECIFIED,
3583 options: [
3584 dhcp_protocol::DhcpOption::DhcpMessageType(
3585 dhcp_protocol::MessageType::DHCPNAK,
3586 ),
3587 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3588 dhcp_protocol::DhcpOption::Message(NAK_MESSAGE.to_owned()),
3589 ]
3590 .into_iter()
3591 .chain(test_parameter_values())
3592 .collect(),
3593 } => RequestingTestResult {
3594 outcome: RequestingOutcome::Nak(crate::parse::FieldsToRetainFromNak {
3595 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3596 .try_into()
3597 .expect("should be specified"),
3598 message: Some(NAK_MESSAGE.to_owned()),
3599 client_identifier: None,
3600 }),
3601 counters: RequestingTestCounters {
3602 send_message: 1,
3603 recv_message: 1,
3604 recv_nak: 1,
3605 ..Default::default()
3606 },
3607 }; "transitions to Init after receiving DHCPNAK")]
3608 #[test_case(VaryingIncomingMessageFields {
3609 yiaddr: YIADDR,
3610 options: [
3611 dhcp_protocol::DhcpOption::DhcpMessageType(
3612 dhcp_protocol::MessageType::DHCPACK,
3613 ),
3614 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3615 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3616 DEFAULT_LEASE_LENGTH_SECONDS,
3617 ),
3618 dhcp_protocol::DhcpOption::RenewalTimeValue(DEFAULT_LEASE_LENGTH_SECONDS - 1),
3619 dhcp_protocol::DhcpOption::RebindingTimeValue(DEFAULT_LEASE_LENGTH_SECONDS - 2),
3620 ]
3621 .into_iter()
3622 .chain(test_parameter_values())
3623 .collect(),
3624 } => RequestingTestResult {
3625 outcome: RequestingOutcome::Bound(LeaseState {
3626 discover_options: TEST_DISCOVER_OPTIONS,
3627 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
3628 .try_into()
3629 .expect("should be specified"),
3630 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3631 .try_into()
3632 .expect("should be specified"),
3633 ip_address_lease_time: RelativeTime::Explicit(std::time::Duration::from_secs(
3634 DEFAULT_LEASE_LENGTH_SECONDS.into()
3635 )),
3636 renewal_time: DEFAULT_RENEWAL_TIME,
3637 rebinding_time: DEFAULT_REBINDING_TIME,
3638 start_time: TestInstant(std::time::Duration::from_secs(0)),
3639 }, test_parameter_values().into_iter().collect()),
3640 counters: RequestingTestCounters {
3641 send_message: 1,
3642 recv_message: 1,
3643 recv_ack_renewal_time_after_rebinding_time: 1,
3644 recv_ack_rebinding_time_outlives_lease: 0,
3645 ..Default::default()
3646 },
3647 }; "transitions to Bound with default T1 and T2 if provided T1 greater than T2")]
3648 #[test_case(VaryingIncomingMessageFields {
3649 yiaddr: YIADDR,
3650 options: [
3651 dhcp_protocol::DhcpOption::DhcpMessageType(
3652 dhcp_protocol::MessageType::DHCPACK,
3653 ),
3654 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3655 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3656 DEFAULT_LEASE_LENGTH_SECONDS,
3657 ),
3658 dhcp_protocol::DhcpOption::RebindingTimeValue(DEFAULT_LEASE_LENGTH_SECONDS + 1),
3659 ]
3660 .into_iter()
3661 .chain(test_parameter_values())
3662 .collect(),
3663 } => RequestingTestResult {
3664 outcome: RequestingOutcome::Bound(LeaseState {
3665 discover_options: TEST_DISCOVER_OPTIONS,
3666 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
3667 .try_into()
3668 .expect("should be specified"),
3669 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3670 .try_into()
3671 .expect("should be specified"),
3672 ip_address_lease_time: RelativeTime::Explicit(std::time::Duration::from_secs(
3673 DEFAULT_LEASE_LENGTH_SECONDS.into()
3674 )),
3675 renewal_time: DEFAULT_RENEWAL_TIME,
3676 rebinding_time: DEFAULT_REBINDING_TIME,
3677 start_time: TestInstant(std::time::Duration::from_secs(0)),
3678 }, test_parameter_values().into_iter().collect()),
3679 counters: RequestingTestCounters {
3680 send_message: 1,
3681 recv_message: 1,
3682 recv_ack_renewal_time_after_rebinding_time: 0,
3683 recv_ack_rebinding_time_outlives_lease: 1,
3684 ..Default::default()
3685 },
3686 }; "transitions to Bound with default T1 and T2 if provided T2 greater than lease_time")]
3687 fn do_requesting_transitions_on_reply(
3688 incoming_message: VaryingIncomingMessageFields,
3689 ) -> RequestingTestResult {
3690 initialize_logging();
3691 let counters = Counters::default();
3692
3693 let time = &FakeTimeController::new();
3694
3695 let requesting = build_test_requesting_state();
3696 let mut rng = FakeRngProvider::new(0);
3697
3698 let (server_end, client_end) = FakeSocket::new_pair();
3699 let test_socket_provider = FakeSocketProvider::new(client_end);
3700
3701 let client_config = test_client_config();
3702
3703 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3704
3705 let requesting_fut = pin!(
3706 requesting
3707 .do_requesting(
3708 &client_config,
3709 &test_socket_provider,
3710 &mut rng,
3711 time,
3712 &mut stop_receiver,
3713 &counters,
3714 )
3715 .fuse()
3716 );
3717
3718 let server_fut = pin!(
3719 async {
3720 let mut recv_buf = [0u8; BUFFER_SIZE];
3721
3722 let DatagramInfo { length, address } = server_end
3723 .recv_from(&mut recv_buf)
3724 .await
3725 .expect("recv_from on test socket should succeed");
3726 assert_eq!(address, Mac::BROADCAST);
3727
3728 let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
3729 &recv_buf[..length],
3730 dhcp_protocol::SERVER_PORT,
3731 )
3732 .expect("received packet on test socket should parse as DHCP message");
3733
3734 assert_outgoing_message_when_not_assigned_address(
3735 &msg,
3736 VaryingOutgoingMessageFields {
3737 xid: msg.xid,
3738 options: vec![
3739 dhcp_protocol::DhcpOption::DhcpMessageType(
3740 dhcp_protocol::MessageType::DHCPREQUEST,
3741 ),
3742 dhcp_protocol::DhcpOption::ParameterRequestList(
3743 test_requested_parameters()
3744 .iter_keys()
3745 .collect::<Vec<_>>()
3746 .try_into()
3747 .expect("should fit parameter request list size constraints"),
3748 ),
3749 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3750 dhcp_protocol::DhcpOption::RequestedIpAddress(YIADDR),
3751 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3752 DEFAULT_LEASE_LENGTH_SECONDS,
3753 ),
3754 ],
3755 },
3756 );
3757
3758 let reply = build_incoming_message(msg.xid, incoming_message);
3759
3760 server_end
3761 .send_to(
3762 crate::parse::serialize_dhcp_message_to_ip_packet(
3763 reply,
3764 SERVER_IP,
3765 SERVER_PORT,
3766 YIADDR,
3767 CLIENT_PORT,
3768 )
3769 .as_ref(),
3770 TEST_SERVER_MAC_ADDRESS,
3773 )
3774 .await
3775 .expect("send_to on test socket should succeed");
3776 }
3777 .fuse()
3778 );
3779
3780 let main_future = async move {
3781 let (requesting_result, ()) = join!(requesting_fut, server_fut);
3782 requesting_result
3783 }
3784 .fuse();
3785
3786 let mut main_future = pin!(main_future);
3787
3788 let mut executor = fasync::TestExecutor::new();
3789 let requesting_result = run_with_accelerated_time(&mut executor, time, &mut main_future);
3790
3791 let outcome = assert_matches!(requesting_result, Ok(outcome) => outcome);
3792 let counters = RequestingTestCounters {
3793 send_message: counters.requesting.messaging.send_message.load(),
3794 recv_time_out: counters.requesting.messaging.recv_time_out.load(),
3795 recv_failed_dhcp_parse: counters.requesting.messaging.recv_failed_dhcp_parse.load(),
3796 recv_wrong_xid: counters.requesting.messaging.recv_wrong_xid.load(),
3797 recv_wrong_chaddr: counters.requesting.messaging.recv_wrong_chaddr.load(),
3798 recv_message: counters.requesting.messaging.recv_message.load(),
3799 recv_nak: counters.requesting.recv_nak.load(),
3800 recv_missing_option: counters.requesting.recv_error.missing_required_option.load(),
3801 recv_ack_renewal_time_after_rebinding_time: counters
3802 .requesting
3803 .messaging
3804 .recv_ack_renewal_time_after_rebinding_time
3805 .load(),
3806 recv_ack_rebinding_time_outlives_lease: counters
3807 .requesting
3808 .messaging
3809 .recv_ack_rebinding_time_outlives_lease
3810 .load(),
3811 };
3812 RequestingTestResult { outcome, counters }
3813 }
3814
3815 fn build_test_lease_state() -> LeaseState<TestInstant> {
3816 build_test_lease_state_with_times(
3817 Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3818 None,
3819 None,
3820 )
3821 }
3822
3823 fn build_test_lease_state_with_times(
3824 lease_length: Duration,
3825 renewal_time: Option<Duration>,
3826 rebinding_time: Option<Duration>,
3827 ) -> LeaseState<TestInstant> {
3828 LeaseState {
3829 discover_options: TEST_DISCOVER_OPTIONS,
3830 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR).try_into().expect("should be specified"),
3831 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3832 .try_into()
3833 .expect("should be specified"),
3834 ip_address_lease_time: RelativeTime::Explicit(lease_length),
3835 start_time: TestInstant(std::time::Duration::from_secs(0)),
3836 renewal_time: renewal_time
3837 .map(RelativeTime::Explicit)
3838 .unwrap_or_else(|| default_renewal_time(lease_length)),
3839 rebinding_time: rebinding_time
3840 .map(RelativeTime::Explicit)
3841 .unwrap_or_else(|| default_rebinding_time(lease_length)),
3842 }
3843 }
3844
3845 fn build_test_newly_acquired_lease() -> NewlyAcquiredLease<TestInstant> {
3846 NewlyAcquiredLease {
3847 ip_address: net_types::ip::Ipv4Addr::from(YIADDR)
3848 .try_into()
3849 .expect("should be specified"),
3850 start_time: TestInstant(std::time::Duration::from_secs(0)),
3851 lease_time: Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3852 parameters: Vec::new(),
3853 }
3854 }
3855
3856 #[test_case(
3857 (
3858 State::Init(Init::default()),
3859 Transition::BoundWithNewLease(
3860 Bound::AwaitingAssignment{lease_state: build_test_lease_state()},
3861 build_test_newly_acquired_lease()
3862 )
3863 ) => matches Some(TransitionEffect::HandleNewLease(_));
3864 "yields newly-acquired lease effect"
3865 )]
3866 #[test_case(
3867 (
3868 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3869 Transition::Init(Init),
3870 ) => matches Some(TransitionEffect::DropLease {address_rejected: false});
3871 "recognizes loss of lease"
3872 )]
3873 #[test_case(
3874 (
3875 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3876 Transition::WaitingToRestart(WaitingToRestart {
3877 waiting_until: TestInstant(WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION)
3878 }),
3879 ) => matches Some(TransitionEffect::DropLease {address_rejected: true});
3880 "recognizes address rejection"
3881 )]
3882
3883 fn apply_transition(
3884 (state, transition): (State<TestInstant>, Transition<TestInstant>),
3885 ) -> Option<TransitionEffect<TestInstant>> {
3886 let (_next_state, effect) = state.apply(&test_client_config(), transition);
3887 effect
3888 }
3889
3890 enum AddrRemovedExpect {
3891 Decline,
3892 Exit,
3893 Ignore,
3894 }
3895
3896 #[test_case(
3897 State::Selecting(build_test_selecting_state()),
3898 AddrRemovedExpect::Ignore;
3899 "selecting_ignore"
3900 )]
3901 #[test_case(
3902 State::Requesting(build_test_requesting_state()),
3903 AddrRemovedExpect::Ignore;
3904 "requesting_ignore"
3905 )]
3906 #[test_case(
3907 State::Bound(Bound::AwaitingAssignment{lease_state: build_test_lease_state()}),
3908 AddrRemovedExpect::Exit;
3909 "bound_awaiting_assignment_exit"
3910 )]
3911 #[test_case(
3912 State::Bound(Bound::AwaitingAssignment{lease_state: build_test_lease_state()}),
3913 AddrRemovedExpect::Decline;
3914 "bound_awaiting_assignment_decline"
3915 )]
3916 #[test_case(
3917 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3918 AddrRemovedExpect::Exit;
3919 "bound_assigned_exit"
3920 )]
3921 #[test_case(
3922 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3923 AddrRemovedExpect::Decline;
3924 "bound_assigned_decline"
3925 )]
3926 #[test_case(
3927 State::Renewing(Renewing{lease_state: build_test_lease_state()}),
3928 AddrRemovedExpect::Exit;
3929 "renewing_exit"
3930 )]
3931 #[test_case(
3932 State::Renewing(Renewing{lease_state: build_test_lease_state()}),
3933 AddrRemovedExpect::Decline;
3934 "renewing_decline"
3935 )]
3936 #[test_case(
3937 State::Rebinding(Rebinding{lease_state: build_test_lease_state()}),
3938 AddrRemovedExpect::Exit;
3939 "rebinding_exit"
3940 )]
3941 #[test_case(
3942 State::Rebinding(Rebinding{lease_state: build_test_lease_state()}),
3943 AddrRemovedExpect::Decline;
3944 "rebinding_decline"
3945 )]
3946 #[test_case(
3947 State::WaitingToRestart(WaitingToRestart {
3948 waiting_until: TestInstant(WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION)
3949 }),
3950 AddrRemovedExpect::Ignore;
3951 "waiting_to_restart_ignore"
3952 )]
3953 fn on_address_removed(state: State<TestInstant>, expect: AddrRemovedExpect) {
3954 let config = &test_client_config();
3955 let time = &FakeTimeController::new();
3956 let mut rng = FakeRngProvider::new(0);
3957 let (server_end, packet_client_end) = FakeSocket::new_pair();
3958 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
3959 let (_server_end, udp_client_end) = FakeSocket::new_pair();
3960 let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
3961 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3962 let counters = Counters::default();
3963
3964 let address_event_receiver = futures::stream::once(async {
3965 match expect {
3966 AddrRemovedExpect::Decline => AddressEvent::Rejected,
3967 AddrRemovedExpect::Exit => AddressEvent::Removed(FakeRemovedReason),
3968 AddrRemovedExpect::Ignore => panic!("address_event_receiver should not be polled"),
3969 }
3970 })
3971 .chain(futures::stream::pending());
3972
3973 let run_result = state
3974 .run(
3975 config,
3976 packet_socket_provider,
3977 udp_socket_provider,
3978 &mut rng,
3979 time,
3980 &mut stop_receiver,
3981 address_event_receiver,
3982 &counters,
3983 )
3984 .now_or_never();
3985
3986 match expect {
3987 AddrRemovedExpect::Decline => {
3988 let run_result = run_result.expect("fut should finish when declining");
3989 let WaitingToRestart { waiting_until } = assert_matches!(
3990 run_result,
3991 Ok(Step::NextState(Transition::WaitingToRestart(waiting))) => waiting
3992 );
3993 assert_eq!(waiting_until, TestInstant(Duration::from_secs(10)));
3994
3995 let mut buf = [0u8; BUFFER_SIZE];
3996 let DatagramInfo { length, address } = server_end
3997 .recv_from(&mut buf)
3998 .now_or_never()
3999 .expect("should be ready")
4000 .expect("should succeed");
4001 assert_eq!(address, Mac::BROADCAST);
4002
4003 let (_src_addr, message) =
4004 crate::parse::parse_dhcp_message_from_ip_packet(&buf[..length], SERVER_PORT)
4005 .expect("should succeed");
4006
4007 use dhcp_protocol::DhcpOption;
4008 assert_outgoing_message_when_not_assigned_address(
4009 &message,
4010 VaryingOutgoingMessageFields {
4011 xid: message.xid,
4012 options: vec![
4013 DhcpOption::DhcpMessageType(dhcp_protocol::MessageType::DHCPDECLINE),
4014 DhcpOption::ServerIdentifier(SERVER_IP),
4015 DhcpOption::RequestedIpAddress(YIADDR),
4016 ],
4017 },
4018 );
4019 }
4020 AddrRemovedExpect::Exit => {
4021 let run_result = run_result.expect("fut should finish when exiting");
4022 assert_matches!(
4023 run_result,
4024 Ok(Step::Exit(ExitReason::AddressRemoved(FakeRemovedReason)))
4025 );
4026 }
4027 AddrRemovedExpect::Ignore => {
4028 assert_matches!(run_result, None, "fut should not finish when ignored.");
4029 }
4030 }
4031 }
4032
4033 enum AddrAssignmentChangeExpect {
4034 Ignore,
4035 EnterAssigned,
4036 }
4037
4038 #[test_case(
4039 State::Selecting(build_test_selecting_state()),
4040 AddressAssignmentState::Assigned,
4041 AddrAssignmentChangeExpect::Ignore;
4042 "selecting_ignores"
4043 )]
4044 #[test_case(
4045 State::Requesting(build_test_requesting_state()),
4046 AddressAssignmentState::Assigned,
4047 AddrAssignmentChangeExpect::Ignore;
4048 "requesting_ignores"
4049 )]
4050 #[test_case(
4051 State::Bound(Bound::AwaitingAssignment{lease_state: build_test_lease_state()}),
4052 AddressAssignmentState::Tentative,
4053 AddrAssignmentChangeExpect::Ignore;
4054 "bound_awaiting_assignment_ignores_tentative"
4055 )]
4056 #[test_case(
4057 State::Bound(Bound::AwaitingAssignment{lease_state: build_test_lease_state()}),
4058 AddressAssignmentState::Assigned,
4059 AddrAssignmentChangeExpect::EnterAssigned;
4060 "bound_awaiting_assignment_enter_assigned"
4061 )]
4062 #[test_case(
4063 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
4064 AddressAssignmentState::Tentative,
4065 AddrAssignmentChangeExpect::Ignore;
4066 "bound_assigned_ignores_tentative"
4067 )]
4068 #[test_case(
4069 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
4070 AddressAssignmentState::Assigned,
4071 AddrAssignmentChangeExpect::Ignore;
4072 "bound_assigned_ignores_assigned"
4073 )]
4074 #[test_case(
4075 State::Renewing(Renewing{lease_state: build_test_lease_state()}),
4076 AddressAssignmentState::Tentative,
4077 AddrAssignmentChangeExpect::Ignore;
4078 "renewing_ignores_tentative"
4079 )]
4080 #[test_case(
4081 State::Renewing(Renewing{lease_state: build_test_lease_state()}),
4082 AddressAssignmentState::Assigned,
4083 AddrAssignmentChangeExpect::Ignore;
4084 "renewing_ignores_assigned"
4085 )]
4086 #[test_case(
4087 State::Rebinding(Rebinding{lease_state: build_test_lease_state()}),
4088 AddressAssignmentState::Tentative,
4089 AddrAssignmentChangeExpect::Ignore;
4090 "rebinding_ignores_tentative"
4091 )]
4092 #[test_case(
4093 State::Rebinding(Rebinding{lease_state: build_test_lease_state()}),
4094 AddressAssignmentState::Assigned,
4095 AddrAssignmentChangeExpect::Ignore;
4096 "rebinding_ignores_assigned"
4097 )]
4098 #[test_case(
4099 State::WaitingToRestart(WaitingToRestart {
4100 waiting_until: TestInstant(WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION)
4101 }),
4102 AddressAssignmentState::Assigned,
4103 AddrAssignmentChangeExpect::Ignore;
4104 "waiting_to_restart_ignores"
4105 )]
4106 fn on_address_assignment_change(
4107 state: State<TestInstant>,
4108 change: AddressAssignmentState,
4109 expect: AddrAssignmentChangeExpect,
4110 ) {
4111 let config = &test_client_config();
4112 let time = &FakeTimeController::new();
4113 let mut rng = FakeRngProvider::new(0);
4114 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4115 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4116 let (_server_end, udp_client_end) = FakeSocket::new_pair();
4117 let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
4118 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4119 let counters = Counters::default();
4120
4121 let address_event_receiver = futures::stream::once(futures::future::ready(
4122 AddressEvent::<FakeRemovedReason>::AssignmentStateChanged(change),
4123 ))
4124 .chain(futures::stream::pending());
4125 let run_result = state
4126 .run(
4127 config,
4128 packet_socket_provider,
4129 udp_socket_provider,
4130 &mut rng,
4131 time,
4132 &mut stop_receiver,
4133 address_event_receiver,
4134 &counters,
4135 )
4136 .now_or_never();
4137
4138 match expect {
4139 AddrAssignmentChangeExpect::EnterAssigned => {
4140 let run_result = run_result.expect("fut should finish when exiting");
4141 assert_matches!(
4142 run_result,
4143 Ok(Step::NextState(Transition::BoundAssigned(Bound::Assigned { .. })))
4144 );
4145 }
4146 AddrAssignmentChangeExpect::Ignore => {
4147 assert_matches!(run_result, None, "fut should not finish when ignored.");
4148 }
4149 }
4150 }
4151
4152 #[test]
4153 fn waiting_to_restart() {
4154 let time = &FakeTimeController::new();
4155
4156 const WAITING_UNTIL: TestInstant = TestInstant(Duration::from_secs(10));
4157
4158 advance(time, Duration::from_secs(3));
4162
4163 let waiting = WaitingToRestart { waiting_until: WAITING_UNTIL };
4164 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4165 let main_fut = waiting.do_waiting_to_restart(time, &mut stop_receiver).fuse();
4166 let mut main_fut = pin!(main_fut);
4167 let mut executor = fasync::TestExecutor::new();
4168 let outcome = run_with_accelerated_time(&mut executor, time, &mut main_fut);
4169 assert_eq!(outcome, WaitingToRestartOutcome::Init(Init));
4170
4171 assert_eq!(time.now(), WAITING_UNTIL);
4172 }
4173
4174 #[test]
4175 fn bound_awaiting_assignment_times_out() {
4176 let time = &FakeTimeController::new();
4177 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4178 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4179 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4180 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4181 let config = &test_client_config();
4182 let counters = Counters::default();
4183 let bound = Bound::AwaitingAssignment { lease_state: build_test_lease_state() };
4184 let main_fut = bound
4185 .do_bound(
4186 config,
4187 time,
4188 &mut stop_receiver,
4189 packet_socket_provider,
4190 address_event_receiver,
4191 &counters,
4192 )
4193 .fuse();
4194 let mut main_fut = pin!(main_fut);
4195 let mut executor = fasync::TestExecutor::new();
4196 let outcome = run_with_accelerated_time(&mut executor, time, &mut main_fut)
4197 .expect("do_bound should succeed");
4198 assert_eq!(outcome, BoundOutcome::Restart(Init::default()));
4199 assert_eq!(
4200 time.now(),
4201 TestInstant(Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()))
4202 );
4203 }
4204
4205 #[test_case(
4206 build_test_lease_state() =>
4207 TestInstant(Duration::from_secs(u64::from(DEFAULT_LEASE_LENGTH_SECONDS) / 2));
4208 "waits default renewal time when not specified")]
4209 #[test_case(
4210 LeaseState {
4211 renewal_time: RelativeTime::Explicit(Duration::from_secs(10)),
4212 ..build_test_lease_state()
4213 } => TestInstant(Duration::from_secs(10));
4214 "waits specified renewal time")]
4215 fn bound_assigned_waits_for_renewal_time(lease_state: LeaseState<TestInstant>) -> TestInstant {
4216 let time = &FakeTimeController::new();
4217 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4218 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4219 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4220 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4221 let config = &test_client_config();
4222 let counters = Counters::default();
4223 let bound = Bound::Assigned { lease_state };
4224 let main_fut = bound
4225 .do_bound(
4226 config,
4227 time,
4228 &mut stop_receiver,
4229 packet_socket_provider,
4230 address_event_receiver,
4231 &counters,
4232 )
4233 .fuse();
4234 let mut main_fut = pin!(main_fut);
4235 let mut executor = fasync::TestExecutor::new();
4236 let outcome = run_with_accelerated_time(&mut executor, time, &mut main_fut)
4237 .expect("do_bound should succeed");
4238 assert_eq!(outcome, BoundOutcome::Renewing(Renewing { lease_state: lease_state.clone() }));
4239 time.now()
4240 }
4241
4242 #[test_case(Bound::Assigned {lease_state: build_test_lease_state()}; "assigned")]
4243 #[test_case(Bound::AwaitingAssignment {lease_state: build_test_lease_state()};
4244 "awaiting_assignment")]
4245 fn bound_obeys_graceful_shutdown(bound: Bound<TestInstant>) {
4246 let time = &FakeTimeController::new();
4247 let (stop_sender, mut stop_receiver) = mpsc::unbounded();
4248 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4249 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4250 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4251 let config = &test_client_config();
4252 let counters = Counters::default();
4253 let bound_fut = bound
4254 .do_bound(
4255 &config,
4256 time,
4257 &mut stop_receiver,
4258 packet_socket_provider,
4259 address_event_receiver,
4260 &counters,
4261 )
4262 .fuse();
4263
4264 stop_sender.unbounded_send(()).expect("send should succeed");
4265 assert_eq!(
4266 bound_fut
4267 .now_or_never()
4268 .expect("should have completed")
4269 .expect("do_bound should succeed"),
4270 BoundOutcome::GracefulShutdown
4271 );
4272 }
4273
4274 fn build_test_renewing_state(
4275 lease_length: Duration,
4276 renewal_time: Option<Duration>,
4277 rebinding_time: Option<Duration>,
4278 ) -> Renewing<TestInstant> {
4279 Renewing {
4280 lease_state: build_test_lease_state_with_times(
4281 lease_length,
4282 renewal_time,
4283 rebinding_time,
4284 ),
4285 }
4286 }
4287
4288 #[test]
4289 fn do_renewing_obeys_graceful_shutdown() {
4290 initialize_logging();
4291 let counters = Counters::default();
4292
4293 let renewing = build_test_renewing_state(
4294 Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4295 None,
4296 None,
4297 );
4298 let client_config = &test_client_config();
4299
4300 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4301 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4302 let (_server_end, udp_client_end) = FakeSocket::new_pair();
4303 let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
4304 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4305 let (stop_sender, mut stop_receiver) = mpsc::unbounded();
4306 let time = &FakeTimeController::new();
4307
4308 let renewing_fut = renewing
4309 .do_renewing(
4310 client_config,
4311 udp_socket_provider,
4312 packet_socket_provider,
4313 time,
4314 &mut stop_receiver,
4315 address_event_receiver,
4316 &counters,
4317 )
4318 .fuse();
4319 let mut renewing_fut = pin!(renewing_fut);
4320
4321 let mut executor = fasync::TestExecutor::new();
4322 assert_matches!(executor.run_until_stalled(&mut renewing_fut), std::task::Poll::Pending);
4323
4324 stop_sender.unbounded_send(()).expect("sending stop signal should succeed");
4325
4326 let renewing_result = renewing_fut.now_or_never().expect(
4327 "renewing_fut should complete after single poll after stop signal has been sent",
4328 );
4329
4330 assert_matches!(renewing_result, Ok(RenewingOutcome::GracefulShutdown));
4331 }
4332
4333 #[track_caller]
4334 fn assert_outgoing_message_when_assigned_address(
4335 got_message: &dhcp_protocol::Message,
4336 fields: VaryingOutgoingMessageFields,
4337 ) {
4338 let VaryingOutgoingMessageFields { xid, options } = fields;
4339 let want_message = dhcp_protocol::Message {
4340 op: dhcp_protocol::OpCode::BOOTREQUEST,
4341 xid,
4342 secs: 0,
4343 bdcast_flag: false,
4344 ciaddr: YIADDR,
4345 yiaddr: Ipv4Addr::UNSPECIFIED,
4346 siaddr: Ipv4Addr::UNSPECIFIED,
4347 giaddr: Ipv4Addr::UNSPECIFIED,
4348 chaddr: TEST_MAC_ADDRESS,
4349 sname: BString::default(),
4350 file: BString::default(),
4351 options,
4352 };
4353 assert_eq!(got_message, &want_message);
4354 }
4355
4356 #[test]
4357 fn do_renewing_sends_requests() {
4358 initialize_logging();
4359
4360 const LEASE_LENGTH: Duration = Duration::from_secs(100000);
4362
4363 const RENEWAL_TIME: Duration = Duration::from_secs(0);
4365 const REBINDING_TIME: Duration = Duration::from_secs(1024);
4366
4367 let renewing =
4368 build_test_renewing_state(LEASE_LENGTH, Some(RENEWAL_TIME), Some(REBINDING_TIME));
4369 let client_config = &test_client_config();
4370
4371 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4372 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4373 let (udp_server_end, udp_client_end) = FakeSocket::new_pair();
4374 let (binds_sender, mut binds_receiver) = mpsc::unbounded();
4375 let udp_socket_provider =
4376 &FakeSocketProvider::new_with_events(udp_client_end, binds_sender);
4377 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4378 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4379 let time = &FakeTimeController::new();
4380 let counters = Counters::default();
4381 let renewing_fut = pin!(
4382 renewing
4383 .do_renewing(
4384 client_config,
4385 udp_socket_provider,
4386 packet_socket_provider,
4387 time,
4388 &mut stop_receiver,
4389 address_event_receiver,
4390 &counters
4391 )
4392 .fuse()
4393 );
4394
4395 let expected_times_requests_are_sent =
4398 [1024, 512, 256, 128, 64, 4].map(|time_remaining_when_request_is_sent| {
4399 Duration::from_secs(1024 - time_remaining_when_request_is_sent)
4400 });
4401
4402 let receive_fut =
4403 pin!(
4404 async {
4405 for expected_time in expected_times_requests_are_sent {
4406 let mut recv_buf = [0u8; BUFFER_SIZE];
4407 let DatagramInfo { length, address } = udp_server_end
4408 .recv_from(&mut recv_buf)
4409 .await
4410 .expect("recv_from on test socket should succeed");
4411
4412 assert_eq!(
4413 address,
4414 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
4415 SERVER_IP,
4416 SERVER_PORT.get()
4417 ))
4418 );
4419 assert_eq!(time.now(), TestInstant(expected_time));
4420 let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
4421 .expect("received packet should parse as DHCP message");
4422
4423 assert_outgoing_message_when_assigned_address(
4424 &msg,
4425 VaryingOutgoingMessageFields {
4426 xid: msg.xid,
4427 options: vec![
4428 dhcp_protocol::DhcpOption::DhcpMessageType(
4429 dhcp_protocol::MessageType::DHCPREQUEST,
4430 ),
4431 dhcp_protocol::DhcpOption::ParameterRequestList(
4432 test_requested_parameters()
4433 .iter_keys()
4434 .collect::<Vec<_>>()
4435 .try_into()
4436 .expect("should fit parameter request list size constraints"),
4437 ),
4438 ],
4439 },
4440 );
4441 }
4442 }
4443 .fuse()
4444 );
4445
4446 let main_future = async { join!(renewing_fut, receive_fut) };
4447 let mut main_future = pin!(main_future);
4448
4449 let mut executor = fasync::TestExecutor::new();
4450 let (requesting_result, ()) =
4451 run_with_accelerated_time(&mut executor, time, &mut main_future);
4452
4453 assert_matches!(requesting_result, Ok(RenewingOutcome::Rebinding(_)));
4454 assert_matches!(udp_server_end.recv_from(&mut []).now_or_never(), None);
4455
4456 let bound_socket_addr = binds_receiver
4457 .next()
4458 .now_or_never()
4459 .expect("should have completed")
4460 .expect("should be present");
4461 assert_eq!(
4462 bound_socket_addr,
4463 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(YIADDR, CLIENT_PORT.into()))
4464 );
4465 assert_eq!(
4466 counters.renewing.messaging.send_message.load(),
4467 expected_times_requests_are_sent.len()
4468 );
4469 assert_eq!(
4470 counters.renewing.messaging.recv_time_out.load(),
4471 expected_times_requests_are_sent.len()
4472 );
4473 }
4474
4475 #[derive(PartialEq, Debug)]
4476 struct RenewingTestResult {
4477 outcome: RenewingOutcome<TestInstant, FakeRemovedReason>,
4478 counters: RenewingTestCounters,
4479 }
4480
4481 #[derive(PartialEq, Eq, Debug, Default)]
4482 struct RenewingTestCounters {
4483 send_message: usize,
4484 recv_time_out: usize,
4485 recv_failed_dhcp_parse: usize,
4486 recv_wrong_xid: usize,
4487 recv_wrong_chaddr: usize,
4488 recv_message: usize,
4489 recv_nak: usize,
4490 recv_missing_option: usize,
4491 }
4492
4493 #[test_case(VaryingIncomingMessageFields {
4494 yiaddr: YIADDR,
4495 options: [
4496 dhcp_protocol::DhcpOption::DhcpMessageType(
4497 dhcp_protocol::MessageType::DHCPACK,
4498 ),
4499 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4500 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4501 DEFAULT_LEASE_LENGTH_SECONDS,
4502 ),
4503 ]
4504 .into_iter()
4505 .chain(test_parameter_values())
4506 .collect(),
4507 } => RenewingTestResult {
4508 outcome: RenewingOutcome::Renewed(LeaseState {
4509 discover_options: TEST_DISCOVER_OPTIONS,
4510 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
4511 .try_into()
4512 .expect("should be specified"),
4513 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
4514 .try_into()
4515 .expect("should be specified"),
4516 ip_address_lease_time: RelativeTime::Explicit(std::time::Duration::from_secs(
4517 DEFAULT_LEASE_LENGTH_SECONDS.into()
4518 )),
4519 renewal_time: DEFAULT_RENEWAL_TIME,
4520 rebinding_time: DEFAULT_REBINDING_TIME,
4521 start_time: TestInstant(std::time::Duration::from_secs(0)),
4522 }, test_parameter_values().into_iter().collect()),
4523 counters: RenewingTestCounters {
4524 send_message: 1,
4525 recv_message: 1,
4526 ..Default::default()
4527 }
4528 }; "successfully renews after receiving DHCPACK")]
4529 #[test_case(VaryingIncomingMessageFields {
4530 yiaddr: YIADDR,
4531 options: [
4532 dhcp_protocol::DhcpOption::DhcpMessageType(
4533 dhcp_protocol::MessageType::DHCPACK,
4534 ),
4535 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4536 ]
4537 .into_iter()
4538 .chain(test_parameter_values())
4539 .collect(),
4540 } => RenewingTestResult {
4541 outcome: RenewingOutcome::Renewed(LeaseState {
4542 discover_options: TEST_DISCOVER_OPTIONS,
4543 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
4544 .try_into()
4545 .expect("should be specified"),
4546 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
4547 .try_into()
4548 .expect("should be specified"),
4549 ip_address_lease_time: RelativeTime::Previous(std::time::Duration::from_secs(
4550 DEFAULT_LEASE_LENGTH_SECONDS.into()
4551 )),
4552 renewal_time: DEFAULT_RENEWAL_TIME,
4553 rebinding_time: DEFAULT_REBINDING_TIME,
4554 start_time: TestInstant(std::time::Duration::from_secs(0)),
4555 }, test_parameter_values().into_iter().collect()),
4556 counters: RenewingTestCounters {
4557 send_message: 1,
4558 recv_message: 1,
4559 ..Default::default()
4560 }
4561 }; "successfully renews after receiving DHCPACK without lease time")]
4562 #[test_case(VaryingIncomingMessageFields {
4563 yiaddr: OTHER_ADDR,
4564 options: [
4565 dhcp_protocol::DhcpOption::DhcpMessageType(
4566 dhcp_protocol::MessageType::DHCPACK,
4567 ),
4568 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4569 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4570 DEFAULT_LEASE_LENGTH_SECONDS,
4571 ),
4572 ]
4573 .into_iter()
4574 .chain(test_parameter_values())
4575 .collect(),
4576 } => RenewingTestResult {
4577 outcome: RenewingOutcome::NewAddress(LeaseState {
4578 discover_options: TEST_DISCOVER_OPTIONS,
4579 yiaddr: net_types::ip::Ipv4Addr::from(OTHER_ADDR)
4580 .try_into()
4581 .expect("should be specified"),
4582 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
4583 .try_into()
4584 .expect("should be specified"),
4585 ip_address_lease_time: RelativeTime::Explicit(
4586 std::time::Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into())
4587 ),
4588 renewal_time: DEFAULT_RENEWAL_TIME,
4589 rebinding_time: DEFAULT_REBINDING_TIME,
4590 start_time: TestInstant(std::time::Duration::from_secs(0)),
4591 }, test_parameter_values().into_iter().collect()),
4592 counters: RenewingTestCounters {
4593 send_message: 1,
4594 recv_message: 1,
4595 ..Default::default()
4596 }
4597 }; "observes new address from DHCPACK")]
4598 #[test_case(VaryingIncomingMessageFields {
4599 yiaddr: YIADDR,
4600 options: [
4601 dhcp_protocol::DhcpOption::DhcpMessageType(
4602 dhcp_protocol::MessageType::DHCPACK,
4603 ),
4604 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4605 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4606 DEFAULT_LEASE_LENGTH_SECONDS,
4607 ),
4608 ]
4609 .into_iter()
4610 .chain(test_parameter_values_excluding_subnet_mask())
4611 .collect(),
4612 } => RenewingTestResult {
4613 outcome: RenewingOutcome::Rebinding(
4614 Rebinding {
4615 lease_state: build_test_lease_state()
4616 }),
4617 counters: RenewingTestCounters {
4618 send_message: 2,
4619 recv_time_out: 2,
4620 recv_message: 1,
4621 recv_missing_option: 1,
4622 ..Default::default()
4623 },
4624 }; "ignores replies lacking required option SubnetMask")]
4625 #[test_case(VaryingIncomingMessageFields {
4626 yiaddr: Ipv4Addr::UNSPECIFIED,
4627 options: [
4628 dhcp_protocol::DhcpOption::DhcpMessageType(
4629 dhcp_protocol::MessageType::DHCPNAK,
4630 ),
4631 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4632 dhcp_protocol::DhcpOption::Message(NAK_MESSAGE.to_owned()),
4633 ]
4634 .into_iter()
4635 .chain(test_parameter_values())
4636 .collect(),
4637 } => RenewingTestResult {
4638 outcome: RenewingOutcome::Nak(crate::parse::FieldsToRetainFromNak {
4639 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
4640 .try_into()
4641 .expect("should be specified"),
4642 message: Some(NAK_MESSAGE.to_owned()),
4643 client_identifier: None,
4644 }),
4645 counters: RenewingTestCounters {
4646 send_message: 1,
4647 recv_message: 1,
4648 recv_nak: 1,
4649 ..Default::default()
4650 },
4651 }; "transitions to Init after receiving DHCPNAK")]
4652 fn do_renewing_transitions_on_reply(
4653 incoming_message: VaryingIncomingMessageFields,
4654 ) -> RenewingTestResult {
4655 initialize_logging();
4656
4657 let renewing = build_test_renewing_state(
4658 Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4659 None,
4660 None,
4661 );
4662 let client_config = &test_client_config();
4663
4664 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4665 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4666 let (udp_server_end, udp_client_end) = FakeSocket::new_pair();
4667 let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
4668 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4669 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4670 let time = &FakeTimeController::new();
4671 let counters = Counters::default();
4672 let renewing_fut = pin!(
4673 renewing
4674 .do_renewing(
4675 client_config,
4676 udp_socket_provider,
4677 packet_socket_provider,
4678 time,
4679 &mut stop_receiver,
4680 address_event_receiver,
4681 &counters
4682 )
4683 .fuse()
4684 );
4685 let renewing_fut = pin!(renewing_fut);
4686
4687 let server_socket_addr =
4688 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(SERVER_IP, SERVER_PORT.into()));
4689
4690 let server_fut = pin!(
4691 async {
4692 let mut recv_buf = [0u8; BUFFER_SIZE];
4693
4694 let DatagramInfo { length, address } = udp_server_end
4695 .recv_from(&mut recv_buf)
4696 .await
4697 .expect("recv_from on test socket should succeed");
4698 assert_eq!(address, server_socket_addr);
4699
4700 let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
4701 .expect("received packet on test socket should parse as DHCP message");
4702
4703 assert_outgoing_message_when_assigned_address(
4704 &msg,
4705 VaryingOutgoingMessageFields {
4706 xid: msg.xid,
4707 options: vec![
4708 dhcp_protocol::DhcpOption::DhcpMessageType(
4709 dhcp_protocol::MessageType::DHCPREQUEST,
4710 ),
4711 dhcp_protocol::DhcpOption::ParameterRequestList(
4712 test_requested_parameters()
4713 .iter_keys()
4714 .collect::<Vec<_>>()
4715 .try_into()
4716 .expect("should fit parameter request list size constraints"),
4717 ),
4718 ],
4719 },
4720 );
4721
4722 let reply = build_incoming_message(msg.xid, incoming_message);
4723
4724 udp_server_end
4725 .send_to(
4726 &reply.serialize(),
4727 server_socket_addr,
4730 )
4731 .await
4732 .expect("send_to on test socket should succeed");
4733 }
4734 .fuse()
4735 );
4736
4737 let main_future = async move {
4738 let (renewing_result, ()) = join!(renewing_fut, server_fut);
4739 renewing_result
4740 }
4741 .fuse();
4742
4743 let mut main_future = pin!(main_future);
4744
4745 let mut executor = fasync::TestExecutor::new();
4746 let renewing_result = run_with_accelerated_time(&mut executor, time, &mut main_future);
4747
4748 let outcome = assert_matches!(renewing_result, Ok(outcome) => outcome);
4749 let counters = RenewingTestCounters {
4750 send_message: counters.renewing.messaging.send_message.load(),
4751 recv_time_out: counters.renewing.messaging.recv_time_out.load(),
4752 recv_failed_dhcp_parse: counters.renewing.messaging.recv_failed_dhcp_parse.load(),
4753 recv_wrong_xid: counters.renewing.messaging.recv_wrong_xid.load(),
4754 recv_wrong_chaddr: counters.renewing.messaging.recv_wrong_chaddr.load(),
4755 recv_message: counters.renewing.messaging.recv_message.load(),
4756 recv_nak: counters.renewing.recv_nak.load(),
4757 recv_missing_option: counters.renewing.recv_error.missing_required_option.load(),
4758 };
4759 RenewingTestResult { outcome, counters }
4760 }
4761
4762 fn build_test_rebinding_state(
4763 lease_length: Duration,
4764 renewal_time: Option<Duration>,
4765 rebinding_time: Option<Duration>,
4766 ) -> Rebinding<TestInstant> {
4767 Rebinding {
4768 lease_state: build_test_lease_state_with_times(
4769 lease_length,
4770 renewal_time,
4771 rebinding_time,
4772 ),
4773 }
4774 }
4775
4776 #[test]
4777 fn do_rebinding_sends_requests() {
4778 initialize_logging();
4779
4780 const RENEWAL_TIME: Duration = Duration::from_secs(0);
4782 const REBINDING_TIME: Duration = Duration::from_secs(0);
4783 const LEASE_LENGTH: Duration = Duration::from_secs(1024);
4784
4785 let rebinding =
4786 build_test_rebinding_state(LEASE_LENGTH, Some(RENEWAL_TIME), Some(REBINDING_TIME));
4787 let client_config = &test_client_config();
4788
4789 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4790 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4791 let (udp_server_end, udp_client_end) = FakeSocket::new_pair();
4792 let (binds_sender, mut binds_receiver) = mpsc::unbounded();
4793 let udp_socket_provider =
4794 &FakeSocketProvider::new_with_events(udp_client_end, binds_sender);
4795 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4796 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4797 let time = &FakeTimeController::new();
4798 let counters = Counters::default();
4799 let rebinding_fut = pin!(
4800 rebinding
4801 .do_rebinding(
4802 client_config,
4803 udp_socket_provider,
4804 packet_socket_provider,
4805 time,
4806 &mut stop_receiver,
4807 address_event_receiver,
4808 &counters
4809 )
4810 .fuse()
4811 );
4812
4813 let expected_times_requests_are_sent =
4816 [1024, 512, 256, 128, 64, 4].map(|time_remaining_when_request_is_sent| {
4817 Duration::from_secs(1024 - time_remaining_when_request_is_sent)
4818 });
4819
4820 let receive_fut =
4821 pin!(
4822 async {
4823 for expected_time in expected_times_requests_are_sent {
4824 let mut recv_buf = [0u8; BUFFER_SIZE];
4825 let DatagramInfo { length, address } = udp_server_end
4826 .recv_from(&mut recv_buf)
4827 .await
4828 .expect("recv_from on test socket should succeed");
4829
4830 assert_eq!(
4831 address,
4832 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
4833 std::net::Ipv4Addr::BROADCAST,
4834 SERVER_PORT.get()
4835 ))
4836 );
4837 assert_eq!(time.now(), TestInstant(expected_time));
4838 let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
4839 .expect("received packet should parse as DHCP message");
4840
4841 assert_outgoing_message_when_assigned_address(
4842 &msg,
4843 VaryingOutgoingMessageFields {
4844 xid: msg.xid,
4845 options: vec![
4846 dhcp_protocol::DhcpOption::DhcpMessageType(
4847 dhcp_protocol::MessageType::DHCPREQUEST,
4848 ),
4849 dhcp_protocol::DhcpOption::ParameterRequestList(
4850 test_requested_parameters()
4851 .iter_keys()
4852 .collect::<Vec<_>>()
4853 .try_into()
4854 .expect("should fit parameter request list size constraints"),
4855 ),
4856 ],
4857 },
4858 );
4859 }
4860 }
4861 .fuse()
4862 );
4863
4864 let main_future = async { join!(rebinding_fut, receive_fut) };
4865 let mut main_future = pin!(main_future);
4866
4867 let mut executor = fasync::TestExecutor::new();
4868 let (requesting_result, ()) =
4869 run_with_accelerated_time(&mut executor, time, &mut main_future);
4870
4871 assert_matches!(requesting_result, Ok(RebindingOutcome::TimedOut));
4872 assert_matches!(udp_server_end.recv_from(&mut []).now_or_never(), None);
4873
4874 let bound_socket_addr = binds_receiver
4875 .next()
4876 .now_or_never()
4877 .expect("should have completed")
4878 .expect("should be present");
4879 assert_eq!(
4880 bound_socket_addr,
4881 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(YIADDR, CLIENT_PORT.into()))
4882 );
4883 }
4884
4885 #[derive(PartialEq, Debug)]
4886 struct RebindingTestResult {
4887 outcome: RebindingOutcome<TestInstant, FakeRemovedReason>,
4888 counters: RebindingTestCounters,
4889 }
4890
4891 #[derive(PartialEq, Eq, Debug, Default)]
4892 struct RebindingTestCounters {
4893 send_message: usize,
4894 recv_time_out: usize,
4895 recv_failed_dhcp_parse: usize,
4896 recv_wrong_xid: usize,
4897 recv_wrong_chaddr: usize,
4898 recv_message: usize,
4899 recv_nak: usize,
4900 recv_missing_option: usize,
4901 }
4902
4903 #[test_case(VaryingIncomingMessageFields {
4904 yiaddr: YIADDR,
4905 options: [
4906 dhcp_protocol::DhcpOption::DhcpMessageType(
4907 dhcp_protocol::MessageType::DHCPACK,
4908 ),
4909 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4910 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4911 DEFAULT_LEASE_LENGTH_SECONDS,
4912 ),
4913 ]
4914 .into_iter()
4915 .chain(test_parameter_values())
4916 .collect(),
4917 } => RebindingTestResult {
4918 outcome: RebindingOutcome::Renewed(LeaseState {
4919 discover_options: TEST_DISCOVER_OPTIONS,
4920 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
4921 .try_into()
4922 .expect("should be specified"),
4923 server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
4924 .try_into()
4925 .expect("should be specified"),
4926 ip_address_lease_time: RelativeTime::Explicit(std::time::Duration::from_secs(
4927 DEFAULT_LEASE_LENGTH_SECONDS.into()
4928 )),
4929 renewal_time: DEFAULT_RENEWAL_TIME,
4930 rebinding_time: DEFAULT_REBINDING_TIME,
4931 start_time: TestInstant(std::time::Duration::from_secs(0)),
4932 }, test_parameter_values().into_iter().collect()),
4933 counters: RebindingTestCounters {
4934 send_message: 1,
4935 recv_message: 1,
4936 ..Default::default()
4937 }
4938 }; "successfully rebinds after receiving DHCPACK")]
4939 #[test_case(VaryingIncomingMessageFields {
4940 yiaddr: YIADDR,
4941 options: [
4942 dhcp_protocol::DhcpOption::DhcpMessageType(
4943 dhcp_protocol::MessageType::DHCPACK,
4944 ),
4945 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4946 ]
4947 .into_iter()
4948 .chain(test_parameter_values())
4949 .collect(),
4950 } => RebindingTestResult {
4951 outcome: RebindingOutcome::Renewed(LeaseState {
4952 discover_options: TEST_DISCOVER_OPTIONS,
4953 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
4954 .try_into()
4955 .expect("should be specified"),
4956 server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
4957 .try_into()
4958 .expect("should be specified"),
4959 ip_address_lease_time: RelativeTime::Previous(std::time::Duration::from_secs(
4960 DEFAULT_LEASE_LENGTH_SECONDS.into()
4961 )),
4962 renewal_time: DEFAULT_RENEWAL_TIME,
4963 rebinding_time: DEFAULT_REBINDING_TIME,
4964 start_time: TestInstant(std::time::Duration::from_secs(0)),
4965 }, test_parameter_values().into_iter().collect()),
4966 counters: RebindingTestCounters {
4967 send_message: 1,
4968 recv_message: 1,
4969 ..Default::default()
4970 }
4971 }; "successfully rebinds after receiving DHCPACK without lease time")]
4972 #[test_case(VaryingIncomingMessageFields {
4973 yiaddr: OTHER_ADDR,
4974 options: [
4975 dhcp_protocol::DhcpOption::DhcpMessageType(
4976 dhcp_protocol::MessageType::DHCPACK,
4977 ),
4978 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4979 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4980 DEFAULT_LEASE_LENGTH_SECONDS,
4981 ),
4982 ]
4983 .into_iter()
4984 .chain(test_parameter_values())
4985 .collect(),
4986 } => RebindingTestResult {
4987 outcome: RebindingOutcome::NewAddress(LeaseState {
4988 discover_options: TEST_DISCOVER_OPTIONS,
4989 yiaddr: net_types::ip::Ipv4Addr::from(OTHER_ADDR)
4990 .try_into()
4991 .expect("should be specified"),
4992 server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
4993 .try_into()
4994 .expect("should be specified"),
4995 ip_address_lease_time: RelativeTime::Explicit(
4996 std::time::Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into())
4997 ),
4998 renewal_time: DEFAULT_RENEWAL_TIME,
4999 rebinding_time: DEFAULT_REBINDING_TIME,
5000 start_time: TestInstant(std::time::Duration::from_secs(0)),
5001 }, test_parameter_values().into_iter().collect()),
5002 counters: RebindingTestCounters {
5003 send_message: 1,
5004 recv_message: 1,
5005 ..Default::default()
5006 }
5007 } ; "observes new address from DHCPACK")]
5008 #[test_case(VaryingIncomingMessageFields {
5009 yiaddr: YIADDR,
5010 options: [
5011 dhcp_protocol::DhcpOption::DhcpMessageType(
5012 dhcp_protocol::MessageType::DHCPACK,
5013 ),
5014 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
5015 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
5016 DEFAULT_LEASE_LENGTH_SECONDS,
5017 ),
5018 ]
5019 .into_iter()
5020 .chain(test_parameter_values_excluding_subnet_mask())
5021 .collect(),
5022 } => RebindingTestResult {
5023 outcome: RebindingOutcome::TimedOut,
5024 counters: RebindingTestCounters {
5025 send_message: 2,
5026 recv_time_out: 2,
5027 recv_message: 1,
5028 recv_missing_option: 1,
5029 ..Default::default()
5030 }
5031 } ; "ignores replies lacking required option SubnetMask")]
5032 #[test_case(VaryingIncomingMessageFields {
5033 yiaddr: Ipv4Addr::UNSPECIFIED,
5034 options: [
5035 dhcp_protocol::DhcpOption::DhcpMessageType(
5036 dhcp_protocol::MessageType::DHCPNAK,
5037 ),
5038 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
5039 dhcp_protocol::DhcpOption::Message(NAK_MESSAGE.to_owned()),
5040 ]
5041 .into_iter()
5042 .chain(test_parameter_values())
5043 .collect(),
5044 } => RebindingTestResult {
5045 outcome: RebindingOutcome::Nak(crate::parse::FieldsToRetainFromNak {
5046 server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
5047 .try_into()
5048 .expect("should be specified"),
5049 message: Some(NAK_MESSAGE.to_owned()),
5050 client_identifier: None,
5051 }),
5052 counters: RebindingTestCounters {
5053 send_message: 1,
5054 recv_message: 1,
5055 recv_nak: 1,
5056 ..Default::default()
5057 },
5058 } ; "transitions to Init after receiving DHCPNAK")]
5059 fn do_rebinding_transitions_on_reply(
5060 incoming_message: VaryingIncomingMessageFields,
5061 ) -> RebindingTestResult {
5062 initialize_logging();
5063
5064 let rebinding = build_test_rebinding_state(
5065 Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
5066 None,
5067 None,
5068 );
5069 let client_config = &test_client_config();
5070
5071 let (_server_end, packet_client_end) = FakeSocket::new_pair();
5072 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
5073 let (udp_server_end, udp_client_end) = FakeSocket::new_pair();
5074 let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
5075 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
5076 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
5077 let time = &FakeTimeController::new();
5078 let counters = Counters::default();
5079 let rebinding_fut = pin!(
5080 rebinding
5081 .do_rebinding(
5082 client_config,
5083 udp_socket_provider,
5084 packet_socket_provider,
5085 time,
5086 &mut stop_receiver,
5087 address_event_receiver,
5088 &counters
5089 )
5090 .fuse()
5091 );
5092
5093 let server_socket_addr = std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
5094 OTHER_SERVER_IP,
5095 SERVER_PORT.into(),
5096 ));
5097
5098 let server_fut = pin!(
5099 async {
5100 let mut recv_buf = [0u8; BUFFER_SIZE];
5101
5102 let DatagramInfo { length, address } = udp_server_end
5103 .recv_from(&mut recv_buf)
5104 .await
5105 .expect("recv_from on test socket should succeed");
5106 assert_eq!(
5107 address,
5108 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
5109 std::net::Ipv4Addr::BROADCAST,
5110 SERVER_PORT.into()
5111 ))
5112 );
5113
5114 let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
5115 .expect("received packet on test socket should parse as DHCP message");
5116
5117 assert_outgoing_message_when_assigned_address(
5118 &msg,
5119 VaryingOutgoingMessageFields {
5120 xid: msg.xid,
5121 options: vec![
5122 dhcp_protocol::DhcpOption::DhcpMessageType(
5123 dhcp_protocol::MessageType::DHCPREQUEST,
5124 ),
5125 dhcp_protocol::DhcpOption::ParameterRequestList(
5126 test_requested_parameters()
5127 .iter_keys()
5128 .collect::<Vec<_>>()
5129 .try_into()
5130 .expect("should fit parameter request list size constraints"),
5131 ),
5132 ],
5133 },
5134 );
5135
5136 let reply = build_incoming_message(msg.xid, incoming_message);
5137
5138 udp_server_end
5139 .send_to(
5140 &reply.serialize(),
5141 server_socket_addr,
5144 )
5145 .await
5146 .expect("send_to on test socket should succeed");
5147 }
5148 .fuse()
5149 );
5150
5151 let main_future = async move {
5152 let (rebinding_result, ()) = join!(rebinding_fut, server_fut);
5153 rebinding_result
5154 }
5155 .fuse();
5156
5157 let mut main_future = pin!(main_future);
5158
5159 let mut executor = fasync::TestExecutor::new();
5160 let rebinding_result = run_with_accelerated_time(&mut executor, time, &mut main_future);
5161
5162 let outcome = assert_matches!(rebinding_result, Ok(outcome) => outcome);
5163 let counters = RebindingTestCounters {
5164 send_message: counters.rebinding.messaging.send_message.load(),
5165 recv_time_out: counters.rebinding.messaging.recv_time_out.load(),
5166 recv_failed_dhcp_parse: counters.rebinding.messaging.recv_failed_dhcp_parse.load(),
5167 recv_wrong_xid: counters.rebinding.messaging.recv_wrong_xid.load(),
5168 recv_wrong_chaddr: counters.rebinding.messaging.recv_wrong_chaddr.load(),
5169 recv_message: counters.rebinding.messaging.recv_message.load(),
5170 recv_nak: counters.rebinding.recv_nak.load(),
5171 recv_missing_option: counters.rebinding.recv_error.missing_required_option.load(),
5172 };
5173 RebindingTestResult { outcome, counters }
5174 }
5175
5176 struct FailingUdpSocketProvider;
5177
5178 impl deps::UdpSocketProvider for FailingUdpSocketProvider {
5179 type Sock = Rc<FakeSocket<std::net::SocketAddr>>;
5180 async fn bind_new_udp_socket(
5181 &self,
5182 _bound_addr: std::net::SocketAddr,
5183 ) -> Result<Self::Sock, deps::SocketError> {
5184 Err(deps::SocketError::AddrNotAvailable)
5185 }
5186 }
5187
5188 #[test_case(
5189 State::Renewing(build_test_renewing_state(Duration::from_secs(100), None, None));
5190 "renewing"
5191 )]
5192 #[test_case(
5193 State::Rebinding(build_test_rebinding_state(Duration::from_secs(100), None, None));
5194 "rebinding"
5195 )]
5196 fn return_address_removed_on_udp_socket_addr_not_available(state: State<TestInstant>) {
5197 initialize_logging();
5198 let counters = Counters::default();
5199 let client_config = &test_client_config();
5200 let (_server_end, packet_client_end) = FakeSocket::new_pair();
5201 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
5202 let udp_socket_provider = &FailingUdpSocketProvider;
5203 let address_event_receiver =
5204 futures::stream::once(async { AddressEvent::Removed(FakeRemovedReason) }).fuse();
5205 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
5206 let time = &FakeTimeController::new();
5207 let mut rng = FakeRngProvider::new(0);
5208
5209 let mut executor = fasync::TestExecutor::new();
5210 let mut fut = pin!(state.run(
5211 client_config,
5212 packet_socket_provider,
5213 udp_socket_provider,
5214 &mut rng,
5215 time,
5216 &mut stop_receiver,
5217 address_event_receiver,
5218 &counters,
5219 ));
5220
5221 let result = executor.run_until_stalled(&mut fut);
5222 let result = assert_matches!(result, std::task::Poll::Ready(r) => r);
5223 let step = assert_matches!(result, Ok(s) => s);
5224 assert_matches!(step, Step::Exit(ExitReason::AddressRemoved(FakeRemovedReason)));
5225 }
5226}