1use crate::deps::{self, DatagramInfo, Instant as _, Socket as _};
8use crate::inspect::{
9 record_optional_duration_secs, Counters, MessagingRelatedCounters, RebindingCounters,
10 RenewingCounters, RequestingCounters, SelectingCounters,
11};
12use crate::parse::{OptionCodeMap, OptionRequested};
13
14use anyhow::Context as _;
15use dhcp_protocol::{AtLeast, AtMostBytes, CLIENT_PORT, SERVER_PORT};
16use diagnostics_traits::Inspector;
17use futures::channel::mpsc;
18use futures::{select, select_biased, FutureExt as _, Stream, StreamExt as _, TryStreamExt as _};
19use net_types::ethernet::Mac;
20use net_types::{SpecifiedAddr, Witness as _};
21use rand::Rng as _;
22
23use std::fmt::{Debug, Display};
24use std::net::Ipv4Addr;
25use std::num::{NonZeroU32, NonZeroU64};
26use std::pin::pin;
27use std::time::Duration;
28
29#[derive(thiserror::Error, Debug)]
31pub enum Error {
32 #[error("error while using socket: {0:?}")]
34 Socket(deps::SocketError),
35}
36
37pub enum ExitReason {
39 GracefulShutdown,
41}
42
43#[derive(Debug, Clone, Copy)]
48pub enum State<I> {
49 Init(Init),
52 Selecting(Selecting<I>),
55 Requesting(Requesting<I>),
58 Bound(Bound<I>),
61 Renewing(Renewing<I>),
64 Rebinding(Rebinding<I>),
67 WaitingToRestart(WaitingToRestart<I>),
69}
70
71pub enum Step<I> {
73 NextState(Transition<I>),
75 Exit(ExitReason),
77}
78
79pub enum Transition<I> {
82 Init(Init),
84 Selecting(Selecting<I>),
86 Requesting(Requesting<I>),
88 BoundWithNewLease(Bound<I>, NewlyAcquiredLease<I>),
90 BoundWithRenewedLease(Bound<I>, LeaseRenewal<I>),
92 Renewing(Renewing<I>),
94 Rebinding(Rebinding<I>),
96 WaitingToRestart(WaitingToRestart<I>),
98}
99
100#[must_use]
102#[derive(Debug)]
103pub enum TransitionEffect<I> {
104 DropLease,
106 HandleNewLease(NewlyAcquiredLease<I>),
108 HandleRenewedLease(LeaseRenewal<I>),
110}
111
112#[derive(Debug)]
114pub enum AddressRejectionOutcome<I> {
115 ShouldBeImpossible,
118 NextState(State<I>),
120}
121
122const WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION: Duration = Duration::from_secs(10);
126
127impl<I: deps::Instant> State<I> {
128 pub async fn run<C: deps::Clock<Instant = I>>(
130 &self,
131 config: &ClientConfig,
132 packet_socket_provider: &impl deps::PacketSocketProvider,
133 udp_socket_provider: &impl deps::UdpSocketProvider,
134 rng: &mut impl deps::RngProvider,
135 clock: &C,
136 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
137 counters: &Counters,
138 ) -> Result<Step<I>, Error> {
139 let step = self
140 .run_inner(
141 config,
142 packet_socket_provider,
143 udp_socket_provider,
144 rng,
145 clock,
146 stop_receiver,
147 counters,
148 )
149 .await?;
150
151 match &step {
152 Step::NextState(transition) => {
153 let counter_to_increment = match transition {
154 Transition::Init(_) => &counters.init.entered,
155 Transition::Selecting(_) => &counters.selecting.entered,
156 Transition::Requesting(_) => &counters.requesting.entered,
157 Transition::BoundWithNewLease(_, _) => &counters.bound.entered,
158 Transition::BoundWithRenewedLease(_, _) => &counters.bound.entered,
159 Transition::Renewing(_) => &counters.renewing.entered,
160 Transition::Rebinding(_) => &counters.rebinding.entered,
161 Transition::WaitingToRestart(_) => &counters.waiting_to_restart.entered,
162 };
163 counter_to_increment.increment();
164 }
165 Step::Exit(_) => (),
166 };
167
168 Ok(step)
169 }
170
171 async fn run_inner<C: deps::Clock<Instant = I>>(
172 &self,
173 config: &ClientConfig,
174 packet_socket_provider: &impl deps::PacketSocketProvider,
175 udp_socket_provider: &impl deps::UdpSocketProvider,
176 rng: &mut impl deps::RngProvider,
177 clock: &C,
178 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
179 counters: &Counters,
180 ) -> Result<Step<I>, Error> {
181 let debug_log_prefix = &config.debug_log_prefix;
182 match self {
183 State::Init(init) => {
184 Ok(Step::NextState(Transition::Selecting(init.do_init(rng, clock))))
185 }
186 State::Selecting(selecting) => match selecting
187 .do_selecting(config, packet_socket_provider, rng, clock, stop_receiver, counters)
188 .await?
189 {
190 SelectingOutcome::GracefulShutdown => Ok(Step::Exit(ExitReason::GracefulShutdown)),
191 SelectingOutcome::Requesting(requesting) => {
192 Ok(Step::NextState(Transition::Requesting(requesting)))
193 }
194 },
195 State::Requesting(requesting) => {
196 match requesting
197 .do_requesting(
198 config,
199 packet_socket_provider,
200 rng,
201 clock,
202 stop_receiver,
203 counters,
204 )
205 .await?
206 {
207 RequestingOutcome::RanOutOfRetransmits => {
208 log::info!(
209 "{debug_log_prefix} Returning to Init due to \
210 running out of DHCPREQUEST retransmits"
211 );
212 Ok(Step::NextState(Transition::Init(Init)))
213 }
214 RequestingOutcome::GracefulShutdown => {
215 Ok(Step::Exit(ExitReason::GracefulShutdown))
216 }
217 RequestingOutcome::Bound(bound, parameters) => {
218 let Bound {
219 discover_options: _,
220 yiaddr,
221 server_identifier: _,
222 ip_address_lease_time,
223 renewal_time: _,
224 rebinding_time: _,
225 start_time,
226 } = &bound;
227 let newly_acquired_lease = NewlyAcquiredLease {
228 ip_address: *yiaddr,
229 start_time: *start_time,
230 lease_time: *ip_address_lease_time,
231 parameters,
232 };
233 Ok(Step::NextState(Transition::BoundWithNewLease(
234 bound,
235 newly_acquired_lease,
236 )))
237 }
238 RequestingOutcome::Nak(nak) => {
239 log::warn!(
243 "{debug_log_prefix} Returning to Init due to DHCPNAK: {:?}",
244 nak
245 );
246 Ok(Step::NextState(Transition::Init(Init)))
247 }
248 }
249 }
250 State::Bound(bound) => {
251 Ok(match bound.do_bound(config, clock, stop_receiver, counters).await {
252 BoundOutcome::GracefulShutdown => Step::Exit(ExitReason::GracefulShutdown),
253 BoundOutcome::Renewing(renewing) => {
254 Step::NextState(Transition::Renewing(renewing))
255 }
256 })
257 }
258 State::Renewing(renewing) => {
259 match renewing
260 .do_renewing(config, udp_socket_provider, clock, stop_receiver, counters)
261 .await?
262 {
263 RenewingOutcome::GracefulShutdown => {
264 Ok(Step::Exit(ExitReason::GracefulShutdown))
265 }
266 RenewingOutcome::Renewed(bound, parameters) => {
267 let Bound {
268 discover_options: _,
269 yiaddr: _,
270 server_identifier: _,
271 ip_address_lease_time,
272 renewal_time: _,
273 rebinding_time: _,
274 start_time,
275 } = &bound;
276 let lease_renewal = LeaseRenewal {
277 start_time: *start_time,
278 lease_time: *ip_address_lease_time,
279 parameters,
280 };
281 Ok(Step::NextState(Transition::BoundWithRenewedLease(bound, lease_renewal)))
282 }
283 RenewingOutcome::NewAddress(bound, parameters) => {
284 let Bound {
285 discover_options: _,
286 yiaddr,
287 server_identifier: _,
288 ip_address_lease_time,
289 renewal_time: _,
290 rebinding_time: _,
291 start_time,
292 } = &bound;
293 let new_lease = NewlyAcquiredLease {
294 ip_address: *yiaddr,
295 start_time: *start_time,
296 lease_time: *ip_address_lease_time,
297 parameters,
298 };
299 Ok(Step::NextState(Transition::BoundWithNewLease(bound, new_lease)))
300 }
301 RenewingOutcome::Rebinding(rebinding) => {
302 Ok(Step::NextState(Transition::Rebinding(rebinding)))
303 }
304 RenewingOutcome::Nak(nak) => {
305 let Renewing {
310 bound:
311 Bound {
312 discover_options: _,
313 yiaddr,
314 server_identifier: _,
315 ip_address_lease_time: _,
316 start_time: _,
317 renewal_time: _,
318 rebinding_time: _,
319 },
320 } = renewing;
321 log::warn!(
322 "{debug_log_prefix} Dropping lease on {} \
323 and returning to Init due to DHCPNAK: {:?}",
324 yiaddr,
325 nak
326 );
327 Ok(Step::NextState(Transition::Init(Init)))
328 }
329 }
330 }
331 State::Rebinding(rebinding) => {
332 match rebinding
333 .do_rebinding(config, udp_socket_provider, clock, stop_receiver, counters)
334 .await?
335 {
336 RebindingOutcome::GracefulShutdown => {
337 Ok(Step::Exit(ExitReason::GracefulShutdown))
338 }
339 RebindingOutcome::Renewed(bound, parameters) => {
340 let Bound {
341 discover_options: _,
342 yiaddr: _,
343 server_identifier: _,
344 ip_address_lease_time,
345 renewal_time: _,
346 rebinding_time: _,
347 start_time,
348 } = &bound;
349 let renewal = LeaseRenewal {
350 start_time: *start_time,
351 lease_time: *ip_address_lease_time,
352 parameters,
353 };
354 Ok(Step::NextState(Transition::BoundWithRenewedLease(bound, renewal)))
355 }
356 RebindingOutcome::NewAddress(bound, parameters) => {
357 let Bound {
358 discover_options: _,
359 yiaddr,
360 server_identifier: _,
361 ip_address_lease_time,
362 renewal_time: _,
363 rebinding_time: _,
364 start_time,
365 } = &bound;
366 let new_lease = NewlyAcquiredLease {
367 ip_address: *yiaddr,
368 start_time: *start_time,
369 lease_time: *ip_address_lease_time,
370 parameters,
371 };
372 Ok(Step::NextState(Transition::BoundWithNewLease(bound, new_lease)))
373 }
374 RebindingOutcome::Nak(nak) => {
375 let Rebinding {
380 bound:
381 Bound {
382 discover_options: _,
383 yiaddr,
384 server_identifier: _,
385 ip_address_lease_time: _,
386 start_time: _,
387 renewal_time: _,
388 rebinding_time: _,
389 },
390 } = rebinding;
391 log::warn!(
392 "{debug_log_prefix} Dropping lease on {} \
393 and returning to Init due to DHCPNAK: {:?}",
394 yiaddr,
395 nak
396 );
397 Ok(Step::NextState(Transition::Init(Init)))
398 }
399 RebindingOutcome::TimedOut => {
400 let Rebinding {
401 bound:
402 Bound {
403 discover_options: _,
404 yiaddr,
405 server_identifier: _,
406 ip_address_lease_time: _,
407 start_time: _,
408 renewal_time: _,
409 rebinding_time: _,
410 },
411 } = rebinding;
412 log::warn!(
413 "{debug_log_prefix} Dropping lease on {} \
414 and returning to Init due to lease expiration",
415 yiaddr,
416 );
417 Ok(Step::NextState(Transition::Init(Init)))
418 }
419 }
420 }
421 State::WaitingToRestart(waiting_to_restart) => {
422 match waiting_to_restart.do_waiting_to_restart(clock, stop_receiver).await {
423 WaitingToRestartOutcome::GracefulShutdown => {
424 Ok(Step::Exit(ExitReason::GracefulShutdown))
425 }
426 WaitingToRestartOutcome::Init(init) => {
427 Ok(Step::NextState(Transition::Init(init)))
428 }
429 }
430 }
431 }
432 }
433
434 pub async fn on_address_rejection<C: deps::Clock<Instant = I>>(
436 &self,
437 config: &ClientConfig,
438 packet_socket_provider: &impl deps::PacketSocketProvider,
439 clock: &C,
440 ip_address: SpecifiedAddr<net_types::ip::Ipv4Addr>,
441 ) -> Result<AddressRejectionOutcome<I>, Error> {
442 let debug_log_prefix = &config.debug_log_prefix;
443 match self {
444 State::Init(_)
445 | State::Selecting(_)
446 | State::Requesting(_)
447 | State::WaitingToRestart(_) => {
448 log::warn!(
449 "{debug_log_prefix} received address rejection in state {}; ignoring",
450 self.state_name()
451 );
452 Ok(AddressRejectionOutcome::ShouldBeImpossible)
453 }
454 State::Bound(bound)
455 | State::Renewing(Renewing { bound })
456 | State::Rebinding(Rebinding { bound }) => {
457 let Bound {
458 discover_options,
459 yiaddr,
460 server_identifier,
461 ip_address_lease_time: _,
462 start_time: _,
463 renewal_time: _,
464 rebinding_time: _,
465 } = bound;
466
467 if *yiaddr != ip_address {
468 log::warn!(
469 "{debug_log_prefix} received rejection of address {} while bound to \
470 different address {}; ignoring",
471 *yiaddr,
472 ip_address,
473 );
474 return Ok(AddressRejectionOutcome::ShouldBeImpossible);
475 }
476
477 let socket =
478 packet_socket_provider.get_packet_socket().await.map_err(Error::Socket)?;
479
480 let message =
481 build_decline(config, discover_options, ip_address, *server_identifier);
482
483 socket
484 .send_to(
485 crate::parse::serialize_dhcp_message_to_ip_packet(
486 message,
487 Ipv4Addr::UNSPECIFIED,
488 CLIENT_PORT,
489 Ipv4Addr::BROADCAST,
490 SERVER_PORT,
491 )
492 .as_ref(),
493 Mac::BROADCAST,
494 )
495 .await
496 .map_err(Error::Socket)?;
497
498 log::info!(
499 "{debug_log_prefix} sent DHCPDECLINE for {}; waiting to restart",
500 ip_address
501 );
502
503 Ok(AddressRejectionOutcome::NextState(State::WaitingToRestart(WaitingToRestart {
504 waiting_until: clock
505 .now()
506 .add(WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION),
507 })))
508 }
509 }
510 }
511
512 pub fn state_name(&self) -> &'static str {
515 match self {
516 State::Init(_) => "Init",
517 State::Selecting(_) => "Selecting",
518 State::Requesting(_) => "Requesting",
519 State::Bound(_) => "Bound",
520 State::Renewing(_) => "Renewing",
521 State::Rebinding(_) => "Rebinding",
522 State::WaitingToRestart(_) => "Waiting to Restart",
523 }
524 }
525
526 fn has_lease(&self) -> bool {
527 match self {
528 State::Init(_) => false,
529 State::Selecting(_) => false,
530 State::Requesting(_) => false,
531 State::Bound(_) => true,
532 State::Renewing(_) => true,
533 State::Rebinding(_) => true,
534 State::WaitingToRestart(_) => false,
535 }
536 }
537
538 pub fn apply(
541 &self,
542 config: &ClientConfig,
543 transition: Transition<I>,
544 ) -> (State<I>, Option<TransitionEffect<I>>) {
545 let debug_log_prefix = &config.debug_log_prefix;
546
547 let (next_state, effect) = match transition {
548 Transition::Init(init) => (State::Init(init), None),
549 Transition::Selecting(selecting) => (State::Selecting(selecting), None),
550 Transition::Requesting(requesting) => (State::Requesting(requesting), None),
551 Transition::BoundWithRenewedLease(bound, lease_renewal) => {
552 (State::Bound(bound), Some(TransitionEffect::HandleRenewedLease(lease_renewal)))
553 }
554 Transition::BoundWithNewLease(bound, new_lease) => {
555 (State::Bound(bound), Some(TransitionEffect::HandleNewLease(new_lease)))
556 }
557 Transition::Renewing(renewing) => (State::Renewing(renewing), None),
558 Transition::Rebinding(rebinding) => (State::Rebinding(rebinding), None),
559 Transition::WaitingToRestart(waiting) => (State::WaitingToRestart(waiting), None),
560 };
561
562 log::info!(
563 "{debug_log_prefix} transitioning from {} to {}",
564 self.state_name(),
565 next_state.state_name()
566 );
567
568 let effect = match effect {
569 Some(effect) => Some(effect),
570 None => match (self.has_lease(), next_state.has_lease()) {
571 (true, false) => Some(TransitionEffect::DropLease),
572 (false, true) => {
573 unreachable!("should already have decided on TransitionEffect::HandleNewLease")
574 }
575 (false, false) | (true, true) => None,
576 },
577 };
578 (next_state, effect)
579 }
580}
581
582impl<I> Default for State<I> {
583 fn default() -> Self {
584 State::Init(Init::default())
585 }
586}
587
588impl<I: deps::Instant> diagnostics_traits::InspectableValue for State<I> {
589 fn record<II: diagnostics_traits::Inspector>(&self, name: &str, inspector: &mut II) {
590 inspector.record_child(name, |inspector| {
591 inspector.record_str("Kind", self.state_name());
592 match self {
593 State::Init(Init) => (),
594 State::Selecting(selecting) => {
595 selecting.record(inspector);
596 }
597 State::Requesting(requesting) => {
598 requesting.record(inspector);
599 }
600 State::Bound(bound) => {
601 bound.record(inspector);
602 }
603 State::Renewing(Renewing { bound }) => {
604 bound.record(inspector);
605 }
606 State::Rebinding(Rebinding { bound }) => {
607 bound.record(inspector);
608 }
609 State::WaitingToRestart(WaitingToRestart { waiting_until }) => {
610 inspector.record_instant(
611 diagnostics_traits::instant_property_name!("WaitingToRestartUntil"),
612 waiting_until,
613 );
614 }
615 }
616 });
617 }
618}
619
620#[derive(Clone, Copy)]
622pub struct DebugLogPrefix {
623 pub interface_id: NonZeroU64,
625}
626
627impl Display for DebugLogPrefix {
628 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
629 let Self { interface_id } = self;
630 f.write_fmt(format_args!("(interface_id = {interface_id})"))
631 }
632}
633
634#[derive(Clone)]
637pub struct ClientConfig {
638 pub client_hardware_address: Mac,
640 pub client_identifier:
643 Option<AtLeast<2, AtMostBytes<{ dhcp_protocol::U8_MAX_AS_USIZE }, Vec<u8>>>>,
644 pub requested_parameters: OptionCodeMap<OptionRequested>,
646 pub preferred_lease_time_secs: Option<NonZeroU32>,
648 pub requested_ip_address: Option<SpecifiedAddr<net_types::ip::Ipv4Addr>>,
650 pub debug_log_prefix: DebugLogPrefix,
652}
653
654#[derive(Clone, Debug, PartialEq, Copy)]
655struct DiscoverOptions {
656 xid: TransactionId,
657}
658
659impl DiscoverOptions {
660 fn record(&self, inspector: &mut impl Inspector) {
661 let Self { xid: TransactionId(xid) } = self;
662 inspector.record_uint("Xid", xid.get());
663 }
664}
665
666#[derive(Clone, Copy, Debug, PartialEq)]
674struct TransactionId(
675 NonZeroU32,
679);
680
681#[derive(Default, Debug, PartialEq, Clone, Copy)]
684pub struct Init;
685
686impl Init {
687 fn do_init<C: deps::Clock>(
690 &self,
691 rng: &mut impl deps::RngProvider,
692 clock: &C,
693 ) -> Selecting<C::Instant> {
694 let discover_options = DiscoverOptions {
695 xid: TransactionId(NonZeroU32::new(rng.get_rng().gen_range(1..=u32::MAX)).unwrap()),
696 };
697 Selecting {
698 discover_options,
699 start_time: clock.now(),
703 }
704 }
705}
706
707#[derive(Debug, Clone, Copy)]
709pub struct WaitingToRestart<I> {
710 waiting_until: I,
711}
712
713#[derive(Debug, PartialEq)]
714enum WaitingToRestartOutcome {
715 GracefulShutdown,
716 Init(Init),
717}
718
719impl<I: deps::Instant> WaitingToRestart<I> {
720 async fn do_waiting_to_restart<C: deps::Clock<Instant = I>>(
721 &self,
722 clock: &C,
723 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
724 ) -> WaitingToRestartOutcome {
725 let Self { waiting_until } = self;
726 let wait_fut = clock.wait_until(*waiting_until).fuse();
727 let mut wait_fut = pin!(wait_fut);
728
729 select! {
730 () = wait_fut => WaitingToRestartOutcome::Init(Init::default()),
731 () = stop_receiver.select_next_some() => WaitingToRestartOutcome::GracefulShutdown,
732 }
733 }
734}
735
736fn build_decline(
737 client_config: &ClientConfig,
738 discover_options: &DiscoverOptions,
739 ip_address: SpecifiedAddr<net_types::ip::Ipv4Addr>,
740 server_identifier: SpecifiedAddr<net_types::ip::Ipv4Addr>,
741) -> dhcp_protocol::Message {
742 build_outgoing_message(
743 client_config,
744 discover_options,
745 OutgoingOptions {
746 ciaddr: None,
747 requested_ip_address: Some(ip_address),
748 ip_address_lease_time_secs: None,
749 message_type: dhcp_protocol::MessageType::DHCPDECLINE,
750 server_identifier: Some(server_identifier),
751 include_parameter_request_list: false,
752 },
753 )
754}
755
756async fn send_with_retransmits<T: Clone + Send + Debug>(
757 time: &impl deps::Clock,
758 retransmit_schedule: impl IntoIterator<Item = Duration>,
759 message: &[u8],
760 socket: &impl deps::Socket<T>,
761 dest: T,
762 debug_log_prefix: DebugLogPrefix,
763 counters: &MessagingRelatedCounters,
764) -> Result<(), Error> {
765 send_with_retransmits_at_instants(
766 time,
767 retransmit_schedule.into_iter().map(|duration| time.now().add(duration)),
768 message,
769 socket,
770 dest,
771 debug_log_prefix,
772 counters,
773 )
774 .await
775}
776
777async fn send_with_retransmits_at_instants<I: deps::Instant, T: Clone + Send + Debug>(
778 time: &impl deps::Clock<Instant = I>,
779 retransmit_schedule: impl IntoIterator<Item = I>,
780 message: &[u8],
781 socket: &impl deps::Socket<T>,
782 dest: T,
783 debug_log_prefix: DebugLogPrefix,
784 counters: &MessagingRelatedCounters,
785) -> Result<(), Error> {
786 let MessagingRelatedCounters { send_message, recv_time_out, .. } = counters;
787 for wait_until in std::iter::once(None).chain(retransmit_schedule.into_iter().map(Some)) {
788 if let Some(wait_until) = wait_until {
789 time.wait_until(wait_until).await;
790 recv_time_out.increment();
791 }
792 let result = socket.send_to(message, dest.clone()).await;
793 match result {
794 Ok(()) => {
795 send_message.increment();
796 }
797 Err(e) => match e {
798 deps::SocketError::FailedToOpen(_)
801 | deps::SocketError::NoInterface
802 | deps::SocketError::NetworkUnreachable
803 | deps::SocketError::UnsupportedHardwareType => return Err(Error::Socket(e)),
804 deps::SocketError::HostUnreachable => {
809 log::warn!("{debug_log_prefix} destination host unreachable: {:?}", dest);
810 }
811 deps::SocketError::Other(_) => {
814 log::error!(
815 "{debug_log_prefix} socket error while sending to {:?}: {:?}",
816 dest,
817 e
818 );
819 }
820 },
821 }
822 }
823 Ok(())
824}
825
826fn retransmit_schedule_during_acquisition(
827 rng: &mut (impl rand::Rng + ?Sized),
828) -> impl Iterator<Item = Duration> + '_ {
829 const MILLISECONDS_PER_SECOND: i32 = 1000;
830 [4i32, 8, 16, 32]
831 .into_iter()
832 .chain(std::iter::repeat(64))
833 .zip(std::iter::from_fn(|| {
841 Some(rng.gen_range((-MILLISECONDS_PER_SECOND)..=MILLISECONDS_PER_SECOND))
842 }))
843 .map(|(base_seconds, jitter_millis)| {
844 let millis = u64::try_from(base_seconds * MILLISECONDS_PER_SECOND + jitter_millis)
845 .expect("retransmit wait is never negative");
846 Duration::from_millis(millis)
847 })
848}
849
850const BUFFER_SIZE: usize = 1500;
853
854fn recv_stream<'a, T: 'a, U: Send>(
855 socket: &'a impl deps::Socket<U>,
856 recv_buf: &'a mut [u8],
857 parser: impl Fn(&[u8], U) -> T + 'a,
858 debug_log_prefix: DebugLogPrefix,
859 counters: &'a MessagingRelatedCounters,
860) -> impl Stream<Item = Result<T, Error>> + 'a {
861 let MessagingRelatedCounters {
862 recv_message,
863 recv_message_fatal_socket_error,
864 recv_message_non_fatal_socket_error,
865 ..
866 } = counters;
867 futures::stream::try_unfold((recv_buf, parser), move |(recv_buf, parser)| async move {
868 let result = socket.recv_from(recv_buf).await;
869 let DatagramInfo { length, address } = match result {
870 Ok(datagram_info) => {
871 recv_message.increment();
872 datagram_info
873 }
874 Err(e) => match e {
875 deps::SocketError::FailedToOpen(_)
878 | deps::SocketError::NoInterface
879 | deps::SocketError::NetworkUnreachable
880 | deps::SocketError::UnsupportedHardwareType => {
881 recv_message_fatal_socket_error.increment();
882 return Err(Error::Socket(e));
883 }
884 deps::SocketError::HostUnreachable => {
893 log::warn!("{debug_log_prefix} EHOSTUNREACH from recv_from");
894 recv_message_non_fatal_socket_error.increment();
895 return Ok(Some((None, (recv_buf, parser))));
896 }
897 deps::SocketError::Other(_) => {
900 log::error!("{debug_log_prefix} socket error while receiving: {:?}", e);
901 recv_message_non_fatal_socket_error.increment();
902 return Ok(Some((None, (recv_buf, parser))));
903 }
904 },
905 };
906 let raw_msg = &recv_buf[..length];
907 let parsed = parser(raw_msg, address);
908 Ok(Some((Some(parsed), (recv_buf, parser))))
909 })
910 .try_filter_map(|item| futures::future::ok(item))
911}
912
913struct OutgoingOptions {
914 ciaddr: Option<SpecifiedAddr<net_types::ip::Ipv4Addr>>,
915 requested_ip_address: Option<SpecifiedAddr<net_types::ip::Ipv4Addr>>,
916 ip_address_lease_time_secs: Option<NonZeroU32>,
917 message_type: dhcp_protocol::MessageType,
918 server_identifier: Option<SpecifiedAddr<net_types::ip::Ipv4Addr>>,
919 include_parameter_request_list: bool,
920}
921
922fn build_outgoing_message(
923 ClientConfig {
924 client_hardware_address,
925 client_identifier,
926 requested_parameters,
927 preferred_lease_time_secs: _,
928 requested_ip_address: _,
929 debug_log_prefix: _,
930 }: &ClientConfig,
931 DiscoverOptions { xid: TransactionId(xid) }: &DiscoverOptions,
932 OutgoingOptions {
933 ciaddr,
934 requested_ip_address,
935 ip_address_lease_time_secs,
936 message_type,
937 server_identifier,
938 include_parameter_request_list,
939 }: OutgoingOptions,
940) -> dhcp_protocol::Message {
941 use dhcp_protocol::DhcpOption;
942
943 dhcp_protocol::Message {
944 op: dhcp_protocol::OpCode::BOOTREQUEST,
945 xid: xid.get(),
946 secs: 0,
947 bdcast_flag: false,
948 ciaddr: ciaddr.map(|ip| ip.get().into()).unwrap_or(Ipv4Addr::UNSPECIFIED),
949 yiaddr: Ipv4Addr::UNSPECIFIED,
950 siaddr: Ipv4Addr::UNSPECIFIED,
951 giaddr: Ipv4Addr::UNSPECIFIED,
952 chaddr: *client_hardware_address,
953 sname: String::new(),
954 file: String::new(),
955 options: [
956 requested_ip_address.map(|ip| DhcpOption::RequestedIpAddress(ip.get().into())),
957 ip_address_lease_time_secs.map(|time| DhcpOption::IpAddressLeaseTime(time.get())),
958 Some(DhcpOption::DhcpMessageType(message_type)),
959 client_identifier.clone().map(DhcpOption::ClientIdentifier),
960 server_identifier.map(|ip| DhcpOption::ServerIdentifier(ip.get().into())),
961 include_parameter_request_list
962 .then(|| requested_parameters.try_to_parameter_request_list())
963 .flatten()
964 .map(DhcpOption::ParameterRequestList),
965 ]
966 .into_iter()
967 .flatten()
968 .collect(),
969 }
970}
971
972fn build_discover(
973 client_config: &ClientConfig,
974 discover_options: &DiscoverOptions,
975) -> dhcp_protocol::Message {
976 let ClientConfig {
977 client_hardware_address: _,
978 client_identifier: _,
979 requested_parameters: _,
980 preferred_lease_time_secs,
981 requested_ip_address,
982 debug_log_prefix: _,
983 } = client_config;
984
985 build_outgoing_message(
994 client_config,
995 discover_options,
996 OutgoingOptions {
997 ciaddr: None,
998 requested_ip_address: *requested_ip_address,
999 ip_address_lease_time_secs: *preferred_lease_time_secs,
1000 message_type: dhcp_protocol::MessageType::DHCPDISCOVER,
1001 server_identifier: None,
1002 include_parameter_request_list: true,
1003 },
1004 )
1005}
1006
1007fn parse_incoming_dhcp_message_from_ip_packet(
1011 packet: &[u8],
1012 debug_log_prefix: DebugLogPrefix,
1013) -> Result<Option<(net_types::ip::Ipv4Addr, dhcp_protocol::Message)>, anyhow::Error> {
1014 match crate::parse::parse_dhcp_message_from_ip_packet(packet, CLIENT_PORT) {
1015 Ok(message) => Ok(Some(message)),
1016 Err(err) => match err {
1017 crate::parse::ParseError::NotUdp => {
1018 log::debug!("{debug_log_prefix} ignoring non-UDP incoming packet");
1019 return Ok(None);
1020 }
1021 crate::parse::ParseError::WrongPort(port) => {
1022 log::debug!(
1023 "{debug_log_prefix} ignoring incoming UDP packet \
1024 to non-DHCP-client port {port}"
1025 );
1026 return Ok(None);
1027 }
1028 err @ (crate::parse::ParseError::Ipv4(_)
1029 | crate::parse::ParseError::Udp(_)
1030 | crate::parse::ParseError::WrongSource(_)
1031 | crate::parse::ParseError::Dhcp(_)) => {
1032 return Err(err).context("error while parsing DHCP message from IP packet");
1033 }
1034 },
1035 }
1036}
1037
1038#[derive(Debug)]
1039pub(crate) enum SelectingOutcome<I> {
1040 GracefulShutdown,
1041 Requesting(Requesting<I>),
1042}
1043
1044#[derive(Debug, Clone, Copy)]
1048pub struct Selecting<I> {
1049 discover_options: DiscoverOptions,
1050 start_time: I,
1053}
1054
1055impl<I: deps::Instant> Selecting<I> {
1056 async fn do_selecting<C: deps::Clock<Instant = I>>(
1062 &self,
1063 client_config: &ClientConfig,
1064 packet_socket_provider: &impl deps::PacketSocketProvider,
1065 rng: &mut impl deps::RngProvider,
1066 time: &C,
1067 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1068 counters: &Counters,
1069 ) -> Result<SelectingOutcome<I>, Error> {
1070 let Counters { selecting: SelectingCounters { messaging, recv_error, .. }, .. } = counters;
1071 let socket = packet_socket_provider.get_packet_socket().await.map_err(Error::Socket)?;
1075 let Selecting { discover_options, start_time } = self;
1076 let message = build_discover(client_config, discover_options);
1077
1078 let ClientConfig {
1079 client_hardware_address: _,
1080 client_identifier: _,
1081 requested_parameters,
1082 preferred_lease_time_secs: _,
1083 requested_ip_address: _,
1084 debug_log_prefix,
1085 } = client_config;
1086
1087 let message = crate::parse::serialize_dhcp_message_to_ip_packet(
1088 message,
1089 Ipv4Addr::UNSPECIFIED, CLIENT_PORT,
1091 Ipv4Addr::BROADCAST, SERVER_PORT,
1093 );
1094
1095 let mut send_fut = pin!(send_with_retransmits(
1096 time,
1097 retransmit_schedule_during_acquisition(rng.get_rng()),
1098 message.as_ref(),
1099 &socket,
1100 Mac::BROADCAST,
1101 *debug_log_prefix,
1102 messaging
1103 )
1104 .fuse());
1105
1106 let mut recv_buf = [0u8; BUFFER_SIZE];
1107 let mut offer_fields_stream = pin!(recv_stream(
1108 &socket,
1109 &mut recv_buf,
1110 |packet, src_addr| {
1111 let _: Mac = src_addr;
1114
1115 let (src_addr, message) =
1116 match parse_incoming_dhcp_message_from_ip_packet(packet, *debug_log_prefix)
1117 .inspect_err(|_| messaging.recv_failed_dhcp_parse.increment())?
1118 {
1119 Some(message) => message,
1120 None => return Ok(None),
1121 };
1122 validate_message(discover_options, client_config, &message)
1123 .inspect_err(|e| e.increment(&messaging))
1124 .context("invalid DHCP message")?;
1125 crate::parse::fields_to_retain_from_selecting(requested_parameters, message)
1126 .inspect_err(|e| recv_error.increment(&e))
1127 .map(|fields| Some((src_addr, fields)))
1128 .context(
1129 "error while retrieving fields to use in DHCPREQUEST from DHCP message",
1130 )
1131 },
1132 *debug_log_prefix,
1133 messaging
1134 )
1135 .try_filter_map(|parse_result| {
1136 futures::future::ok(match parse_result {
1137 Ok(fields) => fields,
1138 Err(error) => {
1139 log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
1140 None
1141 }
1142 })
1143 })
1144 .fuse());
1145
1146 select_biased! {
1147 fields_to_use_in_request_result = offer_fields_stream.select_next_some() => {
1148 let (src_addr, fields_from_offer_to_use_in_request) =
1149 fields_to_use_in_request_result?;
1150
1151 if src_addr != fields_from_offer_to_use_in_request.server_identifier.get() {
1152 log::warn!("{debug_log_prefix} received offer from {src_addr} with \
1153 differing server_identifier = {}",
1154 fields_from_offer_to_use_in_request.server_identifier);
1155 }
1156
1157 Ok(SelectingOutcome::Requesting(Requesting {
1160 discover_options: discover_options.clone(),
1161 fields_from_offer_to_use_in_request,
1162 start_time: *start_time,
1163 }))
1164 },
1165 () = stop_receiver.select_next_some() => {
1166 Ok(SelectingOutcome::GracefulShutdown)
1167 },
1168 send_discovers_result = send_fut => {
1169 send_discovers_result?;
1170 unreachable!("should never stop retransmitting DHCPDISCOVER unless we hit an error");
1171 }
1172 }
1173 }
1174}
1175
1176impl<I: deps::Instant> Selecting<I> {
1177 fn record(&self, inspector: &mut impl Inspector) {
1178 let Self { discover_options, start_time } = self;
1179 inspector.record_instant(diagnostics_traits::instant_property_name!("Start"), start_time);
1180 discover_options.record(inspector);
1181 }
1182}
1183
1184#[derive(thiserror::Error, Debug, PartialEq)]
1185enum ValidateMessageError {
1186 #[error("xid {actual} doesn't match expected xid {expected}")]
1187 WrongXid { expected: u32, actual: u32 },
1188 #[error("chaddr {actual} doesn't match expected chaddr {expected}")]
1189 WrongChaddr { expected: Mac, actual: Mac },
1190}
1191
1192impl ValidateMessageError {
1193 fn increment(&self, counters: &MessagingRelatedCounters) {
1194 match self {
1195 ValidateMessageError::WrongXid { .. } => counters.recv_wrong_xid.increment(),
1196 ValidateMessageError::WrongChaddr { .. } => counters.recv_wrong_chaddr.increment(),
1197 }
1198 }
1199}
1200
1201fn validate_message(
1202 DiscoverOptions { xid: TransactionId(my_xid) }: &DiscoverOptions,
1203 ClientConfig {
1204 client_hardware_address: my_chaddr,
1205 client_identifier: _,
1206 requested_parameters: _,
1207 preferred_lease_time_secs: _,
1208 requested_ip_address: _,
1209 debug_log_prefix: _,
1210 }: &ClientConfig,
1211 dhcp_protocol::Message {
1212 op: _,
1213 xid: msg_xid,
1214 secs: _,
1215 bdcast_flag: _,
1216 ciaddr: _,
1217 yiaddr: _,
1218 siaddr: _,
1219 giaddr: _,
1220 chaddr: msg_chaddr,
1221 sname: _,
1222 file: _,
1223 options: _,
1224 }: &dhcp_protocol::Message,
1225) -> Result<(), ValidateMessageError> {
1226 if *msg_xid != u32::from(*my_xid) {
1227 return Err(ValidateMessageError::WrongXid { expected: my_xid.get(), actual: *msg_xid });
1228 }
1229
1230 if msg_chaddr != my_chaddr {
1231 return Err(ValidateMessageError::WrongChaddr {
1232 expected: *my_chaddr,
1233 actual: *msg_chaddr,
1234 });
1235 }
1236 Ok(())
1237}
1238
1239#[derive(Debug, PartialEq)]
1240pub(crate) enum RequestingOutcome<I> {
1241 RanOutOfRetransmits,
1242 GracefulShutdown,
1243 Bound(Bound<I>, Vec<dhcp_protocol::DhcpOption>),
1244 Nak(crate::parse::FieldsToRetainFromNak),
1245}
1246
1247#[derive(Debug, PartialEq, Clone, Copy)]
1251pub struct Requesting<I> {
1252 discover_options: DiscoverOptions,
1253 fields_from_offer_to_use_in_request: crate::parse::FieldsFromOfferToUseInRequest,
1254 start_time: I,
1255}
1256
1257const NUM_REQUEST_RETRANSMITS: usize = 4;
1261
1262impl<I: deps::Instant> Requesting<I> {
1263 async fn do_requesting<C: deps::Clock<Instant = I>>(
1269 &self,
1270 client_config: &ClientConfig,
1271 packet_socket_provider: &impl deps::PacketSocketProvider,
1272 rng: &mut impl deps::RngProvider,
1273 time: &C,
1274 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1275 counters: &Counters,
1276 ) -> Result<RequestingOutcome<I>, Error> {
1277 let Counters {
1278 requesting: RequestingCounters { messaging, recv_error, recv_nak, .. }, ..
1279 } = counters;
1280 let socket = packet_socket_provider.get_packet_socket().await.map_err(Error::Socket)?;
1281 let Requesting { discover_options, fields_from_offer_to_use_in_request, start_time } = self;
1282 let message = build_request_during_address_acquisition(
1283 client_config,
1284 discover_options,
1285 fields_from_offer_to_use_in_request,
1286 );
1287
1288 let message = crate::parse::serialize_dhcp_message_to_ip_packet(
1289 message,
1290 Ipv4Addr::UNSPECIFIED, CLIENT_PORT,
1292 Ipv4Addr::BROADCAST, SERVER_PORT,
1294 );
1295
1296 let ClientConfig {
1297 client_hardware_address: _,
1298 client_identifier: _,
1299 requested_parameters,
1300 preferred_lease_time_secs: _,
1301 requested_ip_address: _,
1302 debug_log_prefix,
1303 } = client_config;
1304
1305 let mut send_fut = pin!(send_with_retransmits(
1306 time,
1307 retransmit_schedule_during_acquisition(rng.get_rng()).take(NUM_REQUEST_RETRANSMITS),
1308 message.as_ref(),
1309 &socket,
1310 Mac::BROADCAST,
1311 *debug_log_prefix,
1312 messaging
1313 )
1314 .fuse());
1315
1316 let mut recv_buf = [0u8; BUFFER_SIZE];
1317
1318 let mut ack_or_nak_stream = pin!(recv_stream(
1319 &socket,
1320 &mut recv_buf,
1321 |packet, src_addr| {
1322 let _: Mac = src_addr;
1325 let (src_addr, message) =
1326 match parse_incoming_dhcp_message_from_ip_packet(packet, *debug_log_prefix)
1327 .inspect_err(|_| {
1328 messaging.recv_failed_dhcp_parse.increment();
1329 })? {
1330 Some(message) => message,
1331 None => return Ok(None),
1332 };
1333 validate_message(discover_options, client_config, &message)
1334 .inspect_err(|e| e.increment(&messaging))
1335 .context("invalid DHCP message")?;
1336
1337 crate::parse::fields_to_retain_from_response_to_request(
1338 requested_parameters,
1339 message,
1340 )
1341 .inspect_err(|e| recv_error.increment(e))
1342 .context("error extracting needed fields from DHCP message during Requesting")
1343 .map(|fields| Some((src_addr, fields)))
1344 },
1345 *debug_log_prefix,
1346 messaging
1347 )
1348 .try_filter_map(|parse_result| {
1349 futures::future::ok(match parse_result {
1350 Ok(msg) => msg,
1351 Err(error) => {
1352 log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
1353 None
1354 }
1355 })
1356 })
1357 .fuse());
1358
1359 let (src_addr, fields_to_retain) = select_biased! {
1360 fields_to_retain_result = ack_or_nak_stream.select_next_some() => {
1361 fields_to_retain_result?
1362 },
1363 () = stop_receiver.select_next_some() => {
1364 return Ok(RequestingOutcome::GracefulShutdown)
1365 },
1366 send_requests_result = send_fut => {
1367 send_requests_result?;
1368 messaging.recv_time_out.increment();
1369 return Ok(RequestingOutcome::RanOutOfRetransmits)
1370 }
1371 };
1372
1373 match fields_to_retain {
1374 crate::parse::IncomingResponseToRequest::Ack(ack) => {
1375 let crate::parse::FieldsToRetainFromAck {
1376 yiaddr,
1377 server_identifier,
1378 ip_address_lease_time_secs,
1379 renewal_time_value_secs,
1380 rebinding_time_value_secs,
1381 parameters,
1382 } = ack;
1383
1384 let server_identifier = server_identifier.unwrap_or({
1385 let crate::parse::FieldsFromOfferToUseInRequest {
1386 server_identifier,
1387 ip_address_lease_time_secs: _,
1388 ip_address_to_request: _,
1389 } = fields_from_offer_to_use_in_request;
1390 *server_identifier
1391 });
1392
1393 if src_addr != server_identifier.get() {
1394 log::warn!(
1395 "{debug_log_prefix} accepting DHCPACK from {src_addr} \
1396 with differing server_identifier = {server_identifier}"
1397 );
1398 }
1399 Ok(RequestingOutcome::Bound(
1400 Bound {
1401 discover_options: discover_options.clone(),
1402 yiaddr,
1403 server_identifier,
1404 ip_address_lease_time: Duration::from_secs(
1405 ip_address_lease_time_secs.get().into(),
1406 ),
1407 renewal_time: renewal_time_value_secs
1408 .map(u64::from)
1409 .map(Duration::from_secs),
1410 rebinding_time: rebinding_time_value_secs
1411 .map(u64::from)
1412 .map(Duration::from_secs),
1413 start_time: *start_time,
1414 },
1415 parameters,
1416 ))
1417 }
1418 crate::parse::IncomingResponseToRequest::Nak(nak) => {
1419 recv_nak.increment();
1420 Ok(RequestingOutcome::Nak(nak))
1421 }
1422 }
1423 }
1424
1425 fn record(&self, inspector: &mut impl Inspector) {
1426 let Self { discover_options, start_time, fields_from_offer_to_use_in_request } = self;
1427 inspector.record_instant(diagnostics_traits::instant_property_name!("Start"), start_time);
1428 discover_options.record(inspector);
1429 fields_from_offer_to_use_in_request.record(inspector);
1430 }
1431}
1432
1433fn build_request_during_address_acquisition(
1434 client_config: &ClientConfig,
1435 discover_options: &DiscoverOptions,
1436 crate::parse::FieldsFromOfferToUseInRequest {
1437 server_identifier,
1438 ip_address_lease_time_secs,
1439 ip_address_to_request,
1440 }: &crate::parse::FieldsFromOfferToUseInRequest,
1441) -> dhcp_protocol::Message {
1442 let ClientConfig {
1443 client_hardware_address: _,
1444 client_identifier: _,
1445 requested_parameters: _,
1446 preferred_lease_time_secs,
1447 requested_ip_address: _,
1448 debug_log_prefix: _,
1449 } = client_config;
1450
1451 build_outgoing_message(
1460 client_config,
1461 discover_options,
1462 OutgoingOptions {
1463 ciaddr: None,
1464 requested_ip_address: Some(*ip_address_to_request),
1465 ip_address_lease_time_secs: ip_address_lease_time_secs.or(*preferred_lease_time_secs),
1466 message_type: dhcp_protocol::MessageType::DHCPREQUEST,
1467 server_identifier: Some(*server_identifier),
1468 include_parameter_request_list: true,
1469 },
1470 )
1471}
1472
1473#[derive(Debug, PartialEq)]
1475pub struct NewlyAcquiredLease<I> {
1476 pub ip_address: SpecifiedAddr<net_types::ip::Ipv4Addr>,
1478 pub start_time: I,
1480 pub lease_time: Duration,
1482 pub parameters: Vec<dhcp_protocol::DhcpOption>,
1486}
1487
1488#[derive(Debug, PartialEq)]
1489pub(crate) enum BoundOutcome<I> {
1490 GracefulShutdown,
1491 Renewing(Renewing<I>),
1492}
1493
1494#[derive(Debug, PartialEq, Clone, Copy)]
1498pub struct Bound<I> {
1499 discover_options: DiscoverOptions,
1500 yiaddr: SpecifiedAddr<net_types::ip::Ipv4Addr>,
1501 server_identifier: SpecifiedAddr<net_types::ip::Ipv4Addr>,
1502 ip_address_lease_time: Duration,
1503 start_time: I,
1504 renewal_time: Option<Duration>,
1505 rebinding_time: Option<Duration>,
1506}
1507
1508impl<I: deps::Instant> Bound<I> {
1509 async fn do_bound<C: deps::Clock<Instant = I>>(
1510 &self,
1511 client_config: &ClientConfig,
1512 time: &C,
1513 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1514 _counters: &Counters,
1517 ) -> BoundOutcome<I> {
1518 let Self {
1519 discover_options: _,
1520 yiaddr: _,
1521 server_identifier,
1522 ip_address_lease_time,
1523 start_time,
1524 renewal_time,
1525 rebinding_time: _,
1526 } = self;
1527
1528 let renewal_time = renewal_time.unwrap_or(*ip_address_lease_time / 2);
1531
1532 let debug_log_prefix = &client_config.debug_log_prefix;
1533 log::info!(
1534 "{debug_log_prefix} In Bound state; ip_address_lease_time = {}, renewal_time = {}, \
1535 server_identifier = {server_identifier}",
1536 ip_address_lease_time.as_secs(),
1537 renewal_time.as_secs(),
1538 );
1539
1540 let renewal_timeout_fut = time.wait_until(start_time.add(renewal_time)).fuse();
1541 let mut renewal_timeout_fut = pin!(renewal_timeout_fut);
1542 select! {
1543 () = renewal_timeout_fut => BoundOutcome::Renewing(Renewing { bound: self.clone() }),
1544 () = stop_receiver.select_next_some() => BoundOutcome::GracefulShutdown,
1545 }
1546 }
1547
1548 fn record(&self, inspector: &mut impl Inspector) {
1549 let Self {
1550 discover_options,
1551 yiaddr,
1552 server_identifier,
1553 ip_address_lease_time,
1554 start_time,
1555 renewal_time,
1556 rebinding_time,
1557 } = self;
1558 inspector.record_instant(diagnostics_traits::instant_property_name!("Start"), start_time);
1559 discover_options.record(inspector);
1560 inspector.record_ip_addr("Yiaddr", **yiaddr);
1561 inspector.record_ip_addr("ServerIdentifier", **server_identifier);
1562 inspector.record_uint("IpAddressLeaseTimeSecs", ip_address_lease_time.as_secs());
1563 record_optional_duration_secs(inspector, "RenewalTimeSecs", *renewal_time);
1564 record_optional_duration_secs(inspector, "RebindingTimeSecs", *rebinding_time);
1565 }
1566}
1567
1568#[derive(Debug, PartialEq)]
1569pub(crate) enum RenewingOutcome<I> {
1570 GracefulShutdown,
1571 Renewed(Bound<I>, Vec<dhcp_protocol::DhcpOption>),
1572 NewAddress(Bound<I>, Vec<dhcp_protocol::DhcpOption>),
1579 Nak(crate::parse::FieldsToRetainFromNak),
1580 Rebinding(Rebinding<I>),
1581}
1582
1583#[derive(Debug, PartialEq, Clone, Copy)]
1587pub struct Renewing<I> {
1588 bound: Bound<I>,
1589}
1590
1591const RENEW_RETRANSMIT_MINIMUM_DELAY: Duration = Duration::from_secs(60);
1598
1599impl<I: deps::Instant> Renewing<I> {
1600 async fn do_renewing<C: deps::Clock<Instant = I>>(
1601 &self,
1602 client_config: &ClientConfig,
1603 udp_socket_provider: &impl deps::UdpSocketProvider,
1607 time: &C,
1608 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1609 counters: &Counters,
1610 ) -> Result<RenewingOutcome<I>, Error> {
1611 let Counters { renewing: RenewingCounters { messaging, recv_error, recv_nak, .. }, .. } =
1612 counters;
1613 let renewal_start_time = time.now();
1614 let debug_log_prefix = client_config.debug_log_prefix;
1615
1616 let Self {
1617 bound:
1618 bound @ Bound {
1619 discover_options,
1620 yiaddr,
1621 server_identifier,
1622 ip_address_lease_time,
1623 start_time,
1624 renewal_time: _,
1625 rebinding_time,
1626 },
1627 } = self;
1628 let rebinding_time = rebinding_time.unwrap_or(*ip_address_lease_time / 8 * 7);
1629 let socket = udp_socket_provider
1630 .bind_new_udp_socket(std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
1631 yiaddr.get().into(),
1632 CLIENT_PORT.get(),
1633 )))
1634 .await
1635 .map_err(Error::Socket)?;
1636
1637 let message = build_outgoing_message(
1646 client_config,
1647 discover_options,
1648 OutgoingOptions {
1649 ciaddr: Some(*yiaddr),
1650 requested_ip_address: None,
1651 ip_address_lease_time_secs: client_config.preferred_lease_time_secs,
1652 message_type: dhcp_protocol::MessageType::DHCPREQUEST,
1653 server_identifier: None,
1654 include_parameter_request_list: true,
1655 },
1656 );
1657 let message_bytes = message.serialize();
1658
1659 let t2 = start_time.add(rebinding_time);
1660 let server_sockaddr = std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
1661 server_identifier.get().into(),
1662 SERVER_PORT.get(),
1663 ));
1664 let mut send_fut = pin!(send_with_retransmits_at_instants(
1665 time,
1666 std::iter::from_fn(|| {
1667 let now = time.now();
1668 let half_time_until_t2 = now.average(t2);
1669 Some(half_time_until_t2.max(now.add(RENEW_RETRANSMIT_MINIMUM_DELAY)))
1670 }),
1671 message_bytes.as_ref(),
1672 &socket,
1673 server_sockaddr,
1674 debug_log_prefix,
1675 messaging
1676 )
1677 .fuse());
1678
1679 let mut recv_buf = [0u8; BUFFER_SIZE];
1680 let mut responses_stream = pin!(recv_stream(
1681 &socket,
1682 &mut recv_buf,
1683 |packet, addr| {
1684 if addr != server_sockaddr {
1685 return Err(anyhow::Error::from(crate::parse::ParseError::WrongSource(addr)));
1686 }
1687 let message = dhcp_protocol::Message::from_buffer(packet)
1688 .map_err(crate::parse::ParseError::Dhcp)
1689 .context("error while parsing DHCP message from UDP datagram")
1690 .inspect_err(|_| messaging.recv_failed_dhcp_parse.increment())?;
1691 validate_message(discover_options, client_config, &message)
1692 .inspect_err(|e| e.increment(&messaging))
1693 .context("invalid DHCP message")?;
1694 crate::parse::fields_to_retain_from_response_to_request(
1695 &client_config.requested_parameters,
1696 message,
1697 )
1698 .inspect_err(|e| recv_error.increment(e))
1699 .context("error extracting needed fields from DHCP message during Renewing")
1700 },
1701 debug_log_prefix,
1702 messaging
1703 )
1704 .try_filter_map(|parse_result| {
1705 futures::future::ok(match parse_result {
1706 Ok(msg) => Some(msg),
1707 Err(error) => {
1708 log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
1709 None
1710 }
1711 })
1712 })
1713 .fuse());
1714
1715 let mut timeout_fut = pin!(time.wait_until(t2).fuse());
1716
1717 let response = select_biased! {
1718 response = responses_stream.select_next_some() => {
1719 response?
1720 },
1721 () = stop_receiver.select_next_some() => return Ok(RenewingOutcome::GracefulShutdown),
1722 send_result = send_fut => {
1723 return Err(send_result.expect_err("send_fut should never complete without error"))
1724 },
1725 () = timeout_fut => {
1726 messaging.recv_time_out.increment();
1727 return Ok(RenewingOutcome::Rebinding(
1728 Rebinding { bound: bound.clone() }
1729 ))
1730 }
1731 };
1732
1733 match response {
1734 crate::parse::IncomingResponseToRequest::Ack(ack) => {
1735 let crate::parse::FieldsToRetainFromAck {
1736 yiaddr: new_yiaddr,
1737 server_identifier: _,
1738 ip_address_lease_time_secs,
1739 renewal_time_value_secs,
1740 rebinding_time_value_secs,
1741 parameters,
1742 } = ack;
1743 let variant = if new_yiaddr == *yiaddr {
1744 log::debug!(
1745 "{debug_log_prefix} renewed with new lease time: {}",
1746 ip_address_lease_time_secs
1747 );
1748 RenewingOutcome::Renewed
1749 } else {
1750 log::info!(
1751 "{debug_log_prefix} obtained different address from renewal: {}",
1752 new_yiaddr
1753 );
1754 RenewingOutcome::NewAddress
1755 };
1756 Ok(variant(
1757 Bound {
1758 discover_options: discover_options.clone(),
1759 yiaddr: new_yiaddr,
1760 server_identifier: *server_identifier,
1761 ip_address_lease_time: Duration::from_secs(
1762 ip_address_lease_time_secs.get().into(),
1763 ),
1764 renewal_time: renewal_time_value_secs
1765 .map(u64::from)
1766 .map(Duration::from_secs),
1767 rebinding_time: rebinding_time_value_secs
1768 .map(u64::from)
1769 .map(Duration::from_secs),
1770 start_time: renewal_start_time,
1771 },
1772 parameters,
1773 ))
1774 }
1775 crate::parse::IncomingResponseToRequest::Nak(nak) => {
1776 recv_nak.increment();
1777 Ok(RenewingOutcome::Nak(nak))
1778 }
1779 }
1780 }
1781}
1782
1783#[derive(Debug, PartialEq)]
1785pub struct LeaseRenewal<I> {
1786 pub start_time: I,
1788 pub lease_time: Duration,
1790 pub parameters: Vec<dhcp_protocol::DhcpOption>,
1794}
1795
1796#[derive(Debug, PartialEq, Clone, Copy)]
1801pub struct Rebinding<I> {
1802 bound: Bound<I>,
1803}
1804
1805#[derive(Debug, PartialEq)]
1806pub(crate) enum RebindingOutcome<I> {
1807 GracefulShutdown,
1808 Renewed(Bound<I>, Vec<dhcp_protocol::DhcpOption>),
1809 NewAddress(Bound<I>, Vec<dhcp_protocol::DhcpOption>),
1816 Nak(crate::parse::FieldsToRetainFromNak),
1817 TimedOut,
1818}
1819
1820impl<I: deps::Instant> Rebinding<I> {
1821 async fn do_rebinding<C: deps::Clock<Instant = I>>(
1822 &self,
1823 client_config: &ClientConfig,
1824 udp_socket_provider: &impl deps::UdpSocketProvider,
1828 time: &C,
1829 stop_receiver: &mut mpsc::UnboundedReceiver<()>,
1830 counters: &Counters,
1831 ) -> Result<RebindingOutcome<I>, Error> {
1832 let Counters {
1833 rebinding: RebindingCounters { messaging, recv_error, recv_nak, .. }, ..
1834 } = counters;
1835 let rebinding_start_time = time.now();
1836 let debug_log_prefix = client_config.debug_log_prefix;
1837
1838 let Self {
1839 bound:
1840 Bound {
1841 discover_options,
1842 yiaddr,
1843 server_identifier: _,
1844 ip_address_lease_time,
1845 start_time,
1846 renewal_time: _,
1847 rebinding_time: _,
1848 },
1849 } = self;
1850 let socket = udp_socket_provider
1851 .bind_new_udp_socket(std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
1852 yiaddr.get().into(),
1853 CLIENT_PORT.get(),
1854 )))
1855 .await
1856 .map_err(Error::Socket)?;
1857
1858 let message = build_outgoing_message(
1867 client_config,
1868 discover_options,
1869 OutgoingOptions {
1870 ciaddr: Some(*yiaddr),
1871 requested_ip_address: None,
1872 ip_address_lease_time_secs: client_config.preferred_lease_time_secs,
1873 message_type: dhcp_protocol::MessageType::DHCPREQUEST,
1874 server_identifier: None,
1875 include_parameter_request_list: true,
1876 },
1877 );
1878 let message_bytes = message.serialize();
1879
1880 let lease_expiry = start_time.add(*ip_address_lease_time);
1881 let server_sockaddr = std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
1882 Ipv4Addr::BROADCAST,
1883 SERVER_PORT.get(),
1884 ));
1885 let mut send_fut = pin!(send_with_retransmits_at_instants(
1886 time,
1887 std::iter::from_fn(|| {
1888 let now = time.now();
1889 let half_time_until_lease_expiry = now.average(lease_expiry);
1890 Some(half_time_until_lease_expiry.max(now.add(RENEW_RETRANSMIT_MINIMUM_DELAY)))
1891 }),
1892 message_bytes.as_ref(),
1893 &socket,
1894 server_sockaddr,
1895 debug_log_prefix,
1896 messaging
1897 )
1898 .fuse());
1899
1900 let mut recv_buf = [0u8; BUFFER_SIZE];
1901 let mut responses_stream = pin!(recv_stream(
1902 &socket,
1903 &mut recv_buf,
1904 |packet, _addr| {
1905 let message = dhcp_protocol::Message::from_buffer(packet)
1906 .map_err(crate::parse::ParseError::Dhcp)
1907 .context("error while parsing DHCP message from UDP datagram")
1908 .inspect_err(|_| messaging.recv_failed_dhcp_parse.increment())?;
1909 validate_message(discover_options, client_config, &message)
1910 .inspect_err(|e| e.increment(&messaging))
1911 .context("invalid DHCP message")?;
1912 crate::parse::fields_to_retain_from_response_to_request(
1913 &client_config.requested_parameters,
1914 message,
1915 )
1916 .and_then(|response| match response {
1917 crate::parse::IncomingResponseToRequest::Ack(ack) => {
1918 Ok(crate::parse::IncomingResponseToRequest::Ack(
1922 ack.map_server_identifier(|server_identifier| {
1923 server_identifier.ok_or(
1924 crate::parse::IncomingResponseToRequestError::NoServerIdentifier,
1925 )
1926 })?,
1927 ))
1928 }
1929 crate::parse::IncomingResponseToRequest::Nak(nak) => {
1930 Ok(crate::parse::IncomingResponseToRequest::Nak(nak))
1931 }
1932 })
1933 .inspect_err(|e| {
1934 recv_error.increment(e);
1935 })
1936 .context("error extracting needed fields from DHCP message during Rebinding")
1937 },
1938 debug_log_prefix,
1939 messaging
1940 )
1941 .try_filter_map(|parse_result| {
1942 futures::future::ok(match parse_result {
1943 Ok(msg) => Some(msg),
1944 Err(error) => {
1945 log::warn!("{debug_log_prefix} discarding incoming packet: {:?}", error);
1946 None
1947 }
1948 })
1949 })
1950 .fuse());
1951
1952 let mut timeout_fut = pin!(time.wait_until(lease_expiry).fuse());
1953
1954 let response = select_biased! {
1955 response = responses_stream.select_next_some() => {
1956 response?
1957 },
1958 () = stop_receiver.select_next_some() => return Ok(RebindingOutcome::GracefulShutdown),
1959 send_result = send_fut => {
1960 return Err(send_result.expect_err("send_fut should never complete without error"))
1961 },
1962 () = timeout_fut => {
1963 messaging.recv_time_out.increment();
1964 return Ok(RebindingOutcome::TimedOut)
1965 }
1966 };
1967
1968 match response {
1969 crate::parse::IncomingResponseToRequest::Ack(ack) => {
1970 let crate::parse::FieldsToRetainFromAck {
1971 yiaddr: new_yiaddr,
1972 server_identifier,
1973 ip_address_lease_time_secs,
1974 renewal_time_value_secs,
1975 rebinding_time_value_secs,
1976 parameters,
1977 } = ack;
1978 let variant = if new_yiaddr == *yiaddr {
1979 log::debug!(
1980 "{debug_log_prefix} rebound with new lease time: {}",
1981 ip_address_lease_time_secs
1982 );
1983 RebindingOutcome::Renewed
1984 } else {
1985 log::info!(
1986 "{debug_log_prefix} obtained different address from rebinding: {}",
1987 new_yiaddr
1988 );
1989 RebindingOutcome::NewAddress
1990 };
1991 Ok(variant(
1992 Bound {
1993 discover_options: discover_options.clone(),
1994 yiaddr: new_yiaddr,
1995 server_identifier,
1996 ip_address_lease_time: Duration::from_secs(
1997 ip_address_lease_time_secs.get().into(),
1998 ),
1999 renewal_time: renewal_time_value_secs
2000 .map(u64::from)
2001 .map(Duration::from_secs),
2002 rebinding_time: rebinding_time_value_secs
2003 .map(u64::from)
2004 .map(Duration::from_secs),
2005 start_time: rebinding_start_time,
2006 },
2007 parameters,
2008 ))
2009 }
2010 crate::parse::IncomingResponseToRequest::Nak(nak) => {
2011 recv_nak.increment();
2012 Ok(RebindingOutcome::Nak(nak))
2013 }
2014 }
2015 }
2016}
2017
2018#[cfg(test)]
2019mod test {
2020 use super::*;
2021 use crate::deps::testutil::{
2022 advance, run_until_next_timers_fire, FakeRngProvider, FakeSocket, FakeSocketProvider,
2023 FakeTimeController, TestInstant,
2024 };
2025 use crate::deps::Clock as _;
2026 use assert_matches::assert_matches;
2027 use fuchsia_async as fasync;
2028 use futures::{join, Future};
2029 use itertools::Itertools as _;
2030 use net_declare::net::prefix_length_v4;
2031 use net_declare::{net_mac, std_ip_v4};
2032 use net_types::ip::{Ipv4, PrefixLength};
2033 use simplelog::{Config, LevelFilter, WriteLogger};
2034 use std::cell::RefCell;
2035 use std::rc::Rc;
2036 use test_case::test_case;
2037
2038 fn initialize_logging() {
2039 WriteLogger::init(LevelFilter::Info, Config::default(), std::io::stderr()).unwrap();
2040 }
2041
2042 const TEST_MAC_ADDRESS: Mac = net_mac!("01:01:01:01:01:01");
2043 const TEST_SERVER_MAC_ADDRESS: Mac = net_mac!("02:02:02:02:02:02");
2044 const OTHER_MAC_ADDRESS: Mac = net_mac!("03:03:03:03:03:03");
2045
2046 const SERVER_IP: Ipv4Addr = std_ip_v4!("192.168.1.1");
2047 const OTHER_SERVER_IP: Ipv4Addr = std_ip_v4!("192.168.1.11");
2048 const YIADDR: Ipv4Addr = std_ip_v4!("198.168.1.5");
2049 const OTHER_ADDR: Ipv4Addr = std_ip_v4!("198.168.1.6");
2050 const DEFAULT_LEASE_LENGTH_SECONDS: u32 = 100;
2051 const MAX_LEASE_LENGTH_SECONDS: u32 = 200;
2052 const TEST_PREFIX_LENGTH: PrefixLength<Ipv4> = prefix_length_v4!(24);
2053
2054 fn test_requested_parameters() -> OptionCodeMap<OptionRequested> {
2055 use dhcp_protocol::OptionCode;
2056 [
2057 (OptionCode::SubnetMask, OptionRequested::Required),
2058 (OptionCode::Router, OptionRequested::Optional),
2059 (OptionCode::DomainNameServer, OptionRequested::Optional),
2060 ]
2061 .into_iter()
2062 .collect::<OptionCodeMap<_>>()
2063 }
2064
2065 fn test_parameter_values_excluding_subnet_mask() -> [dhcp_protocol::DhcpOption; 2] {
2066 [
2067 dhcp_protocol::DhcpOption::Router([SERVER_IP].into()),
2068 dhcp_protocol::DhcpOption::DomainNameServer([SERVER_IP, std_ip_v4!("8.8.8.8")].into()),
2069 ]
2070 }
2071
2072 fn test_parameter_values() -> impl IntoIterator<Item = dhcp_protocol::DhcpOption> {
2073 std::iter::once(dhcp_protocol::DhcpOption::SubnetMask(TEST_PREFIX_LENGTH))
2074 .chain(test_parameter_values_excluding_subnet_mask())
2075 }
2076
2077 fn test_client_config() -> ClientConfig {
2078 ClientConfig {
2079 client_hardware_address: TEST_MAC_ADDRESS,
2080 client_identifier: None,
2081 requested_parameters: test_requested_parameters(),
2082 preferred_lease_time_secs: None,
2083 requested_ip_address: None,
2084 debug_log_prefix: DebugLogPrefix { interface_id: NonZeroU64::new(2).unwrap() },
2085 }
2086 }
2087
2088 #[test]
2089 fn do_init_uses_rng() {
2090 let mut rng = FakeRngProvider::new(0);
2091 let time = FakeTimeController::new();
2092 let arbitrary_start_time = std::time::Duration::from_secs(42);
2093 advance(&time, arbitrary_start_time);
2094
2095 let Selecting {
2096 discover_options: DiscoverOptions { xid: xid_a },
2097 start_time: start_time_a,
2098 } = Init.do_init(&mut rng, &time);
2099 let Selecting {
2100 discover_options: DiscoverOptions { xid: xid_b },
2101 start_time: start_time_b,
2102 } = Init.do_init(&mut rng, &time);
2103 assert_ne!(xid_a, xid_b);
2104 assert_eq!(start_time_a, TestInstant(arbitrary_start_time));
2105 assert_eq!(start_time_b, TestInstant(arbitrary_start_time));
2106 }
2107
2108 fn run_with_accelerated_time<F>(
2109 executor: &mut fasync::TestExecutor,
2110 time: &Rc<RefCell<FakeTimeController>>,
2111 main_future: &mut F,
2112 ) -> F::Output
2113 where
2114 F: Future + Unpin,
2115 {
2116 loop {
2117 match run_until_next_timers_fire(executor, time, main_future) {
2118 std::task::Poll::Ready(result) => break result,
2119 std::task::Poll::Pending => (),
2120 }
2121 }
2122 }
2123
2124 fn build_test_selecting_state() -> Selecting<TestInstant> {
2125 Selecting {
2126 discover_options: DiscoverOptions { xid: TransactionId(NonZeroU32::new(1).unwrap()) },
2127 start_time: TestInstant(std::time::Duration::from_secs(0)),
2128 }
2129 }
2130
2131 #[test]
2132 fn do_selecting_obeys_graceful_shutdown() {
2133 initialize_logging();
2134 let counters = Counters::default();
2135
2136 let mut executor = fasync::TestExecutor::new();
2137 let time = FakeTimeController::new();
2138
2139 let selecting = build_test_selecting_state();
2140 let mut rng = FakeRngProvider::new(0);
2141
2142 let (_server_end, client_end) = FakeSocket::new_pair();
2143 let test_socket_provider = FakeSocketProvider::new(client_end);
2144
2145 let client_config = test_client_config();
2146
2147 let (stop_sender, mut stop_receiver) = mpsc::unbounded();
2148
2149 let mut selecting_fut = pin!(selecting
2150 .do_selecting(
2151 &client_config,
2152 &test_socket_provider,
2153 &mut rng,
2154 &time,
2155 &mut stop_receiver,
2156 &counters,
2157 )
2158 .fuse());
2159
2160 let time = &time;
2161
2162 let mut wait_fut = pin!(async {
2163 time.wait_until(TestInstant(std::time::Duration::from_secs(30))).await;
2166 }
2167 .fuse());
2168
2169 {
2170 let main_future = async {
2171 select! {
2172 _ = selecting_fut => unreachable!("should keep retransmitting DHCPDISCOVER forever"),
2173 () = wait_fut => (),
2174 }
2175 };
2176 let mut main_future = pin!(main_future);
2177
2178 run_with_accelerated_time(&mut executor, time, &mut main_future);
2179 }
2180
2181 stop_sender.unbounded_send(()).expect("sending stop signal should succeed");
2182
2183 let selecting_result = selecting_fut.now_or_never().expect(
2184 "selecting_fut should complete after single poll after stop signal has been sent",
2185 );
2186
2187 assert_matches!(selecting_result, Ok(SelectingOutcome::GracefulShutdown));
2188 }
2189
2190 struct VaryingOutgoingMessageFields {
2191 xid: u32,
2192 options: Vec<dhcp_protocol::DhcpOption>,
2193 }
2194
2195 #[track_caller]
2196 fn assert_outgoing_message_when_not_assigned_address(
2197 got_message: &dhcp_protocol::Message,
2198 fields: VaryingOutgoingMessageFields,
2199 ) {
2200 let VaryingOutgoingMessageFields { xid, options } = fields;
2201 let want_message = dhcp_protocol::Message {
2202 op: dhcp_protocol::OpCode::BOOTREQUEST,
2203 xid,
2204 secs: 0,
2205 bdcast_flag: false,
2206 ciaddr: Ipv4Addr::UNSPECIFIED,
2207 yiaddr: Ipv4Addr::UNSPECIFIED,
2208 siaddr: Ipv4Addr::UNSPECIFIED,
2209 giaddr: Ipv4Addr::UNSPECIFIED,
2210 chaddr: TEST_MAC_ADDRESS,
2211 sname: String::new(),
2212 file: String::new(),
2213 options,
2214 };
2215 assert_eq!(got_message, &want_message);
2216 }
2217
2218 #[test]
2219 fn do_selecting_sends_discover() {
2220 initialize_logging();
2221 let counters = Counters::default();
2222
2223 let mut executor = fasync::TestExecutor::new();
2224 let time = FakeTimeController::new();
2225
2226 let selecting = Selecting {
2227 discover_options: DiscoverOptions { xid: TransactionId(NonZeroU32::new(1).unwrap()) },
2228 start_time: TestInstant(std::time::Duration::from_secs(0)),
2229 };
2230 let mut rng = FakeRngProvider::new(0);
2231
2232 let (server_end, client_end) = FakeSocket::new_pair();
2233 let test_socket_provider = FakeSocketProvider::new(client_end);
2234
2235 let client_config = test_client_config();
2236
2237 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
2238
2239 let mut selecting_fut = pin!(selecting
2240 .do_selecting(
2241 &client_config,
2242 &test_socket_provider,
2243 &mut rng,
2244 &time,
2245 &mut stop_receiver,
2246 &counters,
2247 )
2248 .fuse());
2249
2250 let time = &time;
2251
2252 const EXPECTED_RANGES: [(u64, u64); 7] =
2256 [(0, 0), (3, 5), (7, 9), (15, 17), (31, 33), (63, 65), (63, 65)];
2257
2258 let mut receive_fut = pin!(async {
2259 let mut previous_time = std::time::Duration::from_secs(0);
2260
2261 for (start, end) in EXPECTED_RANGES {
2262 let mut recv_buf = [0u8; BUFFER_SIZE];
2263 let DatagramInfo { length, address } = server_end
2264 .recv_from(&mut recv_buf)
2265 .await
2266 .expect("recv_from on test socket should succeed");
2267
2268 assert_eq!(address, Mac::BROADCAST);
2269
2270 let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
2271 &recv_buf[..length],
2272 dhcp_protocol::SERVER_PORT,
2273 )
2274 .expect("received packet should parse as DHCP message");
2275
2276 assert_outgoing_message_when_not_assigned_address(
2277 &msg,
2278 VaryingOutgoingMessageFields {
2279 xid: msg.xid,
2280 options: vec![
2281 dhcp_protocol::DhcpOption::DhcpMessageType(
2282 dhcp_protocol::MessageType::DHCPDISCOVER,
2283 ),
2284 dhcp_protocol::DhcpOption::ParameterRequestList(
2285 test_requested_parameters()
2286 .iter_keys()
2287 .collect::<Vec<_>>()
2288 .try_into()
2289 .expect("should fit parameter request list size constraints"),
2290 ),
2291 ],
2292 },
2293 );
2294
2295 let TestInstant(received_time) = time.now();
2296
2297 let duration_range =
2298 std::time::Duration::from_secs(start)..=std::time::Duration::from_secs(end);
2299 assert!(duration_range.contains(&(received_time - previous_time)));
2300
2301 previous_time = received_time;
2302 }
2303 }
2304 .fuse());
2305
2306 let main_future = async {
2307 select! {
2308 _ = selecting_fut => unreachable!("should keep retransmitting DHCPDISCOVER forever"),
2309 () = receive_fut => (),
2310 }
2311 };
2312 let mut main_future = pin!(main_future);
2313
2314 run_with_accelerated_time(&mut executor, time, &mut main_future);
2315 assert_eq!(counters.selecting.messaging.send_message.load(), EXPECTED_RANGES.len());
2316 assert_eq!(counters.selecting.messaging.recv_time_out.load(), EXPECTED_RANGES.len() - 1);
2317 }
2318
2319 const XID: NonZeroU32 = NonZeroU32::new(1).unwrap();
2320 #[test_case(u32::from(XID), TEST_MAC_ADDRESS => Ok(()) ; "accepts good reply")]
2321 #[test_case(u32::from(XID), TEST_SERVER_MAC_ADDRESS => Err(
2322 ValidateMessageError::WrongChaddr {
2323 expected: TEST_MAC_ADDRESS,
2324 actual: TEST_SERVER_MAC_ADDRESS,
2325 }) ; "rejects wrong chaddr")]
2326 #[test_case(u32::from(XID).wrapping_add(1), TEST_MAC_ADDRESS => Err(
2327 ValidateMessageError::WrongXid {
2328 expected: u32::from(XID),
2329 actual: u32::from(XID).wrapping_add(1),
2330 }) ; "rejects wrong xid")]
2331 fn test_validate_message(
2332 message_xid: u32,
2333 message_chaddr: Mac,
2334 ) -> Result<(), ValidateMessageError> {
2335 let discover_options = DiscoverOptions { xid: TransactionId(XID) };
2336 let client_config = ClientConfig {
2337 client_hardware_address: TEST_MAC_ADDRESS,
2338 client_identifier: None,
2339 requested_parameters: test_requested_parameters(),
2340 preferred_lease_time_secs: None,
2341 requested_ip_address: None,
2342 debug_log_prefix: DebugLogPrefix { interface_id: NonZeroU64::new(2).unwrap() },
2343 };
2344
2345 let reply = dhcp_protocol::Message {
2346 op: dhcp_protocol::OpCode::BOOTREPLY,
2347 xid: message_xid,
2348 secs: 0,
2349 bdcast_flag: false,
2350 ciaddr: Ipv4Addr::UNSPECIFIED,
2351 yiaddr: Ipv4Addr::UNSPECIFIED,
2352 siaddr: Ipv4Addr::UNSPECIFIED,
2353 giaddr: Ipv4Addr::UNSPECIFIED,
2354 chaddr: message_chaddr,
2355 sname: String::new(),
2356 file: String::new(),
2357 options: Vec::new(),
2358 };
2359
2360 validate_message(&discover_options, &client_config, &reply)
2361 }
2362
2363 #[allow(clippy::unused_unit)]
2364 #[test_case(false ; "with no garbage traffic on link")]
2365 #[test_case(true ; "ignoring garbage replies to discover")]
2366 fn do_selecting_good_offer(reply_to_discover_with_garbage: bool) {
2367 initialize_logging();
2368 let counters = Counters::default();
2369
2370 let mut rng = FakeRngProvider::new(0);
2371 let time = FakeTimeController::new();
2372
2373 let arbitrary_start_time = std::time::Duration::from_secs(42);
2374 advance(&time, arbitrary_start_time);
2375
2376 let selecting = Init.do_init(&mut rng, &time);
2377 let TransactionId(xid) = selecting.discover_options.xid;
2378
2379 let (server_end, client_end) = FakeSocket::<Mac>::new_pair();
2380 let test_socket_provider = FakeSocketProvider::new(client_end);
2381
2382 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
2383
2384 let client_config = test_client_config();
2385
2386 let selecting_fut = pin!(selecting
2387 .do_selecting(
2388 &client_config,
2389 &test_socket_provider,
2390 &mut rng,
2391 &time,
2392 &mut stop_receiver,
2393 &counters,
2394 )
2395 .fuse());
2396
2397 let server_fut = pin!(async {
2398 let mut recv_buf = [0u8; BUFFER_SIZE];
2399
2400 if reply_to_discover_with_garbage {
2401 let DatagramInfo { length: _, address: dst_addr } = server_end
2402 .recv_from(&mut recv_buf)
2403 .await
2404 .expect("recv_from on test socket should succeed");
2405 assert_eq!(dst_addr, Mac::BROADCAST);
2406
2407 server_end
2408 .send_to(b"hello", OTHER_MAC_ADDRESS)
2409 .await
2410 .expect("send_to with garbage data should succeed");
2411 }
2412
2413 let DatagramInfo { length, address } = server_end
2414 .recv_from(&mut recv_buf)
2415 .await
2416 .expect("recv_from on test socket should succeed");
2417 assert_eq!(address, Mac::BROADCAST);
2418
2419 let parse_msg = || {
2422 let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
2423 &recv_buf[..length],
2424 dhcp_protocol::SERVER_PORT,
2425 )
2426 .expect("received packet on test socket should parse as DHCP message");
2427 msg
2428 };
2429
2430 let msg = parse_msg();
2431 assert_outgoing_message_when_not_assigned_address(
2432 &parse_msg(),
2433 VaryingOutgoingMessageFields {
2434 xid: msg.xid,
2435 options: vec![
2436 dhcp_protocol::DhcpOption::DhcpMessageType(
2437 dhcp_protocol::MessageType::DHCPDISCOVER,
2438 ),
2439 dhcp_protocol::DhcpOption::ParameterRequestList(
2440 test_requested_parameters()
2441 .iter_keys()
2442 .collect::<Vec<_>>()
2443 .try_into()
2444 .expect("should fit parameter request list size constraints"),
2445 ),
2446 ],
2447 },
2448 );
2449
2450 let build_reply = || {
2451 dhcpv4::server::build_offer(
2452 parse_msg(),
2453 dhcpv4::server::OfferOptions {
2454 offered_ip: YIADDR,
2455 server_ip: SERVER_IP,
2456 lease_length_config: dhcpv4::configuration::LeaseLength {
2457 default_seconds: DEFAULT_LEASE_LENGTH_SECONDS,
2458 max_seconds: MAX_LEASE_LENGTH_SECONDS,
2459 },
2460 renewal_time_value: Some(20),
2464 rebinding_time_value: Some(30),
2465 subnet_mask: TEST_PREFIX_LENGTH,
2466 },
2467 &dhcpv4::server::options_repo(test_parameter_values()),
2468 )
2469 .expect("dhcp server crate error building offer")
2470 };
2471
2472 let reply_with_wrong_xid = dhcp_protocol::Message {
2473 xid: (u32::from(xid).wrapping_add(1)),
2474 yiaddr: OTHER_ADDR,
2478 ..build_reply()
2479 };
2480
2481 let reply_without_subnet_mask = {
2482 let mut reply = build_reply();
2483 let options = std::mem::take(&mut reply.options);
2484 let (subnet_masks, other_options): (Vec<_>, Vec<_>) =
2485 options.into_iter().partition_map(|option| match option {
2486 dhcp_protocol::DhcpOption::SubnetMask(_) => itertools::Either::Left(option),
2487 _ => itertools::Either::Right(option),
2488 });
2489 assert_matches!(
2490 &subnet_masks[..],
2491 &[dhcp_protocol::DhcpOption::SubnetMask(TEST_PREFIX_LENGTH)]
2492 );
2493 reply.options = other_options;
2494
2495 reply.yiaddr = OTHER_ADDR;
2499 reply
2500 };
2501
2502 let good_reply = build_reply();
2503
2504 let send_reply = |reply: dhcp_protocol::Message| async {
2505 let dst_ip = reply.yiaddr;
2506 server_end
2507 .send_to(
2508 crate::parse::serialize_dhcp_message_to_ip_packet(
2509 reply,
2510 SERVER_IP,
2511 SERVER_PORT,
2512 dst_ip,
2513 CLIENT_PORT,
2514 )
2515 .as_ref(),
2516 TEST_SERVER_MAC_ADDRESS,
2519 )
2520 .await
2521 .expect("send_to on test socket should succeed");
2522 };
2523
2524 send_reply(reply_with_wrong_xid).await;
2526
2527 send_reply(reply_without_subnet_mask).await;
2529
2530 send_reply(good_reply).await;
2531 }
2532 .fuse());
2533
2534 let main_future = async move {
2535 let (selecting_result, ()) = join!(selecting_fut, server_fut);
2536 selecting_result
2537 }
2538 .fuse();
2539 let mut main_future = pin!(main_future);
2540 let mut executor = fasync::TestExecutor::new();
2541 let selecting_result = run_with_accelerated_time(&mut executor, &time, &mut main_future);
2542
2543 let requesting = assert_matches!(
2544 selecting_result,
2545 Ok(SelectingOutcome::Requesting(requesting)) => requesting,
2546 "should have successfully transitioned to Requesting"
2547 );
2548
2549 assert_eq!(
2550 requesting,
2551 Requesting {
2552 discover_options: DiscoverOptions { xid: requesting.discover_options.xid },
2553 fields_from_offer_to_use_in_request: crate::parse::FieldsFromOfferToUseInRequest {
2554 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
2555 .try_into()
2556 .expect("should be specified"),
2557 ip_address_lease_time_secs: Some(
2558 NonZeroU32::new(DEFAULT_LEASE_LENGTH_SECONDS).unwrap()
2559 ),
2560 ip_address_to_request: net_types::ip::Ipv4Addr::from(YIADDR)
2561 .try_into()
2562 .expect("should be specified"),
2563 },
2564 start_time: TestInstant(arbitrary_start_time),
2565 }
2566 );
2567 assert_eq!(
2568 counters.selecting.messaging.send_message.load(),
2569 if reply_to_discover_with_garbage { 2 } else { 1 }
2570 );
2571 assert_eq!(
2572 counters.selecting.messaging.recv_time_out.load(),
2573 if reply_to_discover_with_garbage { 1 } else { 0 }
2574 );
2575 assert_eq!(
2576 counters.selecting.messaging.recv_failed_dhcp_parse.load(),
2577 if reply_to_discover_with_garbage { 1 } else { 0 }
2578 );
2579 assert_eq!(counters.selecting.messaging.recv_wrong_xid.load(), 1);
2580 assert_eq!(counters.selecting.messaging.recv_wrong_chaddr.load(), 0);
2581 assert_eq!(counters.selecting.recv_error.missing_required_option.load(), 1);
2582 }
2583
2584 const TEST_XID: TransactionId = TransactionId(NonZeroU32::new(1).unwrap());
2585 const TEST_DISCOVER_OPTIONS: DiscoverOptions = DiscoverOptions { xid: TEST_XID };
2586
2587 fn build_test_requesting_state() -> Requesting<TestInstant> {
2588 Requesting {
2589 discover_options: TEST_DISCOVER_OPTIONS,
2590 start_time: TestInstant(std::time::Duration::from_secs(0)),
2591 fields_from_offer_to_use_in_request: crate::parse::FieldsFromOfferToUseInRequest {
2592 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
2593 .try_into()
2594 .expect("should be specified"),
2595 ip_address_lease_time_secs: Some(
2596 NonZeroU32::new(DEFAULT_LEASE_LENGTH_SECONDS).unwrap(),
2597 ),
2598 ip_address_to_request: net_types::ip::Ipv4Addr::from(YIADDR)
2599 .try_into()
2600 .expect("should be specified"),
2601 },
2602 }
2603 }
2604
2605 #[test]
2606 fn do_requesting_obeys_graceful_shutdown() {
2607 initialize_logging();
2608 let counters = Counters::default();
2609
2610 let time = FakeTimeController::new();
2611
2612 let requesting = build_test_requesting_state();
2613 let mut rng = FakeRngProvider::new(0);
2614
2615 let (_server_end, client_end) = FakeSocket::new_pair();
2616 let test_socket_provider = FakeSocketProvider::new(client_end);
2617
2618 let client_config = test_client_config();
2619
2620 let (stop_sender, mut stop_receiver) = mpsc::unbounded();
2621
2622 let requesting_fut = requesting
2623 .do_requesting(
2624 &client_config,
2625 &test_socket_provider,
2626 &mut rng,
2627 &time,
2628 &mut stop_receiver,
2629 &counters,
2630 )
2631 .fuse();
2632 let mut requesting_fut = pin!(requesting_fut);
2633
2634 let mut executor = fasync::TestExecutor::new();
2635 assert_matches!(executor.run_until_stalled(&mut requesting_fut), std::task::Poll::Pending);
2636
2637 stop_sender.unbounded_send(()).expect("sending stop signal should succeed");
2638
2639 let requesting_result = requesting_fut.now_or_never().expect(
2640 "requesting_fut should complete after single poll after stop signal has been sent",
2641 );
2642
2643 assert_matches!(requesting_result, Ok(RequestingOutcome::GracefulShutdown));
2644 }
2645
2646 #[test]
2647 fn do_requesting_sends_requests() {
2648 initialize_logging();
2649 let counters = Counters::default();
2650
2651 let mut executor = fasync::TestExecutor::new();
2652 let time = FakeTimeController::new();
2653
2654 let requesting = build_test_requesting_state();
2655 let mut rng = FakeRngProvider::new(0);
2656
2657 let (server_end, client_end) = FakeSocket::new_pair();
2658 let test_socket_provider = FakeSocketProvider::new(client_end);
2659
2660 let client_config = test_client_config();
2661
2662 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
2663
2664 let requesting_fut = pin!(requesting
2665 .do_requesting(
2666 &client_config,
2667 &test_socket_provider,
2668 &mut rng,
2669 &time,
2670 &mut stop_receiver,
2671 &counters,
2672 )
2673 .fuse());
2674
2675 let time = &time;
2676
2677 const EXPECTED_RANGES: [(u64, u64); NUM_REQUEST_RETRANSMITS + 1] =
2681 [(0, 0), (3, 5), (7, 9), (15, 17), (31, 33)];
2682
2683 let receive_fut = pin!(async {
2684 let mut previous_time = std::time::Duration::from_secs(0);
2685
2686 for (start, end) in EXPECTED_RANGES {
2687 let mut recv_buf = [0u8; BUFFER_SIZE];
2688 let DatagramInfo { length, address } = server_end
2689 .recv_from(&mut recv_buf)
2690 .await
2691 .expect("recv_from on test socket should succeed");
2692
2693 assert_eq!(address, Mac::BROADCAST);
2694
2695 let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
2696 &recv_buf[..length],
2697 dhcp_protocol::SERVER_PORT,
2698 )
2699 .expect("received packet should parse as DHCP message");
2700
2701 assert_outgoing_message_when_not_assigned_address(
2702 &msg,
2703 VaryingOutgoingMessageFields {
2704 xid: msg.xid,
2705 options: vec![
2706 dhcp_protocol::DhcpOption::RequestedIpAddress(YIADDR),
2707 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
2708 DEFAULT_LEASE_LENGTH_SECONDS,
2709 ),
2710 dhcp_protocol::DhcpOption::DhcpMessageType(
2711 dhcp_protocol::MessageType::DHCPREQUEST,
2712 ),
2713 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
2714 dhcp_protocol::DhcpOption::ParameterRequestList(
2715 test_requested_parameters()
2716 .iter_keys()
2717 .collect::<Vec<_>>()
2718 .try_into()
2719 .expect("should fit parameter request list size constraints"),
2720 ),
2721 ],
2722 },
2723 );
2724
2725 let TestInstant(received_time) = time.now();
2726
2727 let duration_range =
2728 std::time::Duration::from_secs(start)..=std::time::Duration::from_secs(end);
2729 assert!(duration_range.contains(&(received_time - previous_time)));
2730
2731 previous_time = received_time;
2732 }
2733 }
2734 .fuse());
2735
2736 let main_future = async { join!(requesting_fut, receive_fut) };
2737 let mut main_future = pin!(main_future);
2738
2739 let (requesting_result, ()) =
2740 run_with_accelerated_time(&mut executor, time, &mut main_future);
2741
2742 assert_matches!(requesting_result, Ok(RequestingOutcome::RanOutOfRetransmits));
2743 assert_eq!(counters.requesting.messaging.send_message.load(), EXPECTED_RANGES.len());
2744 assert_eq!(counters.requesting.messaging.recv_time_out.load(), EXPECTED_RANGES.len());
2745 }
2746
2747 struct VaryingIncomingMessageFields {
2748 yiaddr: Ipv4Addr,
2749 options: Vec<dhcp_protocol::DhcpOption>,
2750 }
2751
2752 fn build_incoming_message(
2753 xid: u32,
2754 fields: VaryingIncomingMessageFields,
2755 ) -> dhcp_protocol::Message {
2756 let VaryingIncomingMessageFields { yiaddr, options } = fields;
2757
2758 dhcp_protocol::Message {
2759 op: dhcp_protocol::OpCode::BOOTREPLY,
2760 xid,
2761 secs: 0,
2762 bdcast_flag: false,
2763 ciaddr: Ipv4Addr::UNSPECIFIED,
2764 yiaddr,
2765 siaddr: Ipv4Addr::UNSPECIFIED,
2766 giaddr: Ipv4Addr::UNSPECIFIED,
2767 chaddr: TEST_MAC_ADDRESS,
2768 sname: String::new(),
2769 file: String::new(),
2770 options,
2771 }
2772 }
2773
2774 const NAK_MESSAGE: &str = "something went wrong";
2775
2776 #[derive(PartialEq, Debug)]
2777 struct RequestingTestResult {
2778 outcome: RequestingOutcome<TestInstant>,
2779 counters: RequestingTestCounters,
2780 }
2781
2782 #[derive(PartialEq, Eq, Debug, Default)]
2783 struct RequestingTestCounters {
2784 send_message: usize,
2785 recv_time_out: usize,
2786 recv_failed_dhcp_parse: usize,
2787 recv_wrong_xid: usize,
2788 recv_wrong_chaddr: usize,
2789 recv_message: usize,
2790 recv_nak: usize,
2791 recv_missing_option: usize,
2792 }
2793
2794 #[test_case(VaryingIncomingMessageFields {
2795 yiaddr: YIADDR,
2796 options: [
2797 dhcp_protocol::DhcpOption::DhcpMessageType(
2798 dhcp_protocol::MessageType::DHCPACK,
2799 ),
2800 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
2801 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
2802 DEFAULT_LEASE_LENGTH_SECONDS,
2803 ),
2804 ]
2805 .into_iter()
2806 .chain(test_parameter_values())
2807 .collect(),
2808 } => RequestingTestResult {
2809 outcome: RequestingOutcome::Bound(Bound {
2810 discover_options: TEST_DISCOVER_OPTIONS,
2811 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
2812 .try_into()
2813 .expect("should be specified"),
2814 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
2815 .try_into()
2816 .expect("should be specified"),
2817 ip_address_lease_time: std::time::Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
2818 renewal_time: None,
2819 rebinding_time: None,
2820 start_time: TestInstant(std::time::Duration::from_secs(0)),
2821 }, test_parameter_values().into_iter().collect()),
2822 counters: RequestingTestCounters {
2823 send_message: 1,
2824 recv_message: 1,
2825 ..Default::default()
2826 }
2827 } ; "transitions to Bound after receiving DHCPACK")]
2828 #[test_case(VaryingIncomingMessageFields {
2829 yiaddr: YIADDR,
2830 options: [
2831 dhcp_protocol::DhcpOption::DhcpMessageType(
2832 dhcp_protocol::MessageType::DHCPACK,
2833 ),
2834 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
2835 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
2836 DEFAULT_LEASE_LENGTH_SECONDS,
2837 ),
2838 ]
2839 .into_iter()
2840 .chain(test_parameter_values_excluding_subnet_mask())
2841 .collect(),
2842 } => RequestingTestResult {
2843 outcome: RequestingOutcome::RanOutOfRetransmits,
2844 counters: RequestingTestCounters {
2845 send_message: 5,
2846 recv_time_out: 5,
2847 recv_message: 1,
2848 recv_missing_option: 1,
2849 ..Default::default()
2850 },
2851 }; "ignores replies lacking required option SubnetMask")]
2852 #[test_case(VaryingIncomingMessageFields {
2853 yiaddr: Ipv4Addr::UNSPECIFIED,
2854 options: [
2855 dhcp_protocol::DhcpOption::DhcpMessageType(
2856 dhcp_protocol::MessageType::DHCPNAK,
2857 ),
2858 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
2859 dhcp_protocol::DhcpOption::Message(NAK_MESSAGE.to_owned()),
2860 ]
2861 .into_iter()
2862 .chain(test_parameter_values())
2863 .collect(),
2864 } => RequestingTestResult {
2865 outcome: RequestingOutcome::Nak(crate::parse::FieldsToRetainFromNak {
2866 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
2867 .try_into()
2868 .expect("should be specified"),
2869 message: Some(NAK_MESSAGE.to_owned()),
2870 client_identifier: None,
2871 }),
2872 counters: RequestingTestCounters {
2873 send_message: 1,
2874 recv_message: 1,
2875 recv_nak: 1,
2876 ..Default::default()
2877 },
2878 }; "transitions to Init after receiving DHCPNAK")]
2879 fn do_requesting_transitions_on_reply(
2880 incoming_message: VaryingIncomingMessageFields,
2881 ) -> RequestingTestResult {
2882 initialize_logging();
2883 let counters = Counters::default();
2884
2885 let time = &FakeTimeController::new();
2886
2887 let requesting = build_test_requesting_state();
2888 let mut rng = FakeRngProvider::new(0);
2889
2890 let (server_end, client_end) = FakeSocket::new_pair();
2891 let test_socket_provider = FakeSocketProvider::new(client_end);
2892
2893 let client_config = test_client_config();
2894
2895 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
2896
2897 let requesting_fut = pin!(requesting
2898 .do_requesting(
2899 &client_config,
2900 &test_socket_provider,
2901 &mut rng,
2902 time,
2903 &mut stop_receiver,
2904 &counters,
2905 )
2906 .fuse());
2907
2908 let server_fut = pin!(async {
2909 let mut recv_buf = [0u8; BUFFER_SIZE];
2910
2911 let DatagramInfo { length, address } = server_end
2912 .recv_from(&mut recv_buf)
2913 .await
2914 .expect("recv_from on test socket should succeed");
2915 assert_eq!(address, Mac::BROADCAST);
2916
2917 let (_src_addr, msg) = crate::parse::parse_dhcp_message_from_ip_packet(
2918 &recv_buf[..length],
2919 dhcp_protocol::SERVER_PORT,
2920 )
2921 .expect("received packet on test socket should parse as DHCP message");
2922
2923 assert_outgoing_message_when_not_assigned_address(
2924 &msg,
2925 VaryingOutgoingMessageFields {
2926 xid: msg.xid,
2927 options: vec![
2928 dhcp_protocol::DhcpOption::RequestedIpAddress(YIADDR),
2929 dhcp_protocol::DhcpOption::IpAddressLeaseTime(DEFAULT_LEASE_LENGTH_SECONDS),
2930 dhcp_protocol::DhcpOption::DhcpMessageType(
2931 dhcp_protocol::MessageType::DHCPREQUEST,
2932 ),
2933 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
2934 dhcp_protocol::DhcpOption::ParameterRequestList(
2935 test_requested_parameters()
2936 .iter_keys()
2937 .collect::<Vec<_>>()
2938 .try_into()
2939 .expect("should fit parameter request list size constraints"),
2940 ),
2941 ],
2942 },
2943 );
2944
2945 let reply = build_incoming_message(msg.xid, incoming_message);
2946
2947 server_end
2948 .send_to(
2949 crate::parse::serialize_dhcp_message_to_ip_packet(
2950 reply,
2951 SERVER_IP,
2952 SERVER_PORT,
2953 YIADDR,
2954 CLIENT_PORT,
2955 )
2956 .as_ref(),
2957 TEST_SERVER_MAC_ADDRESS,
2960 )
2961 .await
2962 .expect("send_to on test socket should succeed");
2963 }
2964 .fuse());
2965
2966 let main_future = async move {
2967 let (requesting_result, ()) = join!(requesting_fut, server_fut);
2968 requesting_result
2969 }
2970 .fuse();
2971
2972 let mut main_future = pin!(main_future);
2973
2974 let mut executor = fasync::TestExecutor::new();
2975 let requesting_result = run_with_accelerated_time(&mut executor, time, &mut main_future);
2976
2977 let outcome = assert_matches!(requesting_result, Ok(outcome) => outcome);
2978 let counters = RequestingTestCounters {
2979 send_message: counters.requesting.messaging.send_message.load(),
2980 recv_time_out: counters.requesting.messaging.recv_time_out.load(),
2981 recv_failed_dhcp_parse: counters.requesting.messaging.recv_failed_dhcp_parse.load(),
2982 recv_wrong_xid: counters.requesting.messaging.recv_wrong_xid.load(),
2983 recv_wrong_chaddr: counters.requesting.messaging.recv_wrong_chaddr.load(),
2984 recv_message: counters.requesting.messaging.recv_message.load(),
2985 recv_nak: counters.requesting.recv_nak.load(),
2986 recv_missing_option: counters.requesting.recv_error.missing_required_option.load(),
2987 };
2988 RequestingTestResult { outcome, counters }
2989 }
2990
2991 fn build_test_bound_state() -> Bound<TestInstant> {
2992 build_test_bound_state_with_times(
2993 Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
2994 None,
2995 None,
2996 )
2997 }
2998
2999 fn build_test_bound_state_with_times(
3000 lease_length: Duration,
3001 renewal_time: Option<Duration>,
3002 rebinding_time: Option<Duration>,
3003 ) -> Bound<TestInstant> {
3004 Bound {
3005 discover_options: TEST_DISCOVER_OPTIONS,
3006 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR).try_into().expect("should be specified"),
3007 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3008 .try_into()
3009 .expect("should be specified"),
3010 ip_address_lease_time: lease_length,
3011 start_time: TestInstant(std::time::Duration::from_secs(0)),
3012 renewal_time,
3013 rebinding_time,
3014 }
3015 }
3016
3017 fn build_test_newly_acquired_lease() -> NewlyAcquiredLease<TestInstant> {
3018 NewlyAcquiredLease {
3019 ip_address: net_types::ip::Ipv4Addr::from(YIADDR)
3020 .try_into()
3021 .expect("should be specified"),
3022 start_time: TestInstant(std::time::Duration::from_secs(0)),
3023 lease_time: Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3024 parameters: Vec::new(),
3025 }
3026 }
3027
3028 #[test_case(
3029 (
3030 State::Init(Init::default()),
3031 Transition::BoundWithNewLease(build_test_bound_state(), build_test_newly_acquired_lease())
3032 ) => matches Some(TransitionEffect::HandleNewLease(_));
3033 "yields newly-acquired lease effect"
3034 )]
3035 #[test_case(
3036 (
3037 State::Bound(build_test_bound_state()),
3038 Transition::Init(Init),
3039 ) => matches Some(TransitionEffect::DropLease);
3040 "recognizes loss of lease"
3041 )]
3042 fn apply_transition(
3043 (state, transition): (State<TestInstant>, Transition<TestInstant>),
3044 ) -> Option<TransitionEffect<TestInstant>> {
3045 let (_next_state, effect) = state.apply(&test_client_config(), transition);
3046 effect
3047 }
3048
3049 #[test_case(
3050 State::Init(Init),
3051 false;
3052 "should not have lease during Init"
3053 )]
3054 #[test_case(
3055 State::Selecting(build_test_selecting_state()),
3056 false;
3057 "should not have lease during Selecting"
3058 )]
3059 #[test_case(
3060 State::Requesting(build_test_requesting_state()),
3061 false;
3062 "should not have lease during Requesting"
3063 )]
3064 #[test_case(
3065 State::Bound(build_test_bound_state()),
3066 true;
3067 "should decline and restart during Bound"
3068 )]
3069 #[test_case(
3070 State::WaitingToRestart(WaitingToRestart { waiting_until: TestInstant(WAIT_TIME_BEFORE_RESTARTING_AFTER_ADDRESS_REJECTION) }),
3071 false;
3072 "should not have lease during WaitingToRestart"
3073 )]
3074 fn on_address_rejection(state: State<TestInstant>, expect_decline: bool) {
3075 let config = &test_client_config();
3076 let time = &FakeTimeController::new();
3077 let (server_end, client_end) = FakeSocket::new_pair();
3078 let packet_socket_provider = FakeSocketProvider::new(client_end);
3079 let reject_fut = state
3080 .on_address_rejection(
3081 config,
3082 &packet_socket_provider,
3083 time,
3084 net_types::ip::Ipv4Addr::from(YIADDR).try_into().expect("should be specified"),
3085 )
3086 .fuse();
3087 let mut reject_fut = pin!(reject_fut);
3088
3089 let mut executor = fasync::TestExecutor::new();
3090 let reject_result = run_with_accelerated_time(&mut executor, time, &mut reject_fut);
3091
3092 if expect_decline {
3093 let WaitingToRestart { waiting_until } = assert_matches!(
3094 reject_result,
3095 Ok(AddressRejectionOutcome::NextState(
3096 State::WaitingToRestart(waiting)
3097 )) => waiting
3098 );
3099 assert_eq!(waiting_until, TestInstant(Duration::from_secs(10)));
3100
3101 let mut buf = [0u8; BUFFER_SIZE];
3102 let DatagramInfo { length, address } = server_end
3103 .recv_from(&mut buf)
3104 .now_or_never()
3105 .expect("should be ready")
3106 .expect("should succeed");
3107 assert_eq!(address, Mac::BROADCAST);
3108
3109 let (_src_addr, message) =
3110 crate::parse::parse_dhcp_message_from_ip_packet(&buf[..length], SERVER_PORT)
3111 .expect("should succeed");
3112
3113 use dhcp_protocol::DhcpOption;
3114 assert_outgoing_message_when_not_assigned_address(
3115 &message,
3116 VaryingOutgoingMessageFields {
3117 xid: message.xid,
3118 options: vec![
3119 DhcpOption::RequestedIpAddress(YIADDR),
3120 DhcpOption::DhcpMessageType(dhcp_protocol::MessageType::DHCPDECLINE),
3121 DhcpOption::ServerIdentifier(SERVER_IP),
3122 ],
3123 },
3124 );
3125 } else {
3126 assert_matches!(reject_result, Ok(AddressRejectionOutcome::ShouldBeImpossible));
3127 }
3128 }
3129
3130 #[test]
3131 fn waiting_to_restart() {
3132 let time = &FakeTimeController::new();
3133
3134 const WAITING_UNTIL: TestInstant = TestInstant(Duration::from_secs(10));
3135
3136 advance(time, Duration::from_secs(3));
3140
3141 let waiting = WaitingToRestart { waiting_until: WAITING_UNTIL };
3142 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3143 let main_fut = waiting.do_waiting_to_restart(time, &mut stop_receiver).fuse();
3144 let mut main_fut = pin!(main_fut);
3145 let mut executor = fasync::TestExecutor::new();
3146 let outcome = run_with_accelerated_time(&mut executor, time, &mut main_fut);
3147 assert_eq!(outcome, WaitingToRestartOutcome::Init(Init));
3148
3149 assert_eq!(time.now(), WAITING_UNTIL);
3150 }
3151
3152 #[test_case(
3153 build_test_bound_state() =>
3154 TestInstant(Duration::from_secs(u64::from(DEFAULT_LEASE_LENGTH_SECONDS) / 2));
3155 "waits default renewal time when not specified")]
3156 #[test_case(
3157 Bound {
3158 renewal_time: Some(Duration::from_secs(10)),
3159 ..build_test_bound_state()
3160 } => TestInstant(Duration::from_secs(10));
3161 "waits specified renewal time")]
3162 fn bound_waits_for_renewal_time(bound: Bound<TestInstant>) -> TestInstant {
3163 let time = &FakeTimeController::new();
3164 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3165 let config = &test_client_config();
3166 let counters = Counters::default();
3167 let main_fut = bound.do_bound(config, time, &mut stop_receiver, &counters).fuse();
3168 let mut main_fut = pin!(main_fut);
3169 let mut executor = fasync::TestExecutor::new();
3170 let outcome = run_with_accelerated_time(&mut executor, time, &mut main_fut);
3171 assert_eq!(outcome, BoundOutcome::Renewing(Renewing { bound: bound.clone() }));
3172 time.now()
3173 }
3174
3175 #[test]
3176 fn bound_obeys_graceful_shutdown() {
3177 let time = &FakeTimeController::new();
3178 let (stop_sender, mut stop_receiver) = mpsc::unbounded();
3179 let bound = build_test_bound_state();
3180 let config = &test_client_config();
3181 let counters = Counters::default();
3182 let bound_fut = bound.do_bound(&config, time, &mut stop_receiver, &counters).fuse();
3183
3184 stop_sender.unbounded_send(()).expect("send should succeed");
3185 assert_eq!(
3186 bound_fut.now_or_never().expect("should have completed"),
3187 BoundOutcome::GracefulShutdown
3188 );
3189 }
3190
3191 fn build_test_renewing_state(
3192 lease_length: Duration,
3193 renewal_time: Option<Duration>,
3194 rebinding_time: Option<Duration>,
3195 ) -> Renewing<TestInstant> {
3196 Renewing {
3197 bound: build_test_bound_state_with_times(lease_length, renewal_time, rebinding_time),
3198 }
3199 }
3200
3201 #[test]
3202 fn do_renewing_obeys_graceful_shutdown() {
3203 initialize_logging();
3204 let counters = Counters::default();
3205
3206 let renewing = build_test_renewing_state(
3207 Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3208 None,
3209 None,
3210 );
3211 let client_config = &test_client_config();
3212
3213 let (_server_end, client_end) = FakeSocket::new_pair();
3214 let test_socket_provider = &FakeSocketProvider::new(client_end);
3215 let (stop_sender, mut stop_receiver) = mpsc::unbounded();
3216 let time = &FakeTimeController::new();
3217
3218 let renewing_fut = renewing
3219 .do_renewing(client_config, test_socket_provider, time, &mut stop_receiver, &counters)
3220 .fuse();
3221 let mut renewing_fut = pin!(renewing_fut);
3222
3223 let mut executor = fasync::TestExecutor::new();
3224 assert_matches!(executor.run_until_stalled(&mut renewing_fut), std::task::Poll::Pending);
3225
3226 stop_sender.unbounded_send(()).expect("sending stop signal should succeed");
3227
3228 let renewing_result = renewing_fut.now_or_never().expect(
3229 "renewing_fut should complete after single poll after stop signal has been sent",
3230 );
3231
3232 assert_matches!(renewing_result, Ok(RenewingOutcome::GracefulShutdown));
3233 }
3234
3235 #[track_caller]
3236 fn assert_outgoing_message_when_assigned_address(
3237 got_message: &dhcp_protocol::Message,
3238 fields: VaryingOutgoingMessageFields,
3239 ) {
3240 let VaryingOutgoingMessageFields { xid, options } = fields;
3241 let want_message = dhcp_protocol::Message {
3242 op: dhcp_protocol::OpCode::BOOTREQUEST,
3243 xid,
3244 secs: 0,
3245 bdcast_flag: false,
3246 ciaddr: YIADDR,
3247 yiaddr: Ipv4Addr::UNSPECIFIED,
3248 siaddr: Ipv4Addr::UNSPECIFIED,
3249 giaddr: Ipv4Addr::UNSPECIFIED,
3250 chaddr: TEST_MAC_ADDRESS,
3251 sname: String::new(),
3252 file: String::new(),
3253 options,
3254 };
3255 assert_eq!(got_message, &want_message);
3256 }
3257
3258 #[test]
3259 fn do_renewing_sends_requests() {
3260 initialize_logging();
3261
3262 const LEASE_LENGTH: Duration = Duration::from_secs(100000);
3264
3265 const RENEWAL_TIME: Duration = Duration::from_secs(0);
3267 const REBINDING_TIME: Duration = Duration::from_secs(1024);
3268
3269 let renewing =
3270 build_test_renewing_state(LEASE_LENGTH, Some(RENEWAL_TIME), Some(REBINDING_TIME));
3271 let client_config = &test_client_config();
3272
3273 let (server_end, client_end) = FakeSocket::new_pair();
3274 let (binds_sender, mut binds_receiver) = mpsc::unbounded();
3275 let test_socket_provider = &FakeSocketProvider::new_with_events(client_end, binds_sender);
3276 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3277 let time = &FakeTimeController::new();
3278 let counters = Counters::default();
3279 let renewing_fut = pin!(renewing
3280 .do_renewing(client_config, test_socket_provider, time, &mut stop_receiver, &counters)
3281 .fuse());
3282
3283 let expected_times_requests_are_sent =
3286 [1024, 512, 256, 128, 64, 4].map(|time_remaining_when_request_is_sent| {
3287 Duration::from_secs(1024 - time_remaining_when_request_is_sent)
3288 });
3289
3290 let receive_fut = pin!(async {
3291 for expected_time in expected_times_requests_are_sent {
3292 let mut recv_buf = [0u8; BUFFER_SIZE];
3293 let DatagramInfo { length, address } = server_end
3294 .recv_from(&mut recv_buf)
3295 .await
3296 .expect("recv_from on test socket should succeed");
3297
3298 assert_eq!(
3299 address,
3300 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
3301 SERVER_IP,
3302 SERVER_PORT.get()
3303 ))
3304 );
3305 assert_eq!(time.now(), TestInstant(expected_time));
3306 let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
3307 .expect("received packet should parse as DHCP message");
3308
3309 assert_outgoing_message_when_assigned_address(
3310 &msg,
3311 VaryingOutgoingMessageFields {
3312 xid: msg.xid,
3313 options: vec![
3314 dhcp_protocol::DhcpOption::DhcpMessageType(
3315 dhcp_protocol::MessageType::DHCPREQUEST,
3316 ),
3317 dhcp_protocol::DhcpOption::ParameterRequestList(
3318 test_requested_parameters()
3319 .iter_keys()
3320 .collect::<Vec<_>>()
3321 .try_into()
3322 .expect("should fit parameter request list size constraints"),
3323 ),
3324 ],
3325 },
3326 );
3327 }
3328 }
3329 .fuse());
3330
3331 let main_future = async { join!(renewing_fut, receive_fut) };
3332 let mut main_future = pin!(main_future);
3333
3334 let mut executor = fasync::TestExecutor::new();
3335 let (requesting_result, ()) =
3336 run_with_accelerated_time(&mut executor, time, &mut main_future);
3337
3338 assert_matches!(requesting_result, Ok(RenewingOutcome::Rebinding(_)));
3339 assert_matches!(server_end.recv_from(&mut []).now_or_never(), None);
3340
3341 let bound_socket_addr = binds_receiver
3342 .next()
3343 .now_or_never()
3344 .expect("should have completed")
3345 .expect("should be present");
3346 assert_eq!(
3347 bound_socket_addr,
3348 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(YIADDR, CLIENT_PORT.into()))
3349 );
3350 assert_eq!(
3351 counters.renewing.messaging.send_message.load(),
3352 expected_times_requests_are_sent.len()
3353 );
3354 assert_eq!(
3355 counters.renewing.messaging.recv_time_out.load(),
3356 expected_times_requests_are_sent.len()
3357 );
3358 }
3359
3360 #[derive(PartialEq, Debug)]
3361 struct RenewingTestResult {
3362 outcome: RenewingOutcome<TestInstant>,
3363 counters: RenewingTestCounters,
3364 }
3365
3366 #[derive(PartialEq, Eq, Debug, Default)]
3367 struct RenewingTestCounters {
3368 send_message: usize,
3369 recv_time_out: usize,
3370 recv_failed_dhcp_parse: usize,
3371 recv_wrong_xid: usize,
3372 recv_wrong_chaddr: usize,
3373 recv_message: usize,
3374 recv_nak: usize,
3375 recv_missing_option: usize,
3376 }
3377
3378 #[test_case(VaryingIncomingMessageFields {
3379 yiaddr: YIADDR,
3380 options: [
3381 dhcp_protocol::DhcpOption::DhcpMessageType(
3382 dhcp_protocol::MessageType::DHCPACK,
3383 ),
3384 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3385 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3386 DEFAULT_LEASE_LENGTH_SECONDS,
3387 ),
3388 ]
3389 .into_iter()
3390 .chain(test_parameter_values())
3391 .collect(),
3392 } => RenewingTestResult {
3393 outcome: RenewingOutcome::Renewed(Bound {
3394 discover_options: TEST_DISCOVER_OPTIONS,
3395 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
3396 .try_into()
3397 .expect("should be specified"),
3398 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3399 .try_into()
3400 .expect("should be specified"),
3401 ip_address_lease_time: std::time::Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3402 renewal_time: None,
3403 rebinding_time: None,
3404 start_time: TestInstant(std::time::Duration::from_secs(0)),
3405 }, test_parameter_values().into_iter().collect()),
3406 counters: RenewingTestCounters {
3407 send_message: 1,
3408 recv_message: 1,
3409 ..Default::default()
3410 }
3411 }; "successfully renews after receiving DHCPACK")]
3412 #[test_case(VaryingIncomingMessageFields {
3413 yiaddr: OTHER_ADDR,
3414 options: [
3415 dhcp_protocol::DhcpOption::DhcpMessageType(
3416 dhcp_protocol::MessageType::DHCPACK,
3417 ),
3418 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3419 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3420 DEFAULT_LEASE_LENGTH_SECONDS,
3421 ),
3422 ]
3423 .into_iter()
3424 .chain(test_parameter_values())
3425 .collect(),
3426 } => RenewingTestResult {
3427 outcome: RenewingOutcome::NewAddress(Bound {
3428 discover_options: TEST_DISCOVER_OPTIONS,
3429 yiaddr: net_types::ip::Ipv4Addr::from(OTHER_ADDR)
3430 .try_into()
3431 .expect("should be specified"),
3432 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3433 .try_into()
3434 .expect("should be specified"),
3435 ip_address_lease_time: std::time::Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3436 renewal_time: None,
3437 rebinding_time: None,
3438 start_time: TestInstant(std::time::Duration::from_secs(0)),
3439 }, test_parameter_values().into_iter().collect()),
3440 counters: RenewingTestCounters {
3441 send_message: 1,
3442 recv_message: 1,
3443 ..Default::default()
3444 }
3445 }; "observes new address from DHCPACK")]
3446 #[test_case(VaryingIncomingMessageFields {
3447 yiaddr: YIADDR,
3448 options: [
3449 dhcp_protocol::DhcpOption::DhcpMessageType(
3450 dhcp_protocol::MessageType::DHCPACK,
3451 ),
3452 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3453 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3454 DEFAULT_LEASE_LENGTH_SECONDS,
3455 ),
3456 ]
3457 .into_iter()
3458 .chain(test_parameter_values_excluding_subnet_mask())
3459 .collect(),
3460 } => RenewingTestResult {
3461 outcome: RenewingOutcome::Rebinding(
3462 Rebinding {
3463 bound: build_test_bound_state()
3464 }),
3465 counters: RenewingTestCounters {
3466 send_message: 2,
3467 recv_time_out: 2,
3468 recv_message: 1,
3469 recv_missing_option: 1,
3470 ..Default::default()
3471 },
3472 }; "ignores replies lacking required option SubnetMask")]
3473 #[test_case(VaryingIncomingMessageFields {
3474 yiaddr: Ipv4Addr::UNSPECIFIED,
3475 options: [
3476 dhcp_protocol::DhcpOption::DhcpMessageType(
3477 dhcp_protocol::MessageType::DHCPNAK,
3478 ),
3479 dhcp_protocol::DhcpOption::ServerIdentifier(SERVER_IP),
3480 dhcp_protocol::DhcpOption::Message(NAK_MESSAGE.to_owned()),
3481 ]
3482 .into_iter()
3483 .chain(test_parameter_values())
3484 .collect(),
3485 } => RenewingTestResult {
3486 outcome: RenewingOutcome::Nak(crate::parse::FieldsToRetainFromNak {
3487 server_identifier: net_types::ip::Ipv4Addr::from(SERVER_IP)
3488 .try_into()
3489 .expect("should be specified"),
3490 message: Some(NAK_MESSAGE.to_owned()),
3491 client_identifier: None,
3492 }),
3493 counters: RenewingTestCounters {
3494 send_message: 1,
3495 recv_message: 1,
3496 recv_nak: 1,
3497 ..Default::default()
3498 },
3499 }; "transitions to Init after receiving DHCPNAK")]
3500 fn do_renewing_transitions_on_reply(
3501 incoming_message: VaryingIncomingMessageFields,
3502 ) -> RenewingTestResult {
3503 initialize_logging();
3504
3505 let renewing = build_test_renewing_state(
3506 Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3507 None,
3508 None,
3509 );
3510 let client_config = &test_client_config();
3511
3512 let (server_end, client_end) = FakeSocket::new_pair();
3513 let test_socket_provider = &FakeSocketProvider::new(client_end);
3514 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3515 let time = &FakeTimeController::new();
3516 let counters = Counters::default();
3517 let mut renewing_fut = pin!(renewing
3518 .do_renewing(client_config, test_socket_provider, time, &mut stop_receiver, &counters)
3519 .fuse());
3520 let renewing_fut = pin!(renewing_fut);
3521
3522 let server_socket_addr =
3523 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(SERVER_IP, SERVER_PORT.into()));
3524
3525 let server_fut = pin!(async {
3526 let mut recv_buf = [0u8; BUFFER_SIZE];
3527
3528 let DatagramInfo { length, address } = server_end
3529 .recv_from(&mut recv_buf)
3530 .await
3531 .expect("recv_from on test socket should succeed");
3532 assert_eq!(address, server_socket_addr);
3533
3534 let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
3535 .expect("received packet on test socket should parse as DHCP message");
3536
3537 assert_outgoing_message_when_assigned_address(
3538 &msg,
3539 VaryingOutgoingMessageFields {
3540 xid: msg.xid,
3541 options: vec![
3542 dhcp_protocol::DhcpOption::DhcpMessageType(
3543 dhcp_protocol::MessageType::DHCPREQUEST,
3544 ),
3545 dhcp_protocol::DhcpOption::ParameterRequestList(
3546 test_requested_parameters()
3547 .iter_keys()
3548 .collect::<Vec<_>>()
3549 .try_into()
3550 .expect("should fit parameter request list size constraints"),
3551 ),
3552 ],
3553 },
3554 );
3555
3556 let reply = build_incoming_message(msg.xid, incoming_message);
3557
3558 server_end
3559 .send_to(
3560 &reply.serialize(),
3561 server_socket_addr,
3564 )
3565 .await
3566 .expect("send_to on test socket should succeed");
3567 }
3568 .fuse());
3569
3570 let main_future = async move {
3571 let (renewing_result, ()) = join!(renewing_fut, server_fut);
3572 renewing_result
3573 }
3574 .fuse();
3575
3576 let mut main_future = pin!(main_future);
3577
3578 let mut executor = fasync::TestExecutor::new();
3579 let renewing_result = run_with_accelerated_time(&mut executor, time, &mut main_future);
3580
3581 let outcome = assert_matches!(renewing_result, Ok(outcome) => outcome);
3582 let counters = RenewingTestCounters {
3583 send_message: counters.renewing.messaging.send_message.load(),
3584 recv_time_out: counters.renewing.messaging.recv_time_out.load(),
3585 recv_failed_dhcp_parse: counters.renewing.messaging.recv_failed_dhcp_parse.load(),
3586 recv_wrong_xid: counters.renewing.messaging.recv_wrong_xid.load(),
3587 recv_wrong_chaddr: counters.renewing.messaging.recv_wrong_chaddr.load(),
3588 recv_message: counters.renewing.messaging.recv_message.load(),
3589 recv_nak: counters.renewing.recv_nak.load(),
3590 recv_missing_option: counters.renewing.recv_error.missing_required_option.load(),
3591 };
3592 RenewingTestResult { outcome, counters }
3593 }
3594
3595 fn build_test_rebinding_state(
3596 lease_length: Duration,
3597 renewal_time: Option<Duration>,
3598 rebinding_time: Option<Duration>,
3599 ) -> Rebinding<TestInstant> {
3600 Rebinding {
3601 bound: build_test_bound_state_with_times(lease_length, renewal_time, rebinding_time),
3602 }
3603 }
3604
3605 #[test]
3606 fn do_rebinding_sends_requests() {
3607 initialize_logging();
3608
3609 const RENEWAL_TIME: Duration = Duration::from_secs(0);
3611 const REBINDING_TIME: Duration = Duration::from_secs(0);
3612 const LEASE_LENGTH: Duration = Duration::from_secs(1024);
3613
3614 let rebinding =
3615 build_test_rebinding_state(LEASE_LENGTH, Some(RENEWAL_TIME), Some(REBINDING_TIME));
3616 let client_config = &test_client_config();
3617
3618 let (server_end, client_end) = FakeSocket::new_pair();
3619 let (binds_sender, mut binds_receiver) = mpsc::unbounded();
3620 let test_socket_provider = &FakeSocketProvider::new_with_events(client_end, binds_sender);
3621 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3622 let time = &FakeTimeController::new();
3623 let counters = Counters::default();
3624 let rebinding_fut = pin!(rebinding
3625 .do_rebinding(client_config, test_socket_provider, time, &mut stop_receiver, &counters)
3626 .fuse());
3627
3628 let expected_times_requests_are_sent =
3631 [1024, 512, 256, 128, 64, 4].map(|time_remaining_when_request_is_sent| {
3632 Duration::from_secs(1024 - time_remaining_when_request_is_sent)
3633 });
3634
3635 let receive_fut = pin!(async {
3636 for expected_time in expected_times_requests_are_sent {
3637 let mut recv_buf = [0u8; BUFFER_SIZE];
3638 let DatagramInfo { length, address } = server_end
3639 .recv_from(&mut recv_buf)
3640 .await
3641 .expect("recv_from on test socket should succeed");
3642
3643 assert_eq!(
3644 address,
3645 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
3646 std::net::Ipv4Addr::BROADCAST,
3647 SERVER_PORT.get()
3648 ))
3649 );
3650 assert_eq!(time.now(), TestInstant(expected_time));
3651 let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
3652 .expect("received packet should parse as DHCP message");
3653
3654 assert_outgoing_message_when_assigned_address(
3655 &msg,
3656 VaryingOutgoingMessageFields {
3657 xid: msg.xid,
3658 options: vec![
3659 dhcp_protocol::DhcpOption::DhcpMessageType(
3660 dhcp_protocol::MessageType::DHCPREQUEST,
3661 ),
3662 dhcp_protocol::DhcpOption::ParameterRequestList(
3663 test_requested_parameters()
3664 .iter_keys()
3665 .collect::<Vec<_>>()
3666 .try_into()
3667 .expect("should fit parameter request list size constraints"),
3668 ),
3669 ],
3670 },
3671 );
3672 }
3673 }
3674 .fuse());
3675
3676 let main_future = async { join!(rebinding_fut, receive_fut) };
3677 let mut main_future = pin!(main_future);
3678
3679 let mut executor = fasync::TestExecutor::new();
3680 let (requesting_result, ()) =
3681 run_with_accelerated_time(&mut executor, time, &mut main_future);
3682
3683 assert_matches!(requesting_result, Ok(RebindingOutcome::TimedOut));
3684 assert_matches!(server_end.recv_from(&mut []).now_or_never(), None);
3685
3686 let bound_socket_addr = binds_receiver
3687 .next()
3688 .now_or_never()
3689 .expect("should have completed")
3690 .expect("should be present");
3691 assert_eq!(
3692 bound_socket_addr,
3693 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(YIADDR, CLIENT_PORT.into()))
3694 );
3695 }
3696
3697 #[derive(PartialEq, Debug)]
3698 struct RebindingTestResult {
3699 outcome: RebindingOutcome<TestInstant>,
3700 counters: RebindingTestCounters,
3701 }
3702
3703 #[derive(PartialEq, Eq, Debug, Default)]
3704 struct RebindingTestCounters {
3705 send_message: usize,
3706 recv_time_out: usize,
3707 recv_failed_dhcp_parse: usize,
3708 recv_wrong_xid: usize,
3709 recv_wrong_chaddr: usize,
3710 recv_message: usize,
3711 recv_nak: usize,
3712 recv_missing_option: usize,
3713 }
3714
3715 #[test_case(VaryingIncomingMessageFields {
3716 yiaddr: YIADDR,
3717 options: [
3718 dhcp_protocol::DhcpOption::DhcpMessageType(
3719 dhcp_protocol::MessageType::DHCPACK,
3720 ),
3721 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
3722 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3723 DEFAULT_LEASE_LENGTH_SECONDS,
3724 ),
3725 ]
3726 .into_iter()
3727 .chain(test_parameter_values())
3728 .collect(),
3729 } => RebindingTestResult {
3730 outcome: RebindingOutcome::Renewed(Bound {
3731 discover_options: TEST_DISCOVER_OPTIONS,
3732 yiaddr: net_types::ip::Ipv4Addr::from(YIADDR)
3733 .try_into()
3734 .expect("should be specified"),
3735 server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
3736 .try_into()
3737 .expect("should be specified"),
3738 ip_address_lease_time: std::time::Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3739 renewal_time: None,
3740 rebinding_time: None,
3741 start_time: TestInstant(std::time::Duration::from_secs(0)),
3742 }, test_parameter_values().into_iter().collect()),
3743 counters: RebindingTestCounters {
3744 send_message: 1,
3745 recv_message: 1,
3746 ..Default::default()
3747 }
3748 }; "successfully renews after receiving DHCPACK")]
3749 #[test_case(VaryingIncomingMessageFields {
3750 yiaddr: OTHER_ADDR,
3751 options: [
3752 dhcp_protocol::DhcpOption::DhcpMessageType(
3753 dhcp_protocol::MessageType::DHCPACK,
3754 ),
3755 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
3756 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3757 DEFAULT_LEASE_LENGTH_SECONDS,
3758 ),
3759 ]
3760 .into_iter()
3761 .chain(test_parameter_values())
3762 .collect(),
3763 } => RebindingTestResult {
3764 outcome: RebindingOutcome::NewAddress(Bound {
3765 discover_options: TEST_DISCOVER_OPTIONS,
3766 yiaddr: net_types::ip::Ipv4Addr::from(OTHER_ADDR)
3767 .try_into()
3768 .expect("should be specified"),
3769 server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
3770 .try_into()
3771 .expect("should be specified"),
3772 ip_address_lease_time: std::time::Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3773 renewal_time: None,
3774 rebinding_time: None,
3775 start_time: TestInstant(std::time::Duration::from_secs(0)),
3776 }, test_parameter_values().into_iter().collect()),
3777 counters: RebindingTestCounters {
3778 send_message: 1,
3779 recv_message: 1,
3780 ..Default::default()
3781 }
3782 } ; "observes new address from DHCPACK")]
3783 #[test_case(VaryingIncomingMessageFields {
3784 yiaddr: YIADDR,
3785 options: [
3786 dhcp_protocol::DhcpOption::DhcpMessageType(
3787 dhcp_protocol::MessageType::DHCPACK,
3788 ),
3789 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
3790 dhcp_protocol::DhcpOption::IpAddressLeaseTime(
3791 DEFAULT_LEASE_LENGTH_SECONDS,
3792 ),
3793 ]
3794 .into_iter()
3795 .chain(test_parameter_values_excluding_subnet_mask())
3796 .collect(),
3797 } => RebindingTestResult {
3798 outcome: RebindingOutcome::TimedOut,
3799 counters: RebindingTestCounters {
3800 send_message: 2,
3801 recv_time_out: 2,
3802 recv_message: 1,
3803 recv_missing_option: 1,
3804 ..Default::default()
3805 }
3806 } ; "ignores replies lacking required option SubnetMask")]
3807 #[test_case(VaryingIncomingMessageFields {
3808 yiaddr: Ipv4Addr::UNSPECIFIED,
3809 options: [
3810 dhcp_protocol::DhcpOption::DhcpMessageType(
3811 dhcp_protocol::MessageType::DHCPNAK,
3812 ),
3813 dhcp_protocol::DhcpOption::ServerIdentifier(OTHER_SERVER_IP),
3814 dhcp_protocol::DhcpOption::Message(NAK_MESSAGE.to_owned()),
3815 ]
3816 .into_iter()
3817 .chain(test_parameter_values())
3818 .collect(),
3819 } => RebindingTestResult {
3820 outcome: RebindingOutcome::Nak(crate::parse::FieldsToRetainFromNak {
3821 server_identifier: net_types::ip::Ipv4Addr::from(OTHER_SERVER_IP)
3822 .try_into()
3823 .expect("should be specified"),
3824 message: Some(NAK_MESSAGE.to_owned()),
3825 client_identifier: None,
3826 }),
3827 counters: RebindingTestCounters {
3828 send_message: 1,
3829 recv_message: 1,
3830 recv_nak: 1,
3831 ..Default::default()
3832 },
3833 } ; "transitions to Init after receiving DHCPNAK")]
3834 fn do_rebinding_transitions_on_reply(
3835 incoming_message: VaryingIncomingMessageFields,
3836 ) -> RebindingTestResult {
3837 initialize_logging();
3838
3839 let rebinding = build_test_rebinding_state(
3840 Duration::from_secs(DEFAULT_LEASE_LENGTH_SECONDS.into()),
3841 None,
3842 None,
3843 );
3844 let client_config = &test_client_config();
3845
3846 let (server_end, client_end) = FakeSocket::new_pair();
3847 let test_socket_provider = &FakeSocketProvider::new(client_end);
3848 let (_stop_sender, mut stop_receiver) = mpsc::unbounded();
3849 let time = &FakeTimeController::new();
3850 let counters = Counters::default();
3851 let rebinding_fut = pin!(rebinding
3852 .do_rebinding(client_config, test_socket_provider, time, &mut stop_receiver, &counters)
3853 .fuse());
3854
3855 let server_socket_addr = std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
3856 OTHER_SERVER_IP,
3857 SERVER_PORT.into(),
3858 ));
3859
3860 let server_fut = pin!(async {
3861 let mut recv_buf = [0u8; BUFFER_SIZE];
3862
3863 let DatagramInfo { length, address } = server_end
3864 .recv_from(&mut recv_buf)
3865 .await
3866 .expect("recv_from on test socket should succeed");
3867 assert_eq!(
3868 address,
3869 std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
3870 std::net::Ipv4Addr::BROADCAST,
3871 SERVER_PORT.into()
3872 ))
3873 );
3874
3875 let msg = dhcp_protocol::Message::from_buffer(&recv_buf[..length])
3876 .expect("received packet on test socket should parse as DHCP message");
3877
3878 assert_outgoing_message_when_assigned_address(
3879 &msg,
3880 VaryingOutgoingMessageFields {
3881 xid: msg.xid,
3882 options: vec![
3883 dhcp_protocol::DhcpOption::DhcpMessageType(
3884 dhcp_protocol::MessageType::DHCPREQUEST,
3885 ),
3886 dhcp_protocol::DhcpOption::ParameterRequestList(
3887 test_requested_parameters()
3888 .iter_keys()
3889 .collect::<Vec<_>>()
3890 .try_into()
3891 .expect("should fit parameter request list size constraints"),
3892 ),
3893 ],
3894 },
3895 );
3896
3897 let reply = build_incoming_message(msg.xid, incoming_message);
3898
3899 server_end
3900 .send_to(
3901 &reply.serialize(),
3902 server_socket_addr,
3905 )
3906 .await
3907 .expect("send_to on test socket should succeed");
3908 }
3909 .fuse());
3910
3911 let main_future = async move {
3912 let (rebinding_result, ()) = join!(rebinding_fut, server_fut);
3913 rebinding_result
3914 }
3915 .fuse();
3916
3917 let mut main_future = pin!(main_future);
3918
3919 let mut executor = fasync::TestExecutor::new();
3920 let rebinding_result = run_with_accelerated_time(&mut executor, time, &mut main_future);
3921
3922 let outcome = assert_matches!(rebinding_result, Ok(outcome) => outcome);
3923 let counters = RebindingTestCounters {
3924 send_message: counters.rebinding.messaging.send_message.load(),
3925 recv_time_out: counters.rebinding.messaging.recv_time_out.load(),
3926 recv_failed_dhcp_parse: counters.rebinding.messaging.recv_failed_dhcp_parse.load(),
3927 recv_wrong_xid: counters.rebinding.messaging.recv_wrong_xid.load(),
3928 recv_wrong_chaddr: counters.rebinding.messaging.recv_wrong_chaddr.load(),
3929 recv_message: counters.rebinding.messaging.recv_message.load(),
3930 recv_nak: counters.rebinding.recv_nak.load(),
3931 recv_missing_option: counters.rebinding.recv_error.missing_required_option.load(),
3932 };
3933 RebindingTestResult { outcome, counters }
3934 }
3935}