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