1use std::cell::RefCell;
6use std::num::NonZeroU64;
7use std::sync::Arc;
8
9use anyhow::Context as _;
10use dhcp_client_core::inspect::Counters;
11use diagnostics_traits::Inspector;
12use fidl::endpoints::{self, Proxy as _};
13use fidl_fuchsia_net_dhcp::{
14 self as fdhcp, ClientExitReason, ClientRequestStream, ClientWatchConfigurationResponse,
15 ConfigurationToRequest, NewClientParams,
16};
17use fidl_fuchsia_net_ext::IntoExt as _;
18use futures::channel::mpsc;
19use futures::{select, FutureExt as _, TryStreamExt as _};
20use net_types::ip::{Ipv4, Ipv4Addr, PrefixLength};
21use net_types::{SpecifiedAddr, Witness as _};
22use rand::SeedableRng as _;
23use std::pin::pin;
24use {
25 fidl_fuchsia_net as fnet, fidl_fuchsia_net_interfaces_admin as fnet_interfaces_admin,
26 fuchsia_async as fasync,
27};
28
29use crate::inspect::{Inspect, LeaseChangeInspect, LeaseInspectProperties, StateInspect};
30
31#[derive(thiserror::Error, Debug)]
32pub(crate) enum Error {
33 #[error("DHCP client exiting: {0:?}")]
34 Exit(ClientExitReason),
35
36 #[error("error observed by DHCP client core: {0:?}")]
37 Core(dhcp_client_core::client::Error),
38
39 #[error("fidl error: {0}")]
40 Fidl(fidl::Error),
41}
42
43impl Error {
44 fn from_core(core_error: dhcp_client_core::client::Error) -> Self {
45 match core_error {
46 dhcp_client_core::client::Error::Socket(socket_error) => match socket_error {
47 dhcp_client_core::deps::SocketError::NoInterface
48 | dhcp_client_core::deps::SocketError::UnsupportedHardwareType => {
49 Self::Exit(ClientExitReason::InvalidInterface)
50 }
51 dhcp_client_core::deps::SocketError::FailedToOpen(e) => {
52 log::error!("error while trying to open socket: {:?}", e);
53 Self::Exit(ClientExitReason::UnableToOpenSocket)
54 }
55 dhcp_client_core::deps::SocketError::HostUnreachable
56 | dhcp_client_core::deps::SocketError::Other(_) => {
57 Self::Core(dhcp_client_core::client::Error::Socket(socket_error))
58 }
59 dhcp_client_core::deps::SocketError::NetworkUnreachable => {
60 Self::Exit(ClientExitReason::NetworkUnreachable)
61 }
62 },
63 }
64 }
65}
66
67pub(crate) async fn serve_client(
68 mac: net_types::ethernet::Mac,
69 interface_id: NonZeroU64,
70 provider: &crate::packetsocket::PacketSocketProviderImpl,
71 udp_socket_provider: &impl dhcp_client_core::deps::UdpSocketProvider,
72 params: NewClientParams,
73 requests: ClientRequestStream,
74 inspect_root: &fuchsia_inspect::Node,
75) -> Result<(), Error> {
76 let (stop_sender, stop_receiver) = mpsc::unbounded();
77 let stop_sender = &stop_sender;
78 let debug_log_prefix = dhcp_client_core::client::DebugLogPrefix { interface_id };
79 let inspect = Arc::new(Inspect::new());
80 let client = RefCell::new(Client::new(
81 mac,
82 interface_id,
83 params,
84 rand::rngs::StdRng::seed_from_u64(rand::random()),
85 stop_receiver,
86 debug_log_prefix,
87 )?);
88 let counters = Arc::new(Counters::default());
89 let _node = inspect_root.create_lazy_child(interface_id.get().to_string(), {
90 let counters = counters.clone();
91 let inspect = inspect.clone();
92 move || {
93 let inspector = fuchsia_inspect::Inspector::default();
94 {
95 let mut inspector =
96 diagnostics_traits::FuchsiaInspector::<'_, ()>::new(inspector.root());
97 inspector.record_uint("InterfaceId", interface_id.get());
98 inspect.record(&mut inspector);
99 inspector.record_child("Counters", |inspector| {
100 counters.record(inspector);
101 });
102 }
103 Box::pin(futures::future::ready(Ok(inspector)))
104 }
105 });
106
107 let counters = counters.as_ref();
108 let inspect = inspect.as_ref();
109 requests
110 .map_err(Error::Fidl)
111 .try_for_each_concurrent(None, |request| {
112 let client = &client;
113 async move {
114 match request {
115 fidl_fuchsia_net_dhcp::ClientRequest::WatchConfiguration { responder } => {
116 let mut client = client.try_borrow_mut().map_err(|_| {
117 Error::Exit(ClientExitReason::WatchConfigurationAlreadyPending)
118 })?;
119 responder
120 .send(
121 client
122 .watch_configuration(
123 provider,
124 udp_socket_provider,
125 counters,
126 inspect,
127 )
128 .await?,
129 )
130 .map_err(Error::Fidl)?;
131 Ok(())
132 }
133 fidl_fuchsia_net_dhcp::ClientRequest::Shutdown { control_handle: _ } => {
134 match stop_sender.unbounded_send(()) {
135 Ok(()) => stop_sender.close_channel(),
136 Err(try_send_error) => {
137 if try_send_error.is_disconnected() {
139 log::warn!(
140 "{debug_log_prefix} tried to send shutdown request on \
141 already-closed channel to client core"
142 );
143 } else {
144 log::error!(
145 "{debug_log_prefix} error while sending shutdown request \
146 to client core: {:?}",
147 try_send_error
148 );
149 }
150 }
151 }
152 Ok(())
153 }
154 }
155 }
156 })
157 .await
158}
159
160struct Clock;
161
162impl dhcp_client_core::deps::Clock for Clock {
163 type Instant = fasync::MonotonicInstant;
164
165 fn now(&self) -> Self::Instant {
166 fasync::MonotonicInstant::now()
167 }
168
169 async fn wait_until(&self, time: Self::Instant) {
170 fasync::Timer::new(time).await
171 }
172}
173
174struct Client {
176 config: dhcp_client_core::client::ClientConfig,
177 core: dhcp_client_core::client::State<fasync::MonotonicInstant>,
178 rng: rand::rngs::StdRng,
179 stop_receiver: mpsc::UnboundedReceiver<()>,
180 current_lease: Option<Lease>,
181 interface_id: NonZeroU64,
182}
183
184struct Lease {
185 address_state_provider: fnet_interfaces_admin::AddressStateProviderProxy,
186 event_stream: fnet_interfaces_admin::AddressStateProviderEventStream,
187 ip_address: SpecifiedAddr<net_types::ip::Ipv4Addr>,
188}
189
190impl Lease {
191 async fn watch_for_address_removal(
192 &mut self,
193 ) -> Result<fnet_interfaces_admin::AddressRemovalReason, anyhow::Error> {
194 let Self { address_state_provider, event_stream, ip_address: _ } = self;
195
196 let _: zx::Signals = address_state_provider
197 .on_closed()
198 .await
199 .context("unexpected zx::Status while awaiting AddressStateProvider channel close")?;
200
201 let event_stream = event_stream.try_filter_map(|event| async move {
202 match event {
203 fnet_interfaces_admin::AddressStateProviderEvent::OnAddressAdded {} => Ok(None),
204 fnet_interfaces_admin::AddressStateProviderEvent::OnAddressRemoved { error } => {
205 Ok(Some(error))
206 }
207 }
208 });
209 let mut event_stream = pin!(event_stream);
210
211 event_stream
212 .try_next()
213 .await
214 .context("AddressStateProviderEventStream FIDL error")?
215 .context("AddressStateProvider event stream ended without yielding removal reason")
216 }
217}
218
219impl Client {
220 fn new(
221 mac: net_types::ethernet::Mac,
222 interface_id: NonZeroU64,
223 NewClientParams { configuration_to_request, request_ip_address, .. }: NewClientParams,
224 rng: rand::rngs::StdRng,
225 stop_receiver: mpsc::UnboundedReceiver<()>,
226 debug_log_prefix: dhcp_client_core::client::DebugLogPrefix,
227 ) -> Result<Self, Error> {
228 if !request_ip_address.unwrap_or(false) {
229 log::error!(
230 "{debug_log_prefix} client creation failed: \
231 DHCPINFORM is unimplemented"
232 );
233 return Err(Error::Exit(ClientExitReason::InvalidParams));
234 }
235 let ConfigurationToRequest { routers, dns_servers, .. } =
236 configuration_to_request.unwrap_or_else(ConfigurationToRequest::default);
237
238 let config = dhcp_client_core::client::ClientConfig {
239 client_hardware_address: mac,
240 client_identifier: None,
241 requested_parameters: std::iter::once((
242 dhcp_protocol::OptionCode::SubnetMask,
243 dhcp_client_core::parse::OptionRequested::Required,
244 ))
245 .chain(routers.unwrap_or(false).then_some((
246 dhcp_protocol::OptionCode::Router,
247 dhcp_client_core::parse::OptionRequested::Optional,
248 )))
249 .chain(dns_servers.unwrap_or(false).then_some((
250 dhcp_protocol::OptionCode::DomainNameServer,
251 dhcp_client_core::parse::OptionRequested::Optional,
252 )))
253 .collect::<dhcp_client_core::parse::OptionCodeMap<_>>(),
254 preferred_lease_time_secs: None,
255 requested_ip_address: None,
256 debug_log_prefix,
257 };
258 Ok(Self {
259 core: dhcp_client_core::client::State::default(),
260 rng,
261 config,
262 stop_receiver,
263 current_lease: None,
264 interface_id,
265 })
266 }
267
268 async fn handle_newly_acquired_lease(
269 &mut self,
270 dhcp_client_core::client::NewlyAcquiredLease {
271 ip_address,
272 start_time,
273 lease_time,
274 parameters,
275 }: dhcp_client_core::client::NewlyAcquiredLease<fasync::MonotonicInstant>,
276 ) -> Result<(ClientWatchConfigurationResponse, LeaseChangeInspect), Error> {
277 let Self {
278 core: _,
279 rng: _,
280 config: dhcp_client_core::client::ClientConfig { debug_log_prefix, .. },
281 stop_receiver: _,
282 current_lease,
283 interface_id: _,
284 } = self;
285
286 let mut dns_servers: Option<Vec<_>> = None;
287 let mut routers: Option<Vec<_>> = None;
288 let mut prefix_len: Option<PrefixLength<Ipv4>> = None;
289 let mut unrequested_options = Vec::new();
290
291 for option in parameters {
292 match option {
293 dhcp_protocol::DhcpOption::SubnetMask(len) => {
294 let previous_prefix_len = prefix_len.replace(len);
295 if let Some(prev) = previous_prefix_len {
296 log::warn!("expected previous_prefix_len to be None, got {prev:?}");
297 }
298 }
299 dhcp_protocol::DhcpOption::DomainNameServer(list) => {
300 let previous_dns_servers = dns_servers.replace(list.into());
301 if let Some(prev) = previous_dns_servers {
302 log::warn!("expected previous_dns_servers to be None, got {prev:?}");
303 }
304 }
305 dhcp_protocol::DhcpOption::Router(list) => {
306 let previous_routers = routers.replace(list.into());
307 if let Some(prev) = previous_routers {
308 log::warn!("expected previous_routers to be None, got {prev:?}");
309 }
310 }
311 _ => {
312 unrequested_options.push(option);
313 }
314 }
315 }
316
317 if !unrequested_options.is_empty() {
318 log::warn!(
319 "{debug_log_prefix} Received options from core that we didn't ask for: {:#?}",
320 unrequested_options
321 );
322 }
323
324 let prefix_len = prefix_len
325 .expect(
326 "subnet mask should be present \
327 because it was specified to core as required",
328 )
329 .get();
330
331 let (asp_proxy, asp_server_end) =
332 endpoints::create_proxy::<fnet_interfaces_admin::AddressStateProviderMarker>();
333
334 let previous_lease = current_lease.replace(Lease {
335 event_stream: asp_proxy.take_event_stream(),
336 address_state_provider: asp_proxy,
337 ip_address,
338 });
339
340 if let Some(previous_lease) = previous_lease {
341 self.handle_lease_drop(previous_lease).await?;
342 }
343
344 let lease_inspect_properties = LeaseInspectProperties {
345 ip_address,
346 lease_length: lease_time.into(),
347 dns_server_count: dns_servers.as_ref().map(|list| list.len()).unwrap_or(0),
348 routers_count: routers.as_ref().map(|list| list.len()).unwrap_or(0),
349 };
350
351 Ok((
352 ClientWatchConfigurationResponse {
353 address: Some(fdhcp::Address {
354 address: Some(fnet::Ipv4AddressWithPrefix {
355 addr: ip_address.get().into_ext(),
356 prefix_len,
357 }),
358 address_parameters: Some(fnet_interfaces_admin::AddressParameters {
359 initial_properties: Some(fnet_interfaces_admin::AddressProperties {
360 preferred_lifetime_info: None,
361 valid_lifetime_end: Some(
362 zx::MonotonicInstant::from(start_time + lease_time.into())
363 .into_nanos(),
364 ),
365 ..Default::default()
366 }),
367 add_subnet_route: Some(true),
368 ..Default::default()
369 }),
370 address_state_provider: Some(asp_server_end),
371 ..Default::default()
372 }),
373 dns_servers: dns_servers.map(into_fidl_list),
374 routers: routers.map(into_fidl_list),
375 ..Default::default()
376 },
377 LeaseChangeInspect::LeaseAdded {
378 start_time,
379 prefix_len,
380 properties: lease_inspect_properties,
381 },
382 ))
383 }
384
385 async fn handle_lease_renewal(
386 &mut self,
387 dhcp_client_core::client::LeaseRenewal {
388 start_time,
389 lease_time,
390 parameters,
391 }: dhcp_client_core::client::LeaseRenewal<fasync::MonotonicInstant>,
392 ) -> Result<(ClientWatchConfigurationResponse, LeaseChangeInspect), Error> {
393 let Self {
394 core: _,
395 rng: _,
396 config: dhcp_client_core::client::ClientConfig { debug_log_prefix, .. },
397 stop_receiver: _,
398 current_lease,
399 interface_id: _,
400 } = self;
401
402 let mut dns_servers: Option<Vec<_>> = None;
403 let mut routers: Option<Vec<_>> = None;
404 let mut unrequested_options = Vec::new();
405
406 for option in parameters {
407 match option {
408 dhcp_protocol::DhcpOption::SubnetMask(len) => {
409 log::info!(
410 "{debug_log_prefix} ignoring prefix length={:?} for renewed lease",
411 len
412 );
413 }
414 dhcp_protocol::DhcpOption::DomainNameServer(list) => {
415 let prev = dns_servers.replace(list.into());
416 if let Some(prev) = prev {
417 log::warn!("expected prev_dns_servers to be None, got {prev:?}");
418 }
419 }
420 dhcp_protocol::DhcpOption::Router(list) => {
421 let prev = routers.replace(list.into());
422 if let Some(prev) = prev {
423 log::warn!("expected prev_routers to be None, got {prev:?}");
424 }
425 }
426 option => {
427 unrequested_options.push(option);
428 }
429 }
430 }
431
432 if !unrequested_options.is_empty() {
433 log::warn!(
434 "{debug_log_prefix} Received options from core that we didn't ask for: {:#?}",
435 unrequested_options
436 );
437 }
438
439 let Lease { event_stream: _, address_state_provider, ip_address } =
440 current_lease.as_mut().expect("should have current lease if we're handling a renewal");
441
442 address_state_provider
443 .update_address_properties(&fnet_interfaces_admin::AddressProperties {
444 preferred_lifetime_info: None,
445 valid_lifetime_end: Some(
446 zx::MonotonicInstant::from(start_time + lease_time.into()).into_nanos(),
447 ),
448 ..Default::default()
449 })
450 .await
451 .map_err(Error::Fidl)?;
452
453 let lease_inspect_properties = LeaseInspectProperties {
454 ip_address: *ip_address,
455 lease_length: lease_time.into(),
456 dns_server_count: dns_servers.as_ref().map(|list| list.len()).unwrap_or(0),
457 routers_count: routers.as_ref().map(|list| list.len()).unwrap_or(0),
458 };
459
460 Ok((
461 ClientWatchConfigurationResponse {
462 address: None,
463 dns_servers: dns_servers.map(into_fidl_list),
464 routers: routers.map(into_fidl_list),
465 ..Default::default()
466 },
467 LeaseChangeInspect::LeaseRenewed {
468 renewed_time: start_time,
469 properties: lease_inspect_properties,
470 },
471 ))
472 }
473
474 async fn handle_lease_drop(&mut self, mut lease: Lease) -> Result<(), Error> {
475 lease.address_state_provider.remove().map_err(Error::Fidl)?;
476 let watch_result = lease.watch_for_address_removal().await;
477 let Lease { address_state_provider: _, event_stream: _, ip_address } = lease;
478 let debug_log_prefix = &self.config.debug_log_prefix;
479
480 match watch_result {
481 Err(e) => {
482 log::error!(
483 "{debug_log_prefix} error watching for \
484 AddressRemovalReason after explicitly removing address \
485 {}: {:?}",
486 ip_address,
487 e
488 );
489 }
490 Ok(reason) => match reason {
491 fnet_interfaces_admin::AddressRemovalReason::UserRemoved => (),
492 reason @ (fnet_interfaces_admin::AddressRemovalReason::Invalid
493 | fnet_interfaces_admin::AddressRemovalReason::InvalidProperties
494 | fnet_interfaces_admin::AddressRemovalReason::AlreadyAssigned
495 | fnet_interfaces_admin::AddressRemovalReason::DadFailed
496 | fnet_interfaces_admin::AddressRemovalReason::InterfaceRemoved) => {
497 log::error!(
498 "{debug_log_prefix} unexpected removal reason \
499 after explicitly removing address {}: {:?}",
500 ip_address,
501 reason
502 );
503 }
504 },
505 };
506 Ok(())
507 }
508
509 async fn watch_configuration(
510 &mut self,
511 packet_socket_provider: &crate::packetsocket::PacketSocketProviderImpl,
512 udp_socket_provider: &impl dhcp_client_core::deps::UdpSocketProvider,
513 counters: &Counters,
514 inspect: &Inspect,
515 ) -> Result<ClientWatchConfigurationResponse, Error> {
516 loop {
517 let step = self
518 .watch_configuration_step(packet_socket_provider, udp_socket_provider, counters)
519 .await;
520 let HandledWatchConfigurationStep { state_inspect, lease_inspect, response_to_return } =
521 self.handle_watch_configuration_step(step, packet_socket_provider).await?;
522
523 inspect.update(state_inspect, lease_inspect, self.config.debug_log_prefix);
527 if let Some(response) = response_to_return {
528 return Ok(response);
529 }
530 }
531 }
532
533 async fn handle_watch_configuration_step(
534 &mut self,
535 step: WatchConfigurationStep,
536 packet_socket_provider: &crate::packetsocket::PacketSocketProviderImpl,
537 ) -> Result<HandledWatchConfigurationStep, Error> {
538 let Self { core, rng: _, config, stop_receiver: _, current_lease, interface_id: _ } = self;
539 match step {
540 WatchConfigurationStep::CurrentLeaseAddressRemoved((reason, ip_address)) => {
541 *current_lease = None;
542 let debug_log_prefix = &config.debug_log_prefix;
543 match reason {
544 None => return Err(Error::Exit(ClientExitReason::AddressStateProviderError)),
545 Some(reason) => match reason {
546 fnet_interfaces_admin::AddressRemovalReason::Invalid => {
547 panic!("yielded invalid address")
548 }
549 fnet_interfaces_admin::AddressRemovalReason::InvalidProperties => {
550 panic!("used invalid properties")
551 }
552 fnet_interfaces_admin::AddressRemovalReason::InterfaceRemoved => {
553 log::warn!("{debug_log_prefix} interface removed; stopping");
554 return Err(Error::Exit(ClientExitReason::InvalidInterface));
555 }
556 fnet_interfaces_admin::AddressRemovalReason::UserRemoved => {
557 log::warn!(
558 "{debug_log_prefix} address \
559 administratively removed; stopping"
560 );
561 return Err(Error::Exit(ClientExitReason::AddressRemovedByUser));
562 }
563 fnet_interfaces_admin::AddressRemovalReason::AlreadyAssigned => {
564 log::warn!(
565 "{debug_log_prefix} address already assigned; notifying core"
566 );
567 }
568 fnet_interfaces_admin::AddressRemovalReason::DadFailed => {
569 log::warn!(
570 "{debug_log_prefix} duplicate address detected; notifying core"
571 );
572 }
573 },
574 };
575
576 match core
577 .on_address_rejection(config, packet_socket_provider, &Clock, ip_address)
578 .await
579 .map_err(Error::from_core)?
580 {
581 dhcp_client_core::client::AddressRejectionOutcome::ShouldBeImpossible => {
582 unreachable!("should not observe address rejection without active lease");
583 }
584 dhcp_client_core::client::AddressRejectionOutcome::NextState(state) => {
585 *core = state;
586 Ok(HandledWatchConfigurationStep {
587 state_inspect: StateInspect {
588 state: *core,
589 time: fasync::MonotonicInstant::now(),
590 },
591 lease_inspect: LeaseChangeInspect::LeaseDropped,
592 response_to_return: None,
593 })
594 }
595 }
596 }
597 WatchConfigurationStep::CoreStep(core_step) => {
598 match core_step? {
599 dhcp_client_core::client::Step::NextState(transition) => {
600 let (next_core, effect) = core.apply(config, transition);
601 *core = next_core;
602 match effect {
603 Some(dhcp_client_core::client::TransitionEffect::DropLease) => {
604 let current_lease =
605 self.current_lease.take().expect("should have current lease");
606 self.handle_lease_drop(current_lease).await?;
607 Ok(HandledWatchConfigurationStep {
608 state_inspect: StateInspect {
609 state: next_core,
610 time: fasync::MonotonicInstant::now(),
611 },
612 lease_inspect: LeaseChangeInspect::LeaseDropped,
613 response_to_return: None,
614 })
615 }
616 Some(dhcp_client_core::client::TransitionEffect::HandleNewLease(
617 newly_acquired_lease,
618 )) => {
619 let (response, lease_inspect) =
620 self.handle_newly_acquired_lease(newly_acquired_lease).await?;
621 let start_time = fasync::MonotonicInstant::now();
622 Ok(HandledWatchConfigurationStep {
623 state_inspect: StateInspect {
624 state: next_core,
625 time: start_time,
626 },
627 lease_inspect,
628 response_to_return: Some(response),
629 })
630 }
631 Some(
632 dhcp_client_core::client::TransitionEffect::HandleRenewedLease(
633 lease_renewal,
634 ),
635 ) => {
636 let (response, lease_inspect) =
637 self.handle_lease_renewal(lease_renewal).await?;
638 Ok(HandledWatchConfigurationStep {
639 state_inspect: StateInspect {
640 state: next_core,
641 time: fasync::MonotonicInstant::now(),
642 },
643 lease_inspect,
644 response_to_return: Some(response),
645 })
646 }
647 None => Ok(HandledWatchConfigurationStep {
648 state_inspect: StateInspect {
649 state: *core,
650 time: fasync::MonotonicInstant::now(),
651 },
652 lease_inspect: LeaseChangeInspect::NoChange,
653 response_to_return: None,
654 }),
655 }
656 }
657 dhcp_client_core::client::Step::Exit(reason) => match reason {
658 dhcp_client_core::client::ExitReason::GracefulShutdown => {
659 if let Some(current_lease) = self.current_lease.take() {
660 self.handle_lease_drop(current_lease).await?;
662 }
663 return Err(Error::Exit(ClientExitReason::GracefulShutdown));
664 }
665 },
666 }
667 }
668 }
669 }
670
671 async fn watch_configuration_step(
672 &mut self,
673 packet_socket_provider: &crate::packetsocket::PacketSocketProviderImpl,
674 udp_socket_provider: &impl dhcp_client_core::deps::UdpSocketProvider,
675 counters: &Counters,
676 ) -> WatchConfigurationStep {
677 let Self { core, rng, config, stop_receiver, current_lease, interface_id } = self;
678 let clock = Clock;
679
680 let mut core_step_fut = pin!(core
704 .run(
705 config,
706 packet_socket_provider,
707 udp_socket_provider,
708 rng,
709 &clock,
710 stop_receiver,
711 counters
712 )
713 .fuse());
714 let mut address_removed_fut = pin!(async {
715 match current_lease {
716 Some(current_lease) => match current_lease.watch_for_address_removal().await {
717 Ok(reason) => (Some(reason), current_lease.ip_address),
718 Err(e) => {
719 let debug_log_prefix = &config.debug_log_prefix;
720 log::error!(
721 "{debug_log_prefix} observed error {:?} while watching for removal \
722 of address {} on interface {}; \
723 removing address",
724 e,
725 *current_lease.ip_address,
726 interface_id
727 );
728 (None, current_lease.ip_address)
729 }
730 },
731 None => futures::future::pending().await,
732 }
733 }
734 .fuse());
735
736 select! {
737 address_removed = address_removed_fut => {
738 WatchConfigurationStep::CurrentLeaseAddressRemoved(address_removed)
739 },
740 core_step = core_step_fut => {
741 WatchConfigurationStep::CoreStep(core_step.map_err(Error::from_core))
742 },
743 }
744 }
745}
746
747struct HandledWatchConfigurationStep {
748 state_inspect: StateInspect,
749 lease_inspect: LeaseChangeInspect,
750 response_to_return: Option<ClientWatchConfigurationResponse>,
751}
752
753enum WatchConfigurationStep {
754 CurrentLeaseAddressRemoved(
755 (Option<fnet_interfaces_admin::AddressRemovalReason>, SpecifiedAddr<Ipv4Addr>),
756 ),
757 CoreStep(Result<dhcp_client_core::client::Step<fasync::MonotonicInstant>, Error>),
758}
759
760fn into_fidl_list(list: Vec<std::net::Ipv4Addr>) -> Vec<fidl_fuchsia_net::Ipv4Address> {
761 list.into_iter().map(|addr| net_types::ip::Ipv4Addr::from(addr).into_ext()).collect()
762}