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 pub temporary_addresses: Option<bool>,
266}
267
268#[derive(Debug)]
269struct ShutdownOnDropConfig {
270 enabled: bool,
271 ignore_monikers: HashSet<String>,
272}
273
274struct TestRealmInner<'a> {
275 realm: fnetemul::ManagedRealmProxy,
276 name: Cow<'a, str>,
277 _sandbox: &'a TestSandbox,
278 shutdown_on_drop: Mutex<ShutdownOnDropConfig>,
279}
280
281impl Drop for TestRealmInner<'_> {
282 fn drop(&mut self) {
283 let ShutdownOnDropConfig { enabled, ignore_monikers } =
284 self.shutdown_on_drop.get_mut().unwrap();
285 if !*enabled {
286 return;
287 }
288 let ignore_monikers = std::mem::take(ignore_monikers);
289 let mut crashed = match self.shutdown_sync() {
290 Ok(crashed) => crashed,
291 Err(e) => {
292 if !e.is_closed() {
296 panic!("error verifying clean shutdown on test realm {}: {:?}", self.name, e);
297 }
298 return;
299 }
300 };
301
302 crashed.retain(|m| !ignore_monikers.contains(m));
303 if !crashed.is_empty() {
304 panic!(
305 "TestRealm {} found unclean component stops with monikers: {:?}",
306 self.name, crashed
307 );
308 }
309 }
310}
311
312impl TestRealmInner<'_> {
313 fn shutdown_sync(&self) -> std::result::Result<Vec<String>, fidl::Error> {
314 let (listener, server_end) = fidl::endpoints::create_sync_proxy();
315 self.realm.get_crash_listener(server_end)?;
316 self.realm.shutdown()?;
317 let _: zx::Signals = self
319 .realm
320 .as_channel()
321 .wait_handle(zx::Signals::CHANNEL_PEER_CLOSED, zx::MonotonicInstant::INFINITE)
322 .to_result()
323 .expect("wait channel closed");
324 let mut unclean_stop = Vec::new();
327 while let Some(unclean) =
328 listener.next(zx::MonotonicInstant::INFINITE).map(|v| (!v.is_empty()).then_some(v))?
329 {
330 unclean_stop.extend(unclean);
331 }
332 Ok(unclean_stop)
333 }
334}
335
336#[must_use]
343#[derive(Clone)]
344pub struct TestRealm<'a>(Arc<TestRealmInner<'a>>);
345
346impl<'a> std::fmt::Debug for TestRealm<'a> {
347 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
348 let Self(inner) = self;
349 let TestRealmInner { realm: _, name, _sandbox, shutdown_on_drop } = &**inner;
350 f.debug_struct("TestRealm")
351 .field("name", name)
352 .field("shutdown_on_drop", shutdown_on_drop)
353 .finish_non_exhaustive()
354 }
355}
356
357impl<'a> TestRealm<'a> {
358 fn realm(&self) -> &fnetemul::ManagedRealmProxy {
359 let Self(inner) = self;
360 &inner.realm
361 }
362
363 pub fn name(&self) -> &str {
365 let Self(inner) = self;
366 &inner.name
367 }
368
369 pub fn set_checked_shutdown_on_drop(&self, shutdown_on_drop: bool) {
376 let Self(inner) = self;
377 inner.shutdown_on_drop.lock().unwrap().enabled = shutdown_on_drop;
378 }
379
380 pub fn ignore_checked_shutdown_monikers(
386 &self,
387 monikers: impl IntoIterator<Item: Into<String>>,
388 ) {
389 let Self(inner) = self;
390 inner
391 .shutdown_on_drop
392 .lock()
393 .unwrap()
394 .ignore_monikers
395 .extend(monikers.into_iter().map(|m| m.into()));
396 }
397
398 pub fn connect_to_protocol<S>(&self) -> Result<S::Proxy>
400 where
401 S: fidl::endpoints::DiscoverableProtocolMarker,
402 {
403 (|| {
404 let (proxy, server_end) = fidl::endpoints::create_proxy::<S>();
405 let () = self
406 .connect_to_protocol_with_server_end(server_end)
407 .context("connect to protocol name with server end")?;
408 Result::Ok(proxy)
409 })()
410 .context(S::DEBUG_NAME)
411 }
412
413 pub fn connect_to_protocol_from_child<S>(&self, child: &str) -> Result<S::Proxy>
415 where
416 S: fidl::endpoints::DiscoverableProtocolMarker,
417 {
418 (|| {
419 let (proxy, server_end) = fidl::endpoints::create_proxy::<S>();
420 let () = self
421 .connect_to_protocol_from_child_at_path_with_server_end(
422 S::PROTOCOL_NAME,
423 child,
424 server_end,
425 )
426 .context("connect to protocol name with server end")?;
427 Result::Ok(proxy)
428 })()
429 .with_context(|| format!("{} from {child}", S::DEBUG_NAME))
430 }
431
432 pub fn open_diagnostics_directory(&self, child_name: &str) -> Result<fio::DirectoryProxy> {
434 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
435 let () = self
436 .realm()
437 .open_diagnostics_directory(child_name, server_end)
438 .context("open diagnostics dir")?;
439 Ok(proxy)
440 }
441
442 pub fn connect_to_protocol_with_server_end<S: fidl::endpoints::DiscoverableProtocolMarker>(
444 &self,
445 server_end: fidl::endpoints::ServerEnd<S>,
446 ) -> Result {
447 self.realm()
448 .connect_to_protocol(S::PROTOCOL_NAME, None, server_end.into_channel())
449 .context("connect to protocol")
450 }
451
452 pub fn connect_to_protocol_from_child_at_path_with_server_end<
454 S: fidl::endpoints::DiscoverableProtocolMarker,
455 >(
456 &self,
457 protocol_path: &str,
458 child: &str,
459 server_end: fidl::endpoints::ServerEnd<S>,
460 ) -> Result {
461 self.realm()
462 .connect_to_protocol(protocol_path, Some(child), server_end.into_channel())
463 .context("connect to protocol")
464 }
465
466 pub async fn get_moniker(&self) -> Result<String> {
468 self.realm().get_moniker().await.context("failed to call get moniker")
469 }
470
471 pub async fn start_child_component(&self, child_name: &str) -> Result {
473 self.realm()
474 .start_child_component(child_name)
475 .await?
476 .map_err(zx::Status::from_raw)
477 .with_context(|| format!("failed to start child component '{}'", child_name))
478 }
479
480 pub async fn stop_child_component(&self, child_name: &str) -> Result {
482 self.realm()
483 .stop_child_component(child_name)
484 .await?
485 .map_err(zx::Status::from_raw)
486 .with_context(|| format!("failed to stop child component '{}'", child_name))
487 }
488
489 pub async fn join_network<S>(
495 &self,
496 network: &TestNetwork<'a>,
497 ep_name: S,
498 ) -> Result<TestInterface<'a>>
499 where
500 S: Into<Cow<'a, str>>,
501 {
502 self.join_network_with_if_config(network, ep_name, Default::default()).await
503 }
504
505 pub async fn join_network_with_if_config<S>(
511 &self,
512 network: &TestNetwork<'a>,
513 ep_name: S,
514 if_config: InterfaceConfig<'a>,
515 ) -> Result<TestInterface<'a>>
516 where
517 S: Into<Cow<'a, str>>,
518 {
519 let endpoint =
520 network.create_endpoint(ep_name).await.context("failed to create endpoint")?;
521 self.install_endpoint(endpoint, if_config).await
522 }
523
524 pub async fn join_network_with(
535 &self,
536 network: &TestNetwork<'a>,
537 ep_name: impl Into<Cow<'a, str>>,
538 ep_config: fnetemul_network::EndpointConfig,
539 if_config: InterfaceConfig<'a>,
540 ) -> Result<TestInterface<'a>> {
541 let installer = self
542 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
543 .context("failed to connect to fuchsia.net.interfaces.admin.Installer")?;
544 let interface_state = self
545 .connect_to_protocol::<fnet_interfaces::StateMarker>()
546 .context("failed to connect to fuchsia.net.interfaces.State")?;
547 let (endpoint, id, control, device_control) = self
548 .join_network_with_installer(
549 network,
550 installer,
551 interface_state,
552 ep_name,
553 ep_config,
554 if_config,
555 )
556 .await?;
557
558 Ok(TestInterface {
559 endpoint,
560 id,
561 realm: self.clone(),
562 control,
563 device_control: Some(device_control),
564 dhcp_client_task: futures::lock::Mutex::default(),
565 })
566 }
567
568 pub async fn join_network_with_installer(
579 &self,
580 network: &TestNetwork<'a>,
581 installer: fnet_interfaces_admin::InstallerProxy,
582 interface_state: fnet_interfaces::StateProxy,
583 ep_name: impl Into<Cow<'a, str>>,
584 ep_config: fnetemul_network::EndpointConfig,
585 if_config: InterfaceConfig<'a>,
586 ) -> Result<(TestEndpoint<'a>, u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
587 let endpoint = network
588 .create_endpoint_with(ep_name, ep_config)
589 .await
590 .context("failed to create endpoint")?;
591 let (id, control, device_control) = self
592 .install_endpoint_with_installer(installer, interface_state, &endpoint, if_config)
593 .await?;
594 Ok((endpoint, id, control, device_control))
595 }
596
597 pub async fn install_endpoint_with_installer(
605 &self,
606 installer: fnet_interfaces_admin::InstallerProxy,
607 interface_state: fnet_interfaces::StateProxy,
608 endpoint: &TestEndpoint<'a>,
609 if_config: InterfaceConfig<'a>,
610 ) -> Result<(u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
611 let (id, control, device_control) =
612 endpoint.install(installer, if_config).await.context("failed to add endpoint")?;
613
614 let () = endpoint.set_link_up(true).await.context("failed to start endpoint")?;
615 let _did_enable: bool = control
616 .enable()
617 .await
618 .map_err(anyhow::Error::new)
619 .and_then(|res| {
620 res.map_err(|e: fnet_interfaces_admin::ControlEnableError| {
621 anyhow::anyhow!("{:?}", e)
622 })
623 })
624 .context("failed to enable interface")?;
625
626 let () = fnet_interfaces_ext::wait_interface_with_id(
629 fnet_interfaces_ext::event_stream_from_state::<fnet_interfaces_ext::DefaultInterest>(
630 &interface_state,
631 fnet_interfaces_ext::IncludedAddresses::OnlyAssigned,
632 )?,
633 &mut fnet_interfaces_ext::InterfaceState::<(), _>::Unknown(id),
634 |properties_and_state| properties_and_state.properties.online.then_some(()),
635 )
636 .await
637 .context("failed to observe interface up")?;
638
639 Ok((id, control, device_control))
640 }
641
642 pub async fn install_endpoint(
646 &self,
647 endpoint: TestEndpoint<'a>,
648 if_config: InterfaceConfig<'a>,
649 ) -> Result<TestInterface<'a>> {
650 let installer = self
651 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
652 .context("failed to connect to fuchsia.net.interfaces.admin.Installer")?;
653 let interface_state = self
654 .connect_to_protocol::<fnet_interfaces::StateMarker>()
655 .context("failed to connect to fuchsia.net.interfaces.State")?;
656 let (id, control, device_control) = self
657 .install_endpoint_with_installer(installer, interface_state, &endpoint, if_config)
658 .await?;
659 Ok(TestInterface {
660 endpoint,
661 id,
662 realm: self.clone(),
663 control,
664 device_control: Some(device_control),
665 dhcp_client_task: futures::lock::Mutex::default(),
666 })
667 }
668
669 pub async fn add_raw_device(
671 &self,
672 path: &Path,
673 device: fidl::endpoints::ClientEnd<fnetemul_network::DeviceProxy_Marker>,
674 ) -> Result {
675 let path = path.to_str().with_context(|| format!("convert {} to str", path.display()))?;
676 self.realm()
677 .add_device(path, device)
678 .await
679 .context("add device")?
680 .map_err(zx::Status::from_raw)
681 .context("add device error")
682 }
683
684 pub async fn add_virtual_device(&self, e: &TestEndpoint<'_>, path: &Path) -> Result {
686 let (device, device_server_end) =
687 fidl::endpoints::create_endpoints::<fnetemul_network::DeviceProxy_Marker>();
688 e.get_proxy_(device_server_end).context("get proxy")?;
689
690 self.add_raw_device(path, device).await
691 }
692
693 pub async fn remove_virtual_device(&self, path: &Path) -> Result {
695 let path = path.to_str().with_context(|| format!("convert {} to str", path.display()))?;
696 self.realm()
697 .remove_device(path)
698 .await
699 .context("remove device")?
700 .map_err(zx::Status::from_raw)
701 .context("remove device error")
702 }
703
704 pub async fn datagram_socket(
707 &self,
708 domain: fposix_socket::Domain,
709 proto: fposix_socket::DatagramSocketProtocol,
710 ) -> Result<socket2::Socket> {
711 let socket_provider = self
712 .connect_to_protocol::<fposix_socket::ProviderMarker>()
713 .context("failed to connect to socket provider")?;
714
715 fposix_socket_ext::datagram_socket(&socket_provider, domain, proto)
716 .await
717 .context("failed to call socket")?
718 .context("failed to create socket")
719 }
720
721 pub async fn raw_socket(
724 &self,
725 domain: fposix_socket::Domain,
726 association: fposix_socket_raw::ProtocolAssociation,
727 ) -> Result<socket2::Socket> {
728 let socket_provider = self
729 .connect_to_protocol::<fposix_socket_raw::ProviderMarker>()
730 .context("failed to connect to socket provider")?;
731 let sock = socket_provider
732 .socket(domain, &association)
733 .await
734 .context("failed to call socket")?
735 .map_err(|e| std::io::Error::from_raw_os_error(e.into_primitive()))
736 .context("failed to create socket")?;
737
738 Ok(fdio::create_fd(sock.into()).context("failed to create fd")?.into())
739 }
740
741 pub async fn packet_socket(&self, kind: fposix_socket_packet::Kind) -> Result<socket2::Socket> {
746 let socket_provider = self
747 .connect_to_protocol::<fposix_socket_packet::ProviderMarker>()
748 .context("failed to connect to socket provider")?;
749
750 fposix_socket_ext::packet_socket(&socket_provider, kind)
751 .await
752 .context("failed to call socket")?
753 .context("failed to create socket")
754 }
755
756 pub async fn stream_socket(
759 &self,
760 domain: fposix_socket::Domain,
761 proto: fposix_socket::StreamSocketProtocol,
762 ) -> Result<socket2::Socket> {
763 let socket_provider = self
764 .connect_to_protocol::<fposix_socket::ProviderMarker>()
765 .context("failed to connect to socket provider")?;
766 let sock = socket_provider
767 .stream_socket(domain, proto)
768 .await
769 .context("failed to call socket")?
770 .map_err(|e| std::io::Error::from_raw_os_error(e.into_primitive()))
771 .context("failed to create socket")?;
772
773 Ok(fdio::create_fd(sock.into()).context("failed to create fd")?.into())
774 }
775
776 pub async fn shutdown(&self) -> Result {
783 let () = self.realm().shutdown().context("call shutdown")?;
784 self.set_checked_shutdown_on_drop(false);
787 let events = self
788 .realm()
789 .take_event_stream()
790 .try_collect::<Vec<_>>()
791 .await
792 .context("error on realm event stream")?;
793 assert_matches::assert_matches!(events[..], [fnetemul::ManagedRealmEvent::OnShutdown {}]);
795 Ok(())
796 }
797
798 pub async fn get_crash_stream(&self) -> Result<impl futures::Stream<Item = Result<String>>> {
800 let (listener, server_end) = fidl::endpoints::create_proxy();
801 self.realm().get_crash_listener(server_end).context("creating CrashListener")?;
802 Ok(futures::stream::try_unfold(listener, |listener| async move {
803 let next = listener.next().await.context("listener fetch next moniker")?;
804 Result::Ok(if next.is_empty() {
805 None
806 } else {
807 Some((futures::stream::iter(next.into_iter().map(Ok)), listener))
808 })
809 })
810 .try_flatten())
811 }
812
813 pub async fn icmp_socket<Ip: ping::FuchsiaIpExt>(
815 &self,
816 ) -> Result<fuchsia_async::net::DatagramSocket> {
817 let sock = self
818 .datagram_socket(Ip::DOMAIN_FIDL, fposix_socket::DatagramSocketProtocol::IcmpEcho)
819 .await
820 .context("failed to create ICMP datagram socket")?;
821 fuchsia_async::net::DatagramSocket::new_from_socket(sock)
822 .context("failed to create async ICMP datagram socket")
823 }
824
825 pub async fn ping_once<Ip: ping::FuchsiaIpExt>(&self, addr: Ip::SockAddr, seq: u16) -> Result {
827 let icmp_sock = self.icmp_socket::<Ip>().await?;
828
829 const MESSAGE: &'static str = "hello, world";
830 let (mut sink, mut stream) = ping::new_unicast_sink_and_stream::<
831 Ip,
832 _,
833 { MESSAGE.len() + ping::ICMP_HEADER_LEN },
834 >(&icmp_sock, &addr, MESSAGE.as_bytes());
835
836 let send_fut = sink.send(seq).map_err(anyhow::Error::new);
837 let recv_fut = stream.try_next().map(|r| match r {
838 Ok(Some(got)) if got == seq => Ok(()),
839 Ok(Some(got)) => Err(anyhow!("unexpected echo reply; got: {}, want: {}", got, seq)),
840 Ok(None) => Err(anyhow!("echo reply stream ended unexpectedly")),
841 Err(e) => Err(anyhow::Error::from(e)),
842 });
843
844 let ((), ()) = futures::future::try_join(send_fut, recv_fut)
845 .await
846 .with_context(|| format!("failed to ping from {} to {}", self.name(), addr,))?;
847 Ok(())
848 }
849
850 pub async fn add_neighbor_entry(
854 &self,
855 interface: u64,
856 addr: fnet::IpAddress,
857 mac: fnet::MacAddress,
858 ) -> Result {
859 let controller = self
860 .connect_to_protocol::<fnet_neighbor::ControllerMarker>()
861 .context("connect to protocol")?;
862 controller
863 .add_entry(interface, &addr, &mac)
864 .await
865 .context("add_entry")?
866 .map_err(zx::Status::from_raw)
867 .context("add_entry failed")
868 }
869
870 pub fn get_interface_event_stream(
873 &self,
874 ) -> Result<
875 impl futures::Stream<
876 Item = std::result::Result<
877 fnet_interfaces_ext::EventWithInterest<fnet_interfaces_ext::DefaultInterest>,
878 fidl::Error,
879 >,
880 >,
881 > {
882 self.get_interface_event_stream_with_interest::<fnet_interfaces_ext::DefaultInterest>()
883 }
884
885 pub fn get_interface_event_stream_with_interest<I: fnet_interfaces_ext::FieldInterests>(
888 &self,
889 ) -> Result<
890 impl futures::Stream<
891 Item = std::result::Result<fnet_interfaces_ext::EventWithInterest<I>, fidl::Error>,
892 >,
893 > {
894 let interface_state = self
895 .connect_to_protocol::<fnet_interfaces::StateMarker>()
896 .context("connect to protocol")?;
897 fnet_interfaces_ext::event_stream_from_state::<I>(
898 &interface_state,
899 fnet_interfaces_ext::IncludedAddresses::OnlyAssigned,
900 )
901 .context("get interface event stream")
902 }
903
904 pub async fn main_table_id<
906 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
907 >(
908 &self,
909 ) -> u32 {
910 let main_route_table = self
911 .connect_to_protocol::<I::RouteTableMarker>()
912 .expect("failed to connect to main route table");
913 fnet_routes_ext::admin::get_table_id::<I>(&main_route_table)
914 .await
915 .expect("failed to get_table_id")
916 .get()
917 }
918}
919
920#[must_use]
925pub struct TestNetwork<'a> {
926 network: fnetemul_network::NetworkProxy,
927 name: Cow<'a, str>,
928 sandbox: &'a TestSandbox,
929}
930
931impl<'a> std::fmt::Debug for TestNetwork<'a> {
932 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
933 let Self { name, network: _, sandbox: _ } = self;
934 f.debug_struct("TestNetwork").field("name", name).finish_non_exhaustive()
935 }
936}
937
938impl<'a> TestNetwork<'a> {
939 pub fn into_proxy(self) -> fnetemul_network::NetworkProxy {
946 let Self { network, name: _, sandbox: _ } = self;
947 network
948 }
949
950 async fn get_client_end_clone(
952 &self,
953 ) -> Result<fidl::endpoints::ClientEnd<fnetemul_network::NetworkMarker>> {
954 let network_manager =
955 self.sandbox.get_network_manager().context("get_network_manager failed")?;
956 let client = network_manager
957 .get_network(&self.name)
958 .await
959 .context("get_network failed")?
960 .with_context(|| format!("no network found with name {}", self.name))?;
961 Ok(client)
962 }
963
964 pub async fn set_config(&self, config: fnetemul_network::NetworkConfig) -> Result<()> {
966 let status = self.network.set_config(&config).await.context("call set_config")?;
967 zx::Status::ok(status).context("set config")
968 }
969
970 pub async fn attach_endpoint(&self, ep: &TestEndpoint<'a>) -> Result<()> {
972 let status =
973 self.network.attach_endpoint(&ep.name).await.context("attach_endpoint FIDL error")?;
974 let () = zx::Status::ok(status).context("attach_endpoint failed")?;
975 Ok(())
976 }
977
978 pub async fn create_endpoint<S>(&self, name: S) -> Result<TestEndpoint<'a>>
982 where
983 S: Into<Cow<'a, str>>,
984 {
985 let ep = self
986 .sandbox
987 .create_endpoint(name)
988 .await
989 .with_context(|| format!("failed to create endpoint for network {}", self.name))?;
990 let () = self.attach_endpoint(&ep).await.with_context(|| {
991 format!("failed to attach endpoint {} to network {}", ep.name, self.name)
992 })?;
993 Ok(ep)
994 }
995
996 pub async fn create_endpoint_with(
1000 &self,
1001 name: impl Into<Cow<'a, str>>,
1002 config: fnetemul_network::EndpointConfig,
1003 ) -> Result<TestEndpoint<'a>> {
1004 let ep = self
1005 .sandbox
1006 .create_endpoint_with(name, config)
1007 .await
1008 .with_context(|| format!("failed to create endpoint for network {}", self.name))?;
1009 let () = self.attach_endpoint(&ep).await.with_context(|| {
1010 format!("failed to attach endpoint {} to network {}", ep.name, self.name)
1011 })?;
1012 Ok(ep)
1013 }
1014
1015 pub fn create_fake_endpoint(&self) -> Result<TestFakeEndpoint<'a>> {
1017 let (endpoint, server) =
1018 fidl::endpoints::create_proxy::<fnetemul_network::FakeEndpointMarker>();
1019 let () = self.network.create_fake_endpoint(server)?;
1020 return Ok(TestFakeEndpoint { endpoint, _sandbox: self.sandbox });
1021 }
1022
1023 pub async fn start_capture(&self, name: &str) -> Result<PacketCapture> {
1029 let manager = self.sandbox.get_network_manager()?;
1030 let client = manager.get_network(&self.name).await?.expect("network must exist");
1031 zx::ok(self.network.start_capture(name).await?)?;
1032 let sync_proxy = fnetemul_network::NetworkSynchronousProxy::new(client.into_channel());
1033 Ok(PacketCapture { sync_proxy })
1034 }
1035
1036 pub async fn stop_capture(&self) -> Result<()> {
1038 Ok(self.network.stop_capture().await?)
1039 }
1040}
1041
1042pub struct PacketCapture {
1045 sync_proxy: fnetemul_network::NetworkSynchronousProxy,
1046}
1047
1048impl Drop for PacketCapture {
1049 fn drop(&mut self) {
1050 self.sync_proxy
1051 .stop_capture(zx::MonotonicInstant::INFINITE)
1052 .expect("failed to stop packet capture")
1053 }
1054}
1055
1056#[must_use]
1058pub struct TestEndpoint<'a> {
1059 endpoint: fnetemul_network::EndpointProxy,
1060 name: Cow<'a, str>,
1061 _sandbox: &'a TestSandbox,
1062}
1063
1064impl<'a> std::fmt::Debug for TestEndpoint<'a> {
1065 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
1066 let Self { endpoint: _, name, _sandbox } = self;
1067 f.debug_struct("TestEndpoint").field("name", name).finish_non_exhaustive()
1068 }
1069}
1070
1071impl<'a> std::ops::Deref for TestEndpoint<'a> {
1072 type Target = fnetemul_network::EndpointProxy;
1073
1074 fn deref(&self) -> &Self::Target {
1075 &self.endpoint
1076 }
1077}
1078
1079#[must_use]
1081pub struct TestFakeEndpoint<'a> {
1082 endpoint: fnetemul_network::FakeEndpointProxy,
1083 _sandbox: &'a TestSandbox,
1084}
1085
1086impl<'a> std::ops::Deref for TestFakeEndpoint<'a> {
1087 type Target = fnetemul_network::FakeEndpointProxy;
1088
1089 fn deref(&self) -> &Self::Target {
1090 &self.endpoint
1091 }
1092}
1093
1094impl<'a> TestFakeEndpoint<'a> {
1095 pub fn frame_stream(
1099 &self,
1100 ) -> impl futures::Stream<Item = std::result::Result<(Vec<u8>, u64), fidl::Error>> + '_ {
1101 futures::stream::try_unfold(&self.endpoint, |ep| ep.read().map_ok(move |r| Some((r, ep))))
1102 }
1103}
1104
1105async fn to_netdevice_inner(
1108 port: fidl::endpoints::ClientEnd<fnetwork::PortMarker>,
1109) -> Result<(fidl::endpoints::ClientEnd<fnetwork::DeviceMarker>, fnetwork::PortId)> {
1110 let port = port.into_proxy();
1111 let (device, server_end) = fidl::endpoints::create_endpoints::<fnetwork::DeviceMarker>();
1112 let () = port.get_device(server_end)?;
1113 let port_id = port
1114 .get_info()
1115 .await
1116 .context("get port info")?
1117 .id
1118 .ok_or_else(|| anyhow::anyhow!("missing port id"))?;
1119 Ok((device, port_id))
1120}
1121
1122impl<'a> TestEndpoint<'a> {
1123 pub fn into_proxy(self) -> fnetemul_network::EndpointProxy {
1130 let Self { endpoint, name: _, _sandbox: _ } = self;
1131 endpoint
1132 }
1133
1134 pub async fn get_netdevice(
1139 &self,
1140 ) -> Result<(fidl::endpoints::ClientEnd<fnetwork::DeviceMarker>, fnetwork::PortId)> {
1141 let (port, server_end) = fidl::endpoints::create_endpoints();
1142 self.get_port(server_end)
1143 .with_context(|| format!("failed to get device connection for {}", self.name))?;
1144 to_netdevice_inner(port).await
1145 }
1146
1147 pub async fn install(
1153 &self,
1154 installer: fnet_interfaces_admin::InstallerProxy,
1155 InterfaceConfig {
1156 name,
1157 metric,
1158 ipv4_dad_transmits,
1159 ipv6_dad_transmits,
1160 temporary_addresses,
1161 }: InterfaceConfig<'_>,
1162 ) -> Result<(u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
1163 let name = name.map(|n| {
1164 truncate_dropping_front(n.into(), fnet_interfaces::INTERFACE_NAME_LENGTH.into())
1165 .to_string()
1166 });
1167 let (device, port_id) = self.get_netdevice().await?;
1168 let device_control = {
1169 let (control, server_end) =
1170 fidl::endpoints::create_proxy::<fnet_interfaces_admin::DeviceControlMarker>();
1171 let () = installer.install_device(device, server_end).context("install device")?;
1172 control
1173 };
1174 let (control, server_end) = Control::create_endpoints().context("create endpoints")?;
1175 let () = device_control
1176 .create_interface(
1177 &port_id,
1178 server_end,
1179 fnet_interfaces_admin::Options { name, metric, ..Default::default() },
1180 )
1181 .context("create interface")?;
1182 if let Some(ipv4_dad_transmits) = ipv4_dad_transmits {
1183 let _: Option<u16> = set_ipv4_dad_transmits(&control, ipv4_dad_transmits)
1184 .await
1185 .context("set dad transmits")?;
1186 }
1187 if let Some(ipv6_dad_transmits) = ipv6_dad_transmits {
1188 let _: Option<u16> = set_ipv6_dad_transmits(&control, ipv6_dad_transmits)
1189 .await
1190 .context("set dad transmits")?;
1191 }
1192 if let Some(enabled) = temporary_addresses {
1193 set_temporary_address_generation_enabled(&control, enabled)
1194 .await
1195 .context("set temporary addresses")?;
1196 }
1197
1198 let id = control.get_id().await.context("get id")?;
1199 Ok((id, control, device_control))
1200 }
1201
1202 pub async fn add_to_stack(
1207 &self,
1208 realm: &TestRealm<'a>,
1209 config: InterfaceConfig<'a>,
1210 ) -> Result<(u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
1211 let installer = realm
1212 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
1213 .context("connect to protocol")?;
1214
1215 self.install(installer, config).await
1216 }
1217
1218 pub async fn into_interface_in_realm(self, realm: &TestRealm<'a>) -> Result<TestInterface<'a>> {
1220 self.into_interface_in_realm_with_name(realm, Default::default()).await
1221 }
1222
1223 pub async fn into_interface_in_realm_with_name(
1226 self,
1227 realm: &TestRealm<'a>,
1228 config: InterfaceConfig<'a>,
1229 ) -> Result<TestInterface<'a>> {
1230 let installer = realm
1231 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
1232 .context("connect to protocol")?;
1233
1234 let (id, control, device_control) =
1235 self.install(installer, config).await.context("failed to install")?;
1236
1237 Ok(TestInterface {
1238 endpoint: self,
1239 id,
1240 realm: realm.clone(),
1241 control,
1242 device_control: Some(device_control),
1243 dhcp_client_task: futures::lock::Mutex::default(),
1244 })
1245 }
1246}
1247
1248#[derive(Copy, Clone, PartialEq, Debug)]
1250pub enum DhcpClientVersion {
1251 InStack,
1253 OutOfStack,
1255}
1256
1257pub trait DhcpClient {
1259 const DHCP_CLIENT_VERSION: DhcpClientVersion;
1261}
1262
1263pub enum InStack {}
1265
1266impl DhcpClient for InStack {
1267 const DHCP_CLIENT_VERSION: DhcpClientVersion = DhcpClientVersion::InStack;
1268}
1269
1270pub enum OutOfStack {}
1272
1273impl DhcpClient for OutOfStack {
1274 const DHCP_CLIENT_VERSION: DhcpClientVersion = DhcpClientVersion::OutOfStack;
1275}
1276
1277#[must_use]
1283pub struct TestInterface<'a> {
1284 endpoint: TestEndpoint<'a>,
1285 realm: TestRealm<'a>,
1286 id: u64,
1287 control: Control,
1288 device_control: Option<fnet_interfaces_admin::DeviceControlProxy>,
1289 dhcp_client_task: futures::lock::Mutex<Option<fnet_dhcp_ext::testutil::DhcpClientTask>>,
1290}
1291
1292impl<'a> std::fmt::Debug for TestInterface<'a> {
1293 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
1294 let Self { endpoint, id, realm: _, control: _, device_control: _, dhcp_client_task: _ } =
1295 self;
1296 f.debug_struct("TestInterface")
1297 .field("endpoint", endpoint)
1298 .field("id", id)
1299 .finish_non_exhaustive()
1300 }
1301}
1302
1303impl<'a> std::ops::Deref for TestInterface<'a> {
1304 type Target = fnetemul_network::EndpointProxy;
1305
1306 fn deref(&self) -> &Self::Target {
1307 &self.endpoint
1308 }
1309}
1310
1311impl<'a> TestInterface<'a> {
1312 pub fn id(&self) -> u64 {
1314 self.id
1315 }
1316
1317 pub fn endpoint(&self) -> &TestEndpoint<'a> {
1319 &self.endpoint
1320 }
1321
1322 pub fn control(&self) -> &Control {
1324 &self.control
1325 }
1326
1327 pub async fn get_authorization(&self) -> Result<GrantForInterfaceAuthorization> {
1329 Ok(self.control.get_authorization_for_interface().await?)
1330 }
1331
1332 pub fn connect_stack(&self) -> Result<fnet_stack::StackProxy> {
1334 self.realm.connect_to_protocol::<fnet_stack::StackMarker>()
1335 }
1336
1337 async fn add_route<
1342 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1343 >(
1344 &self,
1345 destination: Subnet<I::Addr>,
1346 next_hop: Option<SpecifiedAddr<I::Addr>>,
1347 metric: fnet_routes::SpecifiedMetric,
1348 ) -> Result<bool> {
1349 let route_set = self.create_authenticated_global_route_set::<I>().await?;
1350 fnet_routes_ext::admin::add_route::<I>(
1351 &route_set,
1352 &fnet_routes_ext::Route::<I>::new_forward(destination, self.id(), next_hop, metric)
1353 .try_into()
1354 .expect("convert to FIDL should succeed"),
1355 )
1356 .await
1357 .context("FIDL error adding route")?
1358 .map_err(|e| anyhow::anyhow!("error adding route: {e:?}"))
1359 }
1360
1361 pub async fn add_route_either(
1367 &self,
1368 destination: fnet::Subnet,
1369 next_hop: Option<fnet::IpAddress>,
1370 metric: fnet_routes::SpecifiedMetric,
1371 ) -> Result<bool> {
1372 let fnet::Subnet { addr: destination_addr, prefix_len } = destination;
1373 match destination_addr {
1374 fnet::IpAddress::Ipv4(destination_addr) => {
1375 let next_hop = match next_hop {
1376 Some(fnet::IpAddress::Ipv4(next_hop)) => Some(
1377 SpecifiedAddr::new(net_types::ip::Ipv4Addr::from_ext(next_hop))
1378 .ok_or(anyhow::anyhow!("next hop must not be unspecified address"))?,
1379 ),
1380 Some(fnet::IpAddress::Ipv6(_)) => {
1381 return Err(anyhow::anyhow!(
1382 "next hop must be same IP version as destination"
1383 ))
1384 }
1385 None => None,
1386 };
1387 self.add_route::<Ipv4>(
1388 Subnet::new(destination_addr.into_ext(), prefix_len)
1389 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1390 next_hop,
1391 metric,
1392 )
1393 .await
1394 }
1395 fnet::IpAddress::Ipv6(destination_addr) => {
1396 let next_hop = match next_hop {
1397 Some(fnet::IpAddress::Ipv6(next_hop)) => Some(
1398 SpecifiedAddr::new(net_types::ip::Ipv6Addr::from_ext(next_hop))
1399 .ok_or(anyhow::anyhow!("next hop must not be unspecified address"))?,
1400 ),
1401 Some(fnet::IpAddress::Ipv4(_)) => {
1402 return Err(anyhow::anyhow!(
1403 "next hop must be same IP version as destination"
1404 ))
1405 }
1406 None => None,
1407 };
1408 self.add_route::<Ipv6>(
1409 Subnet::new(destination_addr.into_ext(), prefix_len)
1410 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1411 next_hop,
1412 metric,
1413 )
1414 .await
1415 }
1416 }
1417 }
1418
1419 async fn remove_route<
1424 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1425 >(
1426 &self,
1427 destination: Subnet<I::Addr>,
1428 next_hop: Option<SpecifiedAddr<I::Addr>>,
1429 metric: fnet_routes::SpecifiedMetric,
1430 ) -> Result<bool> {
1431 let route_set = self.create_authenticated_global_route_set::<I>().await?;
1432 fnet_routes_ext::admin::remove_route::<I>(
1433 &route_set,
1434 &fnet_routes_ext::Route::<I>::new_forward(destination, self.id(), next_hop, metric)
1435 .try_into()
1436 .expect("convert to FIDL should succeed"),
1437 )
1438 .await
1439 .context("FIDL error removing route")?
1440 .map_err(|e| anyhow::anyhow!("error removing route: {e:?}"))
1441 }
1442
1443 async fn remove_route_either(
1449 &self,
1450 destination: fnet::Subnet,
1451 next_hop: Option<fnet::IpAddress>,
1452 metric: fnet_routes::SpecifiedMetric,
1453 ) -> Result<bool> {
1454 let fnet::Subnet { addr: destination_addr, prefix_len } = destination;
1455 match destination_addr {
1456 fnet::IpAddress::Ipv4(destination_addr) => {
1457 let next_hop = match next_hop {
1458 Some(fnet::IpAddress::Ipv4(next_hop)) => Some(
1459 SpecifiedAddr::new(net_types::ip::Ipv4Addr::from_ext(next_hop))
1460 .ok_or(anyhow::anyhow!("next hop must not be unspecified address"))?,
1461 ),
1462 Some(fnet::IpAddress::Ipv6(_)) => {
1463 return Err(anyhow::anyhow!(
1464 "next hop must be same IP version as destination"
1465 ))
1466 }
1467 None => None,
1468 };
1469 self.remove_route::<Ipv4>(
1470 Subnet::new(destination_addr.into_ext(), prefix_len)
1471 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1472 next_hop,
1473 metric,
1474 )
1475 .await
1476 }
1477 fnet::IpAddress::Ipv6(destination_addr) => {
1478 let next_hop = match next_hop {
1479 Some(fnet::IpAddress::Ipv6(next_hop)) => Some(
1480 SpecifiedAddr::new(net_types::ip::Ipv6Addr::from_ext(next_hop))
1481 .ok_or(anyhow::anyhow!("next hop must not be unspecified address"))?,
1482 ),
1483 Some(fnet::IpAddress::Ipv4(_)) => {
1484 return Err(anyhow::anyhow!(
1485 "next hop must be same IP version as destination"
1486 ))
1487 }
1488 None => None,
1489 };
1490 self.remove_route::<Ipv6>(
1491 Subnet::new(destination_addr.into_ext(), prefix_len)
1492 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1493 next_hop,
1494 metric,
1495 )
1496 .await
1497 }
1498 }
1499 }
1500
1501 pub async fn add_subnet_route(&self, subnet: fnet::Subnet) -> Result<()> {
1503 let subnet = fnet_ext::apply_subnet_mask(subnet);
1504 let newly_added = self
1505 .add_route_either(
1506 subnet,
1507 None,
1508 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1509 )
1510 .await?;
1511
1512 if !newly_added {
1513 Err(anyhow::anyhow!(
1514 "route to {subnet:?} on {} should not have already existed",
1515 self.id()
1516 ))
1517 } else {
1518 Ok(())
1519 }
1520 }
1521
1522 pub async fn del_subnet_route(&self, subnet: fnet::Subnet) -> Result<()> {
1524 let subnet = fnet_ext::apply_subnet_mask(subnet);
1525 let newly_removed = self
1526 .remove_route_either(
1527 subnet,
1528 None,
1529 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1530 )
1531 .await?;
1532
1533 if !newly_removed {
1534 Err(anyhow::anyhow!(
1535 "route to {subnet:?} on {} should have previously existed before being removed",
1536 self.id()
1537 ))
1538 } else {
1539 Ok(())
1540 }
1541 }
1542
1543 pub async fn add_default_route_with_metric(
1545 &self,
1546 next_hop: fnet::IpAddress,
1547 metric: fnet_routes::SpecifiedMetric,
1548 ) -> Result<()> {
1549 let corresponding_default_subnet = match next_hop {
1550 fnet::IpAddress::Ipv4(_) => net_declare::fidl_subnet!("0.0.0.0/0"),
1551 fnet::IpAddress::Ipv6(_) => net_declare::fidl_subnet!("::/0"),
1552 };
1553
1554 let newly_added =
1555 self.add_route_either(corresponding_default_subnet, Some(next_hop), metric).await?;
1556
1557 if !newly_added {
1558 Err(anyhow::anyhow!(
1559 "default route through {} via {next_hop:?} already exists",
1560 self.id()
1561 ))
1562 } else {
1563 Ok(())
1564 }
1565 }
1566
1567 pub async fn add_default_route_with_explicit_metric(
1569 &self,
1570 next_hop: fnet::IpAddress,
1571 metric: u32,
1572 ) -> Result<()> {
1573 self.add_default_route_with_metric(
1574 next_hop,
1575 fnet_routes::SpecifiedMetric::ExplicitMetric(metric),
1576 )
1577 .await
1578 }
1579
1580 pub async fn add_default_route(&self, next_hop: fnet::IpAddress) -> Result<()> {
1582 self.add_default_route_with_metric(
1583 next_hop,
1584 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1585 )
1586 .await
1587 }
1588
1589 pub async fn remove_default_route(&self, next_hop: fnet::IpAddress) -> Result<()> {
1591 let corresponding_default_subnet = match next_hop {
1592 fnet::IpAddress::Ipv4(_) => net_declare::fidl_subnet!("0.0.0.0/0"),
1593 fnet::IpAddress::Ipv6(_) => net_declare::fidl_subnet!("::/0"),
1594 };
1595
1596 let newly_removed = self
1597 .remove_route_either(
1598 corresponding_default_subnet,
1599 Some(next_hop),
1600 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1601 )
1602 .await?;
1603
1604 if !newly_removed {
1605 Err(anyhow::anyhow!(
1606 "default route through {} via {next_hop:?} does not exist",
1607 self.id()
1608 ))
1609 } else {
1610 Ok(())
1611 }
1612 }
1613
1614 pub async fn add_gateway_route(
1616 &self,
1617 destination: fnet::Subnet,
1618 next_hop: fnet::IpAddress,
1619 ) -> Result<()> {
1620 let newly_added = self
1621 .add_route_either(
1622 destination,
1623 Some(next_hop),
1624 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1625 )
1626 .await?;
1627
1628 if !newly_added {
1629 Err(anyhow::anyhow!(
1630 "should have newly added route to {destination:?} via {next_hop:?} through {}",
1631 self.id()
1632 ))
1633 } else {
1634 Ok(())
1635 }
1636 }
1637
1638 pub async fn create_authenticated_global_route_set<
1640 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1641 >(
1642 &self,
1643 ) -> Result<<I::RouteSetMarker as ProtocolMarker>::Proxy> {
1644 #[derive(GenericOverIp)]
1645 #[generic_over_ip(I, Ip)]
1646 struct Out<'a, I: fnet_routes_ext::admin::FidlRouteAdminIpExt>(
1647 LocalBoxFuture<'a, <I::RouteSetMarker as ProtocolMarker>::Proxy>,
1648 );
1649
1650 let Out(proxy_fut) = I::map_ip_out(
1651 self,
1652 |this| {
1653 Out(this
1654 .get_global_route_set_v4()
1655 .map(|result| result.expect("get global route set"))
1656 .boxed_local())
1657 },
1658 |this| {
1659 Out(this
1660 .get_global_route_set_v6()
1661 .map(|result| result.expect("get global route set"))
1662 .boxed_local())
1663 },
1664 );
1665
1666 let route_set = proxy_fut.await;
1667 let fnet_interfaces_admin::GrantForInterfaceAuthorization { interface_id, token } =
1668 self.get_authorization().await.expect("get interface grant");
1669 fnet_routes_ext::admin::authenticate_for_interface::<I>(
1670 &route_set,
1671 fnet_interfaces_admin::ProofOfInterfaceAuthorization { interface_id, token },
1672 )
1673 .await
1674 .expect("authentication should not have FIDL error")
1675 .expect("authentication should succeed");
1676 Ok(route_set)
1677 }
1678
1679 async fn get_global_route_set_v4(&self) -> Result<fnet_routes_admin::RouteSetV4Proxy> {
1680 let root_routes = self
1681 .realm
1682 .connect_to_protocol::<fnet_root::RoutesV4Marker>()
1683 .expect("get fuchsia.net.root.RoutesV4");
1684 let (route_set, server_end) =
1685 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteSetV4Marker>();
1686 root_routes.global_route_set(server_end).expect("calling global_route_set should succeed");
1687 Ok(route_set)
1688 }
1689
1690 async fn get_global_route_set_v6(&self) -> Result<fnet_routes_admin::RouteSetV6Proxy> {
1691 let root_routes = self
1692 .realm
1693 .connect_to_protocol::<fnet_root::RoutesV6Marker>()
1694 .expect("get fuchsia.net.root.RoutesV6");
1695 let (route_set, server_end) =
1696 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteSetV6Marker>();
1697 root_routes.global_route_set(server_end).expect("calling global_route_set should succeed");
1698 Ok(route_set)
1699 }
1700
1701 async fn get_properties(
1703 &self,
1704 included_addresses: fnet_interfaces_ext::IncludedAddresses,
1705 ) -> Result<fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>> {
1706 let interface_state = self.realm.connect_to_protocol::<fnet_interfaces::StateMarker>()?;
1707 let properties = fnet_interfaces_ext::existing(
1708 fnet_interfaces_ext::event_stream_from_state(&interface_state, included_addresses)?,
1709 fnet_interfaces_ext::InterfaceState::<(), _>::Unknown(self.id),
1710 )
1711 .await
1712 .context("failed to get existing interfaces")?;
1713 match properties {
1714 fnet_interfaces_ext::InterfaceState::Unknown(id) => Err(anyhow::anyhow!(
1715 "could not find interface {} for endpoint {}",
1716 id,
1717 self.endpoint.name
1718 )),
1719 fnet_interfaces_ext::InterfaceState::Known(
1720 fnet_interfaces_ext::PropertiesAndState { properties, state: () },
1721 ) => Ok(properties),
1722 }
1723 }
1724
1725 pub async fn get_addrs(
1727 &self,
1728 included_addresses: fnet_interfaces_ext::IncludedAddresses,
1729 ) -> Result<Vec<fnet_interfaces_ext::Address<fnet_interfaces_ext::AllInterest>>> {
1730 let fnet_interfaces_ext::Properties { addresses, .. } =
1731 self.get_properties(included_addresses).await?;
1732 Ok(addresses)
1733 }
1734
1735 pub async fn get_interface_name(&self) -> Result<String> {
1737 let fnet_interfaces_ext::Properties { name, .. } =
1738 self.get_properties(fnet_interfaces_ext::IncludedAddresses::OnlyAssigned).await?;
1739 Ok(name)
1740 }
1741
1742 pub async fn get_port_class(&self) -> Result<fnet_interfaces_ext::PortClass> {
1744 let fnet_interfaces_ext::Properties { port_class, .. } =
1745 self.get_properties(fnet_interfaces_ext::IncludedAddresses::OnlyAssigned).await?;
1746 Ok(port_class)
1747 }
1748
1749 pub async fn mac(&self) -> fnet::MacAddress {
1751 let (port, server_end) =
1752 fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_network::PortMarker>();
1753 self.get_port(server_end).expect("get_port");
1754 let (mac_addressing, server_end) =
1755 fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_network::MacAddressingMarker>();
1756 port.get_mac(server_end).expect("get_mac");
1757 mac_addressing.get_unicast_address().await.expect("get_unicast_address")
1758 }
1759
1760 async fn set_dhcp_client_enabled(&self, enable: bool) -> Result<()> {
1761 self.connect_stack()
1762 .context("connect stack")?
1763 .set_dhcp_client_enabled(self.id, enable)
1764 .await
1765 .context("failed to call SetDhcpClientEnabled")?
1766 .map_err(|e| anyhow!("{:?}", e))
1767 }
1768
1769 pub async fn start_dhcp<D: DhcpClient>(&self) -> Result<()> {
1771 match D::DHCP_CLIENT_VERSION {
1772 DhcpClientVersion::InStack => self.start_dhcp_in_stack().await,
1773 DhcpClientVersion::OutOfStack => self.start_dhcp_client_out_of_stack().await,
1774 }
1775 }
1776
1777 async fn start_dhcp_in_stack(&self) -> Result<()> {
1778 self.set_dhcp_client_enabled(true).await.context("failed to start dhcp client")
1779 }
1780
1781 async fn start_dhcp_client_out_of_stack(&self) -> Result<()> {
1782 let Self { endpoint: _, realm, id, control, device_control: _, dhcp_client_task } = self;
1783 let id = NonZeroU64::new(*id).expect("interface ID should be nonzero");
1784 let mut dhcp_client_task = dhcp_client_task.lock().await;
1785 let dhcp_client_task = dhcp_client_task.deref_mut();
1786
1787 let provider = realm
1788 .connect_to_protocol::<fnet_dhcp::ClientProviderMarker>()
1789 .expect("get fuchsia.net.dhcp.ClientProvider");
1790
1791 provider.check_presence().await.expect("check presence should succeed");
1792
1793 let client = provider.new_client_ext(id, fnet_dhcp_ext::default_new_client_params());
1794 let control = control.clone();
1795 let route_set_provider = realm
1796 .connect_to_protocol::<fnet_routes_admin::RouteTableV4Marker>()
1797 .expect("get fuchsia.net.routes.RouteTableV4");
1798 let (route_set, server_end) =
1799 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteSetV4Marker>();
1800 route_set_provider.new_route_set(server_end).expect("calling new_route_set should succeed");
1801 let task = fnet_dhcp_ext::testutil::DhcpClientTask::new(client, id, route_set, control);
1802 *dhcp_client_task = Some(task);
1803 Ok(())
1804 }
1805
1806 pub async fn stop_dhcp<D: DhcpClient>(&self) -> Result<()> {
1808 match D::DHCP_CLIENT_VERSION {
1809 DhcpClientVersion::InStack => self.stop_dhcp_in_stack().await,
1810 DhcpClientVersion::OutOfStack => {
1811 self.stop_dhcp_out_of_stack().await;
1812 Ok(())
1813 }
1814 }
1815 }
1816
1817 async fn stop_dhcp_in_stack(&self) -> Result<()> {
1818 self.set_dhcp_client_enabled(false).await.context("failed to stop dhcp client")
1819 }
1820
1821 async fn stop_dhcp_out_of_stack(&self) {
1822 let Self { endpoint: _, realm: _, id: _, control: _, device_control: _, dhcp_client_task } =
1823 self;
1824 let mut dhcp_client_task = dhcp_client_task.lock().await;
1825 if let Some(task) = dhcp_client_task.deref_mut().take() {
1826 task.shutdown().await.expect("client shutdown should succeed");
1827 }
1828 }
1829
1830 pub async fn add_address(&self, subnet: fnet::Subnet) -> Result<()> {
1833 let (address_state_provider, server) =
1834 fidl::endpoints::create_proxy::<fnet_interfaces_admin::AddressStateProviderMarker>();
1835 let () = address_state_provider.detach().context("detach address lifetime")?;
1836 let () = self
1837 .control
1838 .add_address(&subnet, &fnet_interfaces_admin::AddressParameters::default(), server)
1839 .context("FIDL error")?;
1840
1841 let mut state_stream =
1842 fnet_interfaces_ext::admin::assignment_state_stream(address_state_provider);
1843 fnet_interfaces_ext::admin::wait_assignment_state(
1844 &mut state_stream,
1845 fnet_interfaces::AddressAssignmentState::Assigned,
1846 )
1847 .await?;
1848 Ok(())
1849 }
1850
1851 pub async fn add_address_and_subnet_route(&self, subnet: fnet::Subnet) -> Result<()> {
1854 let (address_state_provider, server) =
1855 fidl::endpoints::create_proxy::<fnet_interfaces_admin::AddressStateProviderMarker>();
1856 address_state_provider.detach().context("detach address lifetime")?;
1857 self.control
1858 .add_address(
1859 &subnet,
1860 &fnet_interfaces_admin::AddressParameters {
1861 add_subnet_route: Some(true),
1862 ..Default::default()
1863 },
1864 server,
1865 )
1866 .context("FIDL error")?;
1867
1868 let state_stream =
1869 fnet_interfaces_ext::admin::assignment_state_stream(address_state_provider);
1870 let mut state_stream = pin!(state_stream);
1871
1872 fnet_interfaces_ext::admin::wait_assignment_state(
1873 &mut state_stream,
1874 fnet_interfaces::AddressAssignmentState::Assigned,
1875 )
1876 .await
1877 .context("assignment state")?;
1878 Ok(())
1879 }
1880
1881 pub async fn del_address_and_subnet_route(
1883 &self,
1884 addr_with_prefix: fnet::Subnet,
1885 ) -> Result<bool> {
1886 let did_remove =
1887 self.control.remove_address(&addr_with_prefix).await.context("FIDL error").and_then(
1888 |res| {
1889 res.map_err(|e: fnet_interfaces_admin::ControlRemoveAddressError| {
1890 anyhow::anyhow!("{:?}", e)
1891 })
1892 },
1893 )?;
1894
1895 if did_remove {
1896 let destination = fnet_ext::apply_subnet_mask(addr_with_prefix);
1897 let newly_removed_route = self
1898 .remove_route_either(
1899 destination,
1900 None,
1901 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1902 )
1903 .await?;
1904
1905 let _: bool = newly_removed_route;
1908 }
1909 Ok(did_remove)
1910 }
1911
1912 pub async fn remove_ipv6_linklocal_addresses(
1916 &self,
1917 ) -> Result<Vec<fnet_interfaces_ext::Address<fnet_interfaces_ext::AllInterest>>> {
1918 let mut result = Vec::new();
1919 for address in self.get_addrs(fnet_interfaces_ext::IncludedAddresses::All).await? {
1920 let fnet_interfaces_ext::Address { addr: fnet::Subnet { addr, prefix_len }, .. } =
1921 &address;
1922 match addr {
1923 fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address { addr: _ }) => {
1924 continue
1925 }
1926 fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address { addr }) => {
1927 let v6_addr = net_types::ip::Ipv6Addr::from_bytes(*addr);
1928 if !v6_addr.is_unicast_link_local() {
1929 continue;
1930 }
1931 }
1932 }
1933 let _newly_removed: bool = self
1934 .del_address_and_subnet_route(fnet::Subnet { addr: *addr, prefix_len: *prefix_len })
1935 .await?;
1936 result.push(address);
1937 }
1938 Ok(result)
1939 }
1940
1941 async fn set_configuration(&self, config: fnet_interfaces_admin::Configuration) -> Result<()> {
1951 let fnet_interfaces_admin::Configuration {
1952 ipv4: previous_ipv4, ipv6: previous_ipv6, ..
1953 } = self
1954 .control()
1955 .set_configuration(&config.clone())
1956 .await
1957 .context("FIDL error")?
1958 .map_err(|e| anyhow!("set configuration error: {:?}", e))?;
1959
1960 fn verify_config_changed<T: Eq>(previous: Option<T>, current: Option<T>) -> Result<()> {
1961 if let Some(current) = current {
1962 let previous = previous.ok_or_else(|| anyhow!("configuration not supported"))?;
1963 if previous == current {
1964 return Err(anyhow!("configuration change is a no-op"));
1965 }
1966 }
1967 Ok(())
1968 }
1969
1970 let fnet_interfaces_admin::Configuration { ipv4, ipv6, .. } = config;
1971 if let Some(fnet_interfaces_admin::Ipv4Configuration {
1972 unicast_forwarding,
1973 multicast_forwarding,
1974 ..
1975 }) = ipv4
1976 {
1977 let fnet_interfaces_admin::Ipv4Configuration {
1978 unicast_forwarding: previous_unicast_forwarding,
1979 multicast_forwarding: previous_multicast_forwarding,
1980 ..
1981 } = previous_ipv4.ok_or_else(|| anyhow!("IPv4 configuration not supported"))?;
1982 verify_config_changed(previous_unicast_forwarding, unicast_forwarding)
1983 .context("IPv4 unicast forwarding")?;
1984 verify_config_changed(previous_multicast_forwarding, multicast_forwarding)
1985 .context("IPv4 multicast forwarding")?;
1986 }
1987 if let Some(fnet_interfaces_admin::Ipv6Configuration {
1988 unicast_forwarding,
1989 multicast_forwarding,
1990 ..
1991 }) = ipv6
1992 {
1993 let fnet_interfaces_admin::Ipv6Configuration {
1994 unicast_forwarding: previous_unicast_forwarding,
1995 multicast_forwarding: previous_multicast_forwarding,
1996 ..
1997 } = previous_ipv6.ok_or_else(|| anyhow!("IPv6 configuration not supported"))?;
1998 verify_config_changed(previous_unicast_forwarding, unicast_forwarding)
1999 .context("IPv6 unicast forwarding")?;
2000 verify_config_changed(previous_multicast_forwarding, multicast_forwarding)
2001 .context("IPv6 multicast forwarding")?;
2002 }
2003 Ok(())
2004 }
2005
2006 pub async fn set_ipv6_forwarding_enabled(&self, enabled: bool) -> Result<()> {
2008 self.set_configuration(fnet_interfaces_admin::Configuration {
2009 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
2010 unicast_forwarding: Some(enabled),
2011 ..Default::default()
2012 }),
2013 ..Default::default()
2014 })
2015 .await
2016 }
2017
2018 pub async fn set_ipv4_forwarding_enabled(&self, enabled: bool) -> Result<()> {
2020 self.set_configuration(fnet_interfaces_admin::Configuration {
2021 ipv4: Some(fnet_interfaces_admin::Ipv4Configuration {
2022 unicast_forwarding: Some(enabled),
2023 ..Default::default()
2024 }),
2025 ..Default::default()
2026 })
2027 .await
2028 }
2029
2030 pub async fn remove(
2033 self,
2034 ) -> Result<(fnetemul_network::EndpointProxy, Option<fnet_interfaces_admin::DeviceControlProxy>)>
2035 {
2036 let Self {
2037 endpoint: TestEndpoint { endpoint, name: _, _sandbox: _ },
2038 id: _,
2039 realm: _,
2040 control,
2041 device_control,
2042 dhcp_client_task: _,
2043 } = self;
2044 std::mem::drop(control);
2048 Ok((endpoint, device_control))
2049 }
2050
2051 pub fn remove_device(self) -> (Control, Option<fnet_interfaces_admin::DeviceControlProxy>) {
2055 let Self {
2056 endpoint: TestEndpoint { endpoint, name: _, _sandbox: _ },
2057 id: _,
2058 realm: _,
2059 control,
2060 device_control,
2061 dhcp_client_task: _,
2062 } = self;
2063 std::mem::drop(endpoint);
2064 (control, device_control)
2065 }
2066
2067 pub async fn wait_removal(self) -> Result<fnet_interfaces_admin::InterfaceRemovedReason> {
2069 let Self {
2070 endpoint: _endpoint,
2072 id: _,
2073 realm: _,
2074 control,
2075 dhcp_client_task: _,
2076 device_control: _device_control,
2078 } = self;
2079 match control.wait_termination().await {
2080 fnet_interfaces_ext::admin::TerminalError::Fidl(e) => {
2081 Err(e).context("waiting interface control termination")
2082 }
2083 fnet_interfaces_ext::admin::TerminalError::Terminal(reason) => Ok(reason),
2084 }
2085 }
2086
2087 pub async fn set_ipv4_dad_transmits(&self, dad_transmits: u16) -> Result<Option<u16>> {
2091 set_ipv4_dad_transmits(self.control(), dad_transmits).await
2092 }
2093
2094 pub async fn set_ipv6_dad_transmits(&self, dad_transmits: u16) -> Result<Option<u16>> {
2098 set_ipv6_dad_transmits(self.control(), dad_transmits).await
2099 }
2100
2101 pub async fn set_temporary_address_generation_enabled(&self, enabled: bool) -> Result<()> {
2104 set_temporary_address_generation_enabled(self.control(), enabled).await
2105 }
2106}
2107
2108async fn set_ipv4_dad_transmits(control: &Control, dad_transmits: u16) -> Result<Option<u16>> {
2109 control
2110 .set_configuration(&fnet_interfaces_admin::Configuration {
2111 ipv4: Some(fnet_interfaces_admin::Ipv4Configuration {
2112 arp: Some(fnet_interfaces_admin::ArpConfiguration {
2113 dad: Some(fnet_interfaces_admin::DadConfiguration {
2114 transmits: Some(dad_transmits),
2115 ..Default::default()
2116 }),
2117 ..Default::default()
2118 }),
2119 ..Default::default()
2120 }),
2121 ..Default::default()
2122 })
2123 .await?
2124 .map(|config| config.ipv4?.arp?.dad?.transmits)
2125 .map_err(|e| anyhow::anyhow!("set configuration error {e:?}"))
2126}
2127
2128async fn set_ipv6_dad_transmits(control: &Control, dad_transmits: u16) -> Result<Option<u16>> {
2129 control
2130 .set_configuration(&fnet_interfaces_admin::Configuration {
2131 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
2132 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
2133 dad: Some(fnet_interfaces_admin::DadConfiguration {
2134 transmits: Some(dad_transmits),
2135 ..Default::default()
2136 }),
2137 ..Default::default()
2138 }),
2139 ..Default::default()
2140 }),
2141 ..Default::default()
2142 })
2143 .await?
2144 .map(|config| config.ipv6?.ndp?.dad?.transmits)
2145 .map_err(|e| anyhow::anyhow!("set configuration error {e:?}"))
2146}
2147
2148async fn set_temporary_address_generation_enabled(control: &Control, enabled: bool) -> Result<()> {
2149 let _config: fnet_interfaces_admin::Configuration = control
2150 .set_configuration(&fnet_interfaces_admin::Configuration {
2151 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
2152 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
2153 slaac: Some(fnet_interfaces_admin::SlaacConfiguration {
2154 temporary_address: Some(enabled),
2155 ..Default::default()
2156 }),
2157 ..Default::default()
2158 }),
2159 ..Default::default()
2160 }),
2161 ..Default::default()
2162 })
2163 .await
2164 .context("FIDL error")?
2165 .map_err(|e| anyhow::anyhow!("set configuration error {e:?}"))?;
2166 Ok(())
2167}
2168
2169fn get_socket2_domain(addr: &std::net::SocketAddr) -> fposix_socket::Domain {
2171 let domain = match addr {
2172 std::net::SocketAddr::V4(_) => fposix_socket::Domain::Ipv4,
2173 std::net::SocketAddr::V6(_) => fposix_socket::Domain::Ipv6,
2174 };
2175
2176 domain
2177}
2178
2179pub trait RealmUdpSocket: Sized {
2181 fn bind_in_realm<'a>(
2183 realm: &'a TestRealm<'a>,
2184 addr: std::net::SocketAddr,
2185 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2186}
2187
2188impl RealmUdpSocket for std::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 async move {
2194 let sock = realm
2195 .datagram_socket(
2196 get_socket2_domain(&addr),
2197 fposix_socket::DatagramSocketProtocol::Udp,
2198 )
2199 .await
2200 .context("failed to create socket")?;
2201
2202 let () = sock.bind(&addr.into()).context("bind failed")?;
2203
2204 Result::Ok(sock.into())
2205 }
2206 .boxed_local()
2207 }
2208}
2209
2210impl RealmUdpSocket for fuchsia_async::net::UdpSocket {
2211 fn bind_in_realm<'a>(
2212 realm: &'a TestRealm<'a>,
2213 addr: std::net::SocketAddr,
2214 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2215 std::net::UdpSocket::bind_in_realm(realm, addr)
2216 .and_then(|udp| {
2217 futures::future::ready(
2218 fuchsia_async::net::UdpSocket::from_socket(udp)
2219 .context("failed to create fuchsia_async socket"),
2220 )
2221 })
2222 .boxed_local()
2223 }
2224}
2225
2226pub trait RealmTcpListener: Sized {
2228 fn listen_in_realm<'a>(
2230 realm: &'a TestRealm<'a>,
2231 addr: std::net::SocketAddr,
2232 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2233 Self::listen_in_realm_with(realm, addr, |_: &socket2::Socket| Ok(()))
2234 }
2235
2236 fn listen_in_realm_with<'a>(
2239 realm: &'a TestRealm<'a>,
2240 addr: std::net::SocketAddr,
2241 setup: impl FnOnce(&socket2::Socket) -> Result<()> + 'a,
2242 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2243}
2244
2245impl RealmTcpListener for std::net::TcpListener {
2246 fn listen_in_realm_with<'a>(
2247 realm: &'a TestRealm<'a>,
2248 addr: std::net::SocketAddr,
2249 setup: impl FnOnce(&socket2::Socket) -> Result<()> + 'a,
2250 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2251 async move {
2252 let sock = realm
2253 .stream_socket(get_socket2_domain(&addr), fposix_socket::StreamSocketProtocol::Tcp)
2254 .await
2255 .context("failed to create server socket")?;
2256 let () = setup(&sock)?;
2257 let () = sock.bind(&addr.into()).context("failed to bind server socket")?;
2258 let () = sock.listen(128).context("failed to listen on server socket")?;
2261
2262 Result::Ok(sock.into())
2263 }
2264 .boxed_local()
2265 }
2266}
2267
2268impl RealmTcpListener for fuchsia_async::net::TcpListener {
2269 fn listen_in_realm_with<'a>(
2270 realm: &'a TestRealm<'a>,
2271 addr: std::net::SocketAddr,
2272 setup: impl FnOnce(&socket2::Socket) -> Result<()> + 'a,
2273 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2274 std::net::TcpListener::listen_in_realm_with(realm, addr, setup)
2275 .and_then(|listener| {
2276 futures::future::ready(
2277 fuchsia_async::net::TcpListener::from_std(listener)
2278 .context("failed to create fuchsia_async socket"),
2279 )
2280 })
2281 .boxed_local()
2282 }
2283}
2284
2285pub trait RealmTcpStream: Sized {
2287 fn connect_in_realm<'a>(
2289 realm: &'a TestRealm<'a>,
2290 addr: std::net::SocketAddr,
2291 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2292
2293 fn bind_and_connect_in_realm<'a>(
2295 realm: &'a TestRealm<'a>,
2296 local: std::net::SocketAddr,
2297 dst: std::net::SocketAddr,
2298 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2299
2300 fn connect_in_realm_with_sock<'a, F: FnOnce(&socket2::Socket) -> Result + 'a>(
2305 realm: &'a TestRealm<'a>,
2306 dst: std::net::SocketAddr,
2307 with_sock: F,
2308 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2309
2310 }
2312
2313impl RealmTcpStream for fuchsia_async::net::TcpStream {
2314 fn connect_in_realm<'a>(
2315 realm: &'a TestRealm<'a>,
2316 addr: std::net::SocketAddr,
2317 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2318 Self::connect_in_realm_with_sock(realm, addr, |_: &socket2::Socket| Ok(()))
2319 }
2320
2321 fn bind_and_connect_in_realm<'a>(
2322 realm: &'a TestRealm<'a>,
2323 local: std::net::SocketAddr,
2324 dst: std::net::SocketAddr,
2325 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2326 Self::connect_in_realm_with_sock(realm, dst, move |sock| {
2327 sock.bind(&local.into()).context("failed to bind")
2328 })
2329 }
2330
2331 fn connect_in_realm_with_sock<'a, F: FnOnce(&socket2::Socket) -> Result + 'a>(
2332 realm: &'a TestRealm<'a>,
2333 dst: std::net::SocketAddr,
2334 with_sock: F,
2335 ) -> futures::future::LocalBoxFuture<'a, Result<fuchsia_async::net::TcpStream>> {
2336 async move {
2337 let sock = realm
2338 .stream_socket(get_socket2_domain(&dst), fposix_socket::StreamSocketProtocol::Tcp)
2339 .await
2340 .context("failed to create socket")?;
2341
2342 with_sock(&sock)?;
2343
2344 let stream = fuchsia_async::net::TcpStream::connect_from_raw(sock, dst)
2345 .context("failed to create client tcp stream")?
2346 .await
2347 .context("failed to connect to server")?;
2348
2349 Result::Ok(stream)
2350 }
2351 .boxed_local()
2352 }
2353}
2354
2355fn truncate_dropping_front(s: Cow<'_, str>, len: usize) -> Cow<'_, str> {
2356 match s.len().checked_sub(len) {
2357 None => s,
2358 Some(start) => {
2359 match s {
2363 Cow::Borrowed(s) => Cow::Borrowed(&s[start..]),
2364 Cow::Owned(mut s) => {
2365 let _: std::string::Drain<'_> = s.drain(..start);
2366 Cow::Owned(s)
2367 }
2368 }
2369 }
2370 }
2371}