1#![warn(missing_docs, unreachable_patterns)]
6
7pub mod guest;
11
12use std::borrow::Cow;
13use std::collections::HashSet;
14use std::num::NonZeroU64;
15use std::ops::DerefMut as _;
16use std::path::Path;
17use std::pin::pin;
18use std::sync::{Arc, Mutex};
19
20use fidl::endpoints::{ProtocolMarker, Proxy as _};
21use fidl_fuchsia_net_dhcp_ext::{self as fnet_dhcp_ext, ClientProviderExt};
22use fidl_fuchsia_net_ext::{self as fnet_ext};
23use fidl_fuchsia_net_interfaces_ext::admin::Control;
24use fidl_fuchsia_net_interfaces_ext::{self as fnet_interfaces_ext};
25use fnet_ext::{FromExt as _, IntoExt as _};
26use fnet_interfaces_admin::GrantForInterfaceAuthorization;
27use zx::AsHandleRef;
28use {
29 fidl_fuchsia_hardware_network as fnetwork, fidl_fuchsia_io as fio, fidl_fuchsia_net as fnet,
30 fidl_fuchsia_net_dhcp as fnet_dhcp, fidl_fuchsia_net_interfaces as fnet_interfaces,
31 fidl_fuchsia_net_interfaces_admin as fnet_interfaces_admin,
32 fidl_fuchsia_net_neighbor as fnet_neighbor, fidl_fuchsia_net_root as fnet_root,
33 fidl_fuchsia_net_routes as fnet_routes, fidl_fuchsia_net_routes_admin as fnet_routes_admin,
34 fidl_fuchsia_net_routes_ext as fnet_routes_ext, fidl_fuchsia_net_stack as fnet_stack,
35 fidl_fuchsia_netemul as fnetemul, fidl_fuchsia_netemul_network as fnetemul_network,
36 fidl_fuchsia_posix_socket as fposix_socket, fidl_fuchsia_posix_socket_ext as fposix_socket_ext,
37 fidl_fuchsia_posix_socket_packet as fposix_socket_packet,
38 fidl_fuchsia_posix_socket_raw as fposix_socket_raw,
39};
40
41use anyhow::{anyhow, Context as _};
42use futures::future::{FutureExt as _, LocalBoxFuture, TryFutureExt as _};
43use futures::{SinkExt as _, TryStreamExt as _};
44use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv6, Subnet};
45use net_types::SpecifiedAddr;
46
47type Result<T = ()> = std::result::Result<T, anyhow::Error>;
48
49pub const DEFAULT_MTU: u16 = 1500;
51
52pub const NETDEVICE_DEVFS_PATH: &'static str = "class/network";
54
55pub fn devfs_device_path(node_name: &str) -> std::path::PathBuf {
57 std::path::Path::new(NETDEVICE_DEVFS_PATH).join(node_name)
58}
59
60pub fn new_endpoint_config(
62 mtu: u16,
63 mac: Option<fnet::MacAddress>,
64) -> fnetemul_network::EndpointConfig {
65 fnetemul_network::EndpointConfig {
66 mtu,
67 mac: mac.map(Box::new),
68 port_class: fnetwork::PortClass::Virtual,
69 }
70}
71
72#[must_use]
79pub struct TestSandbox {
80 sandbox: fnetemul::SandboxProxy,
81}
82
83impl TestSandbox {
84 pub fn new() -> Result<TestSandbox> {
86 fuchsia_component::client::connect_to_protocol::<fnetemul::SandboxMarker>()
87 .context("failed to connect to sandbox protocol")
88 .map(|sandbox| TestSandbox { sandbox })
89 }
90
91 pub fn create_realm<'a, I>(
93 &'a self,
94 name: impl Into<Cow<'a, str>>,
95 children: I,
96 ) -> Result<TestRealm<'a>>
97 where
98 I: IntoIterator,
99 I::Item: Into<fnetemul::ChildDef>,
100 {
101 let (realm, server) = fidl::endpoints::create_proxy::<fnetemul::ManagedRealmMarker>();
102 let name = name.into();
103 let () = self.sandbox.create_realm(
104 server,
105 fnetemul::RealmOptions {
106 name: Some(name.clone().into_owned()),
107 children: Some(children.into_iter().map(Into::into).collect()),
108 ..Default::default()
109 },
110 )?;
111 Ok(TestRealm(Arc::new(TestRealmInner {
112 realm,
113 name,
114 _sandbox: self,
115 shutdown_on_drop: Mutex::new(ShutdownOnDropConfig {
116 enabled: true,
117 ignore_monikers: HashSet::new(),
118 }),
119 })))
120 }
121
122 pub fn create_empty_realm<'a>(
124 &'a self,
125 name: impl Into<Cow<'a, str>>,
126 ) -> Result<TestRealm<'a>> {
127 self.create_realm(name, std::iter::empty::<fnetemul::ChildDef>())
128 }
129
130 fn get_network_context(&self) -> Result<fnetemul_network::NetworkContextProxy> {
132 let (ctx, server) =
133 fidl::endpoints::create_proxy::<fnetemul_network::NetworkContextMarker>();
134 let () = self.sandbox.get_network_context(server)?;
135 Ok(ctx)
136 }
137
138 pub fn get_network_manager(&self) -> Result<fnetemul_network::NetworkManagerProxy> {
140 let ctx = self.get_network_context()?;
141 let (network_manager, server) =
142 fidl::endpoints::create_proxy::<fnetemul_network::NetworkManagerMarker>();
143 let () = ctx.get_network_manager(server)?;
144 Ok(network_manager)
145 }
146
147 pub fn get_endpoint_manager(&self) -> Result<fnetemul_network::EndpointManagerProxy> {
149 let ctx = self.get_network_context()?;
150 let (ep_manager, server) =
151 fidl::endpoints::create_proxy::<fnetemul_network::EndpointManagerMarker>();
152 let () = ctx.get_endpoint_manager(server)?;
153 Ok(ep_manager)
154 }
155
156 pub async fn create_network<'a>(
158 &'a self,
159 name: impl Into<Cow<'a, str>>,
160 ) -> Result<TestNetwork<'a>> {
161 let name = name.into();
162 let netm = self.get_network_manager()?;
163 let (status, network) = netm
164 .create_network(
165 &name,
166 &fnetemul_network::NetworkConfig {
167 latency: None,
168 packet_loss: None,
169 reorder: None,
170 ..Default::default()
171 },
172 )
173 .await
174 .context("create_network FIDL error")?;
175 let () = zx::Status::ok(status).context("create_network failed")?;
176 let network = network
177 .ok_or_else(|| anyhow::anyhow!("create_network didn't return a valid network"))?
178 .into_proxy();
179 Ok(TestNetwork { network, name, sandbox: self })
180 }
181
182 pub async fn setup_networks<'a>(
184 &'a self,
185 networks: Vec<fnetemul_network::NetworkSetup>,
186 ) -> Result<TestNetworkSetup<'a>> {
187 let ctx = self.get_network_context()?;
188 let (status, handle) = ctx.setup(&networks).await.context("setup FIDL error")?;
189 let () = zx::Status::ok(status).context("setup failed")?;
190 let handle = handle
191 .ok_or_else(|| anyhow::anyhow!("setup didn't return a valid handle"))?
192 .into_proxy();
193 Ok(TestNetworkSetup { _setup: handle, _sandbox: self })
194 }
195
196 pub async fn create_endpoint<'a, S>(&'a self, name: S) -> Result<TestEndpoint<'a>>
200 where
201 S: Into<Cow<'a, str>>,
202 {
203 self.create_endpoint_with(name, new_endpoint_config(DEFAULT_MTU, None)).await
204 }
205
206 pub async fn create_endpoint_with<'a>(
210 &'a self,
211 name: impl Into<Cow<'a, str>>,
212 config: fnetemul_network::EndpointConfig,
213 ) -> Result<TestEndpoint<'a>> {
214 let name = name.into();
215 let epm = self.get_endpoint_manager()?;
216 let (status, endpoint) =
217 epm.create_endpoint(&name, &config).await.context("create_endpoint FIDL error")?;
218 let () = zx::Status::ok(status).context("create_endpoint failed")?;
219 let endpoint = endpoint
220 .ok_or_else(|| anyhow::anyhow!("create_endpoint didn't return a valid endpoint"))?
221 .into_proxy();
222 Ok(TestEndpoint { endpoint, name, _sandbox: self })
223 }
224}
225
226#[must_use]
230pub struct TestNetworkSetup<'a> {
231 _setup: fnetemul_network::SetupHandleProxy,
232 _sandbox: &'a TestSandbox,
233}
234
235impl TestNetworkSetup<'_> {
236 pub fn into_proxy(self) -> fnetemul_network::SetupHandleProxy {
243 let Self { _setup, _sandbox: _ } = self;
244 _setup
245 }
246}
247
248#[derive(Default)]
250pub struct InterfaceConfig<'a> {
251 pub name: Option<Cow<'a, str>>,
253 pub metric: Option<u32>,
255 pub ipv4_dad_transmits: Option<u16>,
258 pub ipv6_dad_transmits: Option<u16>,
261}
262
263#[derive(Debug)]
264struct ShutdownOnDropConfig {
265 enabled: bool,
266 ignore_monikers: HashSet<String>,
267}
268
269struct TestRealmInner<'a> {
270 realm: fnetemul::ManagedRealmProxy,
271 name: Cow<'a, str>,
272 _sandbox: &'a TestSandbox,
273 shutdown_on_drop: Mutex<ShutdownOnDropConfig>,
274}
275
276impl Drop for TestRealmInner<'_> {
277 fn drop(&mut self) {
278 let ShutdownOnDropConfig { enabled, ignore_monikers } =
279 self.shutdown_on_drop.get_mut().unwrap();
280 if !*enabled {
281 return;
282 }
283 let ignore_monikers = std::mem::take(ignore_monikers);
284 let mut crashed = match self.shutdown_sync() {
285 Ok(crashed) => crashed,
286 Err(e) => {
287 if !e.is_closed() {
291 panic!("error verifying clean shutdown on test realm {}: {:?}", self.name, e);
292 }
293 return;
294 }
295 };
296
297 crashed.retain(|m| !ignore_monikers.contains(m));
298 if !crashed.is_empty() {
299 panic!(
300 "TestRealm {} found unclean component stops with monikers: {:?}",
301 self.name, crashed
302 );
303 }
304 }
305}
306
307impl TestRealmInner<'_> {
308 fn shutdown_sync(&self) -> std::result::Result<Vec<String>, fidl::Error> {
309 let (listener, server_end) = fidl::endpoints::create_sync_proxy();
310 self.realm.get_crash_listener(server_end)?;
311 self.realm.shutdown()?;
312 let _: zx::Signals = self
314 .realm
315 .as_channel()
316 .wait_handle(zx::Signals::CHANNEL_PEER_CLOSED, zx::MonotonicInstant::INFINITE)
317 .to_result()
318 .expect("wait channel closed");
319 let mut unclean_stop = Vec::new();
322 while let Some(unclean) =
323 listener.next(zx::MonotonicInstant::INFINITE).map(|v| (!v.is_empty()).then_some(v))?
324 {
325 unclean_stop.extend(unclean);
326 }
327 Ok(unclean_stop)
328 }
329}
330
331#[must_use]
338#[derive(Clone)]
339pub struct TestRealm<'a>(Arc<TestRealmInner<'a>>);
340
341impl<'a> std::fmt::Debug for TestRealm<'a> {
342 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
343 let Self(inner) = self;
344 let TestRealmInner { realm: _, name, _sandbox, shutdown_on_drop } = &**inner;
345 f.debug_struct("TestRealm")
346 .field("name", name)
347 .field("shutdown_on_drop", shutdown_on_drop)
348 .finish_non_exhaustive()
349 }
350}
351
352impl<'a> TestRealm<'a> {
353 fn realm(&self) -> &fnetemul::ManagedRealmProxy {
354 let Self(inner) = self;
355 &inner.realm
356 }
357
358 pub fn name(&self) -> &str {
360 let Self(inner) = self;
361 &inner.name
362 }
363
364 pub fn set_checked_shutdown_on_drop(&self, shutdown_on_drop: bool) {
371 let Self(inner) = self;
372 inner.shutdown_on_drop.lock().unwrap().enabled = shutdown_on_drop;
373 }
374
375 pub fn ignore_checked_shutdown_monikers(
381 &self,
382 monikers: impl IntoIterator<Item: Into<String>>,
383 ) {
384 let Self(inner) = self;
385 inner
386 .shutdown_on_drop
387 .lock()
388 .unwrap()
389 .ignore_monikers
390 .extend(monikers.into_iter().map(|m| m.into()));
391 }
392
393 pub fn connect_to_protocol<S>(&self) -> Result<S::Proxy>
395 where
396 S: fidl::endpoints::DiscoverableProtocolMarker,
397 {
398 (|| {
399 let (proxy, server_end) = fidl::endpoints::create_proxy::<S>();
400 let () = self
401 .connect_to_protocol_with_server_end(server_end)
402 .context("connect to protocol name with server end")?;
403 Result::Ok(proxy)
404 })()
405 .context(S::DEBUG_NAME)
406 }
407
408 pub fn connect_to_protocol_from_child<S>(&self, child: &str) -> Result<S::Proxy>
410 where
411 S: fidl::endpoints::DiscoverableProtocolMarker,
412 {
413 (|| {
414 let (proxy, server_end) = fidl::endpoints::create_proxy::<S>();
415 let () = self
416 .connect_to_protocol_from_child_at_path_with_server_end(
417 S::PROTOCOL_NAME,
418 child,
419 server_end,
420 )
421 .context("connect to protocol name with server end")?;
422 Result::Ok(proxy)
423 })()
424 .with_context(|| format!("{} from {child}", S::DEBUG_NAME))
425 }
426
427 pub fn open_diagnostics_directory(&self, child_name: &str) -> Result<fio::DirectoryProxy> {
429 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
430 let () = self
431 .realm()
432 .open_diagnostics_directory(child_name, server_end)
433 .context("open diagnostics dir")?;
434 Ok(proxy)
435 }
436
437 pub fn connect_to_protocol_with_server_end<S: fidl::endpoints::DiscoverableProtocolMarker>(
439 &self,
440 server_end: fidl::endpoints::ServerEnd<S>,
441 ) -> Result {
442 self.realm()
443 .connect_to_protocol(S::PROTOCOL_NAME, None, server_end.into_channel())
444 .context("connect to protocol")
445 }
446
447 pub fn connect_to_protocol_from_child_at_path_with_server_end<
449 S: fidl::endpoints::DiscoverableProtocolMarker,
450 >(
451 &self,
452 protocol_path: &str,
453 child: &str,
454 server_end: fidl::endpoints::ServerEnd<S>,
455 ) -> Result {
456 self.realm()
457 .connect_to_protocol(protocol_path, Some(child), server_end.into_channel())
458 .context("connect to protocol")
459 }
460
461 pub async fn get_moniker(&self) -> Result<String> {
463 self.realm().get_moniker().await.context("failed to call get moniker")
464 }
465
466 pub async fn start_child_component(&self, child_name: &str) -> Result {
468 self.realm()
469 .start_child_component(child_name)
470 .await?
471 .map_err(zx::Status::from_raw)
472 .with_context(|| format!("failed to start child component '{}'", child_name))
473 }
474
475 pub async fn stop_child_component(&self, child_name: &str) -> Result {
477 self.realm()
478 .stop_child_component(child_name)
479 .await?
480 .map_err(zx::Status::from_raw)
481 .with_context(|| format!("failed to stop child component '{}'", child_name))
482 }
483
484 pub async fn join_network<S>(
490 &self,
491 network: &TestNetwork<'a>,
492 ep_name: S,
493 ) -> Result<TestInterface<'a>>
494 where
495 S: Into<Cow<'a, str>>,
496 {
497 self.join_network_with_if_config(network, ep_name, Default::default()).await
498 }
499
500 pub async fn join_network_with_if_config<S>(
506 &self,
507 network: &TestNetwork<'a>,
508 ep_name: S,
509 if_config: InterfaceConfig<'a>,
510 ) -> Result<TestInterface<'a>>
511 where
512 S: Into<Cow<'a, str>>,
513 {
514 let endpoint =
515 network.create_endpoint(ep_name).await.context("failed to create endpoint")?;
516 self.install_endpoint(endpoint, if_config).await
517 }
518
519 pub async fn join_network_with(
530 &self,
531 network: &TestNetwork<'a>,
532 ep_name: impl Into<Cow<'a, str>>,
533 ep_config: fnetemul_network::EndpointConfig,
534 if_config: InterfaceConfig<'a>,
535 ) -> Result<TestInterface<'a>> {
536 let installer = self
537 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
538 .context("failed to connect to fuchsia.net.interfaces.admin.Installer")?;
539 let interface_state = self
540 .connect_to_protocol::<fnet_interfaces::StateMarker>()
541 .context("failed to connect to fuchsia.net.interfaces.State")?;
542 let (endpoint, id, control, device_control) = self
543 .join_network_with_installer(
544 network,
545 installer,
546 interface_state,
547 ep_name,
548 ep_config,
549 if_config,
550 )
551 .await?;
552
553 Ok(TestInterface {
554 endpoint,
555 id,
556 realm: self.clone(),
557 control,
558 device_control: Some(device_control),
559 dhcp_client_task: futures::lock::Mutex::default(),
560 })
561 }
562
563 pub async fn join_network_with_installer(
574 &self,
575 network: &TestNetwork<'a>,
576 installer: fnet_interfaces_admin::InstallerProxy,
577 interface_state: fnet_interfaces::StateProxy,
578 ep_name: impl Into<Cow<'a, str>>,
579 ep_config: fnetemul_network::EndpointConfig,
580 if_config: InterfaceConfig<'a>,
581 ) -> Result<(TestEndpoint<'a>, u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
582 let endpoint = network
583 .create_endpoint_with(ep_name, ep_config)
584 .await
585 .context("failed to create endpoint")?;
586 let (id, control, device_control) = self
587 .install_endpoint_with_installer(installer, interface_state, &endpoint, if_config)
588 .await?;
589 Ok((endpoint, id, control, device_control))
590 }
591
592 pub async fn install_endpoint_with_installer(
600 &self,
601 installer: fnet_interfaces_admin::InstallerProxy,
602 interface_state: fnet_interfaces::StateProxy,
603 endpoint: &TestEndpoint<'a>,
604 if_config: InterfaceConfig<'a>,
605 ) -> Result<(u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
606 let (id, control, device_control) =
607 endpoint.install(installer, if_config).await.context("failed to add endpoint")?;
608
609 let () = endpoint.set_link_up(true).await.context("failed to start endpoint")?;
610 let _did_enable: bool = control
611 .enable()
612 .await
613 .map_err(anyhow::Error::new)
614 .and_then(|res| {
615 res.map_err(|e: fnet_interfaces_admin::ControlEnableError| {
616 anyhow::anyhow!("{:?}", e)
617 })
618 })
619 .context("failed to enable interface")?;
620
621 let () = fnet_interfaces_ext::wait_interface_with_id(
624 fnet_interfaces_ext::event_stream_from_state::<fnet_interfaces_ext::DefaultInterest>(
625 &interface_state,
626 fnet_interfaces_ext::IncludedAddresses::OnlyAssigned,
627 )?,
628 &mut fnet_interfaces_ext::InterfaceState::<(), _>::Unknown(id),
629 |properties_and_state| properties_and_state.properties.online.then_some(()),
630 )
631 .await
632 .context("failed to observe interface up")?;
633
634 Ok((id, control, device_control))
635 }
636
637 pub async fn install_endpoint(
641 &self,
642 endpoint: TestEndpoint<'a>,
643 if_config: InterfaceConfig<'a>,
644 ) -> Result<TestInterface<'a>> {
645 let installer = self
646 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
647 .context("failed to connect to fuchsia.net.interfaces.admin.Installer")?;
648 let interface_state = self
649 .connect_to_protocol::<fnet_interfaces::StateMarker>()
650 .context("failed to connect to fuchsia.net.interfaces.State")?;
651 let (id, control, device_control) = self
652 .install_endpoint_with_installer(installer, interface_state, &endpoint, if_config)
653 .await?;
654 Ok(TestInterface {
655 endpoint,
656 id,
657 realm: self.clone(),
658 control,
659 device_control: Some(device_control),
660 dhcp_client_task: futures::lock::Mutex::default(),
661 })
662 }
663
664 pub async fn add_raw_device(
666 &self,
667 path: &Path,
668 device: fidl::endpoints::ClientEnd<fnetemul_network::DeviceProxy_Marker>,
669 ) -> Result {
670 let path = path.to_str().with_context(|| format!("convert {} to str", path.display()))?;
671 self.realm()
672 .add_device(path, device)
673 .await
674 .context("add device")?
675 .map_err(zx::Status::from_raw)
676 .context("add device error")
677 }
678
679 pub async fn add_virtual_device(&self, e: &TestEndpoint<'_>, path: &Path) -> Result {
681 let (device, device_server_end) =
682 fidl::endpoints::create_endpoints::<fnetemul_network::DeviceProxy_Marker>();
683 e.get_proxy_(device_server_end).context("get proxy")?;
684
685 self.add_raw_device(path, device).await
686 }
687
688 pub async fn remove_virtual_device(&self, path: &Path) -> Result {
690 let path = path.to_str().with_context(|| format!("convert {} to str", path.display()))?;
691 self.realm()
692 .remove_device(path)
693 .await
694 .context("remove device")?
695 .map_err(zx::Status::from_raw)
696 .context("remove device error")
697 }
698
699 pub async fn datagram_socket(
702 &self,
703 domain: fposix_socket::Domain,
704 proto: fposix_socket::DatagramSocketProtocol,
705 ) -> Result<socket2::Socket> {
706 let socket_provider = self
707 .connect_to_protocol::<fposix_socket::ProviderMarker>()
708 .context("failed to connect to socket provider")?;
709
710 fposix_socket_ext::datagram_socket(&socket_provider, domain, proto)
711 .await
712 .context("failed to call socket")?
713 .context("failed to create socket")
714 }
715
716 pub async fn raw_socket(
719 &self,
720 domain: fposix_socket::Domain,
721 association: fposix_socket_raw::ProtocolAssociation,
722 ) -> Result<socket2::Socket> {
723 let socket_provider = self
724 .connect_to_protocol::<fposix_socket_raw::ProviderMarker>()
725 .context("failed to connect to socket provider")?;
726 let sock = socket_provider
727 .socket(domain, &association)
728 .await
729 .context("failed to call socket")?
730 .map_err(|e| std::io::Error::from_raw_os_error(e.into_primitive()))
731 .context("failed to create socket")?;
732
733 Ok(fdio::create_fd(sock.into()).context("failed to create fd")?.into())
734 }
735
736 pub async fn packet_socket(&self, kind: fposix_socket_packet::Kind) -> Result<socket2::Socket> {
741 let socket_provider = self
742 .connect_to_protocol::<fposix_socket_packet::ProviderMarker>()
743 .context("failed to connect to socket provider")?;
744
745 fposix_socket_ext::packet_socket(&socket_provider, kind)
746 .await
747 .context("failed to call socket")?
748 .context("failed to create socket")
749 }
750
751 pub async fn stream_socket(
754 &self,
755 domain: fposix_socket::Domain,
756 proto: fposix_socket::StreamSocketProtocol,
757 ) -> Result<socket2::Socket> {
758 let socket_provider = self
759 .connect_to_protocol::<fposix_socket::ProviderMarker>()
760 .context("failed to connect to socket provider")?;
761 let sock = socket_provider
762 .stream_socket(domain, proto)
763 .await
764 .context("failed to call socket")?
765 .map_err(|e| std::io::Error::from_raw_os_error(e.into_primitive()))
766 .context("failed to create socket")?;
767
768 Ok(fdio::create_fd(sock.into()).context("failed to create fd")?.into())
769 }
770
771 pub async fn shutdown(&self) -> Result {
778 let () = self.realm().shutdown().context("call shutdown")?;
779 self.set_checked_shutdown_on_drop(false);
782 let events = self
783 .realm()
784 .take_event_stream()
785 .try_collect::<Vec<_>>()
786 .await
787 .context("error on realm event stream")?;
788 assert_matches::assert_matches!(events[..], [fnetemul::ManagedRealmEvent::OnShutdown {}]);
790 Ok(())
791 }
792
793 pub async fn get_crash_stream(&self) -> Result<impl futures::Stream<Item = Result<String>>> {
795 let (listener, server_end) = fidl::endpoints::create_proxy();
796 self.realm().get_crash_listener(server_end).context("creating CrashListener")?;
797 Ok(futures::stream::try_unfold(listener, |listener| async move {
798 let next = listener.next().await.context("listener fetch next moniker")?;
799 Result::Ok(if next.is_empty() {
800 None
801 } else {
802 Some((futures::stream::iter(next.into_iter().map(Ok)), listener))
803 })
804 })
805 .try_flatten())
806 }
807
808 pub async fn icmp_socket<Ip: ping::FuchsiaIpExt>(
810 &self,
811 ) -> Result<fuchsia_async::net::DatagramSocket> {
812 let sock = self
813 .datagram_socket(Ip::DOMAIN_FIDL, fposix_socket::DatagramSocketProtocol::IcmpEcho)
814 .await
815 .context("failed to create ICMP datagram socket")?;
816 fuchsia_async::net::DatagramSocket::new_from_socket(sock)
817 .context("failed to create async ICMP datagram socket")
818 }
819
820 pub async fn ping_once<Ip: ping::FuchsiaIpExt>(&self, addr: Ip::SockAddr, seq: u16) -> Result {
822 let icmp_sock = self.icmp_socket::<Ip>().await?;
823
824 const MESSAGE: &'static str = "hello, world";
825 let (mut sink, mut stream) = ping::new_unicast_sink_and_stream::<
826 Ip,
827 _,
828 { MESSAGE.len() + ping::ICMP_HEADER_LEN },
829 >(&icmp_sock, &addr, MESSAGE.as_bytes());
830
831 let send_fut = sink.send(seq).map_err(anyhow::Error::new);
832 let recv_fut = stream.try_next().map(|r| match r {
833 Ok(Some(got)) if got == seq => Ok(()),
834 Ok(Some(got)) => Err(anyhow!("unexpected echo reply; got: {}, want: {}", got, seq)),
835 Ok(None) => Err(anyhow!("echo reply stream ended unexpectedly")),
836 Err(e) => Err(anyhow::Error::from(e)),
837 });
838
839 let ((), ()) = futures::future::try_join(send_fut, recv_fut)
840 .await
841 .with_context(|| format!("failed to ping from {} to {}", self.name(), addr,))?;
842 Ok(())
843 }
844
845 pub async fn add_neighbor_entry(
849 &self,
850 interface: u64,
851 addr: fnet::IpAddress,
852 mac: fnet::MacAddress,
853 ) -> Result {
854 let controller = self
855 .connect_to_protocol::<fnet_neighbor::ControllerMarker>()
856 .context("connect to protocol")?;
857 controller
858 .add_entry(interface, &addr, &mac)
859 .await
860 .context("add_entry")?
861 .map_err(zx::Status::from_raw)
862 .context("add_entry failed")
863 }
864
865 pub fn get_interface_event_stream(
868 &self,
869 ) -> Result<
870 impl futures::Stream<
871 Item = std::result::Result<
872 fnet_interfaces_ext::EventWithInterest<fnet_interfaces_ext::DefaultInterest>,
873 fidl::Error,
874 >,
875 >,
876 > {
877 self.get_interface_event_stream_with_interest::<fnet_interfaces_ext::DefaultInterest>()
878 }
879
880 pub fn get_interface_event_stream_with_interest<I: fnet_interfaces_ext::FieldInterests>(
883 &self,
884 ) -> Result<
885 impl futures::Stream<
886 Item = std::result::Result<fnet_interfaces_ext::EventWithInterest<I>, fidl::Error>,
887 >,
888 > {
889 let interface_state = self
890 .connect_to_protocol::<fnet_interfaces::StateMarker>()
891 .context("connect to protocol")?;
892 fnet_interfaces_ext::event_stream_from_state::<I>(
893 &interface_state,
894 fnet_interfaces_ext::IncludedAddresses::OnlyAssigned,
895 )
896 .context("get interface event stream")
897 }
898
899 pub async fn main_table_id<
901 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
902 >(
903 &self,
904 ) -> u32 {
905 let main_route_table = self
906 .connect_to_protocol::<I::RouteTableMarker>()
907 .expect("failed to connect to main route table");
908 fnet_routes_ext::admin::get_table_id::<I>(&main_route_table)
909 .await
910 .expect("failed to get_table_id")
911 .get()
912 }
913}
914
915#[must_use]
920pub struct TestNetwork<'a> {
921 network: fnetemul_network::NetworkProxy,
922 name: Cow<'a, str>,
923 sandbox: &'a TestSandbox,
924}
925
926impl<'a> std::fmt::Debug for TestNetwork<'a> {
927 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
928 let Self { name, network: _, sandbox: _ } = self;
929 f.debug_struct("TestNetwork").field("name", name).finish_non_exhaustive()
930 }
931}
932
933impl<'a> TestNetwork<'a> {
934 pub fn into_proxy(self) -> fnetemul_network::NetworkProxy {
941 let Self { network, name: _, sandbox: _ } = self;
942 network
943 }
944
945 async fn get_client_end_clone(
947 &self,
948 ) -> Result<fidl::endpoints::ClientEnd<fnetemul_network::NetworkMarker>> {
949 let network_manager =
950 self.sandbox.get_network_manager().context("get_network_manager failed")?;
951 let client = network_manager
952 .get_network(&self.name)
953 .await
954 .context("get_network failed")?
955 .with_context(|| format!("no network found with name {}", self.name))?;
956 Ok(client)
957 }
958
959 pub async fn set_config(&self, config: fnetemul_network::NetworkConfig) -> Result<()> {
961 let status = self.network.set_config(&config).await.context("call set_config")?;
962 zx::Status::ok(status).context("set config")
963 }
964
965 pub async fn attach_endpoint(&self, ep: &TestEndpoint<'a>) -> Result<()> {
967 let status =
968 self.network.attach_endpoint(&ep.name).await.context("attach_endpoint FIDL error")?;
969 let () = zx::Status::ok(status).context("attach_endpoint failed")?;
970 Ok(())
971 }
972
973 pub async fn create_endpoint<S>(&self, name: S) -> Result<TestEndpoint<'a>>
977 where
978 S: Into<Cow<'a, str>>,
979 {
980 let ep = self
981 .sandbox
982 .create_endpoint(name)
983 .await
984 .with_context(|| format!("failed to create endpoint for network {}", self.name))?;
985 let () = self.attach_endpoint(&ep).await.with_context(|| {
986 format!("failed to attach endpoint {} to network {}", ep.name, self.name)
987 })?;
988 Ok(ep)
989 }
990
991 pub async fn create_endpoint_with(
995 &self,
996 name: impl Into<Cow<'a, str>>,
997 config: fnetemul_network::EndpointConfig,
998 ) -> Result<TestEndpoint<'a>> {
999 let ep = self
1000 .sandbox
1001 .create_endpoint_with(name, config)
1002 .await
1003 .with_context(|| format!("failed to create endpoint for network {}", self.name))?;
1004 let () = self.attach_endpoint(&ep).await.with_context(|| {
1005 format!("failed to attach endpoint {} to network {}", ep.name, self.name)
1006 })?;
1007 Ok(ep)
1008 }
1009
1010 pub fn create_fake_endpoint(&self) -> Result<TestFakeEndpoint<'a>> {
1012 let (endpoint, server) =
1013 fidl::endpoints::create_proxy::<fnetemul_network::FakeEndpointMarker>();
1014 let () = self.network.create_fake_endpoint(server)?;
1015 return Ok(TestFakeEndpoint { endpoint, _sandbox: self.sandbox });
1016 }
1017
1018 pub async fn start_capture(&self, name: &str) -> Result<PacketCapture> {
1024 let manager = self.sandbox.get_network_manager()?;
1025 let client = manager.get_network(&self.name).await?.expect("network must exist");
1026 zx::ok(self.network.start_capture(name).await?)?;
1027 let sync_proxy = fnetemul_network::NetworkSynchronousProxy::new(client.into_channel());
1028 Ok(PacketCapture { sync_proxy })
1029 }
1030
1031 pub async fn stop_capture(&self) -> Result<()> {
1033 Ok(self.network.stop_capture().await?)
1034 }
1035}
1036
1037pub struct PacketCapture {
1040 sync_proxy: fnetemul_network::NetworkSynchronousProxy,
1041}
1042
1043impl Drop for PacketCapture {
1044 fn drop(&mut self) {
1045 self.sync_proxy
1046 .stop_capture(zx::MonotonicInstant::INFINITE)
1047 .expect("failed to stop packet capture")
1048 }
1049}
1050
1051#[must_use]
1053pub struct TestEndpoint<'a> {
1054 endpoint: fnetemul_network::EndpointProxy,
1055 name: Cow<'a, str>,
1056 _sandbox: &'a TestSandbox,
1057}
1058
1059impl<'a> std::fmt::Debug for TestEndpoint<'a> {
1060 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
1061 let Self { endpoint: _, name, _sandbox } = self;
1062 f.debug_struct("TestEndpoint").field("name", name).finish_non_exhaustive()
1063 }
1064}
1065
1066impl<'a> std::ops::Deref for TestEndpoint<'a> {
1067 type Target = fnetemul_network::EndpointProxy;
1068
1069 fn deref(&self) -> &Self::Target {
1070 &self.endpoint
1071 }
1072}
1073
1074#[must_use]
1076pub struct TestFakeEndpoint<'a> {
1077 endpoint: fnetemul_network::FakeEndpointProxy,
1078 _sandbox: &'a TestSandbox,
1079}
1080
1081impl<'a> std::ops::Deref for TestFakeEndpoint<'a> {
1082 type Target = fnetemul_network::FakeEndpointProxy;
1083
1084 fn deref(&self) -> &Self::Target {
1085 &self.endpoint
1086 }
1087}
1088
1089impl<'a> TestFakeEndpoint<'a> {
1090 pub fn frame_stream(
1094 &self,
1095 ) -> impl futures::Stream<Item = std::result::Result<(Vec<u8>, u64), fidl::Error>> + '_ {
1096 futures::stream::try_unfold(&self.endpoint, |ep| ep.read().map_ok(move |r| Some((r, ep))))
1097 }
1098}
1099
1100async fn to_netdevice_inner(
1103 port: fidl::endpoints::ClientEnd<fnetwork::PortMarker>,
1104) -> Result<(fidl::endpoints::ClientEnd<fnetwork::DeviceMarker>, fnetwork::PortId)> {
1105 let port = port.into_proxy();
1106 let (device, server_end) = fidl::endpoints::create_endpoints::<fnetwork::DeviceMarker>();
1107 let () = port.get_device(server_end)?;
1108 let port_id = port
1109 .get_info()
1110 .await
1111 .context("get port info")?
1112 .id
1113 .ok_or_else(|| anyhow::anyhow!("missing port id"))?;
1114 Ok((device, port_id))
1115}
1116
1117impl<'a> TestEndpoint<'a> {
1118 pub fn into_proxy(self) -> fnetemul_network::EndpointProxy {
1125 let Self { endpoint, name: _, _sandbox: _ } = self;
1126 endpoint
1127 }
1128
1129 pub async fn get_netdevice(
1134 &self,
1135 ) -> Result<(fidl::endpoints::ClientEnd<fnetwork::DeviceMarker>, fnetwork::PortId)> {
1136 let (port, server_end) = fidl::endpoints::create_endpoints();
1137 self.get_port(server_end)
1138 .with_context(|| format!("failed to get device connection for {}", self.name))?;
1139 to_netdevice_inner(port).await
1140 }
1141
1142 pub async fn install(
1148 &self,
1149 installer: fnet_interfaces_admin::InstallerProxy,
1150 InterfaceConfig { name, metric, ipv4_dad_transmits, ipv6_dad_transmits }: InterfaceConfig<
1151 '_,
1152 >,
1153 ) -> Result<(u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
1154 let name = name.map(|n| {
1155 truncate_dropping_front(n.into(), fnet_interfaces::INTERFACE_NAME_LENGTH.into())
1156 .to_string()
1157 });
1158 let (device, port_id) = self.get_netdevice().await?;
1159 let device_control = {
1160 let (control, server_end) =
1161 fidl::endpoints::create_proxy::<fnet_interfaces_admin::DeviceControlMarker>();
1162 let () = installer.install_device(device, server_end).context("install device")?;
1163 control
1164 };
1165 let (control, server_end) = Control::create_endpoints().context("create endpoints")?;
1166 let () = device_control
1167 .create_interface(
1168 &port_id,
1169 server_end,
1170 &fnet_interfaces_admin::Options { name, metric, ..Default::default() },
1171 )
1172 .context("create interface")?;
1173 if let Some(ipv4_dad_transmits) = ipv4_dad_transmits {
1174 let _: Option<u16> = set_ipv4_dad_transmits(&control, ipv4_dad_transmits)
1175 .await
1176 .context("set dad transmits")?;
1177 }
1178 if let Some(ipv6_dad_transmits) = ipv6_dad_transmits {
1179 let _: Option<u16> = set_ipv6_dad_transmits(&control, ipv6_dad_transmits)
1180 .await
1181 .context("set dad transmits")?;
1182 }
1183
1184 let id = control.get_id().await.context("get id")?;
1185 Ok((id, control, device_control))
1186 }
1187
1188 pub async fn add_to_stack(
1193 &self,
1194 realm: &TestRealm<'a>,
1195 config: InterfaceConfig<'a>,
1196 ) -> Result<(u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
1197 let installer = realm
1198 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
1199 .context("connect to protocol")?;
1200
1201 self.install(installer, config).await
1202 }
1203
1204 pub async fn into_interface_in_realm(self, realm: &TestRealm<'a>) -> Result<TestInterface<'a>> {
1206 self.into_interface_in_realm_with_name(realm, Default::default()).await
1207 }
1208
1209 pub async fn into_interface_in_realm_with_name(
1212 self,
1213 realm: &TestRealm<'a>,
1214 config: InterfaceConfig<'a>,
1215 ) -> Result<TestInterface<'a>> {
1216 let installer = realm
1217 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
1218 .context("connect to protocol")?;
1219
1220 let (id, control, device_control) =
1221 self.install(installer, config).await.context("failed to install")?;
1222
1223 Ok(TestInterface {
1224 endpoint: self,
1225 id,
1226 realm: realm.clone(),
1227 control,
1228 device_control: Some(device_control),
1229 dhcp_client_task: futures::lock::Mutex::default(),
1230 })
1231 }
1232}
1233
1234#[derive(Copy, Clone, PartialEq, Debug)]
1236pub enum DhcpClientVersion {
1237 InStack,
1239 OutOfStack,
1241}
1242
1243pub trait DhcpClient {
1245 const DHCP_CLIENT_VERSION: DhcpClientVersion;
1247}
1248
1249pub enum InStack {}
1251
1252impl DhcpClient for InStack {
1253 const DHCP_CLIENT_VERSION: DhcpClientVersion = DhcpClientVersion::InStack;
1254}
1255
1256pub enum OutOfStack {}
1258
1259impl DhcpClient for OutOfStack {
1260 const DHCP_CLIENT_VERSION: DhcpClientVersion = DhcpClientVersion::OutOfStack;
1261}
1262
1263#[must_use]
1269pub struct TestInterface<'a> {
1270 endpoint: TestEndpoint<'a>,
1271 realm: TestRealm<'a>,
1272 id: u64,
1273 control: Control,
1274 device_control: Option<fnet_interfaces_admin::DeviceControlProxy>,
1275 dhcp_client_task: futures::lock::Mutex<Option<fnet_dhcp_ext::testutil::DhcpClientTask>>,
1276}
1277
1278impl<'a> std::fmt::Debug for TestInterface<'a> {
1279 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
1280 let Self { endpoint, id, realm: _, control: _, device_control: _, dhcp_client_task: _ } =
1281 self;
1282 f.debug_struct("TestInterface")
1283 .field("endpoint", endpoint)
1284 .field("id", id)
1285 .finish_non_exhaustive()
1286 }
1287}
1288
1289impl<'a> std::ops::Deref for TestInterface<'a> {
1290 type Target = fnetemul_network::EndpointProxy;
1291
1292 fn deref(&self) -> &Self::Target {
1293 &self.endpoint
1294 }
1295}
1296
1297impl<'a> TestInterface<'a> {
1298 pub fn id(&self) -> u64 {
1300 self.id
1301 }
1302
1303 pub fn endpoint(&self) -> &TestEndpoint<'a> {
1305 &self.endpoint
1306 }
1307
1308 pub fn control(&self) -> &Control {
1310 &self.control
1311 }
1312
1313 pub async fn get_authorization(&self) -> Result<GrantForInterfaceAuthorization> {
1315 Ok(self.control.get_authorization_for_interface().await?)
1316 }
1317
1318 pub fn connect_stack(&self) -> Result<fnet_stack::StackProxy> {
1320 self.realm.connect_to_protocol::<fnet_stack::StackMarker>()
1321 }
1322
1323 async fn add_route<
1328 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1329 >(
1330 &self,
1331 destination: Subnet<I::Addr>,
1332 next_hop: Option<SpecifiedAddr<I::Addr>>,
1333 metric: fnet_routes::SpecifiedMetric,
1334 ) -> Result<bool> {
1335 let route_set = self.create_authenticated_global_route_set::<I>().await?;
1336 fnet_routes_ext::admin::add_route::<I>(
1337 &route_set,
1338 &fnet_routes_ext::Route::<I>::new_forward(destination, self.id(), next_hop, metric)
1339 .try_into()
1340 .expect("convert to FIDL should succeed"),
1341 )
1342 .await
1343 .context("FIDL error adding route")?
1344 .map_err(|e| anyhow::anyhow!("error adding route: {e:?}"))
1345 }
1346
1347 pub async fn add_route_either(
1353 &self,
1354 destination: fnet::Subnet,
1355 next_hop: Option<fnet::IpAddress>,
1356 metric: fnet_routes::SpecifiedMetric,
1357 ) -> Result<bool> {
1358 let fnet::Subnet { addr: destination_addr, prefix_len } = destination;
1359 match destination_addr {
1360 fnet::IpAddress::Ipv4(destination_addr) => {
1361 let next_hop = match next_hop {
1362 Some(fnet::IpAddress::Ipv4(next_hop)) => Some(
1363 SpecifiedAddr::new(net_types::ip::Ipv4Addr::from_ext(next_hop))
1364 .ok_or(anyhow::anyhow!("next hop must not be unspecified address"))?,
1365 ),
1366 Some(fnet::IpAddress::Ipv6(_)) => {
1367 return Err(anyhow::anyhow!(
1368 "next hop must be same IP version as destination"
1369 ))
1370 }
1371 None => None,
1372 };
1373 self.add_route::<Ipv4>(
1374 Subnet::new(destination_addr.into_ext(), prefix_len)
1375 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1376 next_hop,
1377 metric,
1378 )
1379 .await
1380 }
1381 fnet::IpAddress::Ipv6(destination_addr) => {
1382 let next_hop = match next_hop {
1383 Some(fnet::IpAddress::Ipv6(next_hop)) => Some(
1384 SpecifiedAddr::new(net_types::ip::Ipv6Addr::from_ext(next_hop))
1385 .ok_or(anyhow::anyhow!("next hop must not be unspecified address"))?,
1386 ),
1387 Some(fnet::IpAddress::Ipv4(_)) => {
1388 return Err(anyhow::anyhow!(
1389 "next hop must be same IP version as destination"
1390 ))
1391 }
1392 None => None,
1393 };
1394 self.add_route::<Ipv6>(
1395 Subnet::new(destination_addr.into_ext(), prefix_len)
1396 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1397 next_hop,
1398 metric,
1399 )
1400 .await
1401 }
1402 }
1403 }
1404
1405 async fn remove_route<
1410 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1411 >(
1412 &self,
1413 destination: Subnet<I::Addr>,
1414 next_hop: Option<SpecifiedAddr<I::Addr>>,
1415 metric: fnet_routes::SpecifiedMetric,
1416 ) -> Result<bool> {
1417 let route_set = self.create_authenticated_global_route_set::<I>().await?;
1418 fnet_routes_ext::admin::remove_route::<I>(
1419 &route_set,
1420 &fnet_routes_ext::Route::<I>::new_forward(destination, self.id(), next_hop, metric)
1421 .try_into()
1422 .expect("convert to FIDL should succeed"),
1423 )
1424 .await
1425 .context("FIDL error removing route")?
1426 .map_err(|e| anyhow::anyhow!("error removing route: {e:?}"))
1427 }
1428
1429 async fn remove_route_either(
1435 &self,
1436 destination: fnet::Subnet,
1437 next_hop: Option<fnet::IpAddress>,
1438 metric: fnet_routes::SpecifiedMetric,
1439 ) -> Result<bool> {
1440 let fnet::Subnet { addr: destination_addr, prefix_len } = destination;
1441 match destination_addr {
1442 fnet::IpAddress::Ipv4(destination_addr) => {
1443 let next_hop = match next_hop {
1444 Some(fnet::IpAddress::Ipv4(next_hop)) => Some(
1445 SpecifiedAddr::new(net_types::ip::Ipv4Addr::from_ext(next_hop))
1446 .ok_or(anyhow::anyhow!("next hop must not be unspecified address"))?,
1447 ),
1448 Some(fnet::IpAddress::Ipv6(_)) => {
1449 return Err(anyhow::anyhow!(
1450 "next hop must be same IP version as destination"
1451 ))
1452 }
1453 None => None,
1454 };
1455 self.remove_route::<Ipv4>(
1456 Subnet::new(destination_addr.into_ext(), prefix_len)
1457 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1458 next_hop,
1459 metric,
1460 )
1461 .await
1462 }
1463 fnet::IpAddress::Ipv6(destination_addr) => {
1464 let next_hop = match next_hop {
1465 Some(fnet::IpAddress::Ipv6(next_hop)) => Some(
1466 SpecifiedAddr::new(net_types::ip::Ipv6Addr::from_ext(next_hop))
1467 .ok_or(anyhow::anyhow!("next hop must not be unspecified address"))?,
1468 ),
1469 Some(fnet::IpAddress::Ipv4(_)) => {
1470 return Err(anyhow::anyhow!(
1471 "next hop must be same IP version as destination"
1472 ))
1473 }
1474 None => None,
1475 };
1476 self.remove_route::<Ipv6>(
1477 Subnet::new(destination_addr.into_ext(), prefix_len)
1478 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1479 next_hop,
1480 metric,
1481 )
1482 .await
1483 }
1484 }
1485 }
1486
1487 pub async fn add_subnet_route(&self, subnet: fnet::Subnet) -> Result<()> {
1489 let subnet = fnet_ext::apply_subnet_mask(subnet);
1490 let newly_added = self
1491 .add_route_either(
1492 subnet,
1493 None,
1494 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1495 )
1496 .await?;
1497
1498 if !newly_added {
1499 Err(anyhow::anyhow!(
1500 "route to {subnet:?} on {} should not have already existed",
1501 self.id()
1502 ))
1503 } else {
1504 Ok(())
1505 }
1506 }
1507
1508 pub async fn del_subnet_route(&self, subnet: fnet::Subnet) -> Result<()> {
1510 let subnet = fnet_ext::apply_subnet_mask(subnet);
1511 let newly_removed = self
1512 .remove_route_either(
1513 subnet,
1514 None,
1515 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1516 )
1517 .await?;
1518
1519 if !newly_removed {
1520 Err(anyhow::anyhow!(
1521 "route to {subnet:?} on {} should have previously existed before being removed",
1522 self.id()
1523 ))
1524 } else {
1525 Ok(())
1526 }
1527 }
1528
1529 pub async fn add_default_route_with_metric(
1531 &self,
1532 next_hop: fnet::IpAddress,
1533 metric: fnet_routes::SpecifiedMetric,
1534 ) -> Result<()> {
1535 let corresponding_default_subnet = match next_hop {
1536 fnet::IpAddress::Ipv4(_) => net_declare::fidl_subnet!("0.0.0.0/0"),
1537 fnet::IpAddress::Ipv6(_) => net_declare::fidl_subnet!("::/0"),
1538 };
1539
1540 let newly_added =
1541 self.add_route_either(corresponding_default_subnet, Some(next_hop), metric).await?;
1542
1543 if !newly_added {
1544 Err(anyhow::anyhow!(
1545 "default route through {} via {next_hop:?} already exists",
1546 self.id()
1547 ))
1548 } else {
1549 Ok(())
1550 }
1551 }
1552
1553 pub async fn add_default_route_with_explicit_metric(
1555 &self,
1556 next_hop: fnet::IpAddress,
1557 metric: u32,
1558 ) -> Result<()> {
1559 self.add_default_route_with_metric(
1560 next_hop,
1561 fnet_routes::SpecifiedMetric::ExplicitMetric(metric),
1562 )
1563 .await
1564 }
1565
1566 pub async fn add_default_route(&self, next_hop: fnet::IpAddress) -> Result<()> {
1568 self.add_default_route_with_metric(
1569 next_hop,
1570 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1571 )
1572 .await
1573 }
1574
1575 pub async fn remove_default_route(&self, next_hop: fnet::IpAddress) -> Result<()> {
1577 let corresponding_default_subnet = match next_hop {
1578 fnet::IpAddress::Ipv4(_) => net_declare::fidl_subnet!("0.0.0.0/0"),
1579 fnet::IpAddress::Ipv6(_) => net_declare::fidl_subnet!("::/0"),
1580 };
1581
1582 let newly_removed = self
1583 .remove_route_either(
1584 corresponding_default_subnet,
1585 Some(next_hop),
1586 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1587 )
1588 .await?;
1589
1590 if !newly_removed {
1591 Err(anyhow::anyhow!(
1592 "default route through {} via {next_hop:?} does not exist",
1593 self.id()
1594 ))
1595 } else {
1596 Ok(())
1597 }
1598 }
1599
1600 pub async fn add_gateway_route(
1602 &self,
1603 destination: fnet::Subnet,
1604 next_hop: fnet::IpAddress,
1605 ) -> Result<()> {
1606 let newly_added = self
1607 .add_route_either(
1608 destination,
1609 Some(next_hop),
1610 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1611 )
1612 .await?;
1613
1614 if !newly_added {
1615 Err(anyhow::anyhow!(
1616 "should have newly added route to {destination:?} via {next_hop:?} through {}",
1617 self.id()
1618 ))
1619 } else {
1620 Ok(())
1621 }
1622 }
1623
1624 pub async fn create_authenticated_global_route_set<
1626 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1627 >(
1628 &self,
1629 ) -> Result<<I::RouteSetMarker as ProtocolMarker>::Proxy> {
1630 #[derive(GenericOverIp)]
1631 #[generic_over_ip(I, Ip)]
1632 struct Out<'a, I: fnet_routes_ext::admin::FidlRouteAdminIpExt>(
1633 LocalBoxFuture<'a, <I::RouteSetMarker as ProtocolMarker>::Proxy>,
1634 );
1635
1636 let Out(proxy_fut) = I::map_ip_out(
1637 self,
1638 |this| {
1639 Out(this
1640 .get_global_route_set_v4()
1641 .map(|result| result.expect("get global route set"))
1642 .boxed_local())
1643 },
1644 |this| {
1645 Out(this
1646 .get_global_route_set_v6()
1647 .map(|result| result.expect("get global route set"))
1648 .boxed_local())
1649 },
1650 );
1651
1652 let route_set = proxy_fut.await;
1653 let fnet_interfaces_admin::GrantForInterfaceAuthorization { interface_id, token } =
1654 self.get_authorization().await.expect("get interface grant");
1655 fnet_routes_ext::admin::authenticate_for_interface::<I>(
1656 &route_set,
1657 fnet_interfaces_admin::ProofOfInterfaceAuthorization { interface_id, token },
1658 )
1659 .await
1660 .expect("authentication should not have FIDL error")
1661 .expect("authentication should succeed");
1662 Ok(route_set)
1663 }
1664
1665 async fn get_global_route_set_v4(&self) -> Result<fnet_routes_admin::RouteSetV4Proxy> {
1666 let root_routes = self
1667 .realm
1668 .connect_to_protocol::<fnet_root::RoutesV4Marker>()
1669 .expect("get fuchsia.net.root.RoutesV4");
1670 let (route_set, server_end) =
1671 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteSetV4Marker>();
1672 root_routes.global_route_set(server_end).expect("calling global_route_set should succeed");
1673 Ok(route_set)
1674 }
1675
1676 async fn get_global_route_set_v6(&self) -> Result<fnet_routes_admin::RouteSetV6Proxy> {
1677 let root_routes = self
1678 .realm
1679 .connect_to_protocol::<fnet_root::RoutesV6Marker>()
1680 .expect("get fuchsia.net.root.RoutesV6");
1681 let (route_set, server_end) =
1682 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteSetV6Marker>();
1683 root_routes.global_route_set(server_end).expect("calling global_route_set should succeed");
1684 Ok(route_set)
1685 }
1686
1687 async fn get_properties(
1689 &self,
1690 included_addresses: fnet_interfaces_ext::IncludedAddresses,
1691 ) -> Result<fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>> {
1692 let interface_state = self.realm.connect_to_protocol::<fnet_interfaces::StateMarker>()?;
1693 let properties = fnet_interfaces_ext::existing(
1694 fnet_interfaces_ext::event_stream_from_state(&interface_state, included_addresses)?,
1695 fnet_interfaces_ext::InterfaceState::<(), _>::Unknown(self.id),
1696 )
1697 .await
1698 .context("failed to get existing interfaces")?;
1699 match properties {
1700 fnet_interfaces_ext::InterfaceState::Unknown(id) => Err(anyhow::anyhow!(
1701 "could not find interface {} for endpoint {}",
1702 id,
1703 self.endpoint.name
1704 )),
1705 fnet_interfaces_ext::InterfaceState::Known(
1706 fnet_interfaces_ext::PropertiesAndState { properties, state: () },
1707 ) => Ok(properties),
1708 }
1709 }
1710
1711 pub async fn get_addrs(
1713 &self,
1714 included_addresses: fnet_interfaces_ext::IncludedAddresses,
1715 ) -> Result<Vec<fnet_interfaces_ext::Address<fnet_interfaces_ext::AllInterest>>> {
1716 let fnet_interfaces_ext::Properties { addresses, .. } =
1717 self.get_properties(included_addresses).await?;
1718 Ok(addresses)
1719 }
1720
1721 pub async fn get_interface_name(&self) -> Result<String> {
1723 let fnet_interfaces_ext::Properties { name, .. } =
1724 self.get_properties(fnet_interfaces_ext::IncludedAddresses::OnlyAssigned).await?;
1725 Ok(name)
1726 }
1727
1728 pub async fn get_port_class(&self) -> Result<fnet_interfaces_ext::PortClass> {
1730 let fnet_interfaces_ext::Properties { port_class, .. } =
1731 self.get_properties(fnet_interfaces_ext::IncludedAddresses::OnlyAssigned).await?;
1732 Ok(port_class)
1733 }
1734
1735 pub async fn mac(&self) -> fnet::MacAddress {
1737 let (port, server_end) =
1738 fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_network::PortMarker>();
1739 self.get_port(server_end).expect("get_port");
1740 let (mac_addressing, server_end) =
1741 fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_network::MacAddressingMarker>();
1742 port.get_mac(server_end).expect("get_mac");
1743 mac_addressing.get_unicast_address().await.expect("get_unicast_address")
1744 }
1745
1746 async fn set_dhcp_client_enabled(&self, enable: bool) -> Result<()> {
1747 self.connect_stack()
1748 .context("connect stack")?
1749 .set_dhcp_client_enabled(self.id, enable)
1750 .await
1751 .context("failed to call SetDhcpClientEnabled")?
1752 .map_err(|e| anyhow!("{:?}", e))
1753 }
1754
1755 pub async fn start_dhcp<D: DhcpClient>(&self) -> Result<()> {
1757 match D::DHCP_CLIENT_VERSION {
1758 DhcpClientVersion::InStack => self.start_dhcp_in_stack().await,
1759 DhcpClientVersion::OutOfStack => self.start_dhcp_client_out_of_stack().await,
1760 }
1761 }
1762
1763 async fn start_dhcp_in_stack(&self) -> Result<()> {
1764 self.set_dhcp_client_enabled(true).await.context("failed to start dhcp client")
1765 }
1766
1767 async fn start_dhcp_client_out_of_stack(&self) -> Result<()> {
1768 let Self { endpoint: _, realm, id, control, device_control: _, dhcp_client_task } = self;
1769 let id = NonZeroU64::new(*id).expect("interface ID should be nonzero");
1770 let mut dhcp_client_task = dhcp_client_task.lock().await;
1771 let dhcp_client_task = dhcp_client_task.deref_mut();
1772
1773 let provider = realm
1774 .connect_to_protocol::<fnet_dhcp::ClientProviderMarker>()
1775 .expect("get fuchsia.net.dhcp.ClientProvider");
1776
1777 provider.check_presence().await.expect("check presence should succeed");
1778
1779 let client = provider.new_client_ext(id, fnet_dhcp_ext::default_new_client_params());
1780 let control = control.clone();
1781 let route_set_provider = realm
1782 .connect_to_protocol::<fnet_routes_admin::RouteTableV4Marker>()
1783 .expect("get fuchsia.net.routes.RouteTableV4");
1784 let (route_set, server_end) =
1785 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteSetV4Marker>();
1786 route_set_provider.new_route_set(server_end).expect("calling new_route_set should succeed");
1787 let task = fnet_dhcp_ext::testutil::DhcpClientTask::new(client, id, route_set, control);
1788 *dhcp_client_task = Some(task);
1789 Ok(())
1790 }
1791
1792 pub async fn stop_dhcp<D: DhcpClient>(&self) -> Result<()> {
1794 match D::DHCP_CLIENT_VERSION {
1795 DhcpClientVersion::InStack => self.stop_dhcp_in_stack().await,
1796 DhcpClientVersion::OutOfStack => {
1797 self.stop_dhcp_out_of_stack().await;
1798 Ok(())
1799 }
1800 }
1801 }
1802
1803 async fn stop_dhcp_in_stack(&self) -> Result<()> {
1804 self.set_dhcp_client_enabled(false).await.context("failed to stop dhcp client")
1805 }
1806
1807 async fn stop_dhcp_out_of_stack(&self) {
1808 let Self { endpoint: _, realm: _, id: _, control: _, device_control: _, dhcp_client_task } =
1809 self;
1810 let mut dhcp_client_task = dhcp_client_task.lock().await;
1811 if let Some(task) = dhcp_client_task.deref_mut().take() {
1812 task.shutdown().await.expect("client shutdown should succeed");
1813 }
1814 }
1815
1816 pub async fn add_address(&self, subnet: fnet::Subnet) -> Result<()> {
1819 let (address_state_provider, server) =
1820 fidl::endpoints::create_proxy::<fnet_interfaces_admin::AddressStateProviderMarker>();
1821 let () = address_state_provider.detach().context("detach address lifetime")?;
1822 let () = self
1823 .control
1824 .add_address(&subnet, &fnet_interfaces_admin::AddressParameters::default(), server)
1825 .context("FIDL error")?;
1826
1827 let mut state_stream =
1828 fnet_interfaces_ext::admin::assignment_state_stream(address_state_provider);
1829 fnet_interfaces_ext::admin::wait_assignment_state(
1830 &mut state_stream,
1831 fnet_interfaces::AddressAssignmentState::Assigned,
1832 )
1833 .await?;
1834 Ok(())
1835 }
1836
1837 pub async fn add_address_and_subnet_route(&self, subnet: fnet::Subnet) -> Result<()> {
1840 let (address_state_provider, server) =
1841 fidl::endpoints::create_proxy::<fnet_interfaces_admin::AddressStateProviderMarker>();
1842 address_state_provider.detach().context("detach address lifetime")?;
1843 self.control
1844 .add_address(
1845 &subnet,
1846 &fnet_interfaces_admin::AddressParameters {
1847 add_subnet_route: Some(true),
1848 ..Default::default()
1849 },
1850 server,
1851 )
1852 .context("FIDL error")?;
1853
1854 let state_stream =
1855 fnet_interfaces_ext::admin::assignment_state_stream(address_state_provider);
1856 let mut state_stream = pin!(state_stream);
1857
1858 fnet_interfaces_ext::admin::wait_assignment_state(
1859 &mut state_stream,
1860 fnet_interfaces::AddressAssignmentState::Assigned,
1861 )
1862 .await
1863 .context("assignment state")?;
1864 Ok(())
1865 }
1866
1867 pub async fn del_address_and_subnet_route(
1869 &self,
1870 addr_with_prefix: fnet::Subnet,
1871 ) -> Result<bool> {
1872 let did_remove =
1873 self.control.remove_address(&addr_with_prefix).await.context("FIDL error").and_then(
1874 |res| {
1875 res.map_err(|e: fnet_interfaces_admin::ControlRemoveAddressError| {
1876 anyhow::anyhow!("{:?}", e)
1877 })
1878 },
1879 )?;
1880
1881 if did_remove {
1882 let destination = fnet_ext::apply_subnet_mask(addr_with_prefix);
1883 let newly_removed_route = self
1884 .remove_route_either(
1885 destination,
1886 None,
1887 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1888 )
1889 .await?;
1890
1891 let _: bool = newly_removed_route;
1894 }
1895 Ok(did_remove)
1896 }
1897
1898 pub async fn remove_ipv6_linklocal_addresses(
1902 &self,
1903 ) -> Result<Vec<fnet_interfaces_ext::Address<fnet_interfaces_ext::AllInterest>>> {
1904 let mut result = Vec::new();
1905 for address in self.get_addrs(fnet_interfaces_ext::IncludedAddresses::All).await? {
1906 let fnet_interfaces_ext::Address { addr: fnet::Subnet { addr, prefix_len }, .. } =
1907 &address;
1908 match addr {
1909 fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address { addr: _ }) => {
1910 continue
1911 }
1912 fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address { addr }) => {
1913 let v6_addr = net_types::ip::Ipv6Addr::from_bytes(*addr);
1914 if !v6_addr.is_unicast_link_local() {
1915 continue;
1916 }
1917 }
1918 }
1919 let _newly_removed: bool = self
1920 .del_address_and_subnet_route(fnet::Subnet { addr: *addr, prefix_len: *prefix_len })
1921 .await?;
1922 result.push(address);
1923 }
1924 Ok(result)
1925 }
1926
1927 async fn set_configuration(&self, config: fnet_interfaces_admin::Configuration) -> Result<()> {
1937 let fnet_interfaces_admin::Configuration {
1938 ipv4: previous_ipv4, ipv6: previous_ipv6, ..
1939 } = self
1940 .control()
1941 .set_configuration(&config.clone())
1942 .await
1943 .context("FIDL error")?
1944 .map_err(|e| anyhow!("set configuration error: {:?}", e))?;
1945
1946 fn verify_config_changed<T: Eq>(previous: Option<T>, current: Option<T>) -> Result<()> {
1947 if let Some(current) = current {
1948 let previous = previous.ok_or_else(|| anyhow!("configuration not supported"))?;
1949 if previous == current {
1950 return Err(anyhow!("configuration change is a no-op"));
1951 }
1952 }
1953 Ok(())
1954 }
1955
1956 let fnet_interfaces_admin::Configuration { ipv4, ipv6, .. } = config;
1957 if let Some(fnet_interfaces_admin::Ipv4Configuration {
1958 unicast_forwarding,
1959 multicast_forwarding,
1960 ..
1961 }) = ipv4
1962 {
1963 let fnet_interfaces_admin::Ipv4Configuration {
1964 unicast_forwarding: previous_unicast_forwarding,
1965 multicast_forwarding: previous_multicast_forwarding,
1966 ..
1967 } = previous_ipv4.ok_or_else(|| anyhow!("IPv4 configuration not supported"))?;
1968 verify_config_changed(previous_unicast_forwarding, unicast_forwarding)
1969 .context("IPv4 unicast forwarding")?;
1970 verify_config_changed(previous_multicast_forwarding, multicast_forwarding)
1971 .context("IPv4 multicast forwarding")?;
1972 }
1973 if let Some(fnet_interfaces_admin::Ipv6Configuration {
1974 unicast_forwarding,
1975 multicast_forwarding,
1976 ..
1977 }) = ipv6
1978 {
1979 let fnet_interfaces_admin::Ipv6Configuration {
1980 unicast_forwarding: previous_unicast_forwarding,
1981 multicast_forwarding: previous_multicast_forwarding,
1982 ..
1983 } = previous_ipv6.ok_or_else(|| anyhow!("IPv6 configuration not supported"))?;
1984 verify_config_changed(previous_unicast_forwarding, unicast_forwarding)
1985 .context("IPv6 unicast forwarding")?;
1986 verify_config_changed(previous_multicast_forwarding, multicast_forwarding)
1987 .context("IPv6 multicast forwarding")?;
1988 }
1989 Ok(())
1990 }
1991
1992 pub async fn set_ipv6_forwarding_enabled(&self, enabled: bool) -> Result<()> {
1994 self.set_configuration(fnet_interfaces_admin::Configuration {
1995 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
1996 unicast_forwarding: Some(enabled),
1997 ..Default::default()
1998 }),
1999 ..Default::default()
2000 })
2001 .await
2002 }
2003
2004 pub async fn set_ipv4_forwarding_enabled(&self, enabled: bool) -> Result<()> {
2006 self.set_configuration(fnet_interfaces_admin::Configuration {
2007 ipv4: Some(fnet_interfaces_admin::Ipv4Configuration {
2008 unicast_forwarding: Some(enabled),
2009 ..Default::default()
2010 }),
2011 ..Default::default()
2012 })
2013 .await
2014 }
2015
2016 pub async fn remove(
2019 self,
2020 ) -> Result<(fnetemul_network::EndpointProxy, Option<fnet_interfaces_admin::DeviceControlProxy>)>
2021 {
2022 let Self {
2023 endpoint: TestEndpoint { endpoint, name: _, _sandbox: _ },
2024 id: _,
2025 realm: _,
2026 control,
2027 device_control,
2028 dhcp_client_task: _,
2029 } = self;
2030 std::mem::drop(control);
2034 Ok((endpoint, device_control))
2035 }
2036
2037 pub fn remove_device(self) -> (Control, Option<fnet_interfaces_admin::DeviceControlProxy>) {
2041 let Self {
2042 endpoint: TestEndpoint { endpoint, name: _, _sandbox: _ },
2043 id: _,
2044 realm: _,
2045 control,
2046 device_control,
2047 dhcp_client_task: _,
2048 } = self;
2049 std::mem::drop(endpoint);
2050 (control, device_control)
2051 }
2052
2053 pub async fn wait_removal(self) -> Result<fnet_interfaces_admin::InterfaceRemovedReason> {
2055 let Self {
2056 endpoint: _endpoint,
2058 id: _,
2059 realm: _,
2060 control,
2061 dhcp_client_task: _,
2062 device_control: _device_control,
2064 } = self;
2065 match control.wait_termination().await {
2066 fnet_interfaces_ext::admin::TerminalError::Fidl(e) => {
2067 Err(e).context("waiting interface control termination")
2068 }
2069 fnet_interfaces_ext::admin::TerminalError::Terminal(reason) => Ok(reason),
2070 }
2071 }
2072
2073 pub async fn set_ipv4_dad_transmits(&self, dad_transmits: u16) -> Result<Option<u16>> {
2077 set_ipv4_dad_transmits(self.control(), dad_transmits).await
2078 }
2079
2080 pub async fn set_ipv6_dad_transmits(&self, dad_transmits: u16) -> Result<Option<u16>> {
2084 set_ipv6_dad_transmits(self.control(), dad_transmits).await
2085 }
2086
2087 pub async fn set_temporary_address_generation_enabled(&self, enabled: bool) -> Result<()> {
2090 self.set_configuration(fnet_interfaces_admin::Configuration {
2091 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
2092 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
2093 slaac: Some(fnet_interfaces_admin::SlaacConfiguration {
2094 temporary_address: Some(enabled),
2095 ..Default::default()
2096 }),
2097 ..Default::default()
2098 }),
2099 ..Default::default()
2100 }),
2101 ..Default::default()
2102 })
2103 .await
2104 }
2105}
2106
2107async fn set_ipv4_dad_transmits(control: &Control, dad_transmits: u16) -> Result<Option<u16>> {
2108 control
2109 .set_configuration(&fnet_interfaces_admin::Configuration {
2110 ipv4: Some(fnet_interfaces_admin::Ipv4Configuration {
2111 arp: Some(fnet_interfaces_admin::ArpConfiguration {
2112 dad: Some(fnet_interfaces_admin::DadConfiguration {
2113 transmits: Some(dad_transmits),
2114 ..Default::default()
2115 }),
2116 ..Default::default()
2117 }),
2118 ..Default::default()
2119 }),
2120 ..Default::default()
2121 })
2122 .await?
2123 .map(|config| config.ipv4?.arp?.dad?.transmits)
2124 .map_err(|e| anyhow::anyhow!("set configuration error {e:?}"))
2125}
2126
2127async fn set_ipv6_dad_transmits(control: &Control, dad_transmits: u16) -> Result<Option<u16>> {
2128 control
2129 .set_configuration(&fnet_interfaces_admin::Configuration {
2130 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
2131 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
2132 dad: Some(fnet_interfaces_admin::DadConfiguration {
2133 transmits: Some(dad_transmits),
2134 ..Default::default()
2135 }),
2136 ..Default::default()
2137 }),
2138 ..Default::default()
2139 }),
2140 ..Default::default()
2141 })
2142 .await?
2143 .map(|config| config.ipv6?.ndp?.dad?.transmits)
2144 .map_err(|e| anyhow::anyhow!("set configuration error {e:?}"))
2145}
2146
2147fn get_socket2_domain(addr: &std::net::SocketAddr) -> fposix_socket::Domain {
2149 let domain = match addr {
2150 std::net::SocketAddr::V4(_) => fposix_socket::Domain::Ipv4,
2151 std::net::SocketAddr::V6(_) => fposix_socket::Domain::Ipv6,
2152 };
2153
2154 domain
2155}
2156
2157pub trait RealmUdpSocket: Sized {
2159 fn bind_in_realm<'a>(
2161 realm: &'a TestRealm<'a>,
2162 addr: std::net::SocketAddr,
2163 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2164}
2165
2166impl RealmUdpSocket for std::net::UdpSocket {
2167 fn bind_in_realm<'a>(
2168 realm: &'a TestRealm<'a>,
2169 addr: std::net::SocketAddr,
2170 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2171 async move {
2172 let sock = realm
2173 .datagram_socket(
2174 get_socket2_domain(&addr),
2175 fposix_socket::DatagramSocketProtocol::Udp,
2176 )
2177 .await
2178 .context("failed to create socket")?;
2179
2180 let () = sock.bind(&addr.into()).context("bind failed")?;
2181
2182 Result::Ok(sock.into())
2183 }
2184 .boxed_local()
2185 }
2186}
2187
2188impl RealmUdpSocket for fuchsia_async::net::UdpSocket {
2189 fn bind_in_realm<'a>(
2190 realm: &'a TestRealm<'a>,
2191 addr: std::net::SocketAddr,
2192 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2193 std::net::UdpSocket::bind_in_realm(realm, addr)
2194 .and_then(|udp| {
2195 futures::future::ready(
2196 fuchsia_async::net::UdpSocket::from_socket(udp)
2197 .context("failed to create fuchsia_async socket"),
2198 )
2199 })
2200 .boxed_local()
2201 }
2202}
2203
2204pub trait RealmTcpListener: Sized {
2206 fn listen_in_realm<'a>(
2208 realm: &'a TestRealm<'a>,
2209 addr: std::net::SocketAddr,
2210 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2211 Self::listen_in_realm_with(realm, addr, |_: &socket2::Socket| Ok(()))
2212 }
2213
2214 fn listen_in_realm_with<'a>(
2217 realm: &'a TestRealm<'a>,
2218 addr: std::net::SocketAddr,
2219 setup: impl FnOnce(&socket2::Socket) -> Result<()> + 'a,
2220 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2221}
2222
2223impl RealmTcpListener for std::net::TcpListener {
2224 fn listen_in_realm_with<'a>(
2225 realm: &'a TestRealm<'a>,
2226 addr: std::net::SocketAddr,
2227 setup: impl FnOnce(&socket2::Socket) -> Result<()> + 'a,
2228 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2229 async move {
2230 let sock = realm
2231 .stream_socket(get_socket2_domain(&addr), fposix_socket::StreamSocketProtocol::Tcp)
2232 .await
2233 .context("failed to create server socket")?;
2234 let () = setup(&sock)?;
2235 let () = sock.bind(&addr.into()).context("failed to bind server socket")?;
2236 let () = sock.listen(128).context("failed to listen on server socket")?;
2239
2240 Result::Ok(sock.into())
2241 }
2242 .boxed_local()
2243 }
2244}
2245
2246impl RealmTcpListener for fuchsia_async::net::TcpListener {
2247 fn listen_in_realm_with<'a>(
2248 realm: &'a TestRealm<'a>,
2249 addr: std::net::SocketAddr,
2250 setup: impl FnOnce(&socket2::Socket) -> Result<()> + 'a,
2251 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2252 std::net::TcpListener::listen_in_realm_with(realm, addr, setup)
2253 .and_then(|listener| {
2254 futures::future::ready(
2255 fuchsia_async::net::TcpListener::from_std(listener)
2256 .context("failed to create fuchsia_async socket"),
2257 )
2258 })
2259 .boxed_local()
2260 }
2261}
2262
2263pub trait RealmTcpStream: Sized {
2265 fn connect_in_realm<'a>(
2267 realm: &'a TestRealm<'a>,
2268 addr: std::net::SocketAddr,
2269 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2270
2271 fn bind_and_connect_in_realm<'a>(
2273 realm: &'a TestRealm<'a>,
2274 local: std::net::SocketAddr,
2275 dst: std::net::SocketAddr,
2276 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2277
2278 fn connect_in_realm_with_sock<'a, F: FnOnce(&socket2::Socket) -> Result + 'a>(
2283 realm: &'a TestRealm<'a>,
2284 dst: std::net::SocketAddr,
2285 with_sock: F,
2286 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2287
2288 }
2290
2291impl RealmTcpStream for fuchsia_async::net::TcpStream {
2292 fn connect_in_realm<'a>(
2293 realm: &'a TestRealm<'a>,
2294 addr: std::net::SocketAddr,
2295 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2296 Self::connect_in_realm_with_sock(realm, addr, |_: &socket2::Socket| Ok(()))
2297 }
2298
2299 fn bind_and_connect_in_realm<'a>(
2300 realm: &'a TestRealm<'a>,
2301 local: std::net::SocketAddr,
2302 dst: std::net::SocketAddr,
2303 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2304 Self::connect_in_realm_with_sock(realm, dst, move |sock| {
2305 sock.bind(&local.into()).context("failed to bind")
2306 })
2307 }
2308
2309 fn connect_in_realm_with_sock<'a, F: FnOnce(&socket2::Socket) -> Result + 'a>(
2310 realm: &'a TestRealm<'a>,
2311 dst: std::net::SocketAddr,
2312 with_sock: F,
2313 ) -> futures::future::LocalBoxFuture<'a, Result<fuchsia_async::net::TcpStream>> {
2314 async move {
2315 let sock = realm
2316 .stream_socket(get_socket2_domain(&dst), fposix_socket::StreamSocketProtocol::Tcp)
2317 .await
2318 .context("failed to create socket")?;
2319
2320 with_sock(&sock)?;
2321
2322 let stream = fuchsia_async::net::TcpStream::connect_from_raw(sock, dst)
2323 .context("failed to create client tcp stream")?
2324 .await
2325 .context("failed to connect to server")?;
2326
2327 Result::Ok(stream)
2328 }
2329 .boxed_local()
2330 }
2331}
2332
2333fn truncate_dropping_front(s: Cow<'_, str>, len: usize) -> Cow<'_, str> {
2334 match s.len().checked_sub(len) {
2335 None => s,
2336 Some(start) => {
2337 match s {
2341 Cow::Borrowed(s) => Cow::Borrowed(&s[start..]),
2342 Cow::Owned(mut s) => {
2343 let _: std::string::Drain<'_> = s.drain(..start);
2344 Cow::Owned(s)
2345 }
2346 }
2347 }
2348 }
2349}