1use crate::deps::{self, DatagramInfo, Instant as _, Socket as _};
8use crate::inspect::{
9 record_optional_duration_secs, Counters, MessagingRelatedCounters, RebindingCounters,
10 RenewingCounters, RequestingCounters, SelectingCounters,
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::{select, select_biased, FutureExt as _, Stream, StreamExt as _, TryStreamExt as _};
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().gen_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.gen_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!(send_with_retransmits(
1180 time,
1181 retransmit_schedule_during_acquisition(rng.get_rng()),
1182 message.as_ref(),
1183 &socket,
1184 Mac::BROADCAST,
1185 *debug_log_prefix,
1186 messaging
1187 )
1188 .fuse());
1189
1190 let mut recv_buf = [0u8; BUFFER_SIZE];
1191 let mut offer_fields_stream = pin!(recv_stream(
1192 &socket,
1193 &mut recv_buf,
1194 |packet, src_addr| {
1195 let _: Mac = src_addr;
1198
1199 let (src_addr, message) =
1200 match parse_incoming_dhcp_message_from_ip_packet(packet, *debug_log_prefix)
1201 .inspect_err(|_| messaging.recv_failed_dhcp_parse.increment())?
1202 {
1203 Some(message) => message,
1204 None => return Ok(None),
1205 };
1206 validate_message(discover_options, client_config, &message)
1207 .inspect_err(|e| e.increment(&messaging))
1208 .context("invalid DHCP message")?;
1209 crate::parse::fields_to_retain_from_selecting(requested_parameters, message)
1210 .inspect_err(|e| recv_error.increment(&e))
1211 .map(|fields| Some((src_addr, fields)))
1212 .context(
1213 "error while retrieving fields to use in DHCPREQUEST from DHCP message",
1214 )
1215 },
1216 *debug_log_prefix,
1217 messaging
1218 )
1219 .try_filter_map(|parse_result| {
1220 futures::future::ok(match parse_result {
1221 Ok(fields) => fields,
1222 Err(error) => {
1223 log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
1224 None
1225 }
1226 })
1227 })
1228 .fuse());
1229
1230 select_biased! {
1231 fields_to_use_in_request_result = offer_fields_stream.select_next_some() => {
1232 let (src_addr, fields_from_offer_to_use_in_request) =
1233 fields_to_use_in_request_result?;
1234
1235 if src_addr != fields_from_offer_to_use_in_request.server_identifier.get() {
1236 log::warn!("{debug_log_prefix} received offer from {src_addr} with \
1237 differing server_identifier = {}",
1238 fields_from_offer_to_use_in_request.server_identifier);
1239 }
1240
1241 Ok(SelectingOutcome::Requesting(Requesting {
1244 discover_options: discover_options.clone(),
1245 fields_from_offer_to_use_in_request,
1246 start_time: *start_time,
1247 }))
1248 },
1249 () = stop_receiver.select_next_some() => {
1250 Ok(SelectingOutcome::GracefulShutdown)
1251 },
1252 send_discovers_result = send_fut => {
1253 send_discovers_result?;
1254 unreachable!("should never stop retransmitting DHCPDISCOVER unless we hit an error");
1255 }
1256 }
1257 }
1258}
1259
1260impl<I: deps::Instant> Selecting<I> {
1261 fn record(&self, inspector: &mut impl Inspector) {
1262 let Self { discover_options, start_time } = self;
1263 inspector.record_instant(diagnostics_traits::instant_property_name!("Start"), start_time);
1264 discover_options.record(inspector);
1265 }
1266}
1267
1268#[derive(thiserror::Error, Debug, PartialEq)]
1269enum ValidateMessageError {
1270 #[error("xid {actual} doesn't match expected xid {expected}")]
1271 WrongXid { expected: u32, actual: u32 },
1272 #[error("chaddr {actual} doesn't match expected chaddr {expected}")]
1273 WrongChaddr { expected: Mac, actual: Mac },
1274}
1275
1276impl ValidateMessageError {
1277 fn increment(&self, counters: &MessagingRelatedCounters) {
1278 match self {
1279 ValidateMessageError::WrongXid { .. } => counters.recv_wrong_xid.increment(),
1280 ValidateMessageError::WrongChaddr { .. } => counters.recv_wrong_chaddr.increment(),
1281 }
1282 }
1283}
1284
1285fn validate_message(
1286 DiscoverOptions { xid: TransactionId(my_xid) }: &DiscoverOptions,
1287 ClientConfig {
1288 client_hardware_address: my_chaddr,
1289 client_identifier: _,
1290 requested_parameters: _,
1291 preferred_lease_time_secs: _,
1292 requested_ip_address: _,
1293 debug_log_prefix: _,
1294 }: &ClientConfig,
1295 dhcp_protocol::Message {
1296 op: _,
1297 xid: msg_xid,
1298 secs: _,
1299 bdcast_flag: _,
1300 ciaddr: _,
1301 yiaddr: _,
1302 siaddr: _,
1303 giaddr: _,
1304 chaddr: msg_chaddr,
1305 sname: _,
1306 file: _,
1307 options: _,
1308 }: &dhcp_protocol::Message,
1309) -> Result<(), ValidateMessageError> {
1310 if *msg_xid != u32::from(*my_xid) {
1311 return Err(ValidateMessageError::WrongXid { expected: my_xid.get(), actual: *msg_xid });
1312 }
1313
1314 if msg_chaddr != my_chaddr {
1315 return Err(ValidateMessageError::WrongChaddr {
1316 expected: *my_chaddr,
1317 actual: *msg_chaddr,
1318 });
1319 }
1320 Ok(())
1321}
1322
1323#[derive(Debug, PartialEq)]
1324pub(crate) enum RequestingOutcome<I> {
1325 RanOutOfRetransmits,
1326 GracefulShutdown,
1327 Bound(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
1328 Nak(crate::parse::FieldsToRetainFromNak),
1329}
1330
1331#[derive(Debug, PartialEq, Clone, Copy)]
1335pub struct Requesting<I> {
1336 discover_options: DiscoverOptions,
1337 fields_from_offer_to_use_in_request: crate::parse::FieldsFromOfferToUseInRequest,
1338 start_time: I,
1339}
1340
1341const NUM_REQUEST_RETRANSMITS: usize = 4;
1345
1346impl<I: deps::Instant> Requesting<I> {
1347 async fn do_requesting<C: deps::Clock<Instant = I>>(
1353 &self,
1354 client_config: &ClientConfig,
1355 packet_socket_provider: &impl deps::PacketSocketProvider,
1356 rng: &mut impl deps::RngProvider,
1357 time: &C,
1358 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1359 counters: &Counters,
1360 ) -> Result<RequestingOutcome<I>, Error> {
1361 let Counters {
1362 requesting: RequestingCounters { messaging, recv_error, recv_nak, .. }, ..
1363 } = counters;
1364 let socket = packet_socket_provider.get_packet_socket().await.map_err(Error::Socket)?;
1365 let Requesting { discover_options, fields_from_offer_to_use_in_request, start_time } = self;
1366 let message = build_request_during_address_acquisition(
1367 client_config,
1368 discover_options,
1369 fields_from_offer_to_use_in_request,
1370 );
1371
1372 let message = crate::parse::serialize_dhcp_message_to_ip_packet(
1373 message,
1374 Ipv4Addr::UNSPECIFIED, CLIENT_PORT,
1376 Ipv4Addr::BROADCAST, SERVER_PORT,
1378 );
1379
1380 let ClientConfig {
1381 client_hardware_address: _,
1382 client_identifier: _,
1383 requested_parameters,
1384 preferred_lease_time_secs: _,
1385 requested_ip_address: _,
1386 debug_log_prefix,
1387 } = client_config;
1388
1389 let mut send_fut = pin!(send_with_retransmits(
1390 time,
1391 retransmit_schedule_during_acquisition(rng.get_rng()).take(NUM_REQUEST_RETRANSMITS),
1392 message.as_ref(),
1393 &socket,
1394 Mac::BROADCAST,
1395 *debug_log_prefix,
1396 messaging
1397 )
1398 .fuse());
1399
1400 let mut recv_buf = [0u8; BUFFER_SIZE];
1401
1402 let mut ack_or_nak_stream = pin!(recv_stream(
1403 &socket,
1404 &mut recv_buf,
1405 |packet, src_addr| {
1406 let _: Mac = src_addr;
1409 let (src_addr, message) =
1410 match parse_incoming_dhcp_message_from_ip_packet(packet, *debug_log_prefix)
1411 .inspect_err(|_| {
1412 messaging.recv_failed_dhcp_parse.increment();
1413 })? {
1414 Some(message) => message,
1415 None => return Ok(None),
1416 };
1417 validate_message(discover_options, client_config, &message)
1418 .inspect_err(|e| e.increment(&messaging))
1419 .context("invalid DHCP message")?;
1420
1421 crate::parse::fields_to_retain_from_response_to_request(
1422 requested_parameters,
1423 message,
1424 )
1425 .inspect_err(|e| recv_error.increment(e))
1426 .context("error extracting needed fields from DHCP message during Requesting")
1427 .map(|fields| Some((src_addr, fields)))
1428 },
1429 *debug_log_prefix,
1430 messaging
1431 )
1432 .try_filter_map(|parse_result| {
1433 futures::future::ok(match parse_result {
1434 Ok(msg) => msg,
1435 Err(error) => {
1436 log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
1437 None
1438 }
1439 })
1440 })
1441 .fuse());
1442
1443 let (src_addr, fields_to_retain) = select_biased! {
1444 fields_to_retain_result = ack_or_nak_stream.select_next_some() => {
1445 fields_to_retain_result?
1446 },
1447 () = stop_receiver.select_next_some() => {
1448 return Ok(RequestingOutcome::GracefulShutdown)
1449 },
1450 send_requests_result = send_fut => {
1451 send_requests_result?;
1452 messaging.recv_time_out.increment();
1453 return Ok(RequestingOutcome::RanOutOfRetransmits)
1454 }
1455 };
1456
1457 match fields_to_retain {
1458 crate::parse::IncomingResponseToRequest::Ack(ack) => {
1459 let crate::parse::FieldsToRetainFromAck {
1460 yiaddr,
1461 server_identifier,
1462 ip_address_lease_time_secs,
1463 renewal_time_value_secs,
1464 rebinding_time_value_secs,
1465 parameters,
1466 } = ack;
1467
1468 let server_identifier = server_identifier.unwrap_or({
1469 let crate::parse::FieldsFromOfferToUseInRequest {
1470 server_identifier,
1471 ip_address_lease_time_secs: _,
1472 ip_address_to_request: _,
1473 } = fields_from_offer_to_use_in_request;
1474 *server_identifier
1475 });
1476
1477 if src_addr != server_identifier.get() {
1478 log::warn!(
1479 "{debug_log_prefix} accepting DHCPACK from {src_addr} \
1480 with differing server_identifier = {server_identifier}"
1481 );
1482 }
1483 Ok(RequestingOutcome::Bound(
1484 LeaseState {
1485 discover_options: discover_options.clone(),
1486 yiaddr,
1487 server_identifier,
1488 ip_address_lease_time: Duration::from_secs(
1489 ip_address_lease_time_secs.get().into(),
1490 ),
1491 renewal_time: renewal_time_value_secs
1492 .map(u64::from)
1493 .map(Duration::from_secs),
1494 rebinding_time: rebinding_time_value_secs
1495 .map(u64::from)
1496 .map(Duration::from_secs),
1497 start_time: *start_time,
1498 },
1499 parameters,
1500 ))
1501 }
1502 crate::parse::IncomingResponseToRequest::Nak(nak) => {
1503 recv_nak.increment();
1504 Ok(RequestingOutcome::Nak(nak))
1505 }
1506 }
1507 }
1508
1509 fn record(&self, inspector: &mut impl Inspector) {
1510 let Self { discover_options, start_time, fields_from_offer_to_use_in_request } = self;
1511 inspector.record_instant(diagnostics_traits::instant_property_name!("Start"), start_time);
1512 discover_options.record(inspector);
1513 fields_from_offer_to_use_in_request.record(inspector);
1514 }
1515}
1516
1517fn build_request_during_address_acquisition(
1518 client_config: &ClientConfig,
1519 discover_options: &DiscoverOptions,
1520 crate::parse::FieldsFromOfferToUseInRequest {
1521 server_identifier,
1522 ip_address_lease_time_secs,
1523 ip_address_to_request,
1524 }: &crate::parse::FieldsFromOfferToUseInRequest,
1525) -> dhcp_protocol::Message {
1526 let ClientConfig {
1527 client_hardware_address: _,
1528 client_identifier: _,
1529 requested_parameters: _,
1530 preferred_lease_time_secs,
1531 requested_ip_address: _,
1532 debug_log_prefix: _,
1533 } = client_config;
1534
1535 build_outgoing_message(
1544 client_config,
1545 discover_options,
1546 OutgoingOptions {
1547 ciaddr: None,
1548 requested_ip_address: Some(*ip_address_to_request),
1549 ip_address_lease_time_secs: ip_address_lease_time_secs.or(*preferred_lease_time_secs),
1550 message_type: dhcp_protocol::MessageType::DHCPREQUEST,
1551 server_identifier: Some(*server_identifier),
1552 include_parameter_request_list: true,
1553 },
1554 )
1555}
1556
1557#[derive(Debug, PartialEq)]
1559pub struct NewlyAcquiredLease<I> {
1560 pub ip_address: SpecifiedAddr<net_types::ip::Ipv4Addr>,
1562 pub start_time: I,
1564 pub lease_time: Duration,
1566 pub parameters: Vec<dhcp_protocol::DhcpOption>,
1570}
1571
1572#[derive(Debug, PartialEq)]
1573pub(crate) enum BoundOutcome<I, R> {
1574 GracefulShutdown,
1575 Renewing(Renewing<I>),
1576 Restart(Init),
1577 AddressRemoved(R),
1578 AddressRejected(WaitingToRestart<I>),
1579 Assigned(Bound<I>),
1580}
1581
1582#[derive(Debug, PartialEq, Clone, Copy)]
1586pub enum Bound<I> {
1587 AwaitingAssignment {
1590 lease_state: LeaseState<I>,
1592 },
1593 Assigned {
1596 lease_state: LeaseState<I>,
1598 },
1599}
1600
1601#[derive(Debug, PartialEq, Clone, Copy)]
1603pub struct LeaseState<I> {
1604 discover_options: DiscoverOptions,
1605 yiaddr: SpecifiedAddr<net_types::ip::Ipv4Addr>,
1606 server_identifier: SpecifiedAddr<net_types::ip::Ipv4Addr>,
1607 ip_address_lease_time: Duration,
1608 start_time: I,
1609 renewal_time: Option<Duration>,
1610 rebinding_time: Option<Duration>,
1611}
1612
1613impl<I: deps::Instant> Bound<I> {
1614 async fn do_bound<C: deps::Clock<Instant = I>, R>(
1615 &self,
1616 client_config: &ClientConfig,
1617 time: &C,
1618 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1619 packet_socket_provider: &impl deps::PacketSocketProvider,
1620 address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
1621 _counters: &Counters,
1624 ) -> Result<BoundOutcome<I, R>, Error> {
1625 match self {
1626 Bound::AwaitingAssignment { lease_state } => {
1627 do_bound_awaiting_assignment(
1628 lease_state,
1629 client_config,
1630 time,
1631 stop_receiver,
1632 packet_socket_provider,
1633 address_event_receiver,
1634 )
1635 .await
1636 }
1637 Bound::Assigned { lease_state } => {
1638 do_bound_assigned(
1639 lease_state,
1640 client_config,
1641 time,
1642 stop_receiver,
1643 packet_socket_provider,
1644 address_event_receiver,
1645 )
1646 .await
1647 }
1648 }
1649 }
1650}
1651
1652async fn do_bound_awaiting_assignment<I: deps::Instant, C: deps::Clock<Instant = I>, R>(
1653 lease_state: &LeaseState<I>,
1654 client_config: &ClientConfig,
1655 time: &C,
1656 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1657 packet_socket_provider: &impl deps::PacketSocketProvider,
1658 address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
1659) -> Result<BoundOutcome<I, R>, Error> {
1660 let LeaseState {
1661 discover_options: _,
1662 yiaddr,
1663 server_identifier: _,
1664 ip_address_lease_time,
1665 start_time,
1666 renewal_time: _,
1667 rebinding_time: _,
1668 } = lease_state;
1669
1670 let lease_timeout_fut = time.wait_until(start_time.add(*ip_address_lease_time)).fuse();
1680 let mut lease_timeout_fut = pin!(lease_timeout_fut);
1681
1682 let mut address_removed_or_assigned = pin!(address_event_receiver.filter_map(async |event| {
1683 match event {
1684 AddressEvent::Rejected => {
1685 handle_address_rejection(client_config, lease_state, packet_socket_provider, time)
1686 .map(|result| Some(result.map(BoundOutcome::AddressRejected)))
1687 .await
1688 }
1689 AddressEvent::Removed(reason) => Some(Ok(BoundOutcome::AddressRemoved(reason))),
1690 AddressEvent::AssignmentStateChanged(new_state) => {
1691 match new_state {
1692 AddressAssignmentState::Assigned => {
1693 Some(Ok(BoundOutcome::Assigned(Bound::Assigned {
1694 lease_state: lease_state.clone(),
1695 })))
1696 }
1697 s @ AddressAssignmentState::Tentative
1700 | s @ AddressAssignmentState::Unavailable => {
1701 log::warn!(
1702 "{yiaddr} address state became {s:?} during \
1703 Bound::AwaitingAssignment; ignoring"
1704 );
1705 None
1706 }
1707 }
1708 }
1709 }
1710 }));
1711
1712 let debug_log_prefix = &client_config.debug_log_prefix;
1713 select! {
1714 () = lease_timeout_fut => {
1718 log::info!(
1719 "{debug_log_prefix} Returning to Init due to failing to observe address \
1720 assignment for {yiaddr} before the lease ({ip_address_lease_time:?}) \
1721 expired."
1722 );
1723 Ok(BoundOutcome::Restart(Init::default()))
1724 },
1725 () = stop_receiver.select_next_some() => Ok(BoundOutcome::GracefulShutdown),
1726 result = address_removed_or_assigned.next() => {
1727 result.unwrap_or(Err(Error::AddressEventReceiverEnded))
1728 }
1729 }
1730}
1731
1732async fn do_bound_assigned<I: deps::Instant, C: deps::Clock<Instant = I>, R>(
1733 lease_state: &LeaseState<I>,
1734 client_config: &ClientConfig,
1735 time: &C,
1736 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1737 packet_socket_provider: &impl deps::PacketSocketProvider,
1738 address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
1739) -> Result<BoundOutcome<I, R>, Error> {
1740 let LeaseState {
1741 discover_options: _,
1742 yiaddr,
1743 server_identifier,
1744 ip_address_lease_time,
1745 start_time,
1746 renewal_time,
1747 rebinding_time: _,
1748 } = lease_state;
1749
1750 let renewal_time = renewal_time.unwrap_or(*ip_address_lease_time / 2);
1754
1755 let debug_log_prefix = &client_config.debug_log_prefix;
1756 log::info!(
1757 "{debug_log_prefix} In Bound state; ip_address_lease_time = {}, \
1758 renewal_time = {}, server_identifier = {server_identifier}",
1759 ip_address_lease_time.as_secs(),
1760 renewal_time.as_secs(),
1761 );
1762
1763 let renewal_timeout_fut = time.wait_until(start_time.add(renewal_time)).fuse();
1764 let mut renewal_timeout_fut = pin!(renewal_timeout_fut);
1765
1766 let mut address_removed = pin!(address_event_receiver.filter_map(async |event| match event {
1767 AddressEvent::Rejected => {
1768 handle_address_rejection(client_config, lease_state, packet_socket_provider, time)
1769 .map(|result| Some(result.map(BoundOutcome::AddressRejected)))
1770 .await
1771 }
1772 AddressEvent::Removed(reason) => Some(Ok(BoundOutcome::AddressRemoved(reason))),
1773 AddressEvent::AssignmentStateChanged(new_state) => {
1774 log::warn!(
1777 "{yiaddr} address state became {new_state:?} during Bound::Assigned; ignoring"
1778 );
1779 None
1780 }
1781 }));
1782
1783 select! {
1784 () = renewal_timeout_fut => Ok(BoundOutcome::Renewing(Renewing {
1785 lease_state: lease_state.clone()
1786 })),
1787
1788 () = stop_receiver.select_next_some() => Ok(BoundOutcome::GracefulShutdown),
1789 result = address_removed.next() => result.unwrap_or(Err(Error::AddressEventReceiverEnded)),
1790 }
1791}
1792
1793impl<I: deps::Instant> LeaseState<I> {
1794 fn record(&self, inspector: &mut impl Inspector) {
1795 let Self {
1796 discover_options,
1797 yiaddr,
1798 server_identifier,
1799 ip_address_lease_time,
1800 start_time,
1801 renewal_time,
1802 rebinding_time,
1803 } = self;
1804 inspector.record_instant(diagnostics_traits::instant_property_name!("Start"), start_time);
1805 discover_options.record(inspector);
1806 inspector.record_ip_addr("Yiaddr", **yiaddr);
1807 inspector.record_ip_addr("ServerIdentifier", **server_identifier);
1808 inspector.record_uint("IpAddressLeaseTimeSecs", ip_address_lease_time.as_secs());
1809 record_optional_duration_secs(inspector, "RenewalTimeSecs", *renewal_time);
1810 record_optional_duration_secs(inspector, "RebindingTimeSecs", *rebinding_time);
1811 }
1812}
1813
1814#[derive(Debug, PartialEq)]
1815pub(crate) enum RenewingOutcome<I, R> {
1816 GracefulShutdown,
1817 Renewed(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
1818 NewAddress(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
1825 Nak(crate::parse::FieldsToRetainFromNak),
1826 Rebinding(Rebinding<I>),
1827 AddressRemoved(R),
1828 AddressRejected(WaitingToRestart<I>),
1829}
1830
1831#[derive(Debug, PartialEq, Clone, Copy)]
1835pub struct Renewing<I> {
1836 lease_state: LeaseState<I>,
1837}
1838
1839const RENEW_RETRANSMIT_MINIMUM_DELAY: Duration = Duration::from_secs(60);
1846
1847impl<I: deps::Instant> Renewing<I> {
1848 async fn do_renewing<C: deps::Clock<Instant = I>, R>(
1849 &self,
1850 client_config: &ClientConfig,
1851 udp_socket_provider: &impl deps::UdpSocketProvider,
1855 packet_socket_provider: &impl deps::PacketSocketProvider,
1856 time: &C,
1857 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1858 address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
1859 counters: &Counters,
1860 ) -> Result<RenewingOutcome<I, R>, Error> {
1861 let Counters { renewing: RenewingCounters { messaging, recv_error, recv_nak, .. }, .. } =
1862 counters;
1863 let renewal_start_time = time.now();
1864 let debug_log_prefix = client_config.debug_log_prefix;
1865
1866 let Self {
1867 lease_state:
1868 lease_state @ LeaseState {
1869 discover_options,
1870 yiaddr,
1871 server_identifier,
1872 ip_address_lease_time,
1873 start_time,
1874 renewal_time: _,
1875 rebinding_time,
1876 },
1877 } = self;
1878 let rebinding_time = rebinding_time.unwrap_or(*ip_address_lease_time / 8 * 7);
1879 let socket = udp_socket_provider
1880 .bind_new_udp_socket(std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
1881 yiaddr.get().into(),
1882 CLIENT_PORT.get(),
1883 )))
1884 .await
1885 .map_err(Error::Socket)?;
1886
1887 let message = build_outgoing_message(
1896 client_config,
1897 discover_options,
1898 OutgoingOptions {
1899 ciaddr: Some(*yiaddr),
1900 requested_ip_address: None,
1901 ip_address_lease_time_secs: client_config.preferred_lease_time_secs,
1902 message_type: dhcp_protocol::MessageType::DHCPREQUEST,
1903 server_identifier: None,
1904 include_parameter_request_list: true,
1905 },
1906 );
1907 let message_bytes = message.serialize();
1908
1909 let t2 = start_time.add(rebinding_time);
1910 let server_sockaddr = std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
1911 server_identifier.get().into(),
1912 SERVER_PORT.get(),
1913 ));
1914 let mut send_fut = pin!(send_with_retransmits_at_instants(
1915 time,
1916 std::iter::from_fn(|| {
1917 let now = time.now();
1918 let half_time_until_t2 = now.average(t2);
1919 Some(half_time_until_t2.max(now.add(RENEW_RETRANSMIT_MINIMUM_DELAY)))
1920 }),
1921 message_bytes.as_ref(),
1922 &socket,
1923 server_sockaddr,
1924 debug_log_prefix,
1925 messaging
1926 )
1927 .fuse());
1928
1929 let mut recv_buf = [0u8; BUFFER_SIZE];
1930 let mut responses_stream = pin!(recv_stream(
1931 &socket,
1932 &mut recv_buf,
1933 |packet, addr| {
1934 if addr != server_sockaddr {
1935 return Err(anyhow::Error::from(crate::parse::ParseError::WrongSource(addr)));
1936 }
1937 let message = dhcp_protocol::Message::from_buffer(packet)
1938 .map_err(crate::parse::ParseError::Dhcp)
1939 .context("error while parsing DHCP message from UDP datagram")
1940 .inspect_err(|_| messaging.recv_failed_dhcp_parse.increment())?;
1941 validate_message(discover_options, client_config, &message)
1942 .inspect_err(|e| e.increment(&messaging))
1943 .context("invalid DHCP message")?;
1944 crate::parse::fields_to_retain_from_response_to_request(
1945 &client_config.requested_parameters,
1946 message,
1947 )
1948 .inspect_err(|e| recv_error.increment(e))
1949 .context("error extracting needed fields from DHCP message during Renewing")
1950 },
1951 debug_log_prefix,
1952 messaging
1953 )
1954 .try_filter_map(|parse_result| {
1955 futures::future::ok(match parse_result {
1956 Ok(msg) => Some(msg),
1957 Err(error) => {
1958 log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
1959 None
1960 }
1961 })
1962 })
1963 .fuse());
1964
1965 let mut timeout_fut = pin!(time.wait_until(t2).fuse());
1966
1967 let mut address_removed =
1968 pin!(address_event_receiver.filter_map(async |event| match event {
1969 AddressEvent::Rejected => {
1970 handle_address_rejection(
1971 client_config,
1972 lease_state,
1973 packet_socket_provider,
1974 time,
1975 )
1976 .map(|result| Some(result.map(RenewingOutcome::AddressRejected)))
1977 .await
1978 }
1979 AddressEvent::Removed(reason) => {
1980 Some(Ok(RenewingOutcome::AddressRemoved(reason)))
1981 }
1982 AddressEvent::AssignmentStateChanged(new_state) => {
1983 log::warn!(
1986 "{yiaddr} address state became {new_state:?} during Renewing; ignoring"
1987 );
1988 None
1989 }
1990 }));
1991
1992 let response = select_biased! {
1993 response = responses_stream.select_next_some() => {
1994 response?
1995 },
1996 () = stop_receiver.select_next_some() => return Ok(RenewingOutcome::GracefulShutdown),
1997 result = address_removed.next() => {
1998 return result.unwrap_or(Err(Error::AddressEventReceiverEnded))
1999 }
2000 send_result = send_fut => {
2001 return Err(send_result.expect_err("send_fut should never complete without error"))
2002 },
2003 () = timeout_fut => {
2004 messaging.recv_time_out.increment();
2005 return Ok(RenewingOutcome::Rebinding(
2006 Rebinding { lease_state: lease_state.clone() }
2007 ))
2008 }
2009 };
2010
2011 match response {
2012 crate::parse::IncomingResponseToRequest::Ack(ack) => {
2013 let crate::parse::FieldsToRetainFromAck {
2014 yiaddr: new_yiaddr,
2015 server_identifier: _,
2016 ip_address_lease_time_secs,
2017 renewal_time_value_secs,
2018 rebinding_time_value_secs,
2019 parameters,
2020 } = ack;
2021 let variant = if new_yiaddr == *yiaddr {
2022 log::debug!(
2023 "{debug_log_prefix} renewed with new lease time: {}",
2024 ip_address_lease_time_secs
2025 );
2026 RenewingOutcome::Renewed
2027 } else {
2028 log::info!(
2029 "{debug_log_prefix} obtained different address from renewal: {}",
2030 new_yiaddr
2031 );
2032 RenewingOutcome::NewAddress
2033 };
2034 Ok(variant(
2035 LeaseState {
2036 discover_options: discover_options.clone(),
2037 yiaddr: new_yiaddr,
2038 server_identifier: *server_identifier,
2039 ip_address_lease_time: Duration::from_secs(
2040 ip_address_lease_time_secs.get().into(),
2041 ),
2042 renewal_time: renewal_time_value_secs
2043 .map(u64::from)
2044 .map(Duration::from_secs),
2045 rebinding_time: rebinding_time_value_secs
2046 .map(u64::from)
2047 .map(Duration::from_secs),
2048 start_time: renewal_start_time,
2049 },
2050 parameters,
2051 ))
2052 }
2053 crate::parse::IncomingResponseToRequest::Nak(nak) => {
2054 recv_nak.increment();
2055 Ok(RenewingOutcome::Nak(nak))
2056 }
2057 }
2058 }
2059}
2060
2061#[derive(Debug, PartialEq)]
2063pub struct LeaseRenewal<I> {
2064 pub start_time: I,
2066 pub lease_time: Duration,
2068 pub parameters: Vec<dhcp_protocol::DhcpOption>,
2072}
2073
2074#[derive(Debug, PartialEq, Clone, Copy)]
2079pub struct Rebinding<I> {
2080 lease_state: LeaseState<I>,
2081}
2082
2083#[derive(Debug, PartialEq)]
2084pub(crate) enum RebindingOutcome<I, R> {
2085 GracefulShutdown,
2086 Renewed(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
2087 NewAddress(LeaseState<I>, Vec<dhcp_protocol::DhcpOption>),
2094 Nak(crate::parse::FieldsToRetainFromNak),
2095 TimedOut,
2096 AddressRemoved(R),
2097 AddressRejected(WaitingToRestart<I>),
2098}
2099
2100impl<I: deps::Instant> Rebinding<I> {
2101 async fn do_rebinding<C: deps::Clock<Instant = I>, R>(
2102 &self,
2103 client_config: &ClientConfig,
2104 udp_socket_provider: &impl deps::UdpSocketProvider,
2108 packet_socket_provider: &impl deps::PacketSocketProvider,
2109 time: &C,
2110 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
2111 address_event_receiver: impl FusedStream<Item = AddressEvent<R>>,
2112 counters: &Counters,
2113 ) -> Result<RebindingOutcome<I, R>, Error> {
2114 let Counters {
2115 rebinding: RebindingCounters { messaging, recv_error, recv_nak, .. }, ..
2116 } = counters;
2117 let rebinding_start_time = time.now();
2118 let debug_log_prefix = client_config.debug_log_prefix;
2119
2120 let Self {
2121 lease_state:
2122 lease_state @ LeaseState {
2123 discover_options,
2124 yiaddr,
2125 server_identifier: _,
2126 ip_address_lease_time,
2127 start_time,
2128 renewal_time: _,
2129 rebinding_time: _,
2130 },
2131 } = self;
2132 let socket = udp_socket_provider
2133 .bind_new_udp_socket(std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
2134 yiaddr.get().into(),
2135 CLIENT_PORT.get(),
2136 )))
2137 .await
2138 .map_err(Error::Socket)?;
2139
2140 let message = build_outgoing_message(
2149 client_config,
2150 discover_options,
2151 OutgoingOptions {
2152 ciaddr: Some(*yiaddr),
2153 requested_ip_address: None,
2154 ip_address_lease_time_secs: client_config.preferred_lease_time_secs,
2155 message_type: dhcp_protocol::MessageType::DHCPREQUEST,
2156 server_identifier: None,
2157 include_parameter_request_list: true,
2158 },
2159 );
2160 let message_bytes = message.serialize();
2161
2162 let lease_expiry = start_time.add(*ip_address_lease_time);
2163 let server_sockaddr = std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
2164 Ipv4Addr::BROADCAST,
2165 SERVER_PORT.get(),
2166 ));
2167 let mut send_fut = pin!(send_with_retransmits_at_instants(
2168 time,
2169 std::iter::from_fn(|| {
2170 let now = time.now();
2171 let half_time_until_lease_expiry = now.average(lease_expiry);
2172 Some(half_time_until_lease_expiry.max(now.add(RENEW_RETRANSMIT_MINIMUM_DELAY)))
2173 }),
2174 message_bytes.as_ref(),
2175 &socket,
2176 server_sockaddr,
2177 debug_log_prefix,
2178 messaging
2179 )
2180 .fuse());
2181
2182 let mut recv_buf = [0u8; BUFFER_SIZE];
2183 let mut responses_stream = pin!(recv_stream(
2184 &socket,
2185 &mut recv_buf,
2186 |packet, _addr| {
2187 let message = dhcp_protocol::Message::from_buffer(packet)
2188 .map_err(crate::parse::ParseError::Dhcp)
2189 .context("error while parsing DHCP message from UDP datagram")
2190 .inspect_err(|_| messaging.recv_failed_dhcp_parse.increment())?;
2191 validate_message(discover_options, client_config, &message)
2192 .inspect_err(|e| e.increment(&messaging))
2193 .context("invalid DHCP message")?;
2194 crate::parse::fields_to_retain_from_response_to_request(
2195 &client_config.requested_parameters,
2196 message,
2197 )
2198 .and_then(|response| match response {
2199 crate::parse::IncomingResponseToRequest::Ack(ack) => {
2200 Ok(crate::parse::IncomingResponseToRequest::Ack(
2204 ack.map_server_identifier(|server_identifier| {
2205 server_identifier.ok_or(
2206 crate::parse::IncomingResponseToRequestError::NoServerIdentifier,
2207 )
2208 })?,
2209 ))
2210 }
2211 crate::parse::IncomingResponseToRequest::Nak(nak) => {
2212 Ok(crate::parse::IncomingResponseToRequest::Nak(nak))
2213 }
2214 })
2215 .inspect_err(|e| {
2216 recv_error.increment(e);
2217 })
2218 .context("error extracting needed fields from DHCP message during Rebinding")
2219 },
2220 debug_log_prefix,
2221 messaging
2222 )
2223 .try_filter_map(|parse_result| {
2224 futures::future::ok(match parse_result {
2225 Ok(msg) => Some(msg),
2226 Err(error) => {
2227 log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
2228 None
2229 }
2230 })
2231 })
2232 .fuse());
2233
2234 let mut timeout_fut = pin!(time.wait_until(lease_expiry).fuse());
2235
2236 let mut address_removed =
2237 pin!(address_event_receiver.filter_map(async |event| match event {
2238 AddressEvent::Rejected => {
2239 handle_address_rejection(
2240 client_config,
2241 lease_state,
2242 packet_socket_provider,
2243 time,
2244 )
2245 .map(|result| Some(result.map(RebindingOutcome::AddressRejected)))
2246 .await
2247 }
2248 AddressEvent::Removed(reason) => {
2249 Some(Ok(RebindingOutcome::AddressRemoved(reason)))
2250 }
2251 AddressEvent::AssignmentStateChanged(new_state) => {
2252 log::warn!(
2255 "{yiaddr} address state became {new_state:?} during Rebinding; ignoring"
2256 );
2257 None
2258 }
2259 }));
2260
2261 let response = select_biased! {
2262 response = responses_stream.select_next_some() => {
2263 response?
2264 },
2265 () = stop_receiver.select_next_some() => return Ok(RebindingOutcome::GracefulShutdown),
2266 result = address_removed.next() => {
2267 return result.unwrap_or(Err(Error::AddressEventReceiverEnded))
2268 }
2269 send_result = send_fut => {
2270 return Err(send_result.expect_err("send_fut should never complete without error"))
2271 },
2272 () = timeout_fut => {
2273 messaging.recv_time_out.increment();
2274 return Ok(RebindingOutcome::TimedOut)
2275 }
2276 };
2277
2278 match response {
2279 crate::parse::IncomingResponseToRequest::Ack(ack) => {
2280 let crate::parse::FieldsToRetainFromAck {
2281 yiaddr: new_yiaddr,
2282 server_identifier,
2283 ip_address_lease_time_secs,
2284 renewal_time_value_secs,
2285 rebinding_time_value_secs,
2286 parameters,
2287 } = ack;
2288 let variant = if new_yiaddr == *yiaddr {
2289 log::debug!(
2290 "{debug_log_prefix} rebound with new lease time: {}",
2291 ip_address_lease_time_secs
2292 );
2293 RebindingOutcome::Renewed
2294 } else {
2295 log::info!(
2296 "{debug_log_prefix} obtained different address from rebinding: {}",
2297 new_yiaddr
2298 );
2299 RebindingOutcome::NewAddress
2300 };
2301 Ok(variant(
2302 LeaseState {
2303 discover_options: discover_options.clone(),
2304 yiaddr: new_yiaddr,
2305 server_identifier,
2306 ip_address_lease_time: Duration::from_secs(
2307 ip_address_lease_time_secs.get().into(),
2308 ),
2309 renewal_time: renewal_time_value_secs
2310 .map(u64::from)
2311 .map(Duration::from_secs),
2312 rebinding_time: rebinding_time_value_secs
2313 .map(u64::from)
2314 .map(Duration::from_secs),
2315 start_time: rebinding_start_time,
2316 },
2317 parameters,
2318 ))
2319 }
2320 crate::parse::IncomingResponseToRequest::Nak(nak) => {
2321 recv_nak.increment();
2322 Ok(RebindingOutcome::Nak(nak))
2323 }
2324 }
2325 }
2326}
2327
2328#[cfg(test)]
2329mod test {
2330 use super::*;
2331 use crate::deps::testutil::{
2332 advance, run_until_next_timers_fire, FakeRngProvider, FakeSocket, FakeSocketProvider,
2333 FakeTimeController, TestInstant,
2334 };
2335 use crate::deps::Clock as _;
2336 use assert_matches::assert_matches;
2337 use fuchsia_async as fasync;
2338 use futures::{join, Future};
2339 use itertools::Itertools as _;
2340 use net_declare::net::prefix_length_v4;
2341 use net_declare::{net_mac, std_ip_v4};
2342 use net_types::ip::{Ipv4, PrefixLength};
2343 use simplelog::{Config, LevelFilter, WriteLogger};
2344 use std::cell::RefCell;
2345 use std::rc::Rc;
2346 use test_case::test_case;
2347
2348 fn initialize_logging() {
2349 WriteLogger::init(LevelFilter::Info, Config::default(), std::io::stderr()).unwrap();
2350 }
2351
2352 const TEST_MAC_ADDRESS: Mac = net_mac!("01:01:01:01:01:01");
2353 const TEST_SERVER_MAC_ADDRESS: Mac = net_mac!("02:02:02:02:02:02");
2354 const OTHER_MAC_ADDRESS: Mac = net_mac!("03:03:03:03:03:03");
2355
2356 const SERVER_IP: Ipv4Addr = std_ip_v4!("192.168.1.1");
2357 const OTHER_SERVER_IP: Ipv4Addr = std_ip_v4!("192.168.1.11");
2358 const YIADDR: Ipv4Addr = std_ip_v4!("198.168.1.5");
2359 const OTHER_ADDR: Ipv4Addr = std_ip_v4!("198.168.1.6");
2360 const DEFAULT_LEASE_LENGTH_SECONDS: u32 = 100;
2361 const MAX_LEASE_LENGTH_SECONDS: u32 = 200;
2362 const TEST_PREFIX_LENGTH: PrefixLength<Ipv4> = prefix_length_v4!(24);
2363
2364 fn test_requested_parameters() -> OptionCodeMap<OptionRequested> {
2365 use dhcp_protocol::OptionCode;
2366 [
2367 (OptionCode::SubnetMask, OptionRequested::Required),
2368 (OptionCode::Router, OptionRequested::Optional),
2369 (OptionCode::DomainNameServer, OptionRequested::Optional),
2370 ]
2371 .into_iter()
2372 .collect::<OptionCodeMap<_>>()
2373 }
2374
2375 fn test_parameter_values_excluding_subnet_mask() -> [dhcp_protocol::DhcpOption; 2] {
2376 [
2377 dhcp_protocol::DhcpOption::Router([SERVER_IP].into()),
2378 dhcp_protocol::DhcpOption::DomainNameServer([SERVER_IP, std_ip_v4!("8.8.8.8")].into()),
2379 ]
2380 }
2381
2382 fn test_parameter_values() -> impl IntoIterator<Item = dhcp_protocol::DhcpOption> {
2383 std::iter::once(dhcp_protocol::DhcpOption::SubnetMask(TEST_PREFIX_LENGTH))
2384 .chain(test_parameter_values_excluding_subnet_mask())
2385 }
2386
2387 fn test_client_config() -> ClientConfig {
2388 ClientConfig {
2389 client_hardware_address: TEST_MAC_ADDRESS,
2390 client_identifier: None,
2391 requested_parameters: test_requested_parameters(),
2392 preferred_lease_time_secs: None,
2393 requested_ip_address: None,
2394 debug_log_prefix: DebugLogPrefix { interface_id: NonZeroU64::new(2).unwrap() },
2395 }
2396 }
2397
2398 #[derive(Debug, PartialEq)]
2400 struct FakeRemovedReason;
2401
2402 #[test]
2403 fn do_init_uses_rng() {
2404 let mut rng = FakeRngProvider::new(0);
2405 let time = FakeTimeController::new();
2406 let arbitrary_start_time = std::time::Duration::from_secs(42);
2407 advance(&time, arbitrary_start_time);
2408
2409 let Selecting {
2410 discover_options: DiscoverOptions { xid: xid_a },
2411 start_time: start_time_a,
2412 } = Init.do_init(&mut rng, &time);
2413 let Selecting {
2414 discover_options: DiscoverOptions { xid: xid_b },
2415 start_time: start_time_b,
2416 } = Init.do_init(&mut rng, &time);
2417 assert_ne!(xid_a, xid_b);
2418 assert_eq!(start_time_a, TestInstant(arbitrary_start_time));
2419 assert_eq!(start_time_b, TestInstant(arbitrary_start_time));
2420 }
2421
2422 fn run_with_accelerated_time<F>(
2423 executor: &mut fasync::TestExecutor,
2424 time: &Rc<RefCell<FakeTimeController>>,
2425 main_future: &mut F,
2426 ) -> F::Output
2427 where
2428 F: Future + Unpin,
2429 {
2430 loop {
2431 match run_until_next_timers_fire(executor, time, main_future) {
2432 std::task::Poll::Ready(result) => break result,
2433 std::task::Poll::Pending => (),
2434 }
2435 }
2436 }
2437
2438 fn build_test_selecting_state() -> Selecting<TestInstant> {
2439 Selecting {
2440 discover_options: DiscoverOptions { xid: TransactionId(NonZeroU32::new(1).unwrap()) },
2441 start_time: TestInstant(std::time::Duration::from_secs(0)),
2442 }
2443 }
2444
2445 #[test]
2446 fn do_selecting_obeys_graceful_shutdown() {
2447 initialize_logging();
2448 let counters = Counters::default();
2449
2450 let mut executor = fasync::TestExecutor::new();
2451 let time = FakeTimeController::new();
2452
2453 let selecting = build_test_selecting_state();
2454 let mut rng = FakeRngProvider::new(0);
2455
2456 let (_server_end, client_end) = FakeSocket::new_pair();
2457 let test_socket_provider = FakeSocketProvider::new(client_end);
2458
2459 let client_config = test_client_config();
2460
2461 let (stop_sender, mut stop_receiver) = mpsc::unbounded();
2462
2463 let mut selecting_fut = pin!(selecting
2464 .do_selecting(
2465 &client_config,
2466 &test_socket_provider,
2467 &mut rng,
2468 &time,
2469 &mut stop_receiver,
2470 &counters,
2471 )
2472 .fuse());
2473
2474 let time = &time;
2475
2476 let mut wait_fut = pin!(async {
2477 time.wait_until(TestInstant(std::time::Duration::from_secs(30))).await;
2480 }
2481 .fuse());
2482
2483 {
2484 let main_future = async {
2485 select! {
2486 _ = selecting_fut => unreachable!("should keep retransmitting DHCPDISCOVER forever"),
2487 () = wait_fut => (),
2488 }
2489 };
2490 let mut main_future = pin!(main_future);
2491
2492 run_with_accelerated_time(&mut executor, time, &mut main_future);
2493 }
2494
2495 stop_sender.unbounded_send(()).expect("sending stop signal should succeed");
2496
2497 let selecting_result = selecting_fut.now_or_never().expect(
2498 "selecting_fut should complete after single poll after stop signal has been sent",
2499 );
2500
2501 assert_matches!(selecting_result, Ok(SelectingOutcome::GracefulShutdown));
2502 }
2503
2504 struct VaryingOutgoingMessageFields {
2505 xid: u32,
2506 options: Vec<dhcp_protocol::DhcpOption>,
2507 }
2508
2509 #[track_caller]
2510 fn assert_outgoing_message_when_not_assigned_address(
2511 got_message: &dhcp_protocol::Message,
2512 fields: VaryingOutgoingMessageFields,
2513 ) {
2514 let VaryingOutgoingMessageFields { xid, options } = fields;
2515 let want_message = dhcp_protocol::Message {
2516 op: dhcp_protocol::OpCode::BOOTREQUEST,
2517 xid,
2518 secs: 0,
2519 bdcast_flag: false,
2520 ciaddr: Ipv4Addr::UNSPECIFIED,
2521 yiaddr: Ipv4Addr::UNSPECIFIED,
2522 siaddr: Ipv4Addr::UNSPECIFIED,
2523 giaddr: Ipv4Addr::UNSPECIFIED,
2524 chaddr: TEST_MAC_ADDRESS,
2525 sname: String::new(),
2526 file: String::new(),
2527 options,
2528 };
2529 assert_eq!(got_message, &want_message);
2530 }
2531
2532 #[test]
2533 fn do_selecting_sends_discover() {
2534 initialize_logging();
2535 let counters = Counters::default();
2536
2537 let mut executor = fasync::TestExecutor::new();
2538 let time = FakeTimeController::new();
2539
2540 let selecting = Selecting {
2541 discover_options: DiscoverOptions { xid: TransactionId(NonZeroU32::new(1).unwrap()) },
2542 start_time: TestInstant(std::time::Duration::from_secs(0)),
2543 };
2544 let mut rng = FakeRngProvider::new(0);
2545
2546 let (server_end, client_end) = FakeSocket::new_pair();
2547 let test_socket_provider = FakeSocketProvider::new(client_end);
2548
2549 let client_config = test_client_config();
2550
2551 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
2552
2553 let mut selecting_fut = pin!(selecting
2554 .do_selecting(
2555 &client_config,
2556 &test_socket_provider,
2557 &mut rng,
2558 &time,
2559 &mut stop_receiver,
2560 &counters,
2561 )
2562 .fuse());
2563
2564 let time = &time;
2565
2566 const EXPECTED_RANGES: [(u64, u64); 7] =
2570 [(0, 0), (3, 5), (7, 9), (15, 17), (31, 33), (63, 65), (63, 65)];
2571
2572 let mut receive_fut = pin!(async {
2573 let mut previous_time = std::time::Duration::from_secs(0);
2574
2575 for (start, end) in EXPECTED_RANGES {
2576 let mut recv_buf = [0u8; BUFFER_SIZE];
2577 let DatagramInfo { length, address } = server_end
2578 .recv_from(&mut recv_buf)
2579 .await
2580 .expect("recv_from on test socket should succeed");
2581
2582 assert_eq!(address, Mac::BROADCAST);
2583
2584 let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
2585 &recv_buf[..length],
2586 dhcp_protocol::SERVER_PORT,
2587 )
2588 .expect("received packet should parse as DHCP message");
2589
2590 assert_outgoing_message_when_not_assigned_address(
2591 &msg,
2592 VaryingOutgoingMessageFields {
2593 xid: msg.xid,
2594 options: vec![
2595 dhcp_protocol::DhcpOption::DhcpMessageType(
2596 dhcp_protocol::MessageType::DHCPDISCOVER,
2597 ),
2598 dhcp_protocol::DhcpOption::ParameterRequestList(
2599 test_requested_parameters()
2600 .iter_keys()
2601 .collect::<Vec<_>>()
2602 .try_into()
2603 .expect("should fit parameter request list size constraints"),
2604 ),
2605 ],
2606 },
2607 );
2608
2609 let TestInstant(received_time) = time.now();
2610
2611 let duration_range =
2612 std::time::Duration::from_secs(start)..=std::time::Duration::from_secs(end);
2613 assert!(duration_range.contains(&(received_time - previous_time)));
2614
2615 previous_time = received_time;
2616 }
2617 }
2618 .fuse());
2619
2620 let main_future = async {
2621 select! {
2622 _ = selecting_fut => unreachable!("should keep retransmitting DHCPDISCOVER forever"),
2623 () = receive_fut => (),
2624 }
2625 };
2626 let mut main_future = pin!(main_future);
2627
2628 run_with_accelerated_time(&mut executor, time, &mut main_future);
2629 assert_eq!(counters.selecting.messaging.send_message.load(), EXPECTED_RANGES.len());
2630 assert_eq!(counters.selecting.messaging.recv_time_out.load(), EXPECTED_RANGES.len() - 1);
2631 }
2632
2633 const XID: NonZeroU32 = NonZeroU32::new(1).unwrap();
2634 #[test_case(u32::from(XID), TEST_MAC_ADDRESS => Ok(()) ; "accepts good reply")]
2635 #[test_case(u32::from(XID), TEST_SERVER_MAC_ADDRESS => Err(
2636 ValidateMessageError::WrongChaddr {
2637 expected: TEST_MAC_ADDRESS,
2638 actual: TEST_SERVER_MAC_ADDRESS,
2639 }) ; "rejects wrong chaddr")]
2640 #[test_case(u32::from(XID).wrapping_add(1), TEST_MAC_ADDRESS => Err(
2641 ValidateMessageError::WrongXid {
2642 expected: u32::from(XID),
2643 actual: u32::from(XID).wrapping_add(1),
2644 }) ; "rejects wrong xid")]
2645 fn test_validate_message(
2646 message_xid: u32,
2647 message_chaddr: Mac,
2648 ) -> Result<(), ValidateMessageError> {
2649 let discover_options = DiscoverOptions { xid: TransactionId(XID) };
2650 let client_config = ClientConfig {
2651 client_hardware_address: TEST_MAC_ADDRESS,
2652 client_identifier: None,
2653 requested_parameters: test_requested_parameters(),
2654 preferred_lease_time_secs: None,
2655 requested_ip_address: None,
2656 debug_log_prefix: DebugLogPrefix { interface_id: NonZeroU64::new(2).unwrap() },
2657 };
2658
2659 let reply = dhcp_protocol::Message {
2660 op: dhcp_protocol::OpCode::BOOTREPLY,
2661 xid: message_xid,
2662 secs: 0,
2663 bdcast_flag: false,
2664 ciaddr: Ipv4Addr::UNSPECIFIED,
2665 yiaddr: Ipv4Addr::UNSPECIFIED,
2666 siaddr: Ipv4Addr::UNSPECIFIED,
2667 giaddr: Ipv4Addr::UNSPECIFIED,
2668 chaddr: message_chaddr,
2669 sname: String::new(),
2670 file: String::new(),
2671 options: Vec::new(),
2672 };
2673
2674 validate_message(&discover_options, &client_config, &reply)
2675 }
2676
2677 #[allow(clippy::unused_unit)]
2678 #[test_case(false ; "with no garbage traffic on link")]
2679 #[test_case(true ; "ignoring garbage replies to discover")]
2680 fn do_selecting_good_offer(reply_to_discover_with_garbage: bool) {
2681 initialize_logging();
2682 let counters = Counters::default();
2683
2684 let mut rng = FakeRngProvider::new(0);
2685 let time = FakeTimeController::new();
2686
2687 let arbitrary_start_time = std::time::Duration::from_secs(42);
2688 advance(&time, arbitrary_start_time);
2689
2690 let selecting = Init.do_init(&mut rng, &time);
2691 let TransactionId(xid) = selecting.discover_options.xid;
2692
2693 let (server_end, client_end) = FakeSocket::<Mac>::new_pair();
2694 let test_socket_provider = FakeSocketProvider::new(client_end);
2695
2696 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
2697
2698 let client_config = test_client_config();
2699
2700 let selecting_fut = pin!(selecting
2701 .do_selecting(
2702 &client_config,
2703 &test_socket_provider,
2704 &mut rng,
2705 &time,
2706 &mut stop_receiver,
2707 &counters,
2708 )
2709 .fuse());
2710
2711 let server_fut = pin!(async {
2712 let mut recv_buf = [0u8; BUFFER_SIZE];
2713
2714 if reply_to_discover_with_garbage {
2715 let DatagramInfo { length: _, address: dst_addr } = server_end
2716 .recv_from(&mut recv_buf)
2717 .await
2718 .expect("recv_from on test socket should succeed");
2719 assert_eq!(dst_addr, Mac::BROADCAST);
2720
2721 server_end
2722 .send_to(b"hello", OTHER_MAC_ADDRESS)
2723 .await
2724 .expect("send_to with garbage data should succeed");
2725 }
2726
2727 let DatagramInfo { length, address } = server_end
2728 .recv_from(&mut recv_buf)
2729 .await
2730 .expect("recv_from on test socket should succeed");
2731 assert_eq!(address, Mac::BROADCAST);
2732
2733 let parse_msg = || {
2736 let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
2737 &recv_buf[..length],
2738 dhcp_protocol::SERVER_PORT,
2739 )
2740 .expect("received packet on test socket should parse as DHCP message");
2741 msg
2742 };
2743
2744 let msg = parse_msg();
2745 assert_outgoing_message_when_not_assigned_address(
2746 &parse_msg(),
2747 VaryingOutgoingMessageFields {
2748 xid: msg.xid,
2749 options: vec![
2750 dhcp_protocol::DhcpOption::DhcpMessageType(
2751 dhcp_protocol::MessageType::DHCPDISCOVER,
2752 ),
2753 dhcp_protocol::DhcpOption::ParameterRequestList(
2754 test_requested_parameters()
2755 .iter_keys()
2756 .collect::<Vec<_>>()
2757 .try_into()
2758 .expect("should fit parameter request list size constraints"),
2759 ),
2760 ],
2761 },
2762 );
2763
2764 let build_reply = || {
2765 dhcpv4::server::build_offer(
2766 parse_msg(),
2767 dhcpv4::server::OfferOptions {
2768 offered_ip: YIADDR,
2769 server_ip: SERVER_IP,
2770 lease_length_config: dhcpv4::configuration::LeaseLength {
2771 default_seconds: DEFAULT_LEASE_LENGTH_SECONDS,
2772 max_seconds: MAX_LEASE_LENGTH_SECONDS,
2773 },
2774 renewal_time_value: Some(20),
2778 rebinding_time_value: Some(30),
2779 subnet_mask: TEST_PREFIX_LENGTH,
2780 },
2781 &dhcpv4::server::options_repo(test_parameter_values()),
2782 )
2783 .expect("dhcp server crate error building offer")
2784 };
2785
2786 let reply_with_wrong_xid = dhcp_protocol::Message {
2787 xid: (u32::from(xid).wrapping_add(1)),
2788 yiaddr: OTHER_ADDR,
2792 ..build_reply()
2793 };
2794
2795 let reply_without_subnet_mask = {
2796 let mut reply = build_reply();
2797 let options = std::mem::take(&mut reply.options);
2798 let (subnet_masks, other_options): (Vec<_>, Vec<_>) =
2799 options.into_iter().partition_map(|option| match option {
2800 dhcp_protocol::DhcpOption::SubnetMask(_) => itertools::Either::Left(option),
2801 _ => itertools::Either::Right(option),
2802 });
2803 assert_matches!(
2804 &subnet_masks[..],
2805 &[dhcp_protocol::DhcpOption::SubnetMask(TEST_PREFIX_LENGTH)]
2806 );
2807 reply.options = other_options;
2808
2809 reply.yiaddr = OTHER_ADDR;
2813 reply
2814 };
2815
2816 let good_reply = build_reply();
2817
2818 let send_reply = |reply: dhcp_protocol::Message| async {
2819 let dst_ip = reply.yiaddr;
2820 server_end
2821 .send_to(
2822 crate::parse::serialize_dhcp_message_to_ip_packet(
2823 reply,
2824 SERVER_IP,
2825 SERVER_PORT,
2826 dst_ip,
2827 CLIENT_PORT,
2828 )
2829 .as_ref(),
2830 TEST_SERVER_MAC_ADDRESS,
2833 )
2834 .await
2835 .expect("send_to on test socket should succeed");
2836 };
2837
2838 send_reply(reply_with_wrong_xid).await;
2840
2841 send_reply(reply_without_subnet_mask).await;
2843
2844 send_reply(good_reply).await;
2845 }
2846 .fuse());
2847
2848 let main_future = async move {
2849 let (selecting_result, ()) = join!(selecting_fut, server_fut);
2850 selecting_result
2851 }
2852 .fuse();
2853 let mut main_future = pin!(main_future);
2854 let mut executor = fasync::TestExecutor::new();
2855 let selecting_result = run_with_accelerated_time(&mut executor, &time, &mut main_future);
2856
2857 let requesting = assert_matches!(
2858 selecting_result,
2859 Ok(SelectingOutcome::Requesting(requesting)) => requesting,
2860 "should have successfully transitioned to Requesting"
2861 );
2862
2863 assert_eq!(
2864 requesting,
2865 Requesting {
2866 discover_options: DiscoverOptions { xid: requesting.discover_options.xid },
2867 fields_from_offer_to_use_in_request: crate::parse::FieldsFromOfferToUseInRequest {
2868 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
2869 .try_into()
2870 .expect("should be specified"),
2871 ip_address_lease_time_secs: Some(
2872 NonZeroU32::new(DEFAULT_LEASE_LENGTH_SECONDS).unwrap()
2873 ),
2874 ip_address_to_request: net_types::ip::Ipv4Addr::from(YIADDR)
2875 .try_into()
2876 .expect("should be specified"),
2877 },
2878 start_time: TestInstant(arbitrary_start_time),
2879 }
2880 );
2881 assert_eq!(
2882 counters.selecting.messaging.send_message.load(),
2883 if reply_to_discover_with_garbage { 2 } else { 1 }
2884 );
2885 assert_eq!(
2886 counters.selecting.messaging.recv_time_out.load(),
2887 if reply_to_discover_with_garbage { 1 } else { 0 }
2888 );
2889 assert_eq!(
2890 counters.selecting.messaging.recv_failed_dhcp_parse.load(),
2891 if reply_to_discover_with_garbage { 1 } else { 0 }
2892 );
2893 assert_eq!(counters.selecting.messaging.recv_wrong_xid.load(), 1);
2894 assert_eq!(counters.selecting.messaging.recv_wrong_chaddr.load(), 0);
2895 assert_eq!(counters.selecting.recv_error.missing_required_option.load(), 1);
2896 }
2897
2898 const TEST_XID: TransactionId = TransactionId(NonZeroU32::new(1).unwrap());
2899 const TEST_DISCOVER_OPTIONS: DiscoverOptions = DiscoverOptions { xid: TEST_XID };
2900
2901 fn build_test_requesting_state() -> Requesting<TestInstant> {
2902 Requesting {
2903 discover_options: TEST_DISCOVER_OPTIONS,
2904 start_time: TestInstant(std::time::Duration::from_secs(0)),
2905 fields_from_offer_to_use_in_request: crate::parse::FieldsFromOfferToUseInRequest {
2906 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
2907 .try_into()
2908 .expect("should be specified"),
2909 ip_address_lease_time_secs: Some(
2910 NonZeroU32::new(DEFAULT_LEASE_LENGTH_SECONDS).unwrap(),
2911 ),
2912 ip_address_to_request: net_types::ip::Ipv4Addr::from(YIADDR)
2913 .try_into()
2914 .expect("should be specified"),
2915 },
2916 }
2917 }
2918
2919 #[test]
2920 fn do_requesting_obeys_graceful_shutdown() {
2921 initialize_logging();
2922 let counters = Counters::default();
2923
2924 let time = FakeTimeController::new();
2925
2926 let requesting = build_test_requesting_state();
2927 let mut rng = FakeRngProvider::new(0);
2928
2929 let (_server_end, client_end) = FakeSocket::new_pair();
2930 let test_socket_provider = FakeSocketProvider::new(client_end);
2931
2932 let client_config = test_client_config();
2933
2934 let (stop_sender, mut stop_receiver) = mpsc::unbounded();
2935
2936 let requesting_fut = requesting
2937 .do_requesting(
2938 &client_config,
2939 &test_socket_provider,
2940 &mut rng,
2941 &time,
2942 &mut stop_receiver,
2943 &counters,
2944 )
2945 .fuse();
2946 let mut requesting_fut = pin!(requesting_fut);
2947
2948 let mut executor = fasync::TestExecutor::new();
2949 assert_matches!(executor.run_until_stalled(&mut requesting_fut), std::task::Poll::Pending);
2950
2951 stop_sender.unbounded_send(()).expect("sending stop signal should succeed");
2952
2953 let requesting_result = requesting_fut.now_or_never().expect(
2954 "requesting_fut should complete after single poll after stop signal has been sent",
2955 );
2956
2957 assert_matches!(requesting_result, Ok(RequestingOutcome::GracefulShutdown));
2958 }
2959
2960 #[test]
2961 fn do_requesting_sends_requests() {
2962 initialize_logging();
2963 let counters = Counters::default();
2964
2965 let mut executor = fasync::TestExecutor::new();
2966 let time = FakeTimeController::new();
2967
2968 let requesting = build_test_requesting_state();
2969 let mut rng = FakeRngProvider::new(0);
2970
2971 let (server_end, client_end) = FakeSocket::new_pair();
2972 let test_socket_provider = FakeSocketProvider::new(client_end);
2973
2974 let client_config = test_client_config();
2975
2976 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
2977
2978 let requesting_fut = pin!(requesting
2979 .do_requesting(
2980 &client_config,
2981 &test_socket_provider,
2982 &mut rng,
2983 &time,
2984 &mut stop_receiver,
2985 &counters,
2986 )
2987 .fuse());
2988
2989 let time = &time;
2990
2991 const EXPECTED_RANGES: [(u64, u64); NUM_REQUEST_RETRANSMITS + 1] =
2995 [(0, 0), (3, 5), (7, 9), (15, 17), (31, 33)];
2996
2997 let receive_fut = pin!(async {
2998 let mut previous_time = std::time::Duration::from_secs(0);
2999
3000 for (start, end) in EXPECTED_RANGES {
3001 let mut recv_buf = [0u8; BUFFER_SIZE];
3002 let DatagramInfo { length, address } = server_end
3003 .recv_from(&mut recv_buf)
3004 .await
3005 .expect("recv_from on test socket should succeed");
3006
3007 assert_eq!(address, Mac::BROADCAST);
3008
3009 let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
3010 &recv_buf[..length],
3011 dhcp_protocol::SERVER_PORT,
3012 )
3013 .expect("received packet should parse as DHCP message");
3014
3015 assert_outgoing_message_when_not_assigned_address(
3016 &msg,
3017 VaryingOutgoingMessageFields {
3018 xid: msg.xid,
3019 options: vec![
3020 dhcp_protocol::DhcpOption::RequestedIpAddress(YIADDR),
3021 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3022 DEFAULT_LEASE_LENGTH_SECONDS,
3023 ),
3024 dhcp_protocol::DhcpOption::DhcpMessageType(
3025 dhcp_protocol::MessageType::DHCPREQUEST,
3026 ),
3027 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3028 dhcp_protocol::DhcpOption::ParameterRequestList(
3029 test_requested_parameters()
3030 .iter_keys()
3031 .collect::<Vec<_>>()
3032 .try_into()
3033 .expect("should fit parameter request list size constraints"),
3034 ),
3035 ],
3036 },
3037 );
3038
3039 let TestInstant(received_time) = time.now();
3040
3041 let duration_range =
3042 std::time::Duration::from_secs(start)..=std::time::Duration::from_secs(end);
3043 assert!(duration_range.contains(&(received_time - previous_time)));
3044
3045 previous_time = received_time;
3046 }
3047 }
3048 .fuse());
3049
3050 let main_future = async { join!(requesting_fut, receive_fut) };
3051 let mut main_future = pin!(main_future);
3052
3053 let (requesting_result, ()) =
3054 run_with_accelerated_time(&mut executor, time, &mut main_future);
3055
3056 assert_matches!(requesting_result, Ok(RequestingOutcome::RanOutOfRetransmits));
3057 assert_eq!(counters.requesting.messaging.send_message.load(), EXPECTED_RANGES.len());
3058 assert_eq!(counters.requesting.messaging.recv_time_out.load(), EXPECTED_RANGES.len());
3059 }
3060
3061 struct VaryingIncomingMessageFields {
3062 yiaddr: Ipv4Addr,
3063 options: Vec<dhcp_protocol::DhcpOption>,
3064 }
3065
3066 fn build_incoming_message(
3067 xid: u32,
3068 fields: VaryingIncomingMessageFields,
3069 ) -> dhcp_protocol::Message {
3070 let VaryingIncomingMessageFields { yiaddr, options } = fields;
3071
3072 dhcp_protocol::Message {
3073 op: dhcp_protocol::OpCode::BOOTREPLY,
3074 xid,
3075 secs: 0,
3076 bdcast_flag: false,
3077 ciaddr: Ipv4Addr::UNSPECIFIED,
3078 yiaddr,
3079 siaddr: Ipv4Addr::UNSPECIFIED,
3080 giaddr: Ipv4Addr::UNSPECIFIED,
3081 chaddr: TEST_MAC_ADDRESS,
3082 sname: String::new(),
3083 file: String::new(),
3084 options,
3085 }
3086 }
3087
3088 const NAK_MESSAGE: &str = "something went wrong";
3089
3090 #[derive(PartialEq, Debug)]
3091 struct RequestingTestResult {
3092 outcome: RequestingOutcome<TestInstant>,
3093 counters: RequestingTestCounters,
3094 }
3095
3096 #[derive(PartialEq, Eq, Debug, Default)]
3097 struct RequestingTestCounters {
3098 send_message: usize,
3099 recv_time_out: usize,
3100 recv_failed_dhcp_parse: usize,
3101 recv_wrong_xid: usize,
3102 recv_wrong_chaddr: usize,
3103 recv_message: usize,
3104 recv_nak: usize,
3105 recv_missing_option: usize,
3106 }
3107
3108 #[test_case(VaryingIncomingMessageFields {
3109 yiaddr: YIADDR,
3110 options: [
3111 dhcp_protocol::DhcpOption::DhcpMessageType(
3112 dhcp_protocol::MessageType::DHCPACK,
3113 ),
3114 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3115 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3116 DEFAULT_LEASE_LENGTH_SECONDS,
3117 ),
3118 ]
3119 .into_iter()
3120 .chain(test_parameter_values())
3121 .collect(),
3122 } => RequestingTestResult {
3123 outcome: RequestingOutcome::Bound(LeaseState {
3124 discover_options: TEST_DISCOVER_OPTIONS,
3125 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
3126 .try_into()
3127 .expect("should be specified"),
3128 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3129 .try_into()
3130 .expect("should be specified"),
3131 ip_address_lease_time: std::time::Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3132 renewal_time: None,
3133 rebinding_time: None,
3134 start_time: TestInstant(std::time::Duration::from_secs(0)),
3135 }, test_parameter_values().into_iter().collect()),
3136 counters: RequestingTestCounters {
3137 send_message: 1,
3138 recv_message: 1,
3139 ..Default::default()
3140 }
3141 } ; "transitions to Bound after receiving DHCPACK")]
3142 #[test_case(VaryingIncomingMessageFields {
3143 yiaddr: YIADDR,
3144 options: [
3145 dhcp_protocol::DhcpOption::DhcpMessageType(
3146 dhcp_protocol::MessageType::DHCPACK,
3147 ),
3148 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3149 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3150 DEFAULT_LEASE_LENGTH_SECONDS,
3151 ),
3152 ]
3153 .into_iter()
3154 .chain(test_parameter_values_excluding_subnet_mask())
3155 .collect(),
3156 } => RequestingTestResult {
3157 outcome: RequestingOutcome::RanOutOfRetransmits,
3158 counters: RequestingTestCounters {
3159 send_message: 5,
3160 recv_time_out: 5,
3161 recv_message: 1,
3162 recv_missing_option: 1,
3163 ..Default::default()
3164 },
3165 }; "ignores replies lacking required option SubnetMask")]
3166 #[test_case(VaryingIncomingMessageFields {
3167 yiaddr: Ipv4Addr::UNSPECIFIED,
3168 options: [
3169 dhcp_protocol::DhcpOption::DhcpMessageType(
3170 dhcp_protocol::MessageType::DHCPNAK,
3171 ),
3172 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3173 dhcp_protocol::DhcpOption::Message(NAK_MESSAGE.to_owned()),
3174 ]
3175 .into_iter()
3176 .chain(test_parameter_values())
3177 .collect(),
3178 } => RequestingTestResult {
3179 outcome: RequestingOutcome::Nak(crate::parse::FieldsToRetainFromNak {
3180 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3181 .try_into()
3182 .expect("should be specified"),
3183 message: Some(NAK_MESSAGE.to_owned()),
3184 client_identifier: None,
3185 }),
3186 counters: RequestingTestCounters {
3187 send_message: 1,
3188 recv_message: 1,
3189 recv_nak: 1,
3190 ..Default::default()
3191 },
3192 }; "transitions to Init after receiving DHCPNAK")]
3193 fn do_requesting_transitions_on_reply(
3194 incoming_message: VaryingIncomingMessageFields,
3195 ) -> RequestingTestResult {
3196 initialize_logging();
3197 let counters = Counters::default();
3198
3199 let time = &FakeTimeController::new();
3200
3201 let requesting = build_test_requesting_state();
3202 let mut rng = FakeRngProvider::new(0);
3203
3204 let (server_end, client_end) = FakeSocket::new_pair();
3205 let test_socket_provider = FakeSocketProvider::new(client_end);
3206
3207 let client_config = test_client_config();
3208
3209 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3210
3211 let requesting_fut = pin!(requesting
3212 .do_requesting(
3213 &client_config,
3214 &test_socket_provider,
3215 &mut rng,
3216 time,
3217 &mut stop_receiver,
3218 &counters,
3219 )
3220 .fuse());
3221
3222 let server_fut = pin!(async {
3223 let mut recv_buf = [0u8; BUFFER_SIZE];
3224
3225 let DatagramInfo { length, address } = server_end
3226 .recv_from(&mut recv_buf)
3227 .await
3228 .expect("recv_from on test socket should succeed");
3229 assert_eq!(address, Mac::BROADCAST);
3230
3231 let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
3232 &recv_buf[..length],
3233 dhcp_protocol::SERVER_PORT,
3234 )
3235 .expect("received packet on test socket should parse as DHCP message");
3236
3237 assert_outgoing_message_when_not_assigned_address(
3238 &msg,
3239 VaryingOutgoingMessageFields {
3240 xid: msg.xid,
3241 options: vec![
3242 dhcp_protocol::DhcpOption::RequestedIpAddress(YIADDR),
3243 dhcp_protocol::DhcpOption::IpAddressLeaseTime(DEFAULT_LEASE_LENGTH_SECONDS),
3244 dhcp_protocol::DhcpOption::DhcpMessageType(
3245 dhcp_protocol::MessageType::DHCPREQUEST,
3246 ),
3247 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3248 dhcp_protocol::DhcpOption::ParameterRequestList(
3249 test_requested_parameters()
3250 .iter_keys()
3251 .collect::<Vec<_>>()
3252 .try_into()
3253 .expect("should fit parameter request list size constraints"),
3254 ),
3255 ],
3256 },
3257 );
3258
3259 let reply = build_incoming_message(msg.xid, incoming_message);
3260
3261 server_end
3262 .send_to(
3263 crate::parse::serialize_dhcp_message_to_ip_packet(
3264 reply,
3265 SERVER_IP,
3266 SERVER_PORT,
3267 YIADDR,
3268 CLIENT_PORT,
3269 )
3270 .as_ref(),
3271 TEST_SERVER_MAC_ADDRESS,
3274 )
3275 .await
3276 .expect("send_to on test socket should succeed");
3277 }
3278 .fuse());
3279
3280 let main_future = async move {
3281 let (requesting_result, ()) = join!(requesting_fut, server_fut);
3282 requesting_result
3283 }
3284 .fuse();
3285
3286 let mut main_future = pin!(main_future);
3287
3288 let mut executor = fasync::TestExecutor::new();
3289 let requesting_result = run_with_accelerated_time(&mut executor, time, &mut main_future);
3290
3291 let outcome = assert_matches!(requesting_result, Ok(outcome) => outcome);
3292 let counters = RequestingTestCounters {
3293 send_message: counters.requesting.messaging.send_message.load(),
3294 recv_time_out: counters.requesting.messaging.recv_time_out.load(),
3295 recv_failed_dhcp_parse: counters.requesting.messaging.recv_failed_dhcp_parse.load(),
3296 recv_wrong_xid: counters.requesting.messaging.recv_wrong_xid.load(),
3297 recv_wrong_chaddr: counters.requesting.messaging.recv_wrong_chaddr.load(),
3298 recv_message: counters.requesting.messaging.recv_message.load(),
3299 recv_nak: counters.requesting.recv_nak.load(),
3300 recv_missing_option: counters.requesting.recv_error.missing_required_option.load(),
3301 };
3302 RequestingTestResult { outcome, counters }
3303 }
3304
3305 fn build_test_lease_state() -> LeaseState<TestInstant> {
3306 build_test_lease_state_with_times(
3307 Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3308 None,
3309 None,
3310 )
3311 }
3312
3313 fn build_test_lease_state_with_times(
3314 lease_length: Duration,
3315 renewal_time: Option<Duration>,
3316 rebinding_time: Option<Duration>,
3317 ) -> LeaseState<TestInstant> {
3318 LeaseState {
3319 discover_options: TEST_DISCOVER_OPTIONS,
3320 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR).try_into().expect("should be specified"),
3321 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3322 .try_into()
3323 .expect("should be specified"),
3324 ip_address_lease_time: lease_length,
3325 start_time: TestInstant(std::time::Duration::from_secs(0)),
3326 renewal_time,
3327 rebinding_time,
3328 }
3329 }
3330
3331 fn build_test_newly_acquired_lease() -> NewlyAcquiredLease<TestInstant> {
3332 NewlyAcquiredLease {
3333 ip_address: net_types::ip::Ipv4Addr::from(YIADDR)
3334 .try_into()
3335 .expect("should be specified"),
3336 start_time: TestInstant(std::time::Duration::from_secs(0)),
3337 lease_time: Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3338 parameters: Vec::new(),
3339 }
3340 }
3341
3342 #[test_case(
3343 (
3344 State::Init(Init::default()),
3345 Transition::BoundWithNewLease(
3346 Bound::AwaitingAssignment{lease_state: build_test_lease_state()},
3347 build_test_newly_acquired_lease()
3348 )
3349 ) => matches Some(TransitionEffect::HandleNewLease(_));
3350 "yields newly-acquired lease effect"
3351 )]
3352 #[test_case(
3353 (
3354 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3355 Transition::Init(Init),
3356 ) => matches Some(TransitionEffect::DropLease {address_rejected: false});
3357 "recognizes loss of lease"
3358 )]
3359 #[test_case(
3360 (
3361 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3362 Transition::WaitingToRestart(WaitingToRestart {
3363 waiting_until: TestInstant(WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION)
3364 }),
3365 ) => matches Some(TransitionEffect::DropLease {address_rejected: true});
3366 "recognizes address rejection"
3367 )]
3368
3369 fn apply_transition(
3370 (state, transition): (State<TestInstant>, Transition<TestInstant>),
3371 ) -> Option<TransitionEffect<TestInstant>> {
3372 let (_next_state, effect) = state.apply(&test_client_config(), transition);
3373 effect
3374 }
3375
3376 enum AddrRemovedExpect {
3377 Decline,
3378 Exit,
3379 Ignore,
3380 }
3381
3382 #[test_case(
3383 State::Selecting(build_test_selecting_state()),
3384 AddrRemovedExpect::Ignore;
3385 "selecting_ignore"
3386 )]
3387 #[test_case(
3388 State::Requesting(build_test_requesting_state()),
3389 AddrRemovedExpect::Ignore;
3390 "requesting_ignore"
3391 )]
3392 #[test_case(
3393 State::Bound(Bound::AwaitingAssignment{lease_state: build_test_lease_state()}),
3394 AddrRemovedExpect::Exit;
3395 "bound_awaiting_assignment_exit"
3396 )]
3397 #[test_case(
3398 State::Bound(Bound::AwaitingAssignment{lease_state: build_test_lease_state()}),
3399 AddrRemovedExpect::Decline;
3400 "bound_awaiting_assignment_decline"
3401 )]
3402 #[test_case(
3403 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3404 AddrRemovedExpect::Exit;
3405 "bound_assigned_exit"
3406 )]
3407 #[test_case(
3408 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3409 AddrRemovedExpect::Decline;
3410 "bound_assigned_decline"
3411 )]
3412 #[test_case(
3413 State::Renewing(Renewing{lease_state: build_test_lease_state()}),
3414 AddrRemovedExpect::Exit;
3415 "renewing_exit"
3416 )]
3417 #[test_case(
3418 State::Renewing(Renewing{lease_state: build_test_lease_state()}),
3419 AddrRemovedExpect::Decline;
3420 "renewing_decline"
3421 )]
3422 #[test_case(
3423 State::Rebinding(Rebinding{lease_state: build_test_lease_state()}),
3424 AddrRemovedExpect::Exit;
3425 "rebinding_exit"
3426 )]
3427 #[test_case(
3428 State::Rebinding(Rebinding{lease_state: build_test_lease_state()}),
3429 AddrRemovedExpect::Decline;
3430 "rebinding_decline"
3431 )]
3432 #[test_case(
3433 State::WaitingToRestart(WaitingToRestart {
3434 waiting_until: TestInstant(WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION)
3435 }),
3436 AddrRemovedExpect::Ignore;
3437 "waiting_to_restart_ignore"
3438 )]
3439 fn on_address_removed(state: State<TestInstant>, expect: AddrRemovedExpect) {
3440 let config = &test_client_config();
3441 let time = &FakeTimeController::new();
3442 let mut rng = FakeRngProvider::new(0);
3443 let (server_end, packet_client_end) = FakeSocket::new_pair();
3444 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
3445 let (_server_end, udp_client_end) = FakeSocket::new_pair();
3446 let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
3447 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3448 let counters = Counters::default();
3449
3450 let address_event_receiver = futures::stream::once(async {
3451 match expect {
3452 AddrRemovedExpect::Decline => AddressEvent::Rejected,
3453 AddrRemovedExpect::Exit => AddressEvent::Removed(FakeRemovedReason),
3454 AddrRemovedExpect::Ignore => panic!("address_event_receiver should not be polled"),
3455 }
3456 })
3457 .chain(futures::stream::pending());
3458
3459 let run_result = state
3460 .run(
3461 config,
3462 packet_socket_provider,
3463 udp_socket_provider,
3464 &mut rng,
3465 time,
3466 &mut stop_receiver,
3467 address_event_receiver,
3468 &counters,
3469 )
3470 .now_or_never();
3471
3472 match expect {
3473 AddrRemovedExpect::Decline => {
3474 let run_result = run_result.expect("fut should finish when declining");
3475 let WaitingToRestart { waiting_until } = assert_matches!(
3476 run_result,
3477 Ok(Step::NextState(Transition::WaitingToRestart(waiting))) => waiting
3478 );
3479 assert_eq!(waiting_until, TestInstant(Duration::from_secs(10)));
3480
3481 let mut buf = [0u8; BUFFER_SIZE];
3482 let DatagramInfo { length, address } = server_end
3483 .recv_from(&mut buf)
3484 .now_or_never()
3485 .expect("should be ready")
3486 .expect("should succeed");
3487 assert_eq!(address, Mac::BROADCAST);
3488
3489 let (_src_addr, message) =
3490 crate::parse::parse_dhcp_message_from_ip_packet(&buf[..length], SERVER_PORT)
3491 .expect("should succeed");
3492
3493 use dhcp_protocol::DhcpOption;
3494 assert_outgoing_message_when_not_assigned_address(
3495 &message,
3496 VaryingOutgoingMessageFields {
3497 xid: message.xid,
3498 options: vec![
3499 DhcpOption::RequestedIpAddress(YIADDR),
3500 DhcpOption::DhcpMessageType(dhcp_protocol::MessageType::DHCPDECLINE),
3501 DhcpOption::ServerIdentifier(SERVER_IP),
3502 ],
3503 },
3504 );
3505 }
3506 AddrRemovedExpect::Exit => {
3507 let run_result = run_result.expect("fut should finish when exiting");
3508 assert_matches!(
3509 run_result,
3510 Ok(Step::Exit(ExitReason::AddressRemoved(FakeRemovedReason)))
3511 );
3512 }
3513 AddrRemovedExpect::Ignore => {
3514 assert_matches!(run_result, None, "fut should not finish when ignored.");
3515 }
3516 }
3517 }
3518
3519 enum AddrAssignmentChangeExpect {
3520 Ignore,
3521 EnterAssigned,
3522 }
3523
3524 #[test_case(
3525 State::Selecting(build_test_selecting_state()),
3526 AddressAssignmentState::Assigned,
3527 AddrAssignmentChangeExpect::Ignore;
3528 "selecting_ignores"
3529 )]
3530 #[test_case(
3531 State::Requesting(build_test_requesting_state()),
3532 AddressAssignmentState::Assigned,
3533 AddrAssignmentChangeExpect::Ignore;
3534 "requesting_ignores"
3535 )]
3536 #[test_case(
3537 State::Bound(Bound::AwaitingAssignment{lease_state: build_test_lease_state()}),
3538 AddressAssignmentState::Tentative,
3539 AddrAssignmentChangeExpect::Ignore;
3540 "bound_awaiting_assignment_ignores_tentative"
3541 )]
3542 #[test_case(
3543 State::Bound(Bound::AwaitingAssignment{lease_state: build_test_lease_state()}),
3544 AddressAssignmentState::Assigned,
3545 AddrAssignmentChangeExpect::EnterAssigned;
3546 "bound_awaiting_assignment_enter_assigned"
3547 )]
3548 #[test_case(
3549 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3550 AddressAssignmentState::Tentative,
3551 AddrAssignmentChangeExpect::Ignore;
3552 "bound_assigned_ignores_tentative"
3553 )]
3554 #[test_case(
3555 State::Bound(Bound::Assigned{lease_state: build_test_lease_state()}),
3556 AddressAssignmentState::Assigned,
3557 AddrAssignmentChangeExpect::Ignore;
3558 "bound_assigned_ignores_assigned"
3559 )]
3560 #[test_case(
3561 State::Renewing(Renewing{lease_state: build_test_lease_state()}),
3562 AddressAssignmentState::Tentative,
3563 AddrAssignmentChangeExpect::Ignore;
3564 "renewing_ignores_tentative"
3565 )]
3566 #[test_case(
3567 State::Renewing(Renewing{lease_state: build_test_lease_state()}),
3568 AddressAssignmentState::Assigned,
3569 AddrAssignmentChangeExpect::Ignore;
3570 "renewing_ignores_assigned"
3571 )]
3572 #[test_case(
3573 State::Rebinding(Rebinding{lease_state: build_test_lease_state()}),
3574 AddressAssignmentState::Tentative,
3575 AddrAssignmentChangeExpect::Ignore;
3576 "rebinding_ignores_tentative"
3577 )]
3578 #[test_case(
3579 State::Rebinding(Rebinding{lease_state: build_test_lease_state()}),
3580 AddressAssignmentState::Assigned,
3581 AddrAssignmentChangeExpect::Ignore;
3582 "rebinding_ignores_assigned"
3583 )]
3584 #[test_case(
3585 State::WaitingToRestart(WaitingToRestart {
3586 waiting_until: TestInstant(WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION)
3587 }),
3588 AddressAssignmentState::Assigned,
3589 AddrAssignmentChangeExpect::Ignore;
3590 "waiting_to_restart_ignores"
3591 )]
3592 fn on_address_assignment_change(
3593 state: State<TestInstant>,
3594 change: AddressAssignmentState,
3595 expect: AddrAssignmentChangeExpect,
3596 ) {
3597 let config = &test_client_config();
3598 let time = &FakeTimeController::new();
3599 let mut rng = FakeRngProvider::new(0);
3600 let (_server_end, packet_client_end) = FakeSocket::new_pair();
3601 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
3602 let (_server_end, udp_client_end) = FakeSocket::new_pair();
3603 let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
3604 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3605 let counters = Counters::default();
3606
3607 let address_event_receiver = futures::stream::once(futures::future::ready(
3608 AddressEvent::<FakeRemovedReason>::AssignmentStateChanged(change),
3609 ))
3610 .chain(futures::stream::pending());
3611 let run_result = state
3612 .run(
3613 config,
3614 packet_socket_provider,
3615 udp_socket_provider,
3616 &mut rng,
3617 time,
3618 &mut stop_receiver,
3619 address_event_receiver,
3620 &counters,
3621 )
3622 .now_or_never();
3623
3624 match expect {
3625 AddrAssignmentChangeExpect::EnterAssigned => {
3626 let run_result = run_result.expect("fut should finish when exiting");
3627 assert_matches!(
3628 run_result,
3629 Ok(Step::NextState(Transition::BoundAssigned(Bound::Assigned { .. })))
3630 );
3631 }
3632 AddrAssignmentChangeExpect::Ignore => {
3633 assert_matches!(run_result, None, "fut should not finish when ignored.");
3634 }
3635 }
3636 }
3637
3638 #[test]
3639 fn waiting_to_restart() {
3640 let time = &FakeTimeController::new();
3641
3642 const WAITING_UNTIL: TestInstant = TestInstant(Duration::from_secs(10));
3643
3644 advance(time, Duration::from_secs(3));
3648
3649 let waiting = WaitingToRestart { waiting_until: WAITING_UNTIL };
3650 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3651 let main_fut = waiting.do_waiting_to_restart(time, &mut stop_receiver).fuse();
3652 let mut main_fut = pin!(main_fut);
3653 let mut executor = fasync::TestExecutor::new();
3654 let outcome = run_with_accelerated_time(&mut executor, time, &mut main_fut);
3655 assert_eq!(outcome, WaitingToRestartOutcome::Init(Init));
3656
3657 assert_eq!(time.now(), WAITING_UNTIL);
3658 }
3659
3660 #[test]
3661 fn bound_awaiting_assignment_times_out() {
3662 let time = &FakeTimeController::new();
3663 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3664 let (_server_end, packet_client_end) = FakeSocket::new_pair();
3665 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
3666 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
3667 let config = &test_client_config();
3668 let counters = Counters::default();
3669 let bound = Bound::AwaitingAssignment { lease_state: build_test_lease_state() };
3670 let main_fut = bound
3671 .do_bound(
3672 config,
3673 time,
3674 &mut stop_receiver,
3675 packet_socket_provider,
3676 address_event_receiver,
3677 &counters,
3678 )
3679 .fuse();
3680 let mut main_fut = pin!(main_fut);
3681 let mut executor = fasync::TestExecutor::new();
3682 let outcome = run_with_accelerated_time(&mut executor, time, &mut main_fut)
3683 .expect("do_bound should succeed");
3684 assert_eq!(outcome, BoundOutcome::Restart(Init::default()));
3685 assert_eq!(
3686 time.now(),
3687 TestInstant(Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()))
3688 );
3689 }
3690
3691 #[test_case(
3692 build_test_lease_state() =>
3693 TestInstant(Duration::from_secs(u64::from(DEFAULT_LEASE_LENGTH_SECONDS) / 2));
3694 "waits default renewal time when not specified")]
3695 #[test_case(
3696 LeaseState {
3697 renewal_time: Some(Duration::from_secs(10)),
3698 ..build_test_lease_state()
3699 } => TestInstant(Duration::from_secs(10));
3700 "waits specified renewal time")]
3701 fn bound_assigned_waits_for_renewal_time(lease_state: LeaseState<TestInstant>) -> TestInstant {
3702 let time = &FakeTimeController::new();
3703 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3704 let (_server_end, packet_client_end) = FakeSocket::new_pair();
3705 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
3706 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
3707 let config = &test_client_config();
3708 let counters = Counters::default();
3709 let bound = Bound::Assigned { lease_state };
3710 let main_fut = bound
3711 .do_bound(
3712 config,
3713 time,
3714 &mut stop_receiver,
3715 packet_socket_provider,
3716 address_event_receiver,
3717 &counters,
3718 )
3719 .fuse();
3720 let mut main_fut = pin!(main_fut);
3721 let mut executor = fasync::TestExecutor::new();
3722 let outcome = run_with_accelerated_time(&mut executor, time, &mut main_fut)
3723 .expect("do_bound should succeed");
3724 assert_eq!(outcome, BoundOutcome::Renewing(Renewing { lease_state: lease_state.clone() }));
3725 time.now()
3726 }
3727
3728 #[test_case(Bound::Assigned {lease_state: build_test_lease_state()}; "assigned")]
3729 #[test_case(Bound::AwaitingAssignment {lease_state: build_test_lease_state()};
3730 "awaiting_assignment")]
3731 fn bound_obeys_graceful_shutdown(bound: Bound<TestInstant>) {
3732 let time = &FakeTimeController::new();
3733 let (stop_sender, mut stop_receiver) = mpsc::unbounded();
3734 let (_server_end, packet_client_end) = FakeSocket::new_pair();
3735 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
3736 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
3737 let config = &test_client_config();
3738 let counters = Counters::default();
3739 let bound_fut = bound
3740 .do_bound(
3741 &config,
3742 time,
3743 &mut stop_receiver,
3744 packet_socket_provider,
3745 address_event_receiver,
3746 &counters,
3747 )
3748 .fuse();
3749
3750 stop_sender.unbounded_send(()).expect("send should succeed");
3751 assert_eq!(
3752 bound_fut
3753 .now_or_never()
3754 .expect("should have completed")
3755 .expect("do_bound should succeed"),
3756 BoundOutcome::GracefulShutdown
3757 );
3758 }
3759
3760 fn build_test_renewing_state(
3761 lease_length: Duration,
3762 renewal_time: Option<Duration>,
3763 rebinding_time: Option<Duration>,
3764 ) -> Renewing<TestInstant> {
3765 Renewing {
3766 lease_state: build_test_lease_state_with_times(
3767 lease_length,
3768 renewal_time,
3769 rebinding_time,
3770 ),
3771 }
3772 }
3773
3774 #[test]
3775 fn do_renewing_obeys_graceful_shutdown() {
3776 initialize_logging();
3777 let counters = Counters::default();
3778
3779 let renewing = build_test_renewing_state(
3780 Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3781 None,
3782 None,
3783 );
3784 let client_config = &test_client_config();
3785
3786 let (_server_end, packet_client_end) = FakeSocket::new_pair();
3787 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
3788 let (_server_end, udp_client_end) = FakeSocket::new_pair();
3789 let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
3790 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
3791 let (stop_sender, mut stop_receiver) = mpsc::unbounded();
3792 let time = &FakeTimeController::new();
3793
3794 let renewing_fut = renewing
3795 .do_renewing(
3796 client_config,
3797 udp_socket_provider,
3798 packet_socket_provider,
3799 time,
3800 &mut stop_receiver,
3801 address_event_receiver,
3802 &counters,
3803 )
3804 .fuse();
3805 let mut renewing_fut = pin!(renewing_fut);
3806
3807 let mut executor = fasync::TestExecutor::new();
3808 assert_matches!(executor.run_until_stalled(&mut renewing_fut), std::task::Poll::Pending);
3809
3810 stop_sender.unbounded_send(()).expect("sending stop signal should succeed");
3811
3812 let renewing_result = renewing_fut.now_or_never().expect(
3813 "renewing_fut should complete after single poll after stop signal has been sent",
3814 );
3815
3816 assert_matches!(renewing_result, Ok(RenewingOutcome::GracefulShutdown));
3817 }
3818
3819 #[track_caller]
3820 fn assert_outgoing_message_when_assigned_address(
3821 got_message: &dhcp_protocol::Message,
3822 fields: VaryingOutgoingMessageFields,
3823 ) {
3824 let VaryingOutgoingMessageFields { xid, options } = fields;
3825 let want_message = dhcp_protocol::Message {
3826 op: dhcp_protocol::OpCode::BOOTREQUEST,
3827 xid,
3828 secs: 0,
3829 bdcast_flag: false,
3830 ciaddr: YIADDR,
3831 yiaddr: Ipv4Addr::UNSPECIFIED,
3832 siaddr: Ipv4Addr::UNSPECIFIED,
3833 giaddr: Ipv4Addr::UNSPECIFIED,
3834 chaddr: TEST_MAC_ADDRESS,
3835 sname: String::new(),
3836 file: String::new(),
3837 options,
3838 };
3839 assert_eq!(got_message, &want_message);
3840 }
3841
3842 #[test]
3843 fn do_renewing_sends_requests() {
3844 initialize_logging();
3845
3846 const LEASE_LENGTH: Duration = Duration::from_secs(100000);
3848
3849 const RENEWAL_TIME: Duration = Duration::from_secs(0);
3851 const REBINDING_TIME: Duration = Duration::from_secs(1024);
3852
3853 let renewing =
3854 build_test_renewing_state(LEASE_LENGTH, Some(RENEWAL_TIME), Some(REBINDING_TIME));
3855 let client_config = &test_client_config();
3856
3857 let (_server_end, packet_client_end) = FakeSocket::new_pair();
3858 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
3859 let (udp_server_end, udp_client_end) = FakeSocket::new_pair();
3860 let (binds_sender, mut binds_receiver) = mpsc::unbounded();
3861 let udp_socket_provider =
3862 &FakeSocketProvider::new_with_events(udp_client_end, binds_sender);
3863 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
3864 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3865 let time = &FakeTimeController::new();
3866 let counters = Counters::default();
3867 let renewing_fut = pin!(renewing
3868 .do_renewing(
3869 client_config,
3870 udp_socket_provider,
3871 packet_socket_provider,
3872 time,
3873 &mut stop_receiver,
3874 address_event_receiver,
3875 &counters
3876 )
3877 .fuse());
3878
3879 let expected_times_requests_are_sent =
3882 [1024, 512, 256, 128, 64, 4].map(|time_remaining_when_request_is_sent| {
3883 Duration::from_secs(1024 - time_remaining_when_request_is_sent)
3884 });
3885
3886 let receive_fut = pin!(async {
3887 for expected_time in expected_times_requests_are_sent {
3888 let mut recv_buf = [0u8; BUFFER_SIZE];
3889 let DatagramInfo { length, address } = udp_server_end
3890 .recv_from(&mut recv_buf)
3891 .await
3892 .expect("recv_from on test socket should succeed");
3893
3894 assert_eq!(
3895 address,
3896 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
3897 SERVER_IP,
3898 SERVER_PORT.get()
3899 ))
3900 );
3901 assert_eq!(time.now(), TestInstant(expected_time));
3902 let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
3903 .expect("received packet should parse as DHCP message");
3904
3905 assert_outgoing_message_when_assigned_address(
3906 &msg,
3907 VaryingOutgoingMessageFields {
3908 xid: msg.xid,
3909 options: vec![
3910 dhcp_protocol::DhcpOption::DhcpMessageType(
3911 dhcp_protocol::MessageType::DHCPREQUEST,
3912 ),
3913 dhcp_protocol::DhcpOption::ParameterRequestList(
3914 test_requested_parameters()
3915 .iter_keys()
3916 .collect::<Vec<_>>()
3917 .try_into()
3918 .expect("should fit parameter request list size constraints"),
3919 ),
3920 ],
3921 },
3922 );
3923 }
3924 }
3925 .fuse());
3926
3927 let main_future = async { join!(renewing_fut, receive_fut) };
3928 let mut main_future = pin!(main_future);
3929
3930 let mut executor = fasync::TestExecutor::new();
3931 let (requesting_result, ()) =
3932 run_with_accelerated_time(&mut executor, time, &mut main_future);
3933
3934 assert_matches!(requesting_result, Ok(RenewingOutcome::Rebinding(_)));
3935 assert_matches!(udp_server_end.recv_from(&mut []).now_or_never(), None);
3936
3937 let bound_socket_addr = binds_receiver
3938 .next()
3939 .now_or_never()
3940 .expect("should have completed")
3941 .expect("should be present");
3942 assert_eq!(
3943 bound_socket_addr,
3944 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(YIADDR, CLIENT_PORT.into()))
3945 );
3946 assert_eq!(
3947 counters.renewing.messaging.send_message.load(),
3948 expected_times_requests_are_sent.len()
3949 );
3950 assert_eq!(
3951 counters.renewing.messaging.recv_time_out.load(),
3952 expected_times_requests_are_sent.len()
3953 );
3954 }
3955
3956 #[derive(PartialEq, Debug)]
3957 struct RenewingTestResult {
3958 outcome: RenewingOutcome<TestInstant, FakeRemovedReason>,
3959 counters: RenewingTestCounters,
3960 }
3961
3962 #[derive(PartialEq, Eq, Debug, Default)]
3963 struct RenewingTestCounters {
3964 send_message: usize,
3965 recv_time_out: usize,
3966 recv_failed_dhcp_parse: usize,
3967 recv_wrong_xid: usize,
3968 recv_wrong_chaddr: usize,
3969 recv_message: usize,
3970 recv_nak: usize,
3971 recv_missing_option: usize,
3972 }
3973
3974 #[test_case(VaryingIncomingMessageFields {
3975 yiaddr: YIADDR,
3976 options: [
3977 dhcp_protocol::DhcpOption::DhcpMessageType(
3978 dhcp_protocol::MessageType::DHCPACK,
3979 ),
3980 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3981 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3982 DEFAULT_LEASE_LENGTH_SECONDS,
3983 ),
3984 ]
3985 .into_iter()
3986 .chain(test_parameter_values())
3987 .collect(),
3988 } => RenewingTestResult {
3989 outcome: RenewingOutcome::Renewed(LeaseState {
3990 discover_options: TEST_DISCOVER_OPTIONS,
3991 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
3992 .try_into()
3993 .expect("should be specified"),
3994 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3995 .try_into()
3996 .expect("should be specified"),
3997 ip_address_lease_time: std::time::Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3998 renewal_time: None,
3999 rebinding_time: None,
4000 start_time: TestInstant(std::time::Duration::from_secs(0)),
4001 }, test_parameter_values().into_iter().collect()),
4002 counters: RenewingTestCounters {
4003 send_message: 1,
4004 recv_message: 1,
4005 ..Default::default()
4006 }
4007 }; "successfully renews after receiving DHCPACK")]
4008 #[test_case(VaryingIncomingMessageFields {
4009 yiaddr: OTHER_ADDR,
4010 options: [
4011 dhcp_protocol::DhcpOption::DhcpMessageType(
4012 dhcp_protocol::MessageType::DHCPACK,
4013 ),
4014 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4015 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4016 DEFAULT_LEASE_LENGTH_SECONDS,
4017 ),
4018 ]
4019 .into_iter()
4020 .chain(test_parameter_values())
4021 .collect(),
4022 } => RenewingTestResult {
4023 outcome: RenewingOutcome::NewAddress(LeaseState {
4024 discover_options: TEST_DISCOVER_OPTIONS,
4025 yiaddr: net_types::ip::Ipv4Addr::from(OTHER_ADDR)
4026 .try_into()
4027 .expect("should be specified"),
4028 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
4029 .try_into()
4030 .expect("should be specified"),
4031 ip_address_lease_time: std::time::Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4032 renewal_time: None,
4033 rebinding_time: None,
4034 start_time: TestInstant(std::time::Duration::from_secs(0)),
4035 }, test_parameter_values().into_iter().collect()),
4036 counters: RenewingTestCounters {
4037 send_message: 1,
4038 recv_message: 1,
4039 ..Default::default()
4040 }
4041 }; "observes new address from DHCPACK")]
4042 #[test_case(VaryingIncomingMessageFields {
4043 yiaddr: YIADDR,
4044 options: [
4045 dhcp_protocol::DhcpOption::DhcpMessageType(
4046 dhcp_protocol::MessageType::DHCPACK,
4047 ),
4048 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4049 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4050 DEFAULT_LEASE_LENGTH_SECONDS,
4051 ),
4052 ]
4053 .into_iter()
4054 .chain(test_parameter_values_excluding_subnet_mask())
4055 .collect(),
4056 } => RenewingTestResult {
4057 outcome: RenewingOutcome::Rebinding(
4058 Rebinding {
4059 lease_state: build_test_lease_state()
4060 }),
4061 counters: RenewingTestCounters {
4062 send_message: 2,
4063 recv_time_out: 2,
4064 recv_message: 1,
4065 recv_missing_option: 1,
4066 ..Default::default()
4067 },
4068 }; "ignores replies lacking required option SubnetMask")]
4069 #[test_case(VaryingIncomingMessageFields {
4070 yiaddr: Ipv4Addr::UNSPECIFIED,
4071 options: [
4072 dhcp_protocol::DhcpOption::DhcpMessageType(
4073 dhcp_protocol::MessageType::DHCPNAK,
4074 ),
4075 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
4076 dhcp_protocol::DhcpOption::Message(NAK_MESSAGE.to_owned()),
4077 ]
4078 .into_iter()
4079 .chain(test_parameter_values())
4080 .collect(),
4081 } => RenewingTestResult {
4082 outcome: RenewingOutcome::Nak(crate::parse::FieldsToRetainFromNak {
4083 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
4084 .try_into()
4085 .expect("should be specified"),
4086 message: Some(NAK_MESSAGE.to_owned()),
4087 client_identifier: None,
4088 }),
4089 counters: RenewingTestCounters {
4090 send_message: 1,
4091 recv_message: 1,
4092 recv_nak: 1,
4093 ..Default::default()
4094 },
4095 }; "transitions to Init after receiving DHCPNAK")]
4096 fn do_renewing_transitions_on_reply(
4097 incoming_message: VaryingIncomingMessageFields,
4098 ) -> RenewingTestResult {
4099 initialize_logging();
4100
4101 let renewing = build_test_renewing_state(
4102 Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4103 None,
4104 None,
4105 );
4106 let client_config = &test_client_config();
4107
4108 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4109 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4110 let (udp_server_end, udp_client_end) = FakeSocket::new_pair();
4111 let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
4112 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4113 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4114 let time = &FakeTimeController::new();
4115 let counters = Counters::default();
4116 let renewing_fut = pin!(renewing
4117 .do_renewing(
4118 client_config,
4119 udp_socket_provider,
4120 packet_socket_provider,
4121 time,
4122 &mut stop_receiver,
4123 address_event_receiver,
4124 &counters
4125 )
4126 .fuse());
4127 let renewing_fut = pin!(renewing_fut);
4128
4129 let server_socket_addr =
4130 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(SERVER_IP, SERVER_PORT.into()));
4131
4132 let server_fut = pin!(async {
4133 let mut recv_buf = [0u8; BUFFER_SIZE];
4134
4135 let DatagramInfo { length, address } = udp_server_end
4136 .recv_from(&mut recv_buf)
4137 .await
4138 .expect("recv_from on test socket should succeed");
4139 assert_eq!(address, server_socket_addr);
4140
4141 let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
4142 .expect("received packet on test socket should parse as DHCP message");
4143
4144 assert_outgoing_message_when_assigned_address(
4145 &msg,
4146 VaryingOutgoingMessageFields {
4147 xid: msg.xid,
4148 options: vec![
4149 dhcp_protocol::DhcpOption::DhcpMessageType(
4150 dhcp_protocol::MessageType::DHCPREQUEST,
4151 ),
4152 dhcp_protocol::DhcpOption::ParameterRequestList(
4153 test_requested_parameters()
4154 .iter_keys()
4155 .collect::<Vec<_>>()
4156 .try_into()
4157 .expect("should fit parameter request list size constraints"),
4158 ),
4159 ],
4160 },
4161 );
4162
4163 let reply = build_incoming_message(msg.xid, incoming_message);
4164
4165 udp_server_end
4166 .send_to(
4167 &reply.serialize(),
4168 server_socket_addr,
4171 )
4172 .await
4173 .expect("send_to on test socket should succeed");
4174 }
4175 .fuse());
4176
4177 let main_future = async move {
4178 let (renewing_result, ()) = join!(renewing_fut, server_fut);
4179 renewing_result
4180 }
4181 .fuse();
4182
4183 let mut main_future = pin!(main_future);
4184
4185 let mut executor = fasync::TestExecutor::new();
4186 let renewing_result = run_with_accelerated_time(&mut executor, time, &mut main_future);
4187
4188 let outcome = assert_matches!(renewing_result, Ok(outcome) => outcome);
4189 let counters = RenewingTestCounters {
4190 send_message: counters.renewing.messaging.send_message.load(),
4191 recv_time_out: counters.renewing.messaging.recv_time_out.load(),
4192 recv_failed_dhcp_parse: counters.renewing.messaging.recv_failed_dhcp_parse.load(),
4193 recv_wrong_xid: counters.renewing.messaging.recv_wrong_xid.load(),
4194 recv_wrong_chaddr: counters.renewing.messaging.recv_wrong_chaddr.load(),
4195 recv_message: counters.renewing.messaging.recv_message.load(),
4196 recv_nak: counters.renewing.recv_nak.load(),
4197 recv_missing_option: counters.renewing.recv_error.missing_required_option.load(),
4198 };
4199 RenewingTestResult { outcome, counters }
4200 }
4201
4202 fn build_test_rebinding_state(
4203 lease_length: Duration,
4204 renewal_time: Option<Duration>,
4205 rebinding_time: Option<Duration>,
4206 ) -> Rebinding<TestInstant> {
4207 Rebinding {
4208 lease_state: build_test_lease_state_with_times(
4209 lease_length,
4210 renewal_time,
4211 rebinding_time,
4212 ),
4213 }
4214 }
4215
4216 #[test]
4217 fn do_rebinding_sends_requests() {
4218 initialize_logging();
4219
4220 const RENEWAL_TIME: Duration = Duration::from_secs(0);
4222 const REBINDING_TIME: Duration = Duration::from_secs(0);
4223 const LEASE_LENGTH: Duration = Duration::from_secs(1024);
4224
4225 let rebinding =
4226 build_test_rebinding_state(LEASE_LENGTH, Some(RENEWAL_TIME), Some(REBINDING_TIME));
4227 let client_config = &test_client_config();
4228
4229 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4230 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4231 let (udp_server_end, udp_client_end) = FakeSocket::new_pair();
4232 let (binds_sender, mut binds_receiver) = mpsc::unbounded();
4233 let udp_socket_provider =
4234 &FakeSocketProvider::new_with_events(udp_client_end, binds_sender);
4235 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4236 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4237 let time = &FakeTimeController::new();
4238 let counters = Counters::default();
4239 let rebinding_fut = pin!(rebinding
4240 .do_rebinding(
4241 client_config,
4242 udp_socket_provider,
4243 packet_socket_provider,
4244 time,
4245 &mut stop_receiver,
4246 address_event_receiver,
4247 &counters
4248 )
4249 .fuse());
4250
4251 let expected_times_requests_are_sent =
4254 [1024, 512, 256, 128, 64, 4].map(|time_remaining_when_request_is_sent| {
4255 Duration::from_secs(1024 - time_remaining_when_request_is_sent)
4256 });
4257
4258 let receive_fut = pin!(async {
4259 for expected_time in expected_times_requests_are_sent {
4260 let mut recv_buf = [0u8; BUFFER_SIZE];
4261 let DatagramInfo { length, address } = udp_server_end
4262 .recv_from(&mut recv_buf)
4263 .await
4264 .expect("recv_from on test socket should succeed");
4265
4266 assert_eq!(
4267 address,
4268 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
4269 std::net::Ipv4Addr::BROADCAST,
4270 SERVER_PORT.get()
4271 ))
4272 );
4273 assert_eq!(time.now(), TestInstant(expected_time));
4274 let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
4275 .expect("received packet should parse as DHCP message");
4276
4277 assert_outgoing_message_when_assigned_address(
4278 &msg,
4279 VaryingOutgoingMessageFields {
4280 xid: msg.xid,
4281 options: vec![
4282 dhcp_protocol::DhcpOption::DhcpMessageType(
4283 dhcp_protocol::MessageType::DHCPREQUEST,
4284 ),
4285 dhcp_protocol::DhcpOption::ParameterRequestList(
4286 test_requested_parameters()
4287 .iter_keys()
4288 .collect::<Vec<_>>()
4289 .try_into()
4290 .expect("should fit parameter request list size constraints"),
4291 ),
4292 ],
4293 },
4294 );
4295 }
4296 }
4297 .fuse());
4298
4299 let main_future = async { join!(rebinding_fut, receive_fut) };
4300 let mut main_future = pin!(main_future);
4301
4302 let mut executor = fasync::TestExecutor::new();
4303 let (requesting_result, ()) =
4304 run_with_accelerated_time(&mut executor, time, &mut main_future);
4305
4306 assert_matches!(requesting_result, Ok(RebindingOutcome::TimedOut));
4307 assert_matches!(udp_server_end.recv_from(&mut []).now_or_never(), None);
4308
4309 let bound_socket_addr = binds_receiver
4310 .next()
4311 .now_or_never()
4312 .expect("should have completed")
4313 .expect("should be present");
4314 assert_eq!(
4315 bound_socket_addr,
4316 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(YIADDR, CLIENT_PORT.into()))
4317 );
4318 }
4319
4320 #[derive(PartialEq, Debug)]
4321 struct RebindingTestResult {
4322 outcome: RebindingOutcome<TestInstant, FakeRemovedReason>,
4323 counters: RebindingTestCounters,
4324 }
4325
4326 #[derive(PartialEq, Eq, Debug, Default)]
4327 struct RebindingTestCounters {
4328 send_message: usize,
4329 recv_time_out: usize,
4330 recv_failed_dhcp_parse: usize,
4331 recv_wrong_xid: usize,
4332 recv_wrong_chaddr: usize,
4333 recv_message: usize,
4334 recv_nak: usize,
4335 recv_missing_option: usize,
4336 }
4337
4338 #[test_case(VaryingIncomingMessageFields {
4339 yiaddr: YIADDR,
4340 options: [
4341 dhcp_protocol::DhcpOption::DhcpMessageType(
4342 dhcp_protocol::MessageType::DHCPACK,
4343 ),
4344 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4345 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4346 DEFAULT_LEASE_LENGTH_SECONDS,
4347 ),
4348 ]
4349 .into_iter()
4350 .chain(test_parameter_values())
4351 .collect(),
4352 } => RebindingTestResult {
4353 outcome: RebindingOutcome::Renewed(LeaseState {
4354 discover_options: TEST_DISCOVER_OPTIONS,
4355 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
4356 .try_into()
4357 .expect("should be specified"),
4358 server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
4359 .try_into()
4360 .expect("should be specified"),
4361 ip_address_lease_time: std::time::Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4362 renewal_time: None,
4363 rebinding_time: None,
4364 start_time: TestInstant(std::time::Duration::from_secs(0)),
4365 }, test_parameter_values().into_iter().collect()),
4366 counters: RebindingTestCounters {
4367 send_message: 1,
4368 recv_message: 1,
4369 ..Default::default()
4370 }
4371 }; "successfully renews after receiving DHCPACK")]
4372 #[test_case(VaryingIncomingMessageFields {
4373 yiaddr: OTHER_ADDR,
4374 options: [
4375 dhcp_protocol::DhcpOption::DhcpMessageType(
4376 dhcp_protocol::MessageType::DHCPACK,
4377 ),
4378 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4379 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4380 DEFAULT_LEASE_LENGTH_SECONDS,
4381 ),
4382 ]
4383 .into_iter()
4384 .chain(test_parameter_values())
4385 .collect(),
4386 } => RebindingTestResult {
4387 outcome: RebindingOutcome::NewAddress(LeaseState {
4388 discover_options: TEST_DISCOVER_OPTIONS,
4389 yiaddr: net_types::ip::Ipv4Addr::from(OTHER_ADDR)
4390 .try_into()
4391 .expect("should be specified"),
4392 server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
4393 .try_into()
4394 .expect("should be specified"),
4395 ip_address_lease_time: std::time::Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4396 renewal_time: None,
4397 rebinding_time: None,
4398 start_time: TestInstant(std::time::Duration::from_secs(0)),
4399 }, test_parameter_values().into_iter().collect()),
4400 counters: RebindingTestCounters {
4401 send_message: 1,
4402 recv_message: 1,
4403 ..Default::default()
4404 }
4405 } ; "observes new address from DHCPACK")]
4406 #[test_case(VaryingIncomingMessageFields {
4407 yiaddr: YIADDR,
4408 options: [
4409 dhcp_protocol::DhcpOption::DhcpMessageType(
4410 dhcp_protocol::MessageType::DHCPACK,
4411 ),
4412 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4413 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
4414 DEFAULT_LEASE_LENGTH_SECONDS,
4415 ),
4416 ]
4417 .into_iter()
4418 .chain(test_parameter_values_excluding_subnet_mask())
4419 .collect(),
4420 } => RebindingTestResult {
4421 outcome: RebindingOutcome::TimedOut,
4422 counters: RebindingTestCounters {
4423 send_message: 2,
4424 recv_time_out: 2,
4425 recv_message: 1,
4426 recv_missing_option: 1,
4427 ..Default::default()
4428 }
4429 } ; "ignores replies lacking required option SubnetMask")]
4430 #[test_case(VaryingIncomingMessageFields {
4431 yiaddr: Ipv4Addr::UNSPECIFIED,
4432 options: [
4433 dhcp_protocol::DhcpOption::DhcpMessageType(
4434 dhcp_protocol::MessageType::DHCPNAK,
4435 ),
4436 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
4437 dhcp_protocol::DhcpOption::Message(NAK_MESSAGE.to_owned()),
4438 ]
4439 .into_iter()
4440 .chain(test_parameter_values())
4441 .collect(),
4442 } => RebindingTestResult {
4443 outcome: RebindingOutcome::Nak(crate::parse::FieldsToRetainFromNak {
4444 server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
4445 .try_into()
4446 .expect("should be specified"),
4447 message: Some(NAK_MESSAGE.to_owned()),
4448 client_identifier: None,
4449 }),
4450 counters: RebindingTestCounters {
4451 send_message: 1,
4452 recv_message: 1,
4453 recv_nak: 1,
4454 ..Default::default()
4455 },
4456 } ; "transitions to Init after receiving DHCPNAK")]
4457 fn do_rebinding_transitions_on_reply(
4458 incoming_message: VaryingIncomingMessageFields,
4459 ) -> RebindingTestResult {
4460 initialize_logging();
4461
4462 let rebinding = build_test_rebinding_state(
4463 Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
4464 None,
4465 None,
4466 );
4467 let client_config = &test_client_config();
4468
4469 let (_server_end, packet_client_end) = FakeSocket::new_pair();
4470 let packet_socket_provider = &FakeSocketProvider::new(packet_client_end);
4471 let (udp_server_end, udp_client_end) = FakeSocket::new_pair();
4472 let udp_socket_provider = &FakeSocketProvider::new(udp_client_end);
4473 let address_event_receiver = futures::stream::pending::<AddressEvent<FakeRemovedReason>>();
4474 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
4475 let time = &FakeTimeController::new();
4476 let counters = Counters::default();
4477 let rebinding_fut = pin!(rebinding
4478 .do_rebinding(
4479 client_config,
4480 udp_socket_provider,
4481 packet_socket_provider,
4482 time,
4483 &mut stop_receiver,
4484 address_event_receiver,
4485 &counters
4486 )
4487 .fuse());
4488
4489 let server_socket_addr = std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
4490 OTHER_SERVER_IP,
4491 SERVER_PORT.into(),
4492 ));
4493
4494 let server_fut = pin!(async {
4495 let mut recv_buf = [0u8; BUFFER_SIZE];
4496
4497 let DatagramInfo { length, address } = udp_server_end
4498 .recv_from(&mut recv_buf)
4499 .await
4500 .expect("recv_from on test socket should succeed");
4501 assert_eq!(
4502 address,
4503 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
4504 std::net::Ipv4Addr::BROADCAST,
4505 SERVER_PORT.into()
4506 ))
4507 );
4508
4509 let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
4510 .expect("received packet on test socket should parse as DHCP message");
4511
4512 assert_outgoing_message_when_assigned_address(
4513 &msg,
4514 VaryingOutgoingMessageFields {
4515 xid: msg.xid,
4516 options: vec![
4517 dhcp_protocol::DhcpOption::DhcpMessageType(
4518 dhcp_protocol::MessageType::DHCPREQUEST,
4519 ),
4520 dhcp_protocol::DhcpOption::ParameterRequestList(
4521 test_requested_parameters()
4522 .iter_keys()
4523 .collect::<Vec<_>>()
4524 .try_into()
4525 .expect("should fit parameter request list size constraints"),
4526 ),
4527 ],
4528 },
4529 );
4530
4531 let reply = build_incoming_message(msg.xid, incoming_message);
4532
4533 udp_server_end
4534 .send_to(
4535 &reply.serialize(),
4536 server_socket_addr,
4539 )
4540 .await
4541 .expect("send_to on test socket should succeed");
4542 }
4543 .fuse());
4544
4545 let main_future = async move {
4546 let (rebinding_result, ()) = join!(rebinding_fut, server_fut);
4547 rebinding_result
4548 }
4549 .fuse();
4550
4551 let mut main_future = pin!(main_future);
4552
4553 let mut executor = fasync::TestExecutor::new();
4554 let rebinding_result = run_with_accelerated_time(&mut executor, time, &mut main_future);
4555
4556 let outcome = assert_matches!(rebinding_result, Ok(outcome) => outcome);
4557 let counters = RebindingTestCounters {
4558 send_message: counters.rebinding.messaging.send_message.load(),
4559 recv_time_out: counters.rebinding.messaging.recv_time_out.load(),
4560 recv_failed_dhcp_parse: counters.rebinding.messaging.recv_failed_dhcp_parse.load(),
4561 recv_wrong_xid: counters.rebinding.messaging.recv_wrong_xid.load(),
4562 recv_wrong_chaddr: counters.rebinding.messaging.recv_wrong_chaddr.load(),
4563 recv_message: counters.rebinding.messaging.recv_message.load(),
4564 recv_nak: counters.rebinding.recv_nak.load(),
4565 recv_missing_option: counters.rebinding.recv_error.missing_required_option.load(),
4566 };
4567 RebindingTestResult { outcome, counters }
4568 }
4569}