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