1#![warn(missing_docs, unreachable_patterns)]
6
7pub mod guest;
11
12use std::borrow::Cow;
13use std::num::NonZeroU64;
14use std::ops::DerefMut as _;
15use std::path::Path;
16use std::pin::pin;
17
18use fidl::endpoints::ProtocolMarker;
19use fidl_fuchsia_net_dhcp_ext::{self as fnet_dhcp_ext, ClientProviderExt};
20use fidl_fuchsia_net_ext::{self as fnet_ext};
21use fidl_fuchsia_net_interfaces_ext::admin::Control;
22use fidl_fuchsia_net_interfaces_ext::{self as fnet_interfaces_ext};
23use fnet_ext::{FromExt as _, IntoExt as _};
24use fnet_interfaces_admin::GrantForInterfaceAuthorization;
25use {
26 fidl_fuchsia_hardware_network as fnetwork, fidl_fuchsia_io as fio, fidl_fuchsia_net as fnet,
27 fidl_fuchsia_net_dhcp as fnet_dhcp, fidl_fuchsia_net_interfaces as fnet_interfaces,
28 fidl_fuchsia_net_interfaces_admin as fnet_interfaces_admin,
29 fidl_fuchsia_net_neighbor as fnet_neighbor, fidl_fuchsia_net_root as fnet_root,
30 fidl_fuchsia_net_routes as fnet_routes, fidl_fuchsia_net_routes_admin as fnet_routes_admin,
31 fidl_fuchsia_net_routes_ext as fnet_routes_ext, fidl_fuchsia_net_stack as fnet_stack,
32 fidl_fuchsia_netemul as fnetemul, fidl_fuchsia_netemul_network as fnetemul_network,
33 fidl_fuchsia_posix_socket as fposix_socket, fidl_fuchsia_posix_socket_ext as fposix_socket_ext,
34 fidl_fuchsia_posix_socket_packet as fposix_socket_packet,
35 fidl_fuchsia_posix_socket_raw as fposix_socket_raw,
36};
37
38use anyhow::{anyhow, Context as _};
39use futures::future::{FutureExt as _, LocalBoxFuture, TryFutureExt as _};
40use futures::{SinkExt as _, TryStreamExt as _};
41use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv6, Subnet};
42use net_types::SpecifiedAddr;
43
44type Result<T = ()> = std::result::Result<T, anyhow::Error>;
45
46pub const DEFAULT_MTU: u16 = 1500;
48
49pub const NETDEVICE_DEVFS_PATH: &'static str = "class/network";
51
52pub fn devfs_device_path(node_name: &str) -> std::path::PathBuf {
54 std::path::Path::new(NETDEVICE_DEVFS_PATH).join(node_name)
55}
56
57pub fn new_endpoint_config(
59 mtu: u16,
60 mac: Option<fnet::MacAddress>,
61) -> fnetemul_network::EndpointConfig {
62 fnetemul_network::EndpointConfig {
63 mtu,
64 mac: mac.map(Box::new),
65 port_class: fnetwork::PortClass::Virtual,
66 }
67}
68
69#[must_use]
76pub struct TestSandbox {
77 sandbox: fnetemul::SandboxProxy,
78}
79
80impl TestSandbox {
81 pub fn new() -> Result<TestSandbox> {
83 fuchsia_component::client::connect_to_protocol::<fnetemul::SandboxMarker>()
84 .context("failed to connect to sandbox protocol")
85 .map(|sandbox| TestSandbox { sandbox })
86 }
87
88 pub fn create_realm<'a, I>(
90 &'a self,
91 name: impl Into<Cow<'a, str>>,
92 children: I,
93 ) -> Result<TestRealm<'a>>
94 where
95 I: IntoIterator,
96 I::Item: Into<fnetemul::ChildDef>,
97 {
98 let (realm, server) = fidl::endpoints::create_proxy::<fnetemul::ManagedRealmMarker>();
99 let name = name.into();
100 let () = self.sandbox.create_realm(
101 server,
102 fnetemul::RealmOptions {
103 name: Some(name.clone().into_owned()),
104 children: Some(children.into_iter().map(Into::into).collect()),
105 ..Default::default()
106 },
107 )?;
108 Ok(TestRealm { realm, name, _sandbox: self })
109 }
110
111 pub fn create_empty_realm<'a>(
113 &'a self,
114 name: impl Into<Cow<'a, str>>,
115 ) -> Result<TestRealm<'a>> {
116 self.create_realm(name, std::iter::empty::<fnetemul::ChildDef>())
117 }
118
119 fn get_network_context(&self) -> Result<fnetemul_network::NetworkContextProxy> {
121 let (ctx, server) =
122 fidl::endpoints::create_proxy::<fnetemul_network::NetworkContextMarker>();
123 let () = self.sandbox.get_network_context(server)?;
124 Ok(ctx)
125 }
126
127 pub fn get_network_manager(&self) -> Result<fnetemul_network::NetworkManagerProxy> {
129 let ctx = self.get_network_context()?;
130 let (network_manager, server) =
131 fidl::endpoints::create_proxy::<fnetemul_network::NetworkManagerMarker>();
132 let () = ctx.get_network_manager(server)?;
133 Ok(network_manager)
134 }
135
136 pub fn get_endpoint_manager(&self) -> Result<fnetemul_network::EndpointManagerProxy> {
138 let ctx = self.get_network_context()?;
139 let (ep_manager, server) =
140 fidl::endpoints::create_proxy::<fnetemul_network::EndpointManagerMarker>();
141 let () = ctx.get_endpoint_manager(server)?;
142 Ok(ep_manager)
143 }
144
145 pub async fn create_network<'a>(
147 &'a self,
148 name: impl Into<Cow<'a, str>>,
149 ) -> Result<TestNetwork<'a>> {
150 let name = name.into();
151 let netm = self.get_network_manager()?;
152 let (status, network) = netm
153 .create_network(
154 &name,
155 &fnetemul_network::NetworkConfig {
156 latency: None,
157 packet_loss: None,
158 reorder: None,
159 ..Default::default()
160 },
161 )
162 .await
163 .context("create_network FIDL error")?;
164 let () = zx::Status::ok(status).context("create_network failed")?;
165 let network = network
166 .ok_or_else(|| anyhow::anyhow!("create_network didn't return a valid network"))?
167 .into_proxy();
168 Ok(TestNetwork { network, name, sandbox: self })
169 }
170
171 pub async fn setup_networks<'a>(
173 &'a self,
174 networks: Vec<fnetemul_network::NetworkSetup>,
175 ) -> Result<TestNetworkSetup<'a>> {
176 let ctx = self.get_network_context()?;
177 let (status, handle) = ctx.setup(&networks).await.context("setup FIDL error")?;
178 let () = zx::Status::ok(status).context("setup failed")?;
179 let handle = handle
180 .ok_or_else(|| anyhow::anyhow!("setup didn't return a valid handle"))?
181 .into_proxy();
182 Ok(TestNetworkSetup { _setup: handle, _sandbox: self })
183 }
184
185 pub async fn create_endpoint<'a, S>(&'a self, name: S) -> Result<TestEndpoint<'a>>
189 where
190 S: Into<Cow<'a, str>>,
191 {
192 self.create_endpoint_with(name, new_endpoint_config(DEFAULT_MTU, None)).await
193 }
194
195 pub async fn create_endpoint_with<'a>(
199 &'a self,
200 name: impl Into<Cow<'a, str>>,
201 config: fnetemul_network::EndpointConfig,
202 ) -> Result<TestEndpoint<'a>> {
203 let name = name.into();
204 let epm = self.get_endpoint_manager()?;
205 let (status, endpoint) =
206 epm.create_endpoint(&name, &config).await.context("create_endpoint FIDL error")?;
207 let () = zx::Status::ok(status).context("create_endpoint failed")?;
208 let endpoint = endpoint
209 .ok_or_else(|| anyhow::anyhow!("create_endpoint didn't return a valid endpoint"))?
210 .into_proxy();
211 Ok(TestEndpoint { endpoint, name, _sandbox: self })
212 }
213}
214
215#[must_use]
219pub struct TestNetworkSetup<'a> {
220 _setup: fnetemul_network::SetupHandleProxy,
221 _sandbox: &'a TestSandbox,
222}
223
224impl TestNetworkSetup<'_> {
225 pub fn into_proxy(self) -> fnetemul_network::SetupHandleProxy {
232 let Self { _setup, _sandbox: _ } = self;
233 _setup
234 }
235}
236
237#[derive(Default)]
239pub struct InterfaceConfig<'a> {
240 pub name: Option<Cow<'a, str>>,
242 pub metric: Option<u32>,
244 pub dad_transmits: Option<u16>,
246}
247
248#[must_use]
250#[derive(Clone)]
251pub struct TestRealm<'a> {
252 realm: fnetemul::ManagedRealmProxy,
253 name: Cow<'a, str>,
254 _sandbox: &'a TestSandbox,
255}
256
257impl<'a> std::fmt::Debug for TestRealm<'a> {
258 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
259 let Self { realm: _, name, _sandbox } = self;
260 f.debug_struct("TestRealm").field("name", name).finish_non_exhaustive()
261 }
262}
263
264impl<'a> TestRealm<'a> {
265 pub fn connect_to_protocol<S>(&self) -> Result<S::Proxy>
267 where
268 S: fidl::endpoints::DiscoverableProtocolMarker,
269 {
270 (|| {
271 let (proxy, server_end) = fidl::endpoints::create_proxy::<S>();
272 let () = self
273 .connect_to_protocol_with_server_end(server_end)
274 .context("connect to protocol name with server end")?;
275 Result::Ok(proxy)
276 })()
277 .context(S::DEBUG_NAME)
278 }
279
280 pub fn connect_to_protocol_from_child<S>(&self, child: &str) -> Result<S::Proxy>
282 where
283 S: fidl::endpoints::DiscoverableProtocolMarker,
284 {
285 (|| {
286 let (proxy, server_end) = fidl::endpoints::create_proxy::<S>();
287 let () = self
288 .connect_to_protocol_from_child_at_path_with_server_end(
289 S::PROTOCOL_NAME,
290 child,
291 server_end,
292 )
293 .context("connect to protocol name with server end")?;
294 Result::Ok(proxy)
295 })()
296 .with_context(|| format!("{} from {child}", S::DEBUG_NAME))
297 }
298
299 pub fn open_diagnostics_directory(&self, child_name: &str) -> Result<fio::DirectoryProxy> {
301 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
302 let () = self
303 .realm
304 .open_diagnostics_directory(child_name, server_end)
305 .context("open diagnostics dir")?;
306 Ok(proxy)
307 }
308
309 pub fn connect_to_protocol_with_server_end<S: fidl::endpoints::DiscoverableProtocolMarker>(
311 &self,
312 server_end: fidl::endpoints::ServerEnd<S>,
313 ) -> Result {
314 self.realm
315 .connect_to_protocol(S::PROTOCOL_NAME, None, server_end.into_channel())
316 .context("connect to protocol")
317 }
318
319 pub fn connect_to_protocol_from_child_at_path_with_server_end<
321 S: fidl::endpoints::DiscoverableProtocolMarker,
322 >(
323 &self,
324 protocol_path: &str,
325 child: &str,
326 server_end: fidl::endpoints::ServerEnd<S>,
327 ) -> Result {
328 self.realm
329 .connect_to_protocol(protocol_path, Some(child), server_end.into_channel())
330 .context("connect to protocol")
331 }
332
333 pub async fn get_moniker(&self) -> Result<String> {
335 self.realm.get_moniker().await.context("failed to call get moniker")
336 }
337
338 pub async fn start_child_component(&self, child_name: &str) -> Result {
340 self.realm
341 .start_child_component(child_name)
342 .await?
343 .map_err(zx::Status::from_raw)
344 .with_context(|| format!("failed to start child component '{}'", child_name))
345 }
346
347 pub async fn stop_child_component(&self, child_name: &str) -> Result {
349 self.realm
350 .stop_child_component(child_name)
351 .await?
352 .map_err(zx::Status::from_raw)
353 .with_context(|| format!("failed to stop child component '{}'", child_name))
354 }
355
356 pub async fn join_network<S>(
362 &self,
363 network: &TestNetwork<'a>,
364 ep_name: S,
365 ) -> Result<TestInterface<'a>>
366 where
367 S: Into<Cow<'a, str>>,
368 {
369 self.join_network_with_if_config(network, ep_name, Default::default()).await
370 }
371
372 pub async fn join_network_with_if_config<S>(
378 &self,
379 network: &TestNetwork<'a>,
380 ep_name: S,
381 if_config: InterfaceConfig<'a>,
382 ) -> Result<TestInterface<'a>>
383 where
384 S: Into<Cow<'a, str>>,
385 {
386 let endpoint =
387 network.create_endpoint(ep_name).await.context("failed to create endpoint")?;
388 self.install_endpoint(endpoint, if_config).await
389 }
390
391 pub async fn join_network_with(
402 &self,
403 network: &TestNetwork<'a>,
404 ep_name: impl Into<Cow<'a, str>>,
405 ep_config: fnetemul_network::EndpointConfig,
406 if_config: InterfaceConfig<'a>,
407 ) -> Result<TestInterface<'a>> {
408 let installer = self
409 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
410 .context("failed to connect to fuchsia.net.interfaces.admin.Installer")?;
411 let interface_state = self
412 .connect_to_protocol::<fnet_interfaces::StateMarker>()
413 .context("failed to connect to fuchsia.net.interfaces.State")?;
414 let (endpoint, id, control, device_control) = self
415 .join_network_with_installer(
416 network,
417 installer,
418 interface_state,
419 ep_name,
420 ep_config,
421 if_config,
422 )
423 .await?;
424
425 Ok(TestInterface {
426 endpoint,
427 id,
428 realm: self.clone(),
429 control,
430 device_control: Some(device_control),
431 dhcp_client_task: futures::lock::Mutex::default(),
432 })
433 }
434
435 pub async fn join_network_with_installer(
446 &self,
447 network: &TestNetwork<'a>,
448 installer: fnet_interfaces_admin::InstallerProxy,
449 interface_state: fnet_interfaces::StateProxy,
450 ep_name: impl Into<Cow<'a, str>>,
451 ep_config: fnetemul_network::EndpointConfig,
452 if_config: InterfaceConfig<'a>,
453 ) -> Result<(TestEndpoint<'a>, u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
454 let endpoint = network
455 .create_endpoint_with(ep_name, ep_config)
456 .await
457 .context("failed to create endpoint")?;
458 let (id, control, device_control) = self
459 .install_endpoint_with_installer(installer, interface_state, &endpoint, if_config)
460 .await?;
461 Ok((endpoint, id, control, device_control))
462 }
463
464 pub async fn install_endpoint_with_installer(
472 &self,
473 installer: fnet_interfaces_admin::InstallerProxy,
474 interface_state: fnet_interfaces::StateProxy,
475 endpoint: &TestEndpoint<'a>,
476 if_config: InterfaceConfig<'a>,
477 ) -> Result<(u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
478 let (id, control, device_control) =
479 endpoint.install(installer, if_config).await.context("failed to add endpoint")?;
480
481 let () = endpoint.set_link_up(true).await.context("failed to start endpoint")?;
482 let _did_enable: bool = control
483 .enable()
484 .await
485 .map_err(anyhow::Error::new)
486 .and_then(|res| {
487 res.map_err(|e: fnet_interfaces_admin::ControlEnableError| {
488 anyhow::anyhow!("{:?}", e)
489 })
490 })
491 .context("failed to enable interface")?;
492
493 let () = fnet_interfaces_ext::wait_interface_with_id(
496 fnet_interfaces_ext::event_stream_from_state::<fnet_interfaces_ext::DefaultInterest>(
497 &interface_state,
498 fnet_interfaces_ext::IncludedAddresses::OnlyAssigned,
499 )?,
500 &mut fnet_interfaces_ext::InterfaceState::<(), _>::Unknown(id),
501 |properties_and_state| properties_and_state.properties.online.then_some(()),
502 )
503 .await
504 .context("failed to observe interface up")?;
505
506 Ok((id, control, device_control))
507 }
508
509 pub async fn install_endpoint(
513 &self,
514 endpoint: TestEndpoint<'a>,
515 if_config: InterfaceConfig<'a>,
516 ) -> Result<TestInterface<'a>> {
517 let installer = self
518 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
519 .context("failed to connect to fuchsia.net.interfaces.admin.Installer")?;
520 let interface_state = self
521 .connect_to_protocol::<fnet_interfaces::StateMarker>()
522 .context("failed to connect to fuchsia.net.interfaces.State")?;
523 let (id, control, device_control) = self
524 .install_endpoint_with_installer(installer, interface_state, &endpoint, if_config)
525 .await?;
526 Ok(TestInterface {
527 endpoint,
528 id,
529 realm: self.clone(),
530 control,
531 device_control: Some(device_control),
532 dhcp_client_task: futures::lock::Mutex::default(),
533 })
534 }
535
536 pub async fn add_raw_device(
538 &self,
539 path: &Path,
540 device: fidl::endpoints::ClientEnd<fnetemul_network::DeviceProxy_Marker>,
541 ) -> Result {
542 let path = path.to_str().with_context(|| format!("convert {} to str", path.display()))?;
543 self.realm
544 .add_device(path, device)
545 .await
546 .context("add device")?
547 .map_err(zx::Status::from_raw)
548 .context("add device error")
549 }
550
551 pub async fn add_virtual_device(&self, e: &TestEndpoint<'_>, path: &Path) -> Result {
553 let (device, device_server_end) =
554 fidl::endpoints::create_endpoints::<fnetemul_network::DeviceProxy_Marker>();
555 e.get_proxy_(device_server_end).context("get proxy")?;
556
557 self.add_raw_device(path, device).await
558 }
559
560 pub async fn remove_virtual_device(&self, path: &Path) -> Result {
562 let path = path.to_str().with_context(|| format!("convert {} to str", path.display()))?;
563 self.realm
564 .remove_device(path)
565 .await
566 .context("remove device")?
567 .map_err(zx::Status::from_raw)
568 .context("remove device error")
569 }
570
571 pub async fn datagram_socket(
574 &self,
575 domain: fposix_socket::Domain,
576 proto: fposix_socket::DatagramSocketProtocol,
577 ) -> Result<socket2::Socket> {
578 let socket_provider = self
579 .connect_to_protocol::<fposix_socket::ProviderMarker>()
580 .context("failed to connect to socket provider")?;
581
582 fposix_socket_ext::datagram_socket(&socket_provider, domain, proto)
583 .await
584 .context("failed to call socket")?
585 .context("failed to create socket")
586 }
587
588 pub async fn raw_socket(
591 &self,
592 domain: fposix_socket::Domain,
593 association: fposix_socket_raw::ProtocolAssociation,
594 ) -> Result<socket2::Socket> {
595 let socket_provider = self
596 .connect_to_protocol::<fposix_socket_raw::ProviderMarker>()
597 .context("failed to connect to socket provider")?;
598 let sock = socket_provider
599 .socket(domain, &association)
600 .await
601 .context("failed to call socket")?
602 .map_err(|e| std::io::Error::from_raw_os_error(e.into_primitive()))
603 .context("failed to create socket")?;
604
605 Ok(fdio::create_fd(sock.into()).context("failed to create fd")?.into())
606 }
607
608 pub async fn packet_socket(&self, kind: fposix_socket_packet::Kind) -> Result<socket2::Socket> {
613 let socket_provider = self
614 .connect_to_protocol::<fposix_socket_packet::ProviderMarker>()
615 .context("failed to connect to socket provider")?;
616
617 fposix_socket_ext::packet_socket(&socket_provider, kind)
618 .await
619 .context("failed to call socket")?
620 .context("failed to create socket")
621 }
622
623 pub async fn stream_socket(
626 &self,
627 domain: fposix_socket::Domain,
628 proto: fposix_socket::StreamSocketProtocol,
629 ) -> Result<socket2::Socket> {
630 let socket_provider = self
631 .connect_to_protocol::<fposix_socket::ProviderMarker>()
632 .context("failed to connect to socket provider")?;
633 let sock = socket_provider
634 .stream_socket(domain, proto)
635 .await
636 .context("failed to call socket")?
637 .map_err(|e| std::io::Error::from_raw_os_error(e.into_primitive()))
638 .context("failed to create socket")?;
639
640 Ok(fdio::create_fd(sock.into()).context("failed to create fd")?.into())
641 }
642
643 pub async fn shutdown(&self) -> Result {
650 let () = self.realm.shutdown().context("call shutdown")?;
651 let events = self
652 .realm
653 .take_event_stream()
654 .try_collect::<Vec<_>>()
655 .await
656 .context("error on realm event stream")?;
657 assert_matches::assert_matches!(events[..], [fnetemul::ManagedRealmEvent::OnShutdown {}]);
659 Ok(())
660 }
661
662 pub async fn icmp_socket<Ip: ping::FuchsiaIpExt>(
664 &self,
665 ) -> Result<fuchsia_async::net::DatagramSocket> {
666 let sock = self
667 .datagram_socket(Ip::DOMAIN_FIDL, fposix_socket::DatagramSocketProtocol::IcmpEcho)
668 .await
669 .context("failed to create ICMP datagram socket")?;
670 fuchsia_async::net::DatagramSocket::new_from_socket(sock)
671 .context("failed to create async ICMP datagram socket")
672 }
673
674 pub async fn ping_once<Ip: ping::FuchsiaIpExt>(&self, addr: Ip::SockAddr, seq: u16) -> Result {
676 let icmp_sock = self.icmp_socket::<Ip>().await?;
677
678 const MESSAGE: &'static str = "hello, world";
679 let (mut sink, mut stream) = ping::new_unicast_sink_and_stream::<
680 Ip,
681 _,
682 { MESSAGE.len() + ping::ICMP_HEADER_LEN },
683 >(&icmp_sock, &addr, MESSAGE.as_bytes());
684
685 let send_fut = sink.send(seq).map_err(anyhow::Error::new);
686 let recv_fut = stream.try_next().map(|r| match r {
687 Ok(Some(got)) if got == seq => Ok(()),
688 Ok(Some(got)) => Err(anyhow!("unexpected echo reply; got: {}, want: {}", got, seq)),
689 Ok(None) => Err(anyhow!("echo reply stream ended unexpectedly")),
690 Err(e) => Err(anyhow::Error::from(e)),
691 });
692
693 let ((), ()) = futures::future::try_join(send_fut, recv_fut)
694 .await
695 .with_context(|| format!("failed to ping from {} to {}", self.name, addr,))?;
696 Ok(())
697 }
698
699 pub async fn add_neighbor_entry(
703 &self,
704 interface: u64,
705 addr: fnet::IpAddress,
706 mac: fnet::MacAddress,
707 ) -> Result {
708 let controller = self
709 .connect_to_protocol::<fnet_neighbor::ControllerMarker>()
710 .context("connect to protocol")?;
711 controller
712 .add_entry(interface, &addr, &mac)
713 .await
714 .context("add_entry")?
715 .map_err(zx::Status::from_raw)
716 .context("add_entry failed")
717 }
718
719 pub fn get_interface_event_stream(
722 &self,
723 ) -> Result<
724 impl futures::Stream<
725 Item = std::result::Result<
726 fnet_interfaces_ext::EventWithInterest<fnet_interfaces_ext::DefaultInterest>,
727 fidl::Error,
728 >,
729 >,
730 > {
731 self.get_interface_event_stream_with_interest::<fnet_interfaces_ext::DefaultInterest>()
732 }
733
734 pub fn get_interface_event_stream_with_interest<I: fnet_interfaces_ext::FieldInterests>(
737 &self,
738 ) -> Result<
739 impl futures::Stream<
740 Item = std::result::Result<fnet_interfaces_ext::EventWithInterest<I>, fidl::Error>,
741 >,
742 > {
743 let interface_state = self
744 .connect_to_protocol::<fnet_interfaces::StateMarker>()
745 .context("connect to protocol")?;
746 fnet_interfaces_ext::event_stream_from_state::<I>(
747 &interface_state,
748 fnet_interfaces_ext::IncludedAddresses::OnlyAssigned,
749 )
750 .context("get interface event stream")
751 }
752
753 pub async fn main_table_id<
755 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
756 >(
757 &self,
758 ) -> u32 {
759 let main_route_table = self
760 .connect_to_protocol::<I::RouteTableMarker>()
761 .expect("failed to connect to main route table");
762 fnet_routes_ext::admin::get_table_id::<I>(&main_route_table)
763 .await
764 .expect("failed to get_table_id")
765 .get()
766 }
767}
768
769#[must_use]
774pub struct TestNetwork<'a> {
775 network: fnetemul_network::NetworkProxy,
776 name: Cow<'a, str>,
777 sandbox: &'a TestSandbox,
778}
779
780impl<'a> std::fmt::Debug for TestNetwork<'a> {
781 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
782 let Self { name, network: _, sandbox: _ } = self;
783 f.debug_struct("TestNetwork").field("name", name).finish_non_exhaustive()
784 }
785}
786
787impl<'a> TestNetwork<'a> {
788 pub fn into_proxy(self) -> fnetemul_network::NetworkProxy {
795 let Self { network, name: _, sandbox: _ } = self;
796 network
797 }
798
799 async fn get_client_end_clone(
801 &self,
802 ) -> Result<fidl::endpoints::ClientEnd<fnetemul_network::NetworkMarker>> {
803 let network_manager =
804 self.sandbox.get_network_manager().context("get_network_manager failed")?;
805 let client = network_manager
806 .get_network(&self.name)
807 .await
808 .context("get_network failed")?
809 .with_context(|| format!("no network found with name {}", self.name))?;
810 Ok(client)
811 }
812
813 pub async fn set_config(&self, config: fnetemul_network::NetworkConfig) -> Result<()> {
815 let status = self.network.set_config(&config).await.context("call set_config")?;
816 zx::Status::ok(status).context("set config")
817 }
818
819 pub async fn attach_endpoint(&self, ep: &TestEndpoint<'a>) -> Result<()> {
821 let status =
822 self.network.attach_endpoint(&ep.name).await.context("attach_endpoint FIDL error")?;
823 let () = zx::Status::ok(status).context("attach_endpoint failed")?;
824 Ok(())
825 }
826
827 pub async fn create_endpoint<S>(&self, name: S) -> Result<TestEndpoint<'a>>
831 where
832 S: Into<Cow<'a, str>>,
833 {
834 let ep = self
835 .sandbox
836 .create_endpoint(name)
837 .await
838 .with_context(|| format!("failed to create endpoint for network {}", self.name))?;
839 let () = self.attach_endpoint(&ep).await.with_context(|| {
840 format!("failed to attach endpoint {} to network {}", ep.name, self.name)
841 })?;
842 Ok(ep)
843 }
844
845 pub async fn create_endpoint_with(
849 &self,
850 name: impl Into<Cow<'a, str>>,
851 config: fnetemul_network::EndpointConfig,
852 ) -> Result<TestEndpoint<'a>> {
853 let ep = self
854 .sandbox
855 .create_endpoint_with(name, config)
856 .await
857 .with_context(|| format!("failed to create endpoint for network {}", self.name))?;
858 let () = self.attach_endpoint(&ep).await.with_context(|| {
859 format!("failed to attach endpoint {} to network {}", ep.name, self.name)
860 })?;
861 Ok(ep)
862 }
863
864 pub fn create_fake_endpoint(&self) -> Result<TestFakeEndpoint<'a>> {
866 let (endpoint, server) =
867 fidl::endpoints::create_proxy::<fnetemul_network::FakeEndpointMarker>();
868 let () = self.network.create_fake_endpoint(server)?;
869 return Ok(TestFakeEndpoint { endpoint, _sandbox: self.sandbox });
870 }
871
872 pub async fn start_capture(&self, name: &str) -> Result<PacketCapture> {
878 let manager = self.sandbox.get_network_manager()?;
879 let client = manager.get_network(&self.name).await?.expect("network must exist");
880 zx::ok(self.network.start_capture(name).await?)?;
881 let sync_proxy = fnetemul_network::NetworkSynchronousProxy::new(client.into_channel());
882 Ok(PacketCapture { sync_proxy })
883 }
884
885 pub async fn stop_capture(&self) -> Result<()> {
887 Ok(self.network.stop_capture().await?)
888 }
889}
890
891pub struct PacketCapture {
894 sync_proxy: fnetemul_network::NetworkSynchronousProxy,
895}
896
897impl Drop for PacketCapture {
898 fn drop(&mut self) {
899 self.sync_proxy
900 .stop_capture(zx::MonotonicInstant::INFINITE)
901 .expect("failed to stop packet capture")
902 }
903}
904
905#[must_use]
907pub struct TestEndpoint<'a> {
908 endpoint: fnetemul_network::EndpointProxy,
909 name: Cow<'a, str>,
910 _sandbox: &'a TestSandbox,
911}
912
913impl<'a> std::fmt::Debug for TestEndpoint<'a> {
914 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
915 let Self { endpoint: _, name, _sandbox } = self;
916 f.debug_struct("TestEndpoint").field("name", name).finish_non_exhaustive()
917 }
918}
919
920impl<'a> std::ops::Deref for TestEndpoint<'a> {
921 type Target = fnetemul_network::EndpointProxy;
922
923 fn deref(&self) -> &Self::Target {
924 &self.endpoint
925 }
926}
927
928#[must_use]
930pub struct TestFakeEndpoint<'a> {
931 endpoint: fnetemul_network::FakeEndpointProxy,
932 _sandbox: &'a TestSandbox,
933}
934
935impl<'a> std::ops::Deref for TestFakeEndpoint<'a> {
936 type Target = fnetemul_network::FakeEndpointProxy;
937
938 fn deref(&self) -> &Self::Target {
939 &self.endpoint
940 }
941}
942
943impl<'a> TestFakeEndpoint<'a> {
944 pub fn frame_stream(
948 &self,
949 ) -> impl futures::Stream<Item = std::result::Result<(Vec<u8>, u64), fidl::Error>> + '_ {
950 futures::stream::try_unfold(&self.endpoint, |ep| ep.read().map_ok(move |r| Some((r, ep))))
951 }
952}
953
954async fn to_netdevice_inner(
957 port: fidl::endpoints::ClientEnd<fnetwork::PortMarker>,
958) -> Result<(fidl::endpoints::ClientEnd<fnetwork::DeviceMarker>, fnetwork::PortId)> {
959 let port = port.into_proxy();
960 let (device, server_end) = fidl::endpoints::create_endpoints::<fnetwork::DeviceMarker>();
961 let () = port.get_device(server_end)?;
962 let port_id = port
963 .get_info()
964 .await
965 .context("get port info")?
966 .id
967 .ok_or_else(|| anyhow::anyhow!("missing port id"))?;
968 Ok((device, port_id))
969}
970
971impl<'a> TestEndpoint<'a> {
972 pub fn into_proxy(self) -> fnetemul_network::EndpointProxy {
979 let Self { endpoint, name: _, _sandbox: _ } = self;
980 endpoint
981 }
982
983 pub async fn get_netdevice(
988 &self,
989 ) -> Result<(fidl::endpoints::ClientEnd<fnetwork::DeviceMarker>, fnetwork::PortId)> {
990 let (port, server_end) = fidl::endpoints::create_endpoints();
991 self.get_port(server_end)
992 .with_context(|| format!("failed to get device connection for {}", self.name))?;
993 to_netdevice_inner(port).await
994 }
995
996 pub async fn install(
1002 &self,
1003 installer: fnet_interfaces_admin::InstallerProxy,
1004 InterfaceConfig { name, metric, dad_transmits }: InterfaceConfig<'_>,
1005 ) -> Result<(u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
1006 let name = name.map(|n| {
1007 truncate_dropping_front(n.into(), fnet_interfaces::INTERFACE_NAME_LENGTH.into())
1008 .to_string()
1009 });
1010 let (device, port_id) = self.get_netdevice().await?;
1011 let device_control = {
1012 let (control, server_end) =
1013 fidl::endpoints::create_proxy::<fnet_interfaces_admin::DeviceControlMarker>();
1014 let () = installer.install_device(device, server_end).context("install device")?;
1015 control
1016 };
1017 let (control, server_end) = Control::create_endpoints().context("create endpoints")?;
1018 let () = device_control
1019 .create_interface(
1020 &port_id,
1021 server_end,
1022 &fnet_interfaces_admin::Options { name, metric, ..Default::default() },
1023 )
1024 .context("create interface")?;
1025 if let Some(dad_transmits) = dad_transmits {
1026 let _: Option<u16> =
1027 set_dad_transmits(&control, dad_transmits).await.context("set dad transmits")?;
1028 }
1029
1030 let id = control.get_id().await.context("get id")?;
1031 Ok((id, control, device_control))
1032 }
1033
1034 pub async fn add_to_stack(
1039 &self,
1040 realm: &TestRealm<'a>,
1041 config: InterfaceConfig<'a>,
1042 ) -> Result<(u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
1043 let installer = realm
1044 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
1045 .context("connect to protocol")?;
1046
1047 self.install(installer, config).await
1048 }
1049
1050 pub async fn into_interface_in_realm(self, realm: &TestRealm<'a>) -> Result<TestInterface<'a>> {
1052 self.into_interface_in_realm_with_name(realm, Default::default()).await
1053 }
1054
1055 pub async fn into_interface_in_realm_with_name(
1058 self,
1059 realm: &TestRealm<'a>,
1060 config: InterfaceConfig<'a>,
1061 ) -> Result<TestInterface<'a>> {
1062 let installer = realm
1063 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
1064 .context("connect to protocol")?;
1065
1066 let (id, control, device_control) =
1067 self.install(installer, config).await.context("failed to install")?;
1068
1069 Ok(TestInterface {
1070 endpoint: self,
1071 id,
1072 realm: realm.clone(),
1073 control,
1074 device_control: Some(device_control),
1075 dhcp_client_task: futures::lock::Mutex::default(),
1076 })
1077 }
1078}
1079
1080#[derive(Copy, Clone, PartialEq, Debug)]
1082pub enum DhcpClientVersion {
1083 InStack,
1085 OutOfStack,
1087}
1088
1089pub trait DhcpClient {
1091 const DHCP_CLIENT_VERSION: DhcpClientVersion;
1093}
1094
1095pub enum InStack {}
1097
1098impl DhcpClient for InStack {
1099 const DHCP_CLIENT_VERSION: DhcpClientVersion = DhcpClientVersion::InStack;
1100}
1101
1102pub enum OutOfStack {}
1104
1105impl DhcpClient for OutOfStack {
1106 const DHCP_CLIENT_VERSION: DhcpClientVersion = DhcpClientVersion::OutOfStack;
1107}
1108
1109#[must_use]
1115pub struct TestInterface<'a> {
1116 endpoint: TestEndpoint<'a>,
1117 realm: TestRealm<'a>,
1118 id: u64,
1119 control: Control,
1120 device_control: Option<fnet_interfaces_admin::DeviceControlProxy>,
1121 dhcp_client_task: futures::lock::Mutex<Option<fnet_dhcp_ext::testutil::DhcpClientTask>>,
1122}
1123
1124impl<'a> std::fmt::Debug for TestInterface<'a> {
1125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
1126 let Self { endpoint, id, realm: _, control: _, device_control: _, dhcp_client_task: _ } =
1127 self;
1128 f.debug_struct("TestInterface")
1129 .field("endpoint", endpoint)
1130 .field("id", id)
1131 .finish_non_exhaustive()
1132 }
1133}
1134
1135impl<'a> std::ops::Deref for TestInterface<'a> {
1136 type Target = fnetemul_network::EndpointProxy;
1137
1138 fn deref(&self) -> &Self::Target {
1139 &self.endpoint
1140 }
1141}
1142
1143impl<'a> TestInterface<'a> {
1144 pub fn id(&self) -> u64 {
1146 self.id
1147 }
1148
1149 pub fn endpoint(&self) -> &TestEndpoint<'a> {
1151 &self.endpoint
1152 }
1153
1154 pub fn control(&self) -> &Control {
1156 &self.control
1157 }
1158
1159 pub async fn get_authorization(&self) -> Result<GrantForInterfaceAuthorization> {
1161 Ok(self.control.get_authorization_for_interface().await?)
1162 }
1163
1164 pub fn connect_stack(&self) -> Result<fnet_stack::StackProxy> {
1166 self.realm.connect_to_protocol::<fnet_stack::StackMarker>()
1167 }
1168
1169 async fn add_route<
1174 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1175 >(
1176 &self,
1177 destination: Subnet<I::Addr>,
1178 next_hop: Option<SpecifiedAddr<I::Addr>>,
1179 metric: fnet_routes::SpecifiedMetric,
1180 ) -> Result<bool> {
1181 let route_set = self.create_authenticated_global_route_set::<I>().await?;
1182 fnet_routes_ext::admin::add_route::<I>(
1183 &route_set,
1184 &fnet_routes_ext::Route::<I>::new_forward(destination, self.id(), next_hop, metric)
1185 .try_into()
1186 .expect("convert to FIDL should succeed"),
1187 )
1188 .await
1189 .context("FIDL error adding route")?
1190 .map_err(|e| anyhow::anyhow!("error adding route: {e:?}"))
1191 }
1192
1193 pub async fn add_route_either(
1199 &self,
1200 destination: fnet::Subnet,
1201 next_hop: Option<fnet::IpAddress>,
1202 metric: fnet_routes::SpecifiedMetric,
1203 ) -> Result<bool> {
1204 let fnet::Subnet { addr: destination_addr, prefix_len } = destination;
1205 match destination_addr {
1206 fnet::IpAddress::Ipv4(destination_addr) => {
1207 let next_hop = match next_hop {
1208 Some(fnet::IpAddress::Ipv4(next_hop)) => Some(
1209 SpecifiedAddr::new(net_types::ip::Ipv4Addr::from_ext(next_hop))
1210 .ok_or(anyhow::anyhow!("next hop must not be unspecified address"))?,
1211 ),
1212 Some(fnet::IpAddress::Ipv6(_)) => {
1213 return Err(anyhow::anyhow!(
1214 "next hop must be same IP version as destination"
1215 ))
1216 }
1217 None => None,
1218 };
1219 self.add_route::<Ipv4>(
1220 Subnet::new(destination_addr.into_ext(), prefix_len)
1221 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1222 next_hop,
1223 metric,
1224 )
1225 .await
1226 }
1227 fnet::IpAddress::Ipv6(destination_addr) => {
1228 let next_hop = match next_hop {
1229 Some(fnet::IpAddress::Ipv6(next_hop)) => Some(
1230 SpecifiedAddr::new(net_types::ip::Ipv6Addr::from_ext(next_hop))
1231 .ok_or(anyhow::anyhow!("next hop must not be unspecified address"))?,
1232 ),
1233 Some(fnet::IpAddress::Ipv4(_)) => {
1234 return Err(anyhow::anyhow!(
1235 "next hop must be same IP version as destination"
1236 ))
1237 }
1238 None => None,
1239 };
1240 self.add_route::<Ipv6>(
1241 Subnet::new(destination_addr.into_ext(), prefix_len)
1242 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1243 next_hop,
1244 metric,
1245 )
1246 .await
1247 }
1248 }
1249 }
1250
1251 async fn remove_route<
1256 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1257 >(
1258 &self,
1259 destination: Subnet<I::Addr>,
1260 next_hop: Option<SpecifiedAddr<I::Addr>>,
1261 metric: fnet_routes::SpecifiedMetric,
1262 ) -> Result<bool> {
1263 let route_set = self.create_authenticated_global_route_set::<I>().await?;
1264 fnet_routes_ext::admin::remove_route::<I>(
1265 &route_set,
1266 &fnet_routes_ext::Route::<I>::new_forward(destination, self.id(), next_hop, metric)
1267 .try_into()
1268 .expect("convert to FIDL should succeed"),
1269 )
1270 .await
1271 .context("FIDL error removing route")?
1272 .map_err(|e| anyhow::anyhow!("error removing route: {e:?}"))
1273 }
1274
1275 async fn remove_route_either(
1281 &self,
1282 destination: fnet::Subnet,
1283 next_hop: Option<fnet::IpAddress>,
1284 metric: fnet_routes::SpecifiedMetric,
1285 ) -> Result<bool> {
1286 let fnet::Subnet { addr: destination_addr, prefix_len } = destination;
1287 match destination_addr {
1288 fnet::IpAddress::Ipv4(destination_addr) => {
1289 let next_hop = match next_hop {
1290 Some(fnet::IpAddress::Ipv4(next_hop)) => Some(
1291 SpecifiedAddr::new(net_types::ip::Ipv4Addr::from_ext(next_hop))
1292 .ok_or(anyhow::anyhow!("next hop must not be unspecified address"))?,
1293 ),
1294 Some(fnet::IpAddress::Ipv6(_)) => {
1295 return Err(anyhow::anyhow!(
1296 "next hop must be same IP version as destination"
1297 ))
1298 }
1299 None => None,
1300 };
1301 self.remove_route::<Ipv4>(
1302 Subnet::new(destination_addr.into_ext(), prefix_len)
1303 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1304 next_hop,
1305 metric,
1306 )
1307 .await
1308 }
1309 fnet::IpAddress::Ipv6(destination_addr) => {
1310 let next_hop = match next_hop {
1311 Some(fnet::IpAddress::Ipv6(next_hop)) => Some(
1312 SpecifiedAddr::new(net_types::ip::Ipv6Addr::from_ext(next_hop))
1313 .ok_or(anyhow::anyhow!("next hop must not be unspecified address"))?,
1314 ),
1315 Some(fnet::IpAddress::Ipv4(_)) => {
1316 return Err(anyhow::anyhow!(
1317 "next hop must be same IP version as destination"
1318 ))
1319 }
1320 None => None,
1321 };
1322 self.remove_route::<Ipv6>(
1323 Subnet::new(destination_addr.into_ext(), prefix_len)
1324 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1325 next_hop,
1326 metric,
1327 )
1328 .await
1329 }
1330 }
1331 }
1332
1333 pub async fn add_subnet_route(&self, subnet: fnet::Subnet) -> Result<()> {
1335 let subnet = fnet_ext::apply_subnet_mask(subnet);
1336 let newly_added = self
1337 .add_route_either(
1338 subnet,
1339 None,
1340 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1341 )
1342 .await?;
1343
1344 if !newly_added {
1345 Err(anyhow::anyhow!(
1346 "route to {subnet:?} on {} should not have already existed",
1347 self.id()
1348 ))
1349 } else {
1350 Ok(())
1351 }
1352 }
1353
1354 pub async fn del_subnet_route(&self, subnet: fnet::Subnet) -> Result<()> {
1356 let subnet = fnet_ext::apply_subnet_mask(subnet);
1357 let newly_removed = self
1358 .remove_route_either(
1359 subnet,
1360 None,
1361 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1362 )
1363 .await?;
1364
1365 if !newly_removed {
1366 Err(anyhow::anyhow!(
1367 "route to {subnet:?} on {} should have previously existed before being removed",
1368 self.id()
1369 ))
1370 } else {
1371 Ok(())
1372 }
1373 }
1374
1375 pub async fn add_default_route_with_metric(
1377 &self,
1378 next_hop: fnet::IpAddress,
1379 metric: fnet_routes::SpecifiedMetric,
1380 ) -> Result<()> {
1381 let corresponding_default_subnet = match next_hop {
1382 fnet::IpAddress::Ipv4(_) => net_declare::fidl_subnet!("0.0.0.0/0"),
1383 fnet::IpAddress::Ipv6(_) => net_declare::fidl_subnet!("::/0"),
1384 };
1385
1386 let newly_added =
1387 self.add_route_either(corresponding_default_subnet, Some(next_hop), metric).await?;
1388
1389 if !newly_added {
1390 Err(anyhow::anyhow!(
1391 "default route through {} via {next_hop:?} already exists",
1392 self.id()
1393 ))
1394 } else {
1395 Ok(())
1396 }
1397 }
1398
1399 pub async fn add_default_route_with_explicit_metric(
1401 &self,
1402 next_hop: fnet::IpAddress,
1403 metric: u32,
1404 ) -> Result<()> {
1405 self.add_default_route_with_metric(
1406 next_hop,
1407 fnet_routes::SpecifiedMetric::ExplicitMetric(metric),
1408 )
1409 .await
1410 }
1411
1412 pub async fn add_default_route(&self, next_hop: fnet::IpAddress) -> Result<()> {
1414 self.add_default_route_with_metric(
1415 next_hop,
1416 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1417 )
1418 .await
1419 }
1420
1421 pub async fn remove_default_route(&self, next_hop: fnet::IpAddress) -> Result<()> {
1423 let corresponding_default_subnet = match next_hop {
1424 fnet::IpAddress::Ipv4(_) => net_declare::fidl_subnet!("0.0.0.0/0"),
1425 fnet::IpAddress::Ipv6(_) => net_declare::fidl_subnet!("::/0"),
1426 };
1427
1428 let newly_removed = self
1429 .remove_route_either(
1430 corresponding_default_subnet,
1431 Some(next_hop),
1432 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1433 )
1434 .await?;
1435
1436 if !newly_removed {
1437 Err(anyhow::anyhow!(
1438 "default route through {} via {next_hop:?} does not exist",
1439 self.id()
1440 ))
1441 } else {
1442 Ok(())
1443 }
1444 }
1445
1446 pub async fn add_gateway_route(
1448 &self,
1449 destination: fnet::Subnet,
1450 next_hop: fnet::IpAddress,
1451 ) -> Result<()> {
1452 let newly_added = self
1453 .add_route_either(
1454 destination,
1455 Some(next_hop),
1456 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1457 )
1458 .await?;
1459
1460 if !newly_added {
1461 Err(anyhow::anyhow!(
1462 "should have newly added route to {destination:?} via {next_hop:?} through {}",
1463 self.id()
1464 ))
1465 } else {
1466 Ok(())
1467 }
1468 }
1469
1470 pub async fn create_authenticated_global_route_set<
1472 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1473 >(
1474 &self,
1475 ) -> Result<<I::RouteSetMarker as ProtocolMarker>::Proxy> {
1476 #[derive(GenericOverIp)]
1477 #[generic_over_ip(I, Ip)]
1478 struct Out<'a, I: fnet_routes_ext::admin::FidlRouteAdminIpExt>(
1479 LocalBoxFuture<'a, <I::RouteSetMarker as ProtocolMarker>::Proxy>,
1480 );
1481
1482 let Out(proxy_fut) = I::map_ip_out(
1483 self,
1484 |this| {
1485 Out(this
1486 .get_global_route_set_v4()
1487 .map(|result| result.expect("get global route set"))
1488 .boxed_local())
1489 },
1490 |this| {
1491 Out(this
1492 .get_global_route_set_v6()
1493 .map(|result| result.expect("get global route set"))
1494 .boxed_local())
1495 },
1496 );
1497
1498 let route_set = proxy_fut.await;
1499 let fnet_interfaces_admin::GrantForInterfaceAuthorization { interface_id, token } =
1500 self.get_authorization().await.expect("get interface grant");
1501 fnet_routes_ext::admin::authenticate_for_interface::<I>(
1502 &route_set,
1503 fnet_interfaces_admin::ProofOfInterfaceAuthorization { interface_id, token },
1504 )
1505 .await
1506 .expect("authentication should not have FIDL error")
1507 .expect("authentication should succeed");
1508 Ok(route_set)
1509 }
1510
1511 async fn get_global_route_set_v4(&self) -> Result<fnet_routes_admin::RouteSetV4Proxy> {
1512 let root_routes = self
1513 .realm
1514 .connect_to_protocol::<fnet_root::RoutesV4Marker>()
1515 .expect("get fuchsia.net.root.RoutesV4");
1516 let (route_set, server_end) =
1517 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteSetV4Marker>();
1518 root_routes.global_route_set(server_end).expect("calling global_route_set should succeed");
1519 Ok(route_set)
1520 }
1521
1522 async fn get_global_route_set_v6(&self) -> Result<fnet_routes_admin::RouteSetV6Proxy> {
1523 let root_routes = self
1524 .realm
1525 .connect_to_protocol::<fnet_root::RoutesV6Marker>()
1526 .expect("get fuchsia.net.root.RoutesV6");
1527 let (route_set, server_end) =
1528 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteSetV6Marker>();
1529 root_routes.global_route_set(server_end).expect("calling global_route_set should succeed");
1530 Ok(route_set)
1531 }
1532
1533 async fn get_properties(
1535 &self,
1536 included_addresses: fnet_interfaces_ext::IncludedAddresses,
1537 ) -> Result<fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>> {
1538 let interface_state = self.realm.connect_to_protocol::<fnet_interfaces::StateMarker>()?;
1539 let properties = fnet_interfaces_ext::existing(
1540 fnet_interfaces_ext::event_stream_from_state(&interface_state, included_addresses)?,
1541 fnet_interfaces_ext::InterfaceState::<(), _>::Unknown(self.id),
1542 )
1543 .await
1544 .context("failed to get existing interfaces")?;
1545 match properties {
1546 fnet_interfaces_ext::InterfaceState::Unknown(id) => Err(anyhow::anyhow!(
1547 "could not find interface {} for endpoint {}",
1548 id,
1549 self.endpoint.name
1550 )),
1551 fnet_interfaces_ext::InterfaceState::Known(
1552 fnet_interfaces_ext::PropertiesAndState { properties, state: () },
1553 ) => Ok(properties),
1554 }
1555 }
1556
1557 pub async fn get_addrs(
1559 &self,
1560 included_addresses: fnet_interfaces_ext::IncludedAddresses,
1561 ) -> Result<Vec<fnet_interfaces_ext::Address<fnet_interfaces_ext::AllInterest>>> {
1562 let fnet_interfaces_ext::Properties { addresses, .. } =
1563 self.get_properties(included_addresses).await?;
1564 Ok(addresses)
1565 }
1566
1567 pub async fn get_interface_name(&self) -> Result<String> {
1569 let fnet_interfaces_ext::Properties { name, .. } =
1570 self.get_properties(fnet_interfaces_ext::IncludedAddresses::OnlyAssigned).await?;
1571 Ok(name)
1572 }
1573
1574 pub async fn get_port_class(&self) -> Result<fnet_interfaces_ext::PortClass> {
1576 let fnet_interfaces_ext::Properties { port_class, .. } =
1577 self.get_properties(fnet_interfaces_ext::IncludedAddresses::OnlyAssigned).await?;
1578 Ok(port_class)
1579 }
1580
1581 pub async fn mac(&self) -> fnet::MacAddress {
1583 let (port, server_end) =
1584 fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_network::PortMarker>();
1585 self.get_port(server_end).expect("get_port");
1586 let (mac_addressing, server_end) =
1587 fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_network::MacAddressingMarker>();
1588 port.get_mac(server_end).expect("get_mac");
1589 mac_addressing.get_unicast_address().await.expect("get_unicast_address")
1590 }
1591
1592 async fn set_dhcp_client_enabled(&self, enable: bool) -> Result<()> {
1593 self.connect_stack()
1594 .context("connect stack")?
1595 .set_dhcp_client_enabled(self.id, enable)
1596 .await
1597 .context("failed to call SetDhcpClientEnabled")?
1598 .map_err(|e| anyhow!("{:?}", e))
1599 }
1600
1601 pub async fn start_dhcp<D: DhcpClient>(&self) -> Result<()> {
1603 match D::DHCP_CLIENT_VERSION {
1604 DhcpClientVersion::InStack => self.start_dhcp_in_stack().await,
1605 DhcpClientVersion::OutOfStack => self.start_dhcp_client_out_of_stack().await,
1606 }
1607 }
1608
1609 async fn start_dhcp_in_stack(&self) -> Result<()> {
1610 self.set_dhcp_client_enabled(true).await.context("failed to start dhcp client")
1611 }
1612
1613 async fn start_dhcp_client_out_of_stack(&self) -> Result<()> {
1614 let Self { endpoint: _, realm, id, control, device_control: _, dhcp_client_task } = self;
1615 let id = NonZeroU64::new(*id).expect("interface ID should be nonzero");
1616 let mut dhcp_client_task = dhcp_client_task.lock().await;
1617 let dhcp_client_task = dhcp_client_task.deref_mut();
1618
1619 let provider = realm
1620 .connect_to_protocol::<fnet_dhcp::ClientProviderMarker>()
1621 .expect("get fuchsia.net.dhcp.ClientProvider");
1622
1623 provider.check_presence().await.expect("check presence should succeed");
1624
1625 let client = provider.new_client_ext(id, fnet_dhcp_ext::default_new_client_params());
1626 let control = control.clone();
1627 let route_set_provider = realm
1628 .connect_to_protocol::<fnet_routes_admin::RouteTableV4Marker>()
1629 .expect("get fuchsia.net.routes.RouteTableV4");
1630 let (route_set, server_end) =
1631 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteSetV4Marker>();
1632 route_set_provider.new_route_set(server_end).expect("calling new_route_set should succeed");
1633 let task = fnet_dhcp_ext::testutil::DhcpClientTask::new(client, id, route_set, control);
1634 *dhcp_client_task = Some(task);
1635 Ok(())
1636 }
1637
1638 pub async fn stop_dhcp<D: DhcpClient>(&self) -> Result<()> {
1640 match D::DHCP_CLIENT_VERSION {
1641 DhcpClientVersion::InStack => self.stop_dhcp_in_stack().await,
1642 DhcpClientVersion::OutOfStack => {
1643 self.stop_dhcp_out_of_stack().await;
1644 Ok(())
1645 }
1646 }
1647 }
1648
1649 async fn stop_dhcp_in_stack(&self) -> Result<()> {
1650 self.set_dhcp_client_enabled(false).await.context("failed to stop dhcp client")
1651 }
1652
1653 async fn stop_dhcp_out_of_stack(&self) {
1654 let Self { endpoint: _, realm: _, id: _, control: _, device_control: _, dhcp_client_task } =
1655 self;
1656 let mut dhcp_client_task = dhcp_client_task.lock().await;
1657 if let Some(task) = dhcp_client_task.deref_mut().take() {
1658 task.shutdown().await.expect("client shutdown should succeed");
1659 }
1660 }
1661
1662 pub async fn add_address(&self, subnet: fnet::Subnet) -> Result<()> {
1665 let (address_state_provider, server) =
1666 fidl::endpoints::create_proxy::<fnet_interfaces_admin::AddressStateProviderMarker>();
1667 let () = address_state_provider.detach().context("detach address lifetime")?;
1668 let () = self
1669 .control
1670 .add_address(&subnet, &fnet_interfaces_admin::AddressParameters::default(), server)
1671 .context("FIDL error")?;
1672
1673 let mut state_stream =
1674 fnet_interfaces_ext::admin::assignment_state_stream(address_state_provider);
1675 fnet_interfaces_ext::admin::wait_assignment_state(
1676 &mut state_stream,
1677 fnet_interfaces::AddressAssignmentState::Assigned,
1678 )
1679 .await?;
1680 Ok(())
1681 }
1682
1683 pub async fn add_address_and_subnet_route(&self, subnet: fnet::Subnet) -> Result<()> {
1686 let (address_state_provider, server) =
1687 fidl::endpoints::create_proxy::<fnet_interfaces_admin::AddressStateProviderMarker>();
1688 address_state_provider.detach().context("detach address lifetime")?;
1689 self.control
1690 .add_address(
1691 &subnet,
1692 &fnet_interfaces_admin::AddressParameters {
1693 add_subnet_route: Some(true),
1694 ..Default::default()
1695 },
1696 server,
1697 )
1698 .context("FIDL error")?;
1699
1700 let state_stream =
1701 fnet_interfaces_ext::admin::assignment_state_stream(address_state_provider);
1702 let mut state_stream = pin!(state_stream);
1703
1704 fnet_interfaces_ext::admin::wait_assignment_state(
1705 &mut state_stream,
1706 fnet_interfaces::AddressAssignmentState::Assigned,
1707 )
1708 .await
1709 .context("assignment state")?;
1710 Ok(())
1711 }
1712
1713 pub async fn del_address_and_subnet_route(
1715 &self,
1716 addr_with_prefix: fnet::Subnet,
1717 ) -> Result<bool> {
1718 let did_remove =
1719 self.control.remove_address(&addr_with_prefix).await.context("FIDL error").and_then(
1720 |res| {
1721 res.map_err(|e: fnet_interfaces_admin::ControlRemoveAddressError| {
1722 anyhow::anyhow!("{:?}", e)
1723 })
1724 },
1725 )?;
1726
1727 if did_remove {
1728 let destination = fnet_ext::apply_subnet_mask(addr_with_prefix);
1729 let newly_removed_route = self
1730 .remove_route_either(
1731 destination,
1732 None,
1733 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1734 )
1735 .await?;
1736
1737 let _: bool = newly_removed_route;
1740 }
1741 Ok(did_remove)
1742 }
1743
1744 pub async fn remove_ipv6_linklocal_addresses(
1748 &self,
1749 ) -> Result<Vec<fnet_interfaces_ext::Address<fnet_interfaces_ext::AllInterest>>> {
1750 let mut result = Vec::new();
1751 for address in self.get_addrs(fnet_interfaces_ext::IncludedAddresses::All).await? {
1752 let fnet_interfaces_ext::Address { addr: fnet::Subnet { addr, prefix_len }, .. } =
1753 &address;
1754 match addr {
1755 fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address { addr: _ }) => {
1756 continue
1757 }
1758 fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address { addr }) => {
1759 let v6_addr = net_types::ip::Ipv6Addr::from_bytes(*addr);
1760 if !v6_addr.is_unicast_link_local() {
1761 continue;
1762 }
1763 }
1764 }
1765 let _newly_removed: bool = self
1766 .del_address_and_subnet_route(fnet::Subnet { addr: *addr, prefix_len: *prefix_len })
1767 .await?;
1768 result.push(address);
1769 }
1770 Ok(result)
1771 }
1772
1773 async fn set_configuration(&self, config: fnet_interfaces_admin::Configuration) -> Result<()> {
1783 let fnet_interfaces_admin::Configuration {
1784 ipv4: previous_ipv4, ipv6: previous_ipv6, ..
1785 } = self
1786 .control()
1787 .set_configuration(&config.clone())
1788 .await
1789 .context("FIDL error")?
1790 .map_err(|e| anyhow!("set configuration error: {:?}", e))?;
1791
1792 fn verify_config_changed<T: Eq>(previous: Option<T>, current: Option<T>) -> Result<()> {
1793 if let Some(current) = current {
1794 let previous = previous.ok_or_else(|| anyhow!("configuration not supported"))?;
1795 if previous == current {
1796 return Err(anyhow!("configuration change is a no-op"));
1797 }
1798 }
1799 Ok(())
1800 }
1801
1802 let fnet_interfaces_admin::Configuration { ipv4, ipv6, .. } = config;
1803 if let Some(fnet_interfaces_admin::Ipv4Configuration {
1804 unicast_forwarding,
1805 multicast_forwarding,
1806 ..
1807 }) = ipv4
1808 {
1809 let fnet_interfaces_admin::Ipv4Configuration {
1810 unicast_forwarding: previous_unicast_forwarding,
1811 multicast_forwarding: previous_multicast_forwarding,
1812 ..
1813 } = previous_ipv4.ok_or_else(|| anyhow!("IPv4 configuration not supported"))?;
1814 verify_config_changed(previous_unicast_forwarding, unicast_forwarding)
1815 .context("IPv4 unicast forwarding")?;
1816 verify_config_changed(previous_multicast_forwarding, multicast_forwarding)
1817 .context("IPv4 multicast forwarding")?;
1818 }
1819 if let Some(fnet_interfaces_admin::Ipv6Configuration {
1820 unicast_forwarding,
1821 multicast_forwarding,
1822 ..
1823 }) = ipv6
1824 {
1825 let fnet_interfaces_admin::Ipv6Configuration {
1826 unicast_forwarding: previous_unicast_forwarding,
1827 multicast_forwarding: previous_multicast_forwarding,
1828 ..
1829 } = previous_ipv6.ok_or_else(|| anyhow!("IPv6 configuration not supported"))?;
1830 verify_config_changed(previous_unicast_forwarding, unicast_forwarding)
1831 .context("IPv6 unicast forwarding")?;
1832 verify_config_changed(previous_multicast_forwarding, multicast_forwarding)
1833 .context("IPv6 multicast forwarding")?;
1834 }
1835 Ok(())
1836 }
1837
1838 pub async fn set_ipv6_forwarding_enabled(&self, enabled: bool) -> Result<()> {
1840 self.set_configuration(fnet_interfaces_admin::Configuration {
1841 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
1842 unicast_forwarding: Some(enabled),
1843 ..Default::default()
1844 }),
1845 ..Default::default()
1846 })
1847 .await
1848 }
1849
1850 pub async fn set_ipv4_forwarding_enabled(&self, enabled: bool) -> Result<()> {
1852 self.set_configuration(fnet_interfaces_admin::Configuration {
1853 ipv4: Some(fnet_interfaces_admin::Ipv4Configuration {
1854 unicast_forwarding: Some(enabled),
1855 ..Default::default()
1856 }),
1857 ..Default::default()
1858 })
1859 .await
1860 }
1861
1862 pub async fn remove(
1865 self,
1866 ) -> Result<(fnetemul_network::EndpointProxy, Option<fnet_interfaces_admin::DeviceControlProxy>)>
1867 {
1868 let Self {
1869 endpoint: TestEndpoint { endpoint, name: _, _sandbox: _ },
1870 id: _,
1871 realm: _,
1872 control,
1873 device_control,
1874 dhcp_client_task: _,
1875 } = self;
1876 std::mem::drop(control);
1880 Ok((endpoint, device_control))
1881 }
1882
1883 pub fn remove_device(self) -> (Control, Option<fnet_interfaces_admin::DeviceControlProxy>) {
1887 let Self {
1888 endpoint: TestEndpoint { endpoint, name: _, _sandbox: _ },
1889 id: _,
1890 realm: _,
1891 control,
1892 device_control,
1893 dhcp_client_task: _,
1894 } = self;
1895 std::mem::drop(endpoint);
1896 (control, device_control)
1897 }
1898
1899 pub async fn wait_removal(self) -> Result<fnet_interfaces_admin::InterfaceRemovedReason> {
1901 let Self {
1902 endpoint: _endpoint,
1904 id: _,
1905 realm: _,
1906 control,
1907 dhcp_client_task: _,
1908 device_control: _device_control,
1910 } = self;
1911 match control.wait_termination().await {
1912 fnet_interfaces_ext::admin::TerminalError::Fidl(e) => {
1913 Err(e).context("waiting interface control termination")
1914 }
1915 fnet_interfaces_ext::admin::TerminalError::Terminal(reason) => Ok(reason),
1916 }
1917 }
1918
1919 pub async fn set_dad_transmits(&self, dad_transmits: u16) -> Result<Option<u16>> {
1923 set_dad_transmits(self.control(), dad_transmits).await
1924 }
1925
1926 pub async fn set_temporary_address_generation_enabled(&self, enabled: bool) -> Result<()> {
1929 self.set_configuration(fnet_interfaces_admin::Configuration {
1930 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
1931 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
1932 slaac: Some(fnet_interfaces_admin::SlaacConfiguration {
1933 temporary_address: Some(enabled),
1934 ..Default::default()
1935 }),
1936 ..Default::default()
1937 }),
1938 ..Default::default()
1939 }),
1940 ..Default::default()
1941 })
1942 .await
1943 }
1944}
1945
1946async fn set_dad_transmits(control: &Control, dad_transmits: u16) -> Result<Option<u16>> {
1947 control
1948 .set_configuration(&fnet_interfaces_admin::Configuration {
1949 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
1950 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
1951 dad: Some(fnet_interfaces_admin::DadConfiguration {
1952 transmits: Some(dad_transmits),
1953 ..Default::default()
1954 }),
1955 ..Default::default()
1956 }),
1957 ..Default::default()
1958 }),
1959 ..Default::default()
1960 })
1961 .await?
1962 .map(|config| config.ipv6?.ndp?.dad?.transmits)
1963 .map_err(|e| anyhow::anyhow!("set configuration error {e:?}"))
1964}
1965
1966fn get_socket2_domain(addr: &std::net::SocketAddr) -> fposix_socket::Domain {
1968 let domain = match addr {
1969 std::net::SocketAddr::V4(_) => fposix_socket::Domain::Ipv4,
1970 std::net::SocketAddr::V6(_) => fposix_socket::Domain::Ipv6,
1971 };
1972
1973 domain
1974}
1975
1976pub trait RealmUdpSocket: Sized {
1978 fn bind_in_realm<'a>(
1980 realm: &'a TestRealm<'a>,
1981 addr: std::net::SocketAddr,
1982 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
1983}
1984
1985impl RealmUdpSocket for std::net::UdpSocket {
1986 fn bind_in_realm<'a>(
1987 realm: &'a TestRealm<'a>,
1988 addr: std::net::SocketAddr,
1989 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
1990 async move {
1991 let sock = realm
1992 .datagram_socket(
1993 get_socket2_domain(&addr),
1994 fposix_socket::DatagramSocketProtocol::Udp,
1995 )
1996 .await
1997 .context("failed to create socket")?;
1998
1999 let () = sock.bind(&addr.into()).context("bind failed")?;
2000
2001 Result::Ok(sock.into())
2002 }
2003 .boxed_local()
2004 }
2005}
2006
2007impl RealmUdpSocket for fuchsia_async::net::UdpSocket {
2008 fn bind_in_realm<'a>(
2009 realm: &'a TestRealm<'a>,
2010 addr: std::net::SocketAddr,
2011 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2012 std::net::UdpSocket::bind_in_realm(realm, addr)
2013 .and_then(|udp| {
2014 futures::future::ready(
2015 fuchsia_async::net::UdpSocket::from_socket(udp)
2016 .context("failed to create fuchsia_async socket"),
2017 )
2018 })
2019 .boxed_local()
2020 }
2021}
2022
2023pub trait RealmTcpListener: Sized {
2025 fn listen_in_realm<'a>(
2027 realm: &'a TestRealm<'a>,
2028 addr: std::net::SocketAddr,
2029 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2030 Self::listen_in_realm_with(realm, addr, |_: &socket2::Socket| Ok(()))
2031 }
2032
2033 fn listen_in_realm_with<'a>(
2036 realm: &'a TestRealm<'a>,
2037 addr: std::net::SocketAddr,
2038 setup: impl FnOnce(&socket2::Socket) -> Result<()> + 'a,
2039 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2040}
2041
2042impl RealmTcpListener for std::net::TcpListener {
2043 fn listen_in_realm_with<'a>(
2044 realm: &'a TestRealm<'a>,
2045 addr: std::net::SocketAddr,
2046 setup: impl FnOnce(&socket2::Socket) -> Result<()> + 'a,
2047 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2048 async move {
2049 let sock = realm
2050 .stream_socket(get_socket2_domain(&addr), fposix_socket::StreamSocketProtocol::Tcp)
2051 .await
2052 .context("failed to create server socket")?;
2053 let () = setup(&sock)?;
2054 let () = sock.bind(&addr.into()).context("failed to bind server socket")?;
2055 let () = sock.listen(128).context("failed to listen on server socket")?;
2058
2059 Result::Ok(sock.into())
2060 }
2061 .boxed_local()
2062 }
2063}
2064
2065impl RealmTcpListener for fuchsia_async::net::TcpListener {
2066 fn listen_in_realm_with<'a>(
2067 realm: &'a TestRealm<'a>,
2068 addr: std::net::SocketAddr,
2069 setup: impl FnOnce(&socket2::Socket) -> Result<()> + 'a,
2070 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2071 std::net::TcpListener::listen_in_realm_with(realm, addr, setup)
2072 .and_then(|listener| {
2073 futures::future::ready(
2074 fuchsia_async::net::TcpListener::from_std(listener)
2075 .context("failed to create fuchsia_async socket"),
2076 )
2077 })
2078 .boxed_local()
2079 }
2080}
2081
2082pub trait RealmTcpStream: Sized {
2084 fn connect_in_realm<'a>(
2086 realm: &'a TestRealm<'a>,
2087 addr: std::net::SocketAddr,
2088 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2089
2090 fn bind_and_connect_in_realm<'a>(
2092 realm: &'a TestRealm<'a>,
2093 local: std::net::SocketAddr,
2094 dst: std::net::SocketAddr,
2095 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2096
2097 fn connect_in_realm_with_sock<'a, F: FnOnce(&socket2::Socket) -> Result + 'a>(
2102 realm: &'a TestRealm<'a>,
2103 dst: std::net::SocketAddr,
2104 with_sock: F,
2105 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2106
2107 }
2109
2110impl RealmTcpStream for fuchsia_async::net::TcpStream {
2111 fn connect_in_realm<'a>(
2112 realm: &'a TestRealm<'a>,
2113 addr: std::net::SocketAddr,
2114 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2115 Self::connect_in_realm_with_sock(realm, addr, |_: &socket2::Socket| Ok(()))
2116 }
2117
2118 fn bind_and_connect_in_realm<'a>(
2119 realm: &'a TestRealm<'a>,
2120 local: std::net::SocketAddr,
2121 dst: std::net::SocketAddr,
2122 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2123 Self::connect_in_realm_with_sock(realm, dst, move |sock| {
2124 sock.bind(&local.into()).context("failed to bind")
2125 })
2126 }
2127
2128 fn connect_in_realm_with_sock<'a, F: FnOnce(&socket2::Socket) -> Result + 'a>(
2129 realm: &'a TestRealm<'a>,
2130 dst: std::net::SocketAddr,
2131 with_sock: F,
2132 ) -> futures::future::LocalBoxFuture<'a, Result<fuchsia_async::net::TcpStream>> {
2133 async move {
2134 let sock = realm
2135 .stream_socket(get_socket2_domain(&dst), fposix_socket::StreamSocketProtocol::Tcp)
2136 .await
2137 .context("failed to create socket")?;
2138
2139 with_sock(&sock)?;
2140
2141 let stream = fuchsia_async::net::TcpStream::connect_from_raw(sock, dst)
2142 .context("failed to create client tcp stream")?
2143 .await
2144 .context("failed to connect to server")?;
2145
2146 Result::Ok(stream)
2147 }
2148 .boxed_local()
2149 }
2150}
2151
2152fn truncate_dropping_front(s: Cow<'_, str>, len: usize) -> Cow<'_, str> {
2153 match s.len().checked_sub(len) {
2154 None => s,
2155 Some(start) => {
2156 match s {
2160 Cow::Borrowed(s) => Cow::Borrowed(&s[start..]),
2161 Cow::Owned(mut s) => {
2162 let _: std::string::Drain<'_> = s.drain(..start);
2163 Cow::Owned(s)
2164 }
2165 }
2166 }
2167 }
2168}