1#![warn(missing_docs, unreachable_patterns)]
6
7pub mod guest;
11
12use fuchsia_sync::Mutex;
13use std::borrow::Cow;
14use std::collections::HashSet;
15use std::num::NonZeroU64;
16use std::ops::DerefMut as _;
17use std::path::Path;
18use std::pin::pin;
19use std::sync::Arc;
20
21use fidl::endpoints::{ProtocolMarker, Proxy as _};
22use fidl_fuchsia_net_dhcp_ext::{self as fnet_dhcp_ext, ClientProviderExt};
23use fidl_fuchsia_net_ext::{self as fnet_ext};
24use fidl_fuchsia_net_interfaces_ext::admin::Control;
25use fidl_fuchsia_net_interfaces_ext::{self as fnet_interfaces_ext};
26use fnet_ext::{FromExt as _, IntoExt as _};
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_resources as fnet_resources,
33 fidl_fuchsia_net_root as fnet_root, fidl_fuchsia_net_routes as fnet_routes,
34 fidl_fuchsia_net_routes_admin as fnet_routes_admin,
35 fidl_fuchsia_net_routes_ext as fnet_routes_ext, fidl_fuchsia_net_stack as fnet_stack,
36 fidl_fuchsia_netemul as fnetemul, fidl_fuchsia_netemul_network as fnetemul_network,
37 fidl_fuchsia_posix_socket as fposix_socket, fidl_fuchsia_posix_socket_ext as fposix_socket_ext,
38 fidl_fuchsia_posix_socket_packet as fposix_socket_packet,
39 fidl_fuchsia_posix_socket_raw as fposix_socket_raw,
40};
41
42use anyhow::{Context as _, anyhow};
43use futures::future::{FutureExt as _, LocalBoxFuture, TryFutureExt as _};
44use futures::{SinkExt as _, TryStreamExt as _};
45use net_types::SpecifiedAddr;
46use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv6, Subnet};
47
48type Result<T = ()> = std::result::Result<T, anyhow::Error>;
49
50pub const DEFAULT_MTU: u16 = 1500;
52
53pub const NETDEVICE_DEVFS_PATH: &'static str = "class/network";
55
56pub fn devfs_device_path(node_name: &str) -> std::path::PathBuf {
58 std::path::Path::new(NETDEVICE_DEVFS_PATH).join(node_name)
59}
60
61pub fn new_endpoint_config(
63 mtu: u16,
64 mac: Option<fnet::MacAddress>,
65) -> fnetemul_network::EndpointConfig {
66 fnetemul_network::EndpointConfig {
67 mtu,
68 mac: mac.map(Box::new),
69 port_class: fnetwork::PortClass::Virtual,
70 }
71}
72
73#[must_use]
80pub struct TestSandbox {
81 sandbox: fnetemul::SandboxProxy,
82}
83
84impl TestSandbox {
85 pub fn new() -> Result<TestSandbox> {
87 fuchsia_component::client::connect_to_protocol::<fnetemul::SandboxMarker>()
88 .context("failed to connect to sandbox protocol")
89 .map(|sandbox| TestSandbox { sandbox })
90 }
91
92 pub fn create_realm<'a, I>(
94 &'a self,
95 name: impl Into<Cow<'a, str>>,
96 children: I,
97 ) -> Result<TestRealm<'a>>
98 where
99 I: IntoIterator,
100 I::Item: Into<fnetemul::ChildDef>,
101 {
102 let (realm, server) = fidl::endpoints::create_proxy::<fnetemul::ManagedRealmMarker>();
103 let name = name.into();
104 let () = self.sandbox.create_realm(
105 server,
106 fnetemul::RealmOptions {
107 name: Some(name.clone().into_owned()),
108 children: Some(children.into_iter().map(Into::into).collect()),
109 ..Default::default()
110 },
111 )?;
112 Ok(TestRealm(Arc::new(TestRealmInner {
113 realm,
114 name,
115 _sandbox: self,
116 shutdown_on_drop: Mutex::new(ShutdownOnDropConfig {
117 enabled: true,
118 ignore_monikers: HashSet::new(),
119 }),
120 })))
121 }
122
123 pub fn create_empty_realm<'a>(
125 &'a self,
126 name: impl Into<Cow<'a, str>>,
127 ) -> Result<TestRealm<'a>> {
128 self.create_realm(name, std::iter::empty::<fnetemul::ChildDef>())
129 }
130
131 fn get_network_context(&self) -> Result<fnetemul_network::NetworkContextProxy> {
133 let (ctx, server) =
134 fidl::endpoints::create_proxy::<fnetemul_network::NetworkContextMarker>();
135 let () = self.sandbox.get_network_context(server)?;
136 Ok(ctx)
137 }
138
139 pub fn get_network_manager(&self) -> Result<fnetemul_network::NetworkManagerProxy> {
141 let ctx = self.get_network_context()?;
142 let (network_manager, server) =
143 fidl::endpoints::create_proxy::<fnetemul_network::NetworkManagerMarker>();
144 let () = ctx.get_network_manager(server)?;
145 Ok(network_manager)
146 }
147
148 pub fn get_endpoint_manager(&self) -> Result<fnetemul_network::EndpointManagerProxy> {
150 let ctx = self.get_network_context()?;
151 let (ep_manager, server) =
152 fidl::endpoints::create_proxy::<fnetemul_network::EndpointManagerMarker>();
153 let () = ctx.get_endpoint_manager(server)?;
154 Ok(ep_manager)
155 }
156
157 pub async fn create_network<'a>(
159 &'a self,
160 name: impl Into<Cow<'a, str>>,
161 ) -> Result<TestNetwork<'a>> {
162 let name = name.into();
163 let netm = self.get_network_manager()?;
164 let (status, network) = netm
165 .create_network(
166 &name,
167 &fnetemul_network::NetworkConfig {
168 latency: None,
169 packet_loss: None,
170 reorder: None,
171 ..Default::default()
172 },
173 )
174 .await
175 .context("create_network FIDL error")?;
176 let () = zx::Status::ok(status).context("create_network failed")?;
177 let network = network
178 .ok_or_else(|| anyhow::anyhow!("create_network didn't return a valid network"))?
179 .into_proxy();
180 Ok(TestNetwork { network, name, sandbox: self })
181 }
182
183 pub async fn setup_networks<'a>(
185 &'a self,
186 networks: Vec<fnetemul_network::NetworkSetup>,
187 ) -> Result<TestNetworkSetup<'a>> {
188 let ctx = self.get_network_context()?;
189 let (status, handle) = ctx.setup(&networks).await.context("setup FIDL error")?;
190 let () = zx::Status::ok(status).context("setup failed")?;
191 let handle = handle
192 .ok_or_else(|| anyhow::anyhow!("setup didn't return a valid handle"))?
193 .into_proxy();
194 Ok(TestNetworkSetup { _setup: handle, _sandbox: self })
195 }
196
197 pub async fn create_endpoint<'a, S>(&'a self, name: S) -> Result<TestEndpoint<'a>>
201 where
202 S: Into<Cow<'a, str>>,
203 {
204 self.create_endpoint_with(name, new_endpoint_config(DEFAULT_MTU, None)).await
205 }
206
207 pub async fn create_endpoint_with<'a>(
211 &'a self,
212 name: impl Into<Cow<'a, str>>,
213 config: fnetemul_network::EndpointConfig,
214 ) -> Result<TestEndpoint<'a>> {
215 let name = name.into();
216 let epm = self.get_endpoint_manager()?;
217 let (status, endpoint) =
218 epm.create_endpoint(&name, &config).await.context("create_endpoint FIDL error")?;
219 let () = zx::Status::ok(status).context("create_endpoint failed")?;
220 let endpoint = endpoint
221 .ok_or_else(|| anyhow::anyhow!("create_endpoint didn't return a valid endpoint"))?
222 .into_proxy();
223 Ok(TestEndpoint { endpoint, name, _sandbox: self })
224 }
225}
226
227#[must_use]
231pub struct TestNetworkSetup<'a> {
232 _setup: fnetemul_network::SetupHandleProxy,
233 _sandbox: &'a TestSandbox,
234}
235
236impl TestNetworkSetup<'_> {
237 pub fn into_proxy(self) -> fnetemul_network::SetupHandleProxy {
244 let Self { _setup, _sandbox: _ } = self;
245 _setup
246 }
247}
248
249#[derive(Default)]
251pub struct InterfaceConfig<'a> {
252 pub name: Option<Cow<'a, str>>,
254 pub metric: Option<u32>,
256 pub ipv4_dad_transmits: Option<u16>,
259 pub ipv6_dad_transmits: Option<u16>,
262 pub temporary_addresses: Option<bool>,
267 pub netstack_managed_routes_designation:
272 Option<fnet_interfaces_admin::NetstackManagedRoutesDesignation>,
273}
274
275impl InterfaceConfig<'_> {
276 pub fn use_local_table() -> Self {
278 Self {
279 netstack_managed_routes_designation: Some(
280 fnet_interfaces_admin::NetstackManagedRoutesDesignation::InterfaceLocal(
281 fnet_interfaces_admin::Empty,
282 ),
283 ),
284 ..Default::default()
285 }
286 }
287}
288
289#[derive(Debug)]
290struct ShutdownOnDropConfig {
291 enabled: bool,
292 ignore_monikers: HashSet<String>,
293}
294
295struct TestRealmInner<'a> {
296 realm: fnetemul::ManagedRealmProxy,
297 name: Cow<'a, str>,
298 _sandbox: &'a TestSandbox,
299 shutdown_on_drop: Mutex<ShutdownOnDropConfig>,
300}
301
302impl Drop for TestRealmInner<'_> {
303 fn drop(&mut self) {
304 let ShutdownOnDropConfig { enabled, ignore_monikers } = self.shutdown_on_drop.get_mut();
305 if !*enabled {
306 return;
307 }
308 let ignore_monikers = std::mem::take(ignore_monikers);
309 let mut crashed = match self.shutdown_sync() {
310 Ok(crashed) => crashed,
311 Err(e) => {
312 if !e.is_closed() {
316 panic!("error verifying clean shutdown on test realm {}: {:?}", self.name, e);
317 }
318 return;
319 }
320 };
321
322 crashed.retain(|m| !ignore_monikers.contains(m));
323 if !crashed.is_empty() {
324 panic!(
325 "TestRealm {} found unclean component stops with monikers: {:?}",
326 self.name, crashed
327 );
328 }
329 }
330}
331
332impl TestRealmInner<'_> {
333 fn shutdown_sync(&self) -> std::result::Result<Vec<String>, fidl::Error> {
334 let (listener, server_end) = fidl::endpoints::create_sync_proxy();
335 self.realm.get_crash_listener(server_end)?;
336 self.realm.shutdown()?;
337 let _: zx::Signals = self
339 .realm
340 .as_channel()
341 .wait_handle(zx::Signals::CHANNEL_PEER_CLOSED, zx::MonotonicInstant::INFINITE)
342 .to_result()
343 .expect("wait channel closed");
344 let mut unclean_stop = Vec::new();
347 while let Some(unclean) =
348 listener.next(zx::MonotonicInstant::INFINITE).map(|v| (!v.is_empty()).then_some(v))?
349 {
350 unclean_stop.extend(unclean);
351 }
352 Ok(unclean_stop)
353 }
354}
355
356#[must_use]
363#[derive(Clone)]
364pub struct TestRealm<'a>(Arc<TestRealmInner<'a>>);
365
366impl<'a> std::fmt::Debug for TestRealm<'a> {
367 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
368 let Self(inner) = self;
369 let TestRealmInner { realm: _, name, _sandbox, shutdown_on_drop } = &**inner;
370 f.debug_struct("TestRealm")
371 .field("name", name)
372 .field("shutdown_on_drop", shutdown_on_drop)
373 .finish_non_exhaustive()
374 }
375}
376
377impl<'a> TestRealm<'a> {
378 fn realm(&self) -> &fnetemul::ManagedRealmProxy {
379 let Self(inner) = self;
380 &inner.realm
381 }
382
383 pub fn name(&self) -> &str {
385 let Self(inner) = self;
386 &inner.name
387 }
388
389 pub fn set_checked_shutdown_on_drop(&self, shutdown_on_drop: bool) {
396 let Self(inner) = self;
397 inner.shutdown_on_drop.lock().enabled = shutdown_on_drop;
398 }
399
400 pub fn ignore_checked_shutdown_monikers(
406 &self,
407 monikers: impl IntoIterator<Item: Into<String>>,
408 ) {
409 let Self(inner) = self;
410 inner
411 .shutdown_on_drop
412 .lock()
413 .ignore_monikers
414 .extend(monikers.into_iter().map(|m| m.into()));
415 }
416
417 pub fn connect_to_protocol<S>(&self) -> Result<S::Proxy>
419 where
420 S: fidl::endpoints::DiscoverableProtocolMarker,
421 {
422 (|| {
423 let (proxy, server_end) = fidl::endpoints::create_proxy::<S>();
424 let () = self
425 .connect_to_protocol_with_server_end(server_end)
426 .context("connect to protocol name with server end")?;
427 Result::Ok(proxy)
428 })()
429 .context(S::DEBUG_NAME)
430 }
431
432 pub fn connect_to_protocol_from_child<S>(&self, child: &str) -> Result<S::Proxy>
434 where
435 S: fidl::endpoints::DiscoverableProtocolMarker,
436 {
437 (|| {
438 let (proxy, server_end) = fidl::endpoints::create_proxy::<S>();
439 let () = self
440 .connect_to_protocol_from_child_at_path_with_server_end(
441 S::PROTOCOL_NAME,
442 child,
443 server_end,
444 )
445 .context("connect to protocol name with server end")?;
446 Result::Ok(proxy)
447 })()
448 .with_context(|| format!("{} from {child}", S::DEBUG_NAME))
449 }
450
451 pub fn open_diagnostics_directory(&self, child_name: &str) -> Result<fio::DirectoryProxy> {
453 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
454 let () = self
455 .realm()
456 .open_diagnostics_directory(child_name, server_end)
457 .context("open diagnostics dir")?;
458 Ok(proxy)
459 }
460
461 pub fn connect_to_protocol_with_server_end<S: fidl::endpoints::DiscoverableProtocolMarker>(
463 &self,
464 server_end: fidl::endpoints::ServerEnd<S>,
465 ) -> Result {
466 self.realm()
467 .connect_to_protocol(S::PROTOCOL_NAME, None, server_end.into_channel())
468 .context("connect to protocol")
469 }
470
471 pub fn connect_to_protocol_from_child_at_path_with_server_end<
473 S: fidl::endpoints::DiscoverableProtocolMarker,
474 >(
475 &self,
476 protocol_path: &str,
477 child: &str,
478 server_end: fidl::endpoints::ServerEnd<S>,
479 ) -> Result {
480 self.realm()
481 .connect_to_protocol(protocol_path, Some(child), server_end.into_channel())
482 .context("connect to protocol")
483 }
484
485 pub async fn get_moniker(&self) -> Result<String> {
487 self.realm().get_moniker().await.context("failed to call get moniker")
488 }
489
490 pub async fn start_child_component(&self, child_name: &str) -> Result {
492 self.realm()
493 .start_child_component(child_name)
494 .await?
495 .map_err(zx::Status::from_raw)
496 .with_context(|| format!("failed to start child component '{}'", child_name))
497 }
498
499 pub async fn stop_child_component(&self, child_name: &str) -> Result {
501 self.realm()
502 .stop_child_component(child_name)
503 .await?
504 .map_err(zx::Status::from_raw)
505 .with_context(|| format!("failed to stop child component '{}'", child_name))
506 }
507
508 pub async fn join_network<S>(
514 &self,
515 network: &TestNetwork<'a>,
516 ep_name: S,
517 ) -> Result<TestInterface<'a>>
518 where
519 S: Into<Cow<'a, str>>,
520 {
521 self.join_network_with_if_config(network, ep_name, Default::default()).await
522 }
523
524 pub async fn join_network_with_if_config<S>(
530 &self,
531 network: &TestNetwork<'a>,
532 ep_name: S,
533 if_config: InterfaceConfig<'a>,
534 ) -> Result<TestInterface<'a>>
535 where
536 S: Into<Cow<'a, str>>,
537 {
538 let endpoint =
539 network.create_endpoint(ep_name).await.context("failed to create endpoint")?;
540 self.install_endpoint(endpoint, if_config).await
541 }
542
543 pub async fn join_network_with(
554 &self,
555 network: &TestNetwork<'a>,
556 ep_name: impl Into<Cow<'a, str>>,
557 ep_config: fnetemul_network::EndpointConfig,
558 if_config: InterfaceConfig<'a>,
559 ) -> Result<TestInterface<'a>> {
560 let installer = self
561 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
562 .context("failed to connect to fuchsia.net.interfaces.admin.Installer")?;
563 let interface_state = self
564 .connect_to_protocol::<fnet_interfaces::StateMarker>()
565 .context("failed to connect to fuchsia.net.interfaces.State")?;
566 let (endpoint, id, control, device_control) = self
567 .join_network_with_installer(
568 network,
569 installer,
570 interface_state,
571 ep_name,
572 ep_config,
573 if_config,
574 )
575 .await?;
576
577 Ok(TestInterface {
578 endpoint,
579 id,
580 realm: self.clone(),
581 control,
582 device_control: Some(device_control),
583 dhcp_client_task: futures::lock::Mutex::default(),
584 })
585 }
586
587 pub async fn join_network_with_installer(
598 &self,
599 network: &TestNetwork<'a>,
600 installer: fnet_interfaces_admin::InstallerProxy,
601 interface_state: fnet_interfaces::StateProxy,
602 ep_name: impl Into<Cow<'a, str>>,
603 ep_config: fnetemul_network::EndpointConfig,
604 if_config: InterfaceConfig<'a>,
605 ) -> Result<(TestEndpoint<'a>, u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
606 let endpoint = network
607 .create_endpoint_with(ep_name, ep_config)
608 .await
609 .context("failed to create endpoint")?;
610 let (id, control, device_control) = self
611 .install_endpoint_with_installer(installer, interface_state, &endpoint, if_config)
612 .await?;
613 Ok((endpoint, id, control, device_control))
614 }
615
616 pub async fn install_endpoint_with_installer(
624 &self,
625 installer: fnet_interfaces_admin::InstallerProxy,
626 interface_state: fnet_interfaces::StateProxy,
627 endpoint: &TestEndpoint<'a>,
628 if_config: InterfaceConfig<'a>,
629 ) -> Result<(u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
630 let (id, control, device_control) =
631 endpoint.install(installer, if_config).await.context("failed to add endpoint")?;
632
633 let () = endpoint.set_link_up(true).await.context("failed to start endpoint")?;
634 let _did_enable: bool = control
635 .enable()
636 .await
637 .map_err(anyhow::Error::new)
638 .and_then(|res| {
639 res.map_err(|e: fnet_interfaces_admin::ControlEnableError| {
640 anyhow::anyhow!("{:?}", e)
641 })
642 })
643 .context("failed to enable interface")?;
644
645 let () = fnet_interfaces_ext::wait_interface_with_id(
648 fnet_interfaces_ext::event_stream_from_state::<fnet_interfaces_ext::DefaultInterest>(
649 &interface_state,
650 Default::default(),
651 )?,
652 &mut fnet_interfaces_ext::InterfaceState::<(), _>::Unknown(id),
653 |properties_and_state| properties_and_state.properties.online.then_some(()),
654 )
655 .await
656 .context("failed to observe interface up")?;
657
658 Ok((id, control, device_control))
659 }
660
661 pub async fn install_endpoint(
665 &self,
666 endpoint: TestEndpoint<'a>,
667 if_config: InterfaceConfig<'a>,
668 ) -> Result<TestInterface<'a>> {
669 let installer = self
670 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
671 .context("failed to connect to fuchsia.net.interfaces.admin.Installer")?;
672 let interface_state = self
673 .connect_to_protocol::<fnet_interfaces::StateMarker>()
674 .context("failed to connect to fuchsia.net.interfaces.State")?;
675 let (id, control, device_control) = self
676 .install_endpoint_with_installer(installer, interface_state, &endpoint, if_config)
677 .await?;
678 Ok(TestInterface {
679 endpoint,
680 id,
681 realm: self.clone(),
682 control,
683 device_control: Some(device_control),
684 dhcp_client_task: futures::lock::Mutex::default(),
685 })
686 }
687
688 pub async fn add_raw_device(
690 &self,
691 path: &Path,
692 device: fidl::endpoints::ClientEnd<fnetemul_network::DeviceProxy_Marker>,
693 ) -> Result {
694 let path = path.to_str().with_context(|| format!("convert {} to str", path.display()))?;
695 self.realm()
696 .add_device(path, device)
697 .await
698 .context("add device")?
699 .map_err(zx::Status::from_raw)
700 .context("add device error")
701 }
702
703 pub async fn add_virtual_device(&self, e: &TestEndpoint<'_>, path: &Path) -> Result {
705 let (device, device_server_end) =
706 fidl::endpoints::create_endpoints::<fnetemul_network::DeviceProxy_Marker>();
707 e.get_proxy_(device_server_end).context("get proxy")?;
708
709 self.add_raw_device(path, device).await
710 }
711
712 pub async fn remove_virtual_device(&self, path: &Path) -> Result {
714 let path = path.to_str().with_context(|| format!("convert {} to str", path.display()))?;
715 self.realm()
716 .remove_device(path)
717 .await
718 .context("remove device")?
719 .map_err(zx::Status::from_raw)
720 .context("remove device error")
721 }
722
723 pub async fn datagram_socket(
726 &self,
727 domain: fposix_socket::Domain,
728 proto: fposix_socket::DatagramSocketProtocol,
729 ) -> Result<socket2::Socket> {
730 let socket_provider = self
731 .connect_to_protocol::<fposix_socket::ProviderMarker>()
732 .context("failed to connect to socket provider")?;
733
734 fposix_socket_ext::datagram_socket(&socket_provider, domain, proto)
735 .await
736 .context("failed to call socket")?
737 .context("failed to create socket")
738 }
739
740 pub async fn datagram_socket_with_options(
744 &self,
745 domain: fposix_socket::Domain,
746 proto: fposix_socket::DatagramSocketProtocol,
747 options: fposix_socket::SocketCreationOptions,
748 ) -> Result<socket2::Socket> {
749 let socket_provider = self
750 .connect_to_protocol::<fposix_socket::ProviderMarker>()
751 .context("failed to connect to socket provider")?;
752
753 fposix_socket_ext::datagram_socket_with_options(&socket_provider, domain, proto, options)
754 .await
755 .context("failed to call socket")?
756 .context("failed to create socket")
757 }
758
759 pub async fn raw_socket(
762 &self,
763 domain: fposix_socket::Domain,
764 association: fposix_socket_raw::ProtocolAssociation,
765 ) -> Result<socket2::Socket> {
766 let socket_provider = self
767 .connect_to_protocol::<fposix_socket_raw::ProviderMarker>()
768 .context("failed to connect to socket provider")?;
769 let sock = socket_provider
770 .socket(domain, &association)
771 .await
772 .context("failed to call socket")?
773 .map_err(|e| std::io::Error::from_raw_os_error(e.into_primitive()))
774 .context("failed to create socket")?;
775
776 Ok(fdio::create_fd(sock.into()).context("failed to create fd")?.into())
777 }
778
779 pub async fn packet_socket(&self, kind: fposix_socket_packet::Kind) -> Result<socket2::Socket> {
784 let socket_provider = self
785 .connect_to_protocol::<fposix_socket_packet::ProviderMarker>()
786 .context("failed to connect to socket provider")?;
787
788 fposix_socket_ext::packet_socket(&socket_provider, kind)
789 .await
790 .context("failed to call socket")?
791 .context("failed to create socket")
792 }
793
794 pub async fn stream_socket(
797 &self,
798 domain: fposix_socket::Domain,
799 proto: fposix_socket::StreamSocketProtocol,
800 ) -> Result<socket2::Socket> {
801 let socket_provider = self
802 .connect_to_protocol::<fposix_socket::ProviderMarker>()
803 .context("failed to connect to socket provider")?;
804 let sock = socket_provider
805 .stream_socket(domain, proto)
806 .await
807 .context("failed to call socket")?
808 .map_err(|e| std::io::Error::from_raw_os_error(e.into_primitive()))
809 .context("failed to create socket")?;
810
811 Ok(fdio::create_fd(sock.into()).context("failed to create fd")?.into())
812 }
813
814 pub async fn stream_socket_with_options(
818 &self,
819 domain: fposix_socket::Domain,
820 proto: fposix_socket::StreamSocketProtocol,
821 options: fposix_socket::SocketCreationOptions,
822 ) -> Result<socket2::Socket> {
823 let socket_provider = self
824 .connect_to_protocol::<fposix_socket::ProviderMarker>()
825 .context("failed to connect to socket provider")?;
826 let sock = socket_provider
827 .stream_socket_with_options(domain, proto, options)
828 .await
829 .context("failed to call socket")?
830 .map_err(|e| std::io::Error::from_raw_os_error(e.into_primitive()))
831 .context("failed to create socket")?;
832
833 Ok(fdio::create_fd(sock.into()).context("failed to create fd")?.into())
834 }
835 pub async fn shutdown(&self) -> Result {
842 let () = self.realm().shutdown().context("call shutdown")?;
843 self.set_checked_shutdown_on_drop(false);
846 let events = self
847 .realm()
848 .take_event_stream()
849 .try_collect::<Vec<_>>()
850 .await
851 .context("error on realm event stream")?;
852 assert_matches::assert_matches!(events[..], [fnetemul::ManagedRealmEvent::OnShutdown {}]);
854 Ok(())
855 }
856
857 pub async fn get_crash_stream(&self) -> Result<impl futures::Stream<Item = Result<String>>> {
859 let (listener, server_end) = fidl::endpoints::create_proxy();
860 self.realm().get_crash_listener(server_end).context("creating CrashListener")?;
861 Ok(futures::stream::try_unfold(listener, |listener| async move {
862 let next = listener.next().await.context("listener fetch next moniker")?;
863 Result::Ok(if next.is_empty() {
864 None
865 } else {
866 Some((futures::stream::iter(next.into_iter().map(Ok)), listener))
867 })
868 })
869 .try_flatten())
870 }
871
872 pub async fn icmp_socket<Ip: ping::FuchsiaIpExt>(
874 &self,
875 ) -> Result<fuchsia_async::net::DatagramSocket> {
876 let sock = self
877 .datagram_socket(Ip::DOMAIN_FIDL, fposix_socket::DatagramSocketProtocol::IcmpEcho)
878 .await
879 .context("failed to create ICMP datagram socket")?;
880 fuchsia_async::net::DatagramSocket::new_from_socket(sock)
881 .context("failed to create async ICMP datagram socket")
882 }
883
884 pub async fn ping_once<Ip: ping::FuchsiaIpExt>(&self, addr: Ip::SockAddr, seq: u16) -> Result {
886 let icmp_sock = self.icmp_socket::<Ip>().await?;
887
888 const MESSAGE: &'static str = "hello, world";
889 let (mut sink, mut stream) = ping::new_unicast_sink_and_stream::<
890 Ip,
891 _,
892 { MESSAGE.len() + ping::ICMP_HEADER_LEN },
893 >(&icmp_sock, &addr, MESSAGE.as_bytes());
894
895 let send_fut = sink.send(seq).map_err(anyhow::Error::new);
896 let recv_fut = stream.try_next().map(|r| match r {
897 Ok(Some(got)) if got == seq => Ok(()),
898 Ok(Some(got)) => Err(anyhow!("unexpected echo reply; got: {}, want: {}", got, seq)),
899 Ok(None) => Err(anyhow!("echo reply stream ended unexpectedly")),
900 Err(e) => Err(anyhow::Error::from(e)),
901 });
902
903 let ((), ()) = futures::future::try_join(send_fut, recv_fut)
904 .await
905 .with_context(|| format!("failed to ping from {} to {}", self.name(), addr,))?;
906 Ok(())
907 }
908
909 pub async fn add_neighbor_entry(
913 &self,
914 interface: u64,
915 addr: fnet::IpAddress,
916 mac: fnet::MacAddress,
917 ) -> Result {
918 let controller = self
919 .connect_to_protocol::<fnet_neighbor::ControllerMarker>()
920 .context("connect to protocol")?;
921 controller
922 .add_entry(interface, &addr, &mac)
923 .await
924 .context("add_entry")?
925 .map_err(zx::Status::from_raw)
926 .context("add_entry failed")
927 }
928
929 pub fn get_interface_event_stream(
932 &self,
933 ) -> Result<
934 impl futures::Stream<
935 Item = std::result::Result<
936 fnet_interfaces_ext::EventWithInterest<fnet_interfaces_ext::DefaultInterest>,
937 fidl::Error,
938 >,
939 >,
940 > {
941 self.get_interface_event_stream_with_interest::<fnet_interfaces_ext::DefaultInterest>()
942 }
943
944 pub fn get_interface_event_stream_with_interest<I: fnet_interfaces_ext::FieldInterests>(
947 &self,
948 ) -> Result<
949 impl futures::Stream<
950 Item = std::result::Result<fnet_interfaces_ext::EventWithInterest<I>, fidl::Error>,
951 >,
952 > {
953 let interface_state = self
954 .connect_to_protocol::<fnet_interfaces::StateMarker>()
955 .context("connect to protocol")?;
956 fnet_interfaces_ext::event_stream_from_state::<I>(&interface_state, Default::default())
957 .context("get interface event stream")
958 }
959
960 pub async fn main_table_id<
962 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
963 >(
964 &self,
965 ) -> u32 {
966 let main_route_table = self
967 .connect_to_protocol::<I::RouteTableMarker>()
968 .expect("failed to connect to main route table");
969 fnet_routes_ext::admin::get_table_id::<I>(&main_route_table)
970 .await
971 .expect("failed to get_table_id")
972 .get()
973 }
974}
975
976#[must_use]
981pub struct TestNetwork<'a> {
982 network: fnetemul_network::NetworkProxy,
983 name: Cow<'a, str>,
984 sandbox: &'a TestSandbox,
985}
986
987impl<'a> std::fmt::Debug for TestNetwork<'a> {
988 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
989 let Self { name, network: _, sandbox: _ } = self;
990 f.debug_struct("TestNetwork").field("name", name).finish_non_exhaustive()
991 }
992}
993
994impl<'a> TestNetwork<'a> {
995 pub fn into_proxy(self) -> fnetemul_network::NetworkProxy {
1002 let Self { network, name: _, sandbox: _ } = self;
1003 network
1004 }
1005
1006 async fn get_client_end_clone(
1008 &self,
1009 ) -> Result<fidl::endpoints::ClientEnd<fnetemul_network::NetworkMarker>> {
1010 let network_manager =
1011 self.sandbox.get_network_manager().context("get_network_manager failed")?;
1012 let client = network_manager
1013 .get_network(&self.name)
1014 .await
1015 .context("get_network failed")?
1016 .with_context(|| format!("no network found with name {}", self.name))?;
1017 Ok(client)
1018 }
1019
1020 pub async fn set_config(&self, config: fnetemul_network::NetworkConfig) -> Result<()> {
1022 let status = self.network.set_config(&config).await.context("call set_config")?;
1023 zx::Status::ok(status).context("set config")
1024 }
1025
1026 pub async fn attach_endpoint(&self, ep: &TestEndpoint<'a>) -> Result<()> {
1028 let status =
1029 self.network.attach_endpoint(&ep.name).await.context("attach_endpoint FIDL error")?;
1030 let () = zx::Status::ok(status).context("attach_endpoint failed")?;
1031 Ok(())
1032 }
1033
1034 pub async fn create_endpoint<S>(&self, name: S) -> Result<TestEndpoint<'a>>
1038 where
1039 S: Into<Cow<'a, str>>,
1040 {
1041 let ep = self
1042 .sandbox
1043 .create_endpoint(name)
1044 .await
1045 .with_context(|| format!("failed to create endpoint for network {}", self.name))?;
1046 let () = self.attach_endpoint(&ep).await.with_context(|| {
1047 format!("failed to attach endpoint {} to network {}", ep.name, self.name)
1048 })?;
1049 Ok(ep)
1050 }
1051
1052 pub async fn create_endpoint_with(
1056 &self,
1057 name: impl Into<Cow<'a, str>>,
1058 config: fnetemul_network::EndpointConfig,
1059 ) -> Result<TestEndpoint<'a>> {
1060 let ep = self
1061 .sandbox
1062 .create_endpoint_with(name, config)
1063 .await
1064 .with_context(|| format!("failed to create endpoint for network {}", self.name))?;
1065 let () = self.attach_endpoint(&ep).await.with_context(|| {
1066 format!("failed to attach endpoint {} to network {}", ep.name, self.name)
1067 })?;
1068 Ok(ep)
1069 }
1070
1071 pub fn create_fake_endpoint(&self) -> Result<TestFakeEndpoint<'a>> {
1073 let (endpoint, server) =
1074 fidl::endpoints::create_proxy::<fnetemul_network::FakeEndpointMarker>();
1075 let () = self.network.create_fake_endpoint(server)?;
1076 return Ok(TestFakeEndpoint { endpoint, _sandbox: self.sandbox });
1077 }
1078
1079 pub async fn start_capture(&self, name: &str) -> Result<PacketCapture> {
1085 let manager = self.sandbox.get_network_manager()?;
1086 let client = manager.get_network(&self.name).await?.expect("network must exist");
1087 zx::ok(self.network.start_capture(name).await?)?;
1088 let sync_proxy = fnetemul_network::NetworkSynchronousProxy::new(client.into_channel());
1089 Ok(PacketCapture { sync_proxy })
1090 }
1091
1092 pub async fn stop_capture(&self) -> Result<()> {
1094 Ok(self.network.stop_capture().await?)
1095 }
1096}
1097
1098pub struct PacketCapture {
1101 sync_proxy: fnetemul_network::NetworkSynchronousProxy,
1102}
1103
1104impl Drop for PacketCapture {
1105 fn drop(&mut self) {
1106 self.sync_proxy
1107 .stop_capture(zx::MonotonicInstant::INFINITE)
1108 .expect("failed to stop packet capture")
1109 }
1110}
1111
1112#[must_use]
1114pub struct TestEndpoint<'a> {
1115 endpoint: fnetemul_network::EndpointProxy,
1116 name: Cow<'a, str>,
1117 _sandbox: &'a TestSandbox,
1118}
1119
1120impl<'a> TestEndpoint<'a> {
1121 pub async fn get_port_identity_koid(&self) -> Result<zx::Koid> {
1124 let (client, server) = fidl::endpoints::create_proxy::<fnetwork::PortMarker>();
1125 self.get_port(server)?;
1126 let identity = client.get_identity().await?;
1127 Ok(identity.get_koid()?)
1128 }
1129}
1130
1131impl<'a> std::fmt::Debug for TestEndpoint<'a> {
1132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
1133 let Self { endpoint: _, name, _sandbox } = self;
1134 f.debug_struct("TestEndpoint").field("name", name).finish_non_exhaustive()
1135 }
1136}
1137
1138impl<'a> std::ops::Deref for TestEndpoint<'a> {
1139 type Target = fnetemul_network::EndpointProxy;
1140
1141 fn deref(&self) -> &Self::Target {
1142 &self.endpoint
1143 }
1144}
1145
1146#[must_use]
1148pub struct TestFakeEndpoint<'a> {
1149 endpoint: fnetemul_network::FakeEndpointProxy,
1150 _sandbox: &'a TestSandbox,
1151}
1152
1153impl<'a> std::ops::Deref for TestFakeEndpoint<'a> {
1154 type Target = fnetemul_network::FakeEndpointProxy;
1155
1156 fn deref(&self) -> &Self::Target {
1157 &self.endpoint
1158 }
1159}
1160
1161impl<'a> TestFakeEndpoint<'a> {
1162 pub fn frame_stream(
1166 &self,
1167 ) -> impl futures::Stream<Item = std::result::Result<(Vec<u8>, u64), fidl::Error>> + '_ {
1168 futures::stream::try_unfold(&self.endpoint, |ep| ep.read().map_ok(move |r| Some((r, ep))))
1169 }
1170}
1171
1172async fn to_netdevice_inner(
1175 port: fidl::endpoints::ClientEnd<fnetwork::PortMarker>,
1176) -> Result<(fidl::endpoints::ClientEnd<fnetwork::DeviceMarker>, fnetwork::PortId)> {
1177 let port = port.into_proxy();
1178 let (device, server_end) = fidl::endpoints::create_endpoints::<fnetwork::DeviceMarker>();
1179 let () = port.get_device(server_end)?;
1180 let port_id = port
1181 .get_info()
1182 .await
1183 .context("get port info")?
1184 .id
1185 .ok_or_else(|| anyhow::anyhow!("missing port id"))?;
1186 Ok((device, port_id))
1187}
1188
1189impl<'a> TestEndpoint<'a> {
1190 pub fn into_proxy(self) -> fnetemul_network::EndpointProxy {
1197 let Self { endpoint, name: _, _sandbox: _ } = self;
1198 endpoint
1199 }
1200
1201 pub async fn get_netdevice(
1206 &self,
1207 ) -> Result<(fidl::endpoints::ClientEnd<fnetwork::DeviceMarker>, fnetwork::PortId)> {
1208 let (port, server_end) = fidl::endpoints::create_endpoints();
1209 self.get_port(server_end)
1210 .with_context(|| format!("failed to get device connection for {}", self.name))?;
1211 to_netdevice_inner(port).await
1212 }
1213
1214 pub async fn install(
1220 &self,
1221 installer: fnet_interfaces_admin::InstallerProxy,
1222 InterfaceConfig {
1223 name,
1224 metric,
1225 ipv4_dad_transmits,
1226 ipv6_dad_transmits,
1227 temporary_addresses,
1228 netstack_managed_routes_designation,
1229 }: InterfaceConfig<'_>,
1230 ) -> Result<(u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
1231 let name = name.map(|n| {
1232 truncate_dropping_front(n.into(), fnet_interfaces::INTERFACE_NAME_LENGTH.into())
1233 .to_string()
1234 });
1235 let (device, port_id) = self.get_netdevice().await?;
1236 let device_control = {
1237 let (control, server_end) =
1238 fidl::endpoints::create_proxy::<fnet_interfaces_admin::DeviceControlMarker>();
1239 let () = installer.install_device(device, server_end).context("install device")?;
1240 control
1241 };
1242 let (control, server_end) = Control::create_endpoints().context("create endpoints")?;
1243 let () = device_control
1244 .create_interface(
1245 &port_id,
1246 server_end,
1247 fnet_interfaces_admin::Options {
1248 name,
1249 metric,
1250 netstack_managed_routes_designation,
1251 __source_breaking: fidl::marker::SourceBreaking,
1252 },
1253 )
1254 .context("create interface")?;
1255 if let Some(ipv4_dad_transmits) = ipv4_dad_transmits {
1256 let _: Option<u16> = set_ipv4_dad_transmits(&control, ipv4_dad_transmits)
1257 .await
1258 .context("set dad transmits")?;
1259 }
1260 if let Some(ipv6_dad_transmits) = ipv6_dad_transmits {
1261 let _: Option<u16> = set_ipv6_dad_transmits(&control, ipv6_dad_transmits)
1262 .await
1263 .context("set dad transmits")?;
1264 }
1265 if let Some(enabled) = temporary_addresses {
1266 set_temporary_address_generation_enabled(&control, enabled)
1267 .await
1268 .context("set temporary addresses")?;
1269 }
1270
1271 let id = control.get_id().await.context("get id")?;
1272 Ok((id, control, device_control))
1273 }
1274
1275 pub async fn add_to_stack(
1280 &self,
1281 realm: &TestRealm<'a>,
1282 config: InterfaceConfig<'a>,
1283 ) -> Result<(u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
1284 let installer = realm
1285 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
1286 .context("connect to protocol")?;
1287
1288 self.install(installer, config).await
1289 }
1290
1291 pub async fn into_interface_in_realm(self, realm: &TestRealm<'a>) -> Result<TestInterface<'a>> {
1293 self.into_interface_in_realm_with_name(realm, Default::default()).await
1294 }
1295
1296 pub async fn into_interface_in_realm_with_name(
1299 self,
1300 realm: &TestRealm<'a>,
1301 config: InterfaceConfig<'a>,
1302 ) -> Result<TestInterface<'a>> {
1303 let installer = realm
1304 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
1305 .context("connect to protocol")?;
1306
1307 let (id, control, device_control) =
1308 self.install(installer, config).await.context("failed to install")?;
1309
1310 Ok(TestInterface {
1311 endpoint: self,
1312 id,
1313 realm: realm.clone(),
1314 control,
1315 device_control: Some(device_control),
1316 dhcp_client_task: futures::lock::Mutex::default(),
1317 })
1318 }
1319}
1320
1321#[derive(Copy, Clone, PartialEq, Debug)]
1323pub enum DhcpClientVersion {
1324 InStack,
1326 OutOfStack,
1328}
1329
1330pub trait DhcpClient {
1332 const DHCP_CLIENT_VERSION: DhcpClientVersion;
1334}
1335
1336pub enum InStack {}
1338
1339impl DhcpClient for InStack {
1340 const DHCP_CLIENT_VERSION: DhcpClientVersion = DhcpClientVersion::InStack;
1341}
1342
1343pub enum OutOfStack {}
1345
1346impl DhcpClient for OutOfStack {
1347 const DHCP_CLIENT_VERSION: DhcpClientVersion = DhcpClientVersion::OutOfStack;
1348}
1349
1350#[must_use]
1356pub struct TestInterface<'a> {
1357 endpoint: TestEndpoint<'a>,
1358 realm: TestRealm<'a>,
1359 id: u64,
1360 control: Control,
1361 device_control: Option<fnet_interfaces_admin::DeviceControlProxy>,
1362 dhcp_client_task: futures::lock::Mutex<Option<fnet_dhcp_ext::testutil::DhcpClientTask>>,
1363}
1364
1365impl<'a> std::fmt::Debug for TestInterface<'a> {
1366 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
1367 let Self { endpoint, id, realm: _, control: _, device_control: _, dhcp_client_task: _ } =
1368 self;
1369 f.debug_struct("TestInterface")
1370 .field("endpoint", endpoint)
1371 .field("id", id)
1372 .finish_non_exhaustive()
1373 }
1374}
1375
1376impl<'a> std::ops::Deref for TestInterface<'a> {
1377 type Target = fnetemul_network::EndpointProxy;
1378
1379 fn deref(&self) -> &Self::Target {
1380 &self.endpoint
1381 }
1382}
1383
1384impl<'a> TestInterface<'a> {
1385 pub fn id(&self) -> u64 {
1387 self.id
1388 }
1389
1390 pub fn endpoint(&self) -> &TestEndpoint<'a> {
1392 &self.endpoint
1393 }
1394
1395 pub fn control(&self) -> &Control {
1397 &self.control
1398 }
1399
1400 pub async fn get_authorization(
1402 &self,
1403 ) -> Result<fnet_resources::GrantForInterfaceAuthorization> {
1404 Ok(self.control.get_authorization_for_interface().await?)
1405 }
1406
1407 pub fn connect_stack(&self) -> Result<fnet_stack::StackProxy> {
1409 self.realm.connect_to_protocol::<fnet_stack::StackMarker>()
1410 }
1411
1412 async fn add_route<
1417 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1418 >(
1419 &self,
1420 destination: Subnet<I::Addr>,
1421 next_hop: Option<SpecifiedAddr<I::Addr>>,
1422 metric: fnet_routes::SpecifiedMetric,
1423 ) -> Result<bool> {
1424 let route_set = self.create_authenticated_global_route_set::<I>().await?;
1425 fnet_routes_ext::admin::add_route::<I>(
1426 &route_set,
1427 &fnet_routes_ext::Route::<I>::new_forward(destination, self.id(), next_hop, metric)
1428 .try_into()
1429 .expect("convert to FIDL should succeed"),
1430 )
1431 .await
1432 .context("FIDL error adding route")?
1433 .map_err(|e| anyhow::anyhow!("error adding route: {e:?}"))
1434 }
1435
1436 pub async fn add_route_either(
1442 &self,
1443 destination: fnet::Subnet,
1444 next_hop: Option<fnet::IpAddress>,
1445 metric: fnet_routes::SpecifiedMetric,
1446 ) -> Result<bool> {
1447 let fnet::Subnet { addr: destination_addr, prefix_len } = destination;
1448 match destination_addr {
1449 fnet::IpAddress::Ipv4(destination_addr) => {
1450 let next_hop = match next_hop {
1451 Some(fnet::IpAddress::Ipv4(next_hop)) => Some(
1452 SpecifiedAddr::new(net_types::ip::Ipv4Addr::from_ext(next_hop))
1453 .ok_or_else(|| {
1454 anyhow::anyhow!("next hop must not be unspecified address")
1455 })?,
1456 ),
1457 Some(fnet::IpAddress::Ipv6(_)) => {
1458 return Err(anyhow::anyhow!(
1459 "next hop must be same IP version as destination"
1460 ));
1461 }
1462 None => None,
1463 };
1464 self.add_route::<Ipv4>(
1465 Subnet::new(destination_addr.into_ext(), prefix_len)
1466 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1467 next_hop,
1468 metric,
1469 )
1470 .await
1471 }
1472 fnet::IpAddress::Ipv6(destination_addr) => {
1473 let next_hop = match next_hop {
1474 Some(fnet::IpAddress::Ipv6(next_hop)) => Some(
1475 SpecifiedAddr::new(net_types::ip::Ipv6Addr::from_ext(next_hop))
1476 .ok_or_else(|| {
1477 anyhow::anyhow!("next hop must not be unspecified address")
1478 })?,
1479 ),
1480 Some(fnet::IpAddress::Ipv4(_)) => {
1481 return Err(anyhow::anyhow!(
1482 "next hop must be same IP version as destination"
1483 ));
1484 }
1485 None => None,
1486 };
1487 self.add_route::<Ipv6>(
1488 Subnet::new(destination_addr.into_ext(), prefix_len)
1489 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1490 next_hop,
1491 metric,
1492 )
1493 .await
1494 }
1495 }
1496 }
1497
1498 async fn remove_route<
1503 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1504 >(
1505 &self,
1506 destination: Subnet<I::Addr>,
1507 next_hop: Option<SpecifiedAddr<I::Addr>>,
1508 metric: fnet_routes::SpecifiedMetric,
1509 ) -> Result<bool> {
1510 let route_set = self.create_authenticated_global_route_set::<I>().await?;
1511 fnet_routes_ext::admin::remove_route::<I>(
1512 &route_set,
1513 &fnet_routes_ext::Route::<I>::new_forward(destination, self.id(), next_hop, metric)
1514 .try_into()
1515 .expect("convert to FIDL should succeed"),
1516 )
1517 .await
1518 .context("FIDL error removing route")?
1519 .map_err(|e| anyhow::anyhow!("error removing route: {e:?}"))
1520 }
1521
1522 async fn remove_route_either(
1528 &self,
1529 destination: fnet::Subnet,
1530 next_hop: Option<fnet::IpAddress>,
1531 metric: fnet_routes::SpecifiedMetric,
1532 ) -> Result<bool> {
1533 let fnet::Subnet { addr: destination_addr, prefix_len } = destination;
1534 match destination_addr {
1535 fnet::IpAddress::Ipv4(destination_addr) => {
1536 let next_hop = match next_hop {
1537 Some(fnet::IpAddress::Ipv4(next_hop)) => Some(
1538 SpecifiedAddr::new(net_types::ip::Ipv4Addr::from_ext(next_hop))
1539 .ok_or_else(|| {
1540 anyhow::anyhow!("next hop must not be unspecified address")
1541 })?,
1542 ),
1543 Some(fnet::IpAddress::Ipv6(_)) => {
1544 return Err(anyhow::anyhow!(
1545 "next hop must be same IP version as destination"
1546 ));
1547 }
1548 None => None,
1549 };
1550 self.remove_route::<Ipv4>(
1551 Subnet::new(destination_addr.into_ext(), prefix_len)
1552 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1553 next_hop,
1554 metric,
1555 )
1556 .await
1557 }
1558 fnet::IpAddress::Ipv6(destination_addr) => {
1559 let next_hop = match next_hop {
1560 Some(fnet::IpAddress::Ipv6(next_hop)) => Some(
1561 SpecifiedAddr::new(net_types::ip::Ipv6Addr::from_ext(next_hop))
1562 .ok_or_else(|| {
1563 anyhow::anyhow!("next hop must not be unspecified address")
1564 })?,
1565 ),
1566 Some(fnet::IpAddress::Ipv4(_)) => {
1567 return Err(anyhow::anyhow!(
1568 "next hop must be same IP version as destination"
1569 ));
1570 }
1571 None => None,
1572 };
1573 self.remove_route::<Ipv6>(
1574 Subnet::new(destination_addr.into_ext(), prefix_len)
1575 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1576 next_hop,
1577 metric,
1578 )
1579 .await
1580 }
1581 }
1582 }
1583
1584 pub async fn add_subnet_route(&self, subnet: fnet::Subnet) -> Result<()> {
1586 let subnet = fnet_ext::apply_subnet_mask(subnet);
1587 let newly_added = self
1588 .add_route_either(
1589 subnet,
1590 None,
1591 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1592 )
1593 .await?;
1594
1595 if !newly_added {
1596 Err(anyhow::anyhow!(
1597 "route to {subnet:?} on {} should not have already existed",
1598 self.id()
1599 ))
1600 } else {
1601 Ok(())
1602 }
1603 }
1604
1605 pub async fn del_subnet_route(&self, subnet: fnet::Subnet) -> Result<()> {
1607 let subnet = fnet_ext::apply_subnet_mask(subnet);
1608 let newly_removed = self
1609 .remove_route_either(
1610 subnet,
1611 None,
1612 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1613 )
1614 .await?;
1615
1616 if !newly_removed {
1617 Err(anyhow::anyhow!(
1618 "route to {subnet:?} on {} should have previously existed before being removed",
1619 self.id()
1620 ))
1621 } else {
1622 Ok(())
1623 }
1624 }
1625
1626 pub async fn add_default_route_with_metric(
1628 &self,
1629 next_hop: fnet::IpAddress,
1630 metric: fnet_routes::SpecifiedMetric,
1631 ) -> Result<()> {
1632 let corresponding_default_subnet = match next_hop {
1633 fnet::IpAddress::Ipv4(_) => net_declare::fidl_subnet!("0.0.0.0/0"),
1634 fnet::IpAddress::Ipv6(_) => net_declare::fidl_subnet!("::/0"),
1635 };
1636
1637 let newly_added =
1638 self.add_route_either(corresponding_default_subnet, Some(next_hop), metric).await?;
1639
1640 if !newly_added {
1641 Err(anyhow::anyhow!(
1642 "default route through {} via {next_hop:?} already exists",
1643 self.id()
1644 ))
1645 } else {
1646 Ok(())
1647 }
1648 }
1649
1650 pub async fn add_default_route_with_explicit_metric(
1652 &self,
1653 next_hop: fnet::IpAddress,
1654 metric: u32,
1655 ) -> Result<()> {
1656 self.add_default_route_with_metric(
1657 next_hop,
1658 fnet_routes::SpecifiedMetric::ExplicitMetric(metric),
1659 )
1660 .await
1661 }
1662
1663 pub async fn add_default_route(&self, next_hop: fnet::IpAddress) -> Result<()> {
1665 self.add_default_route_with_metric(
1666 next_hop,
1667 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1668 )
1669 .await
1670 }
1671
1672 pub async fn remove_default_route(&self, next_hop: fnet::IpAddress) -> Result<()> {
1674 let corresponding_default_subnet = match next_hop {
1675 fnet::IpAddress::Ipv4(_) => net_declare::fidl_subnet!("0.0.0.0/0"),
1676 fnet::IpAddress::Ipv6(_) => net_declare::fidl_subnet!("::/0"),
1677 };
1678
1679 let newly_removed = self
1680 .remove_route_either(
1681 corresponding_default_subnet,
1682 Some(next_hop),
1683 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1684 )
1685 .await?;
1686
1687 if !newly_removed {
1688 Err(anyhow::anyhow!(
1689 "default route through {} via {next_hop:?} does not exist",
1690 self.id()
1691 ))
1692 } else {
1693 Ok(())
1694 }
1695 }
1696
1697 pub async fn add_gateway_route(
1699 &self,
1700 destination: fnet::Subnet,
1701 next_hop: fnet::IpAddress,
1702 ) -> Result<()> {
1703 let newly_added = self
1704 .add_route_either(
1705 destination,
1706 Some(next_hop),
1707 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1708 )
1709 .await?;
1710
1711 if !newly_added {
1712 Err(anyhow::anyhow!(
1713 "should have newly added route to {destination:?} via {next_hop:?} through {}",
1714 self.id()
1715 ))
1716 } else {
1717 Ok(())
1718 }
1719 }
1720
1721 pub async fn create_authenticated_global_route_set<
1723 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1724 >(
1725 &self,
1726 ) -> Result<<I::RouteSetMarker as ProtocolMarker>::Proxy> {
1727 #[derive(GenericOverIp)]
1728 #[generic_over_ip(I, Ip)]
1729 struct Out<'a, I: fnet_routes_ext::admin::FidlRouteAdminIpExt>(
1730 LocalBoxFuture<'a, <I::RouteSetMarker as ProtocolMarker>::Proxy>,
1731 );
1732
1733 let Out(proxy_fut) = I::map_ip_out(
1734 self,
1735 |this| {
1736 Out(this
1737 .get_global_route_set_v4()
1738 .map(|result| result.expect("get global route set"))
1739 .boxed_local())
1740 },
1741 |this| {
1742 Out(this
1743 .get_global_route_set_v6()
1744 .map(|result| result.expect("get global route set"))
1745 .boxed_local())
1746 },
1747 );
1748
1749 let route_set = proxy_fut.await;
1750 let fnet_resources::GrantForInterfaceAuthorization { interface_id, token } =
1751 self.get_authorization().await.expect("get interface grant");
1752 fnet_routes_ext::admin::authenticate_for_interface::<I>(
1753 &route_set,
1754 fnet_resources::ProofOfInterfaceAuthorization { interface_id, token },
1755 )
1756 .await
1757 .expect("authentication should not have FIDL error")
1758 .expect("authentication should succeed");
1759 Ok(route_set)
1760 }
1761
1762 async fn get_global_route_set_v4(&self) -> Result<fnet_routes_admin::RouteSetV4Proxy> {
1763 let root_routes = self
1764 .realm
1765 .connect_to_protocol::<fnet_root::RoutesV4Marker>()
1766 .expect("get fuchsia.net.root.RoutesV4");
1767 let (route_set, server_end) =
1768 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteSetV4Marker>();
1769 root_routes.global_route_set(server_end).expect("calling global_route_set should succeed");
1770 Ok(route_set)
1771 }
1772
1773 async fn get_global_route_set_v6(&self) -> Result<fnet_routes_admin::RouteSetV6Proxy> {
1774 let root_routes = self
1775 .realm
1776 .connect_to_protocol::<fnet_root::RoutesV6Marker>()
1777 .expect("get fuchsia.net.root.RoutesV6");
1778 let (route_set, server_end) =
1779 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteSetV6Marker>();
1780 root_routes.global_route_set(server_end).expect("calling global_route_set should succeed");
1781 Ok(route_set)
1782 }
1783
1784 async fn get_properties(
1786 &self,
1787 included_addresses: fnet_interfaces_ext::IncludedAddresses,
1788 ) -> Result<fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>> {
1789 let interface_state = self.realm.connect_to_protocol::<fnet_interfaces::StateMarker>()?;
1790 let properties = fnet_interfaces_ext::existing(
1791 fnet_interfaces_ext::event_stream_from_state(
1792 &interface_state,
1793 fnet_interfaces_ext::WatchOptions { included_addresses, ..Default::default() },
1794 )?,
1795 fnet_interfaces_ext::InterfaceState::<(), _>::Unknown(self.id),
1796 )
1797 .await
1798 .context("failed to get existing interfaces")?;
1799 match properties {
1800 fnet_interfaces_ext::InterfaceState::Unknown(id) => Err(anyhow::anyhow!(
1801 "could not find interface {} for endpoint {}",
1802 id,
1803 self.endpoint.name
1804 )),
1805 fnet_interfaces_ext::InterfaceState::Known(
1806 fnet_interfaces_ext::PropertiesAndState { properties, state: () },
1807 ) => Ok(properties),
1808 }
1809 }
1810
1811 pub async fn get_addrs(
1813 &self,
1814 included_addresses: fnet_interfaces_ext::IncludedAddresses,
1815 ) -> Result<Vec<fnet_interfaces_ext::Address<fnet_interfaces_ext::AllInterest>>> {
1816 let fnet_interfaces_ext::Properties { addresses, .. } =
1817 self.get_properties(included_addresses).await?;
1818 Ok(addresses)
1819 }
1820
1821 pub async fn get_interface_name(&self) -> Result<String> {
1823 let fnet_interfaces_ext::Properties { name, .. } =
1824 self.get_properties(Default::default()).await?;
1825 Ok(name)
1826 }
1827
1828 pub async fn get_port_class(&self) -> Result<fnet_interfaces_ext::PortClass> {
1830 let fnet_interfaces_ext::Properties { port_class, .. } =
1831 self.get_properties(Default::default()).await?;
1832 Ok(port_class)
1833 }
1834
1835 pub async fn mac(&self) -> fnet::MacAddress {
1837 let (port, server_end) =
1838 fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_network::PortMarker>();
1839 self.get_port(server_end).expect("get_port");
1840 let (mac_addressing, server_end) =
1841 fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_network::MacAddressingMarker>();
1842 port.get_mac(server_end).expect("get_mac");
1843 mac_addressing.get_unicast_address().await.expect("get_unicast_address")
1844 }
1845
1846 async fn set_dhcp_client_enabled(&self, enable: bool) -> Result<()> {
1847 self.connect_stack()
1848 .context("connect stack")?
1849 .set_dhcp_client_enabled(self.id, enable)
1850 .await
1851 .context("failed to call SetDhcpClientEnabled")?
1852 .map_err(|e| anyhow!("{:?}", e))
1853 }
1854
1855 pub async fn start_dhcp<D: DhcpClient>(&self) -> Result<()> {
1857 match D::DHCP_CLIENT_VERSION {
1858 DhcpClientVersion::InStack => self.start_dhcp_in_stack().await,
1859 DhcpClientVersion::OutOfStack => self.start_dhcp_client_out_of_stack().await,
1860 }
1861 }
1862
1863 async fn start_dhcp_in_stack(&self) -> Result<()> {
1864 self.set_dhcp_client_enabled(true).await.context("failed to start dhcp client")
1865 }
1866
1867 async fn start_dhcp_client_out_of_stack(&self) -> Result<()> {
1868 let Self { endpoint: _, realm, id, control, device_control: _, dhcp_client_task } = self;
1869 let id = NonZeroU64::new(*id).expect("interface ID should be nonzero");
1870 let mut dhcp_client_task = dhcp_client_task.lock().await;
1871 let dhcp_client_task = dhcp_client_task.deref_mut();
1872
1873 let provider = realm
1874 .connect_to_protocol::<fnet_dhcp::ClientProviderMarker>()
1875 .expect("get fuchsia.net.dhcp.ClientProvider");
1876
1877 provider.check_presence().await.expect("check presence should succeed");
1878
1879 let client = provider.new_client_ext(id, fnet_dhcp_ext::default_new_client_params());
1880 let control = control.clone();
1881 let route_set_provider = realm
1882 .connect_to_protocol::<fnet_routes_admin::RouteTableV4Marker>()
1883 .expect("get fuchsia.net.routes.RouteTableV4");
1884 let (route_set, server_end) =
1885 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteSetV4Marker>();
1886 route_set_provider.new_route_set(server_end).expect("calling new_route_set should succeed");
1887 let task = fnet_dhcp_ext::testutil::DhcpClientTask::new(client, id, route_set, control);
1888 *dhcp_client_task = Some(task);
1889 Ok(())
1890 }
1891
1892 pub async fn stop_dhcp<D: DhcpClient>(&self) -> Result<()> {
1894 match D::DHCP_CLIENT_VERSION {
1895 DhcpClientVersion::InStack => self.stop_dhcp_in_stack().await,
1896 DhcpClientVersion::OutOfStack => {
1897 self.stop_dhcp_out_of_stack().await;
1898 Ok(())
1899 }
1900 }
1901 }
1902
1903 async fn stop_dhcp_in_stack(&self) -> Result<()> {
1904 self.set_dhcp_client_enabled(false).await.context("failed to stop dhcp client")
1905 }
1906
1907 async fn stop_dhcp_out_of_stack(&self) {
1908 let Self { endpoint: _, realm: _, id: _, control: _, device_control: _, dhcp_client_task } =
1909 self;
1910 let mut dhcp_client_task = dhcp_client_task.lock().await;
1911 if let Some(task) = dhcp_client_task.deref_mut().take() {
1912 task.shutdown().await.expect("client shutdown should succeed");
1913 }
1914 }
1915
1916 pub async fn add_address_and_wait_until(
1918 &self,
1919 subnet: fnet::Subnet,
1920 state: fnet_interfaces::AddressAssignmentState,
1921 ) -> Result<()> {
1922 let (address_state_provider, server) =
1923 fidl::endpoints::create_proxy::<fnet_interfaces_admin::AddressStateProviderMarker>();
1924 let () = address_state_provider.detach().context("detach address lifetime")?;
1925 let () = self
1926 .control
1927 .add_address(&subnet, &fnet_interfaces_admin::AddressParameters::default(), server)
1928 .context("FIDL error")?;
1929
1930 let mut state_stream =
1931 fnet_interfaces_ext::admin::assignment_state_stream(address_state_provider);
1932 fnet_interfaces_ext::admin::wait_assignment_state(&mut state_stream, state).await?;
1933 Ok(())
1934 }
1935
1936 pub async fn add_address(&self, subnet: fnet::Subnet) -> Result<()> {
1939 self.add_address_and_wait_until(subnet, fnet_interfaces::AddressAssignmentState::Assigned)
1940 .await
1941 }
1942
1943 pub async fn add_address_and_subnet_route(&self, subnet: fnet::Subnet) -> Result<()> {
1946 let (address_state_provider, server) =
1947 fidl::endpoints::create_proxy::<fnet_interfaces_admin::AddressStateProviderMarker>();
1948 address_state_provider.detach().context("detach address lifetime")?;
1949 self.control
1950 .add_address(
1951 &subnet,
1952 &fnet_interfaces_admin::AddressParameters {
1953 add_subnet_route: Some(true),
1954 ..Default::default()
1955 },
1956 server,
1957 )
1958 .context("FIDL error")?;
1959
1960 let state_stream =
1961 fnet_interfaces_ext::admin::assignment_state_stream(address_state_provider);
1962 let mut state_stream = pin!(state_stream);
1963
1964 fnet_interfaces_ext::admin::wait_assignment_state(
1965 &mut state_stream,
1966 fnet_interfaces::AddressAssignmentState::Assigned,
1967 )
1968 .await
1969 .context("assignment state")?;
1970 Ok(())
1971 }
1972
1973 pub async fn del_address_and_subnet_route(
1975 &self,
1976 addr_with_prefix: fnet::Subnet,
1977 ) -> Result<bool> {
1978 let did_remove =
1979 self.control.remove_address(&addr_with_prefix).await.context("FIDL error").and_then(
1980 |res| {
1981 res.map_err(|e: fnet_interfaces_admin::ControlRemoveAddressError| {
1982 anyhow::anyhow!("{:?}", e)
1983 })
1984 },
1985 )?;
1986
1987 if did_remove {
1988 let destination = fnet_ext::apply_subnet_mask(addr_with_prefix);
1989 let newly_removed_route = self
1990 .remove_route_either(
1991 destination,
1992 None,
1993 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1994 )
1995 .await?;
1996
1997 let _: bool = newly_removed_route;
2000 }
2001 Ok(did_remove)
2002 }
2003
2004 pub async fn remove_ipv6_linklocal_addresses(
2008 &self,
2009 ) -> Result<Vec<fnet_interfaces_ext::Address<fnet_interfaces_ext::AllInterest>>> {
2010 let mut result = Vec::new();
2011 for address in self.get_addrs(fnet_interfaces_ext::IncludedAddresses::All).await? {
2012 let fnet_interfaces_ext::Address { addr: fnet::Subnet { addr, prefix_len }, .. } =
2013 &address;
2014 match addr {
2015 fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address { addr: _ }) => {
2016 continue;
2017 }
2018 fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address { addr }) => {
2019 let v6_addr = net_types::ip::Ipv6Addr::from_bytes(*addr);
2020 if !v6_addr.is_unicast_link_local() {
2021 continue;
2022 }
2023 }
2024 }
2025 let _newly_removed: bool = self
2026 .del_address_and_subnet_route(fnet::Subnet { addr: *addr, prefix_len: *prefix_len })
2027 .await?;
2028 result.push(address);
2029 }
2030 Ok(result)
2031 }
2032
2033 async fn set_configuration(&self, config: fnet_interfaces_admin::Configuration) -> Result<()> {
2043 let fnet_interfaces_admin::Configuration {
2044 ipv4: previous_ipv4, ipv6: previous_ipv6, ..
2045 } = self
2046 .control()
2047 .set_configuration(&config.clone())
2048 .await
2049 .context("FIDL error")?
2050 .map_err(|e| anyhow!("set configuration error: {:?}", e))?;
2051
2052 fn verify_config_changed<T: Eq>(previous: Option<T>, current: Option<T>) -> Result<()> {
2053 if let Some(current) = current {
2054 let previous = previous.ok_or_else(|| anyhow!("configuration not supported"))?;
2055 if previous == current {
2056 return Err(anyhow!("configuration change is a no-op"));
2057 }
2058 }
2059 Ok(())
2060 }
2061
2062 let fnet_interfaces_admin::Configuration { ipv4, ipv6, .. } = config;
2063 if let Some(fnet_interfaces_admin::Ipv4Configuration {
2064 unicast_forwarding,
2065 multicast_forwarding,
2066 ..
2067 }) = ipv4
2068 {
2069 let fnet_interfaces_admin::Ipv4Configuration {
2070 unicast_forwarding: previous_unicast_forwarding,
2071 multicast_forwarding: previous_multicast_forwarding,
2072 ..
2073 } = previous_ipv4.ok_or_else(|| anyhow!("IPv4 configuration not supported"))?;
2074 verify_config_changed(previous_unicast_forwarding, unicast_forwarding)
2075 .context("IPv4 unicast forwarding")?;
2076 verify_config_changed(previous_multicast_forwarding, multicast_forwarding)
2077 .context("IPv4 multicast forwarding")?;
2078 }
2079 if let Some(fnet_interfaces_admin::Ipv6Configuration {
2080 unicast_forwarding,
2081 multicast_forwarding,
2082 ..
2083 }) = ipv6
2084 {
2085 let fnet_interfaces_admin::Ipv6Configuration {
2086 unicast_forwarding: previous_unicast_forwarding,
2087 multicast_forwarding: previous_multicast_forwarding,
2088 ..
2089 } = previous_ipv6.ok_or_else(|| anyhow!("IPv6 configuration not supported"))?;
2090 verify_config_changed(previous_unicast_forwarding, unicast_forwarding)
2091 .context("IPv6 unicast forwarding")?;
2092 verify_config_changed(previous_multicast_forwarding, multicast_forwarding)
2093 .context("IPv6 multicast forwarding")?;
2094 }
2095 Ok(())
2096 }
2097
2098 pub async fn set_ipv6_forwarding_enabled(&self, enabled: bool) -> Result<()> {
2100 self.set_configuration(fnet_interfaces_admin::Configuration {
2101 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
2102 unicast_forwarding: Some(enabled),
2103 ..Default::default()
2104 }),
2105 ..Default::default()
2106 })
2107 .await
2108 }
2109
2110 pub async fn set_ipv4_forwarding_enabled(&self, enabled: bool) -> Result<()> {
2112 self.set_configuration(fnet_interfaces_admin::Configuration {
2113 ipv4: Some(fnet_interfaces_admin::Ipv4Configuration {
2114 unicast_forwarding: Some(enabled),
2115 ..Default::default()
2116 }),
2117 ..Default::default()
2118 })
2119 .await
2120 }
2121
2122 pub async fn remove(
2125 self,
2126 ) -> Result<(fnetemul_network::EndpointProxy, Option<fnet_interfaces_admin::DeviceControlProxy>)>
2127 {
2128 let Self {
2129 endpoint: TestEndpoint { endpoint, name: _, _sandbox: _ },
2130 id: _,
2131 realm: _,
2132 control,
2133 device_control,
2134 dhcp_client_task: _,
2135 } = self;
2136 std::mem::drop(control);
2140 Ok((endpoint, device_control))
2141 }
2142
2143 pub fn remove_device(self) -> (Control, Option<fnet_interfaces_admin::DeviceControlProxy>) {
2147 let Self {
2148 endpoint: TestEndpoint { endpoint, name: _, _sandbox: _ },
2149 id: _,
2150 realm: _,
2151 control,
2152 device_control,
2153 dhcp_client_task: _,
2154 } = self;
2155 std::mem::drop(endpoint);
2156 (control, device_control)
2157 }
2158
2159 pub async fn wait_removal(self) -> Result<fnet_interfaces_admin::InterfaceRemovedReason> {
2161 let Self {
2162 endpoint: _endpoint,
2164 id: _,
2165 realm: _,
2166 control,
2167 dhcp_client_task: _,
2168 device_control: _device_control,
2170 } = self;
2171 match control.wait_termination().await {
2172 fnet_interfaces_ext::admin::TerminalError::Fidl(e) => {
2173 Err(e).context("waiting interface control termination")
2174 }
2175 fnet_interfaces_ext::admin::TerminalError::Terminal(reason) => Ok(reason),
2176 }
2177 }
2178
2179 pub async fn set_ipv4_dad_transmits(&self, dad_transmits: u16) -> Result<Option<u16>> {
2183 set_ipv4_dad_transmits(self.control(), dad_transmits).await
2184 }
2185
2186 pub async fn set_ipv6_dad_transmits(&self, dad_transmits: u16) -> Result<Option<u16>> {
2190 set_ipv6_dad_transmits(self.control(), dad_transmits).await
2191 }
2192
2193 pub async fn set_temporary_address_generation_enabled(&self, enabled: bool) -> Result<()> {
2196 set_temporary_address_generation_enabled(self.control(), enabled).await
2197 }
2198}
2199
2200async fn set_ipv4_dad_transmits(control: &Control, dad_transmits: u16) -> Result<Option<u16>> {
2201 control
2202 .set_configuration(&fnet_interfaces_admin::Configuration {
2203 ipv4: Some(fnet_interfaces_admin::Ipv4Configuration {
2204 arp: Some(fnet_interfaces_admin::ArpConfiguration {
2205 dad: Some(fnet_interfaces_admin::DadConfiguration {
2206 transmits: Some(dad_transmits),
2207 ..Default::default()
2208 }),
2209 ..Default::default()
2210 }),
2211 ..Default::default()
2212 }),
2213 ..Default::default()
2214 })
2215 .await?
2216 .map(|config| config.ipv4?.arp?.dad?.transmits)
2217 .map_err(|e| anyhow::anyhow!("set configuration error {e:?}"))
2218}
2219
2220async fn set_ipv6_dad_transmits(control: &Control, dad_transmits: u16) -> Result<Option<u16>> {
2221 control
2222 .set_configuration(&fnet_interfaces_admin::Configuration {
2223 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
2224 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
2225 dad: Some(fnet_interfaces_admin::DadConfiguration {
2226 transmits: Some(dad_transmits),
2227 ..Default::default()
2228 }),
2229 ..Default::default()
2230 }),
2231 ..Default::default()
2232 }),
2233 ..Default::default()
2234 })
2235 .await?
2236 .map(|config| config.ipv6?.ndp?.dad?.transmits)
2237 .map_err(|e| anyhow::anyhow!("set configuration error {e:?}"))
2238}
2239
2240async fn set_temporary_address_generation_enabled(control: &Control, enabled: bool) -> Result<()> {
2241 let _config: fnet_interfaces_admin::Configuration = control
2242 .set_configuration(&fnet_interfaces_admin::Configuration {
2243 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
2244 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
2245 slaac: Some(fnet_interfaces_admin::SlaacConfiguration {
2246 temporary_address: Some(enabled),
2247 ..Default::default()
2248 }),
2249 ..Default::default()
2250 }),
2251 ..Default::default()
2252 }),
2253 ..Default::default()
2254 })
2255 .await
2256 .context("FIDL error")?
2257 .map_err(|e| anyhow::anyhow!("set configuration error {e:?}"))?;
2258 Ok(())
2259}
2260
2261fn get_socket2_domain(addr: &std::net::SocketAddr) -> fposix_socket::Domain {
2263 let domain = match addr {
2264 std::net::SocketAddr::V4(_) => fposix_socket::Domain::Ipv4,
2265 std::net::SocketAddr::V6(_) => fposix_socket::Domain::Ipv6,
2266 };
2267
2268 domain
2269}
2270
2271pub trait RealmUdpSocket: Sized {
2273 fn bind_in_realm<'a>(
2275 realm: &'a TestRealm<'a>,
2276 addr: std::net::SocketAddr,
2277 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2278}
2279
2280impl RealmUdpSocket for std::net::UdpSocket {
2281 fn bind_in_realm<'a>(
2282 realm: &'a TestRealm<'a>,
2283 addr: std::net::SocketAddr,
2284 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2285 async move {
2286 let sock = realm
2287 .datagram_socket(
2288 get_socket2_domain(&addr),
2289 fposix_socket::DatagramSocketProtocol::Udp,
2290 )
2291 .await
2292 .context("failed to create socket")?;
2293
2294 let () = sock.bind(&addr.into()).context("bind failed")?;
2295
2296 Result::Ok(sock.into())
2297 }
2298 .boxed_local()
2299 }
2300}
2301
2302impl RealmUdpSocket for fuchsia_async::net::UdpSocket {
2303 fn bind_in_realm<'a>(
2304 realm: &'a TestRealm<'a>,
2305 addr: std::net::SocketAddr,
2306 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2307 std::net::UdpSocket::bind_in_realm(realm, addr)
2308 .and_then(|udp| {
2309 futures::future::ready(
2310 fuchsia_async::net::UdpSocket::from_socket(udp)
2311 .context("failed to create fuchsia_async socket"),
2312 )
2313 })
2314 .boxed_local()
2315 }
2316}
2317
2318pub trait RealmTcpListener: Sized {
2320 fn listen_in_realm<'a>(
2322 realm: &'a TestRealm<'a>,
2323 addr: std::net::SocketAddr,
2324 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2325 Self::listen_in_realm_with(realm, addr, |_: &socket2::Socket| Ok(()))
2326 }
2327
2328 fn listen_in_realm_with<'a>(
2331 realm: &'a TestRealm<'a>,
2332 addr: std::net::SocketAddr,
2333 setup: impl FnOnce(&socket2::Socket) -> Result<()> + 'a,
2334 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2335}
2336
2337impl RealmTcpListener for std::net::TcpListener {
2338 fn listen_in_realm_with<'a>(
2339 realm: &'a TestRealm<'a>,
2340 addr: std::net::SocketAddr,
2341 setup: impl FnOnce(&socket2::Socket) -> Result<()> + 'a,
2342 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2343 async move {
2344 let sock = realm
2345 .stream_socket(get_socket2_domain(&addr), fposix_socket::StreamSocketProtocol::Tcp)
2346 .await
2347 .context("failed to create server socket")?;
2348 let () = setup(&sock)?;
2349 let () = sock.bind(&addr.into()).context("failed to bind server socket")?;
2350 let () = sock.listen(128).context("failed to listen on server socket")?;
2353
2354 Result::Ok(sock.into())
2355 }
2356 .boxed_local()
2357 }
2358}
2359
2360impl RealmTcpListener for fuchsia_async::net::TcpListener {
2361 fn listen_in_realm_with<'a>(
2362 realm: &'a TestRealm<'a>,
2363 addr: std::net::SocketAddr,
2364 setup: impl FnOnce(&socket2::Socket) -> Result<()> + 'a,
2365 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2366 std::net::TcpListener::listen_in_realm_with(realm, addr, setup)
2367 .and_then(|listener| {
2368 futures::future::ready(
2369 fuchsia_async::net::TcpListener::from_std(listener)
2370 .context("failed to create fuchsia_async socket"),
2371 )
2372 })
2373 .boxed_local()
2374 }
2375}
2376
2377pub trait RealmTcpStream: Sized {
2379 fn connect_in_realm<'a>(
2381 realm: &'a TestRealm<'a>,
2382 addr: std::net::SocketAddr,
2383 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2384
2385 fn bind_and_connect_in_realm<'a>(
2387 realm: &'a TestRealm<'a>,
2388 local: std::net::SocketAddr,
2389 dst: std::net::SocketAddr,
2390 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2391
2392 fn connect_in_realm_with_sock<'a, F: FnOnce(&socket2::Socket) -> Result + 'a>(
2397 realm: &'a TestRealm<'a>,
2398 dst: std::net::SocketAddr,
2399 with_sock: F,
2400 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2401
2402 }
2404
2405impl RealmTcpStream for fuchsia_async::net::TcpStream {
2406 fn connect_in_realm<'a>(
2407 realm: &'a TestRealm<'a>,
2408 addr: std::net::SocketAddr,
2409 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2410 Self::connect_in_realm_with_sock(realm, addr, |_: &socket2::Socket| Ok(()))
2411 }
2412
2413 fn bind_and_connect_in_realm<'a>(
2414 realm: &'a TestRealm<'a>,
2415 local: std::net::SocketAddr,
2416 dst: std::net::SocketAddr,
2417 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2418 Self::connect_in_realm_with_sock(realm, dst, move |sock| {
2419 sock.bind(&local.into()).context("failed to bind")
2420 })
2421 }
2422
2423 fn connect_in_realm_with_sock<'a, F: FnOnce(&socket2::Socket) -> Result + 'a>(
2424 realm: &'a TestRealm<'a>,
2425 dst: std::net::SocketAddr,
2426 with_sock: F,
2427 ) -> futures::future::LocalBoxFuture<'a, Result<fuchsia_async::net::TcpStream>> {
2428 async move {
2429 let sock = realm
2430 .stream_socket(get_socket2_domain(&dst), fposix_socket::StreamSocketProtocol::Tcp)
2431 .await
2432 .context("failed to create socket")?;
2433
2434 with_sock(&sock)?;
2435
2436 let stream = fuchsia_async::net::TcpStream::connect_from_raw(sock, dst)
2437 .context("failed to create client tcp stream")?
2438 .await
2439 .context("failed to connect to server")?;
2440
2441 Result::Ok(stream)
2442 }
2443 .boxed_local()
2444 }
2445}
2446
2447fn truncate_dropping_front(s: Cow<'_, str>, len: usize) -> Cow<'_, str> {
2448 match s.len().checked_sub(len) {
2449 None => s,
2450 Some(start) => {
2451 match s {
2455 Cow::Borrowed(s) => Cow::Borrowed(&s[start..]),
2456 Cow::Owned(mut s) => {
2457 let _: std::string::Drain<'_> = s.drain(..start);
2458 Cow::Owned(s)
2459 }
2460 }
2461 }
2462 }
2463}