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 fnet_interfaces_ext::IncludedAddresses::OnlyAssigned,
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>(
957 &interface_state,
958 fnet_interfaces_ext::IncludedAddresses::OnlyAssigned,
959 )
960 .context("get interface event stream")
961 }
962
963 pub async fn main_table_id<
965 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
966 >(
967 &self,
968 ) -> u32 {
969 let main_route_table = self
970 .connect_to_protocol::<I::RouteTableMarker>()
971 .expect("failed to connect to main route table");
972 fnet_routes_ext::admin::get_table_id::<I>(&main_route_table)
973 .await
974 .expect("failed to get_table_id")
975 .get()
976 }
977}
978
979#[must_use]
984pub struct TestNetwork<'a> {
985 network: fnetemul_network::NetworkProxy,
986 name: Cow<'a, str>,
987 sandbox: &'a TestSandbox,
988}
989
990impl<'a> std::fmt::Debug for TestNetwork<'a> {
991 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
992 let Self { name, network: _, sandbox: _ } = self;
993 f.debug_struct("TestNetwork").field("name", name).finish_non_exhaustive()
994 }
995}
996
997impl<'a> TestNetwork<'a> {
998 pub fn into_proxy(self) -> fnetemul_network::NetworkProxy {
1005 let Self { network, name: _, sandbox: _ } = self;
1006 network
1007 }
1008
1009 async fn get_client_end_clone(
1011 &self,
1012 ) -> Result<fidl::endpoints::ClientEnd<fnetemul_network::NetworkMarker>> {
1013 let network_manager =
1014 self.sandbox.get_network_manager().context("get_network_manager failed")?;
1015 let client = network_manager
1016 .get_network(&self.name)
1017 .await
1018 .context("get_network failed")?
1019 .with_context(|| format!("no network found with name {}", self.name))?;
1020 Ok(client)
1021 }
1022
1023 pub async fn set_config(&self, config: fnetemul_network::NetworkConfig) -> Result<()> {
1025 let status = self.network.set_config(&config).await.context("call set_config")?;
1026 zx::Status::ok(status).context("set config")
1027 }
1028
1029 pub async fn attach_endpoint(&self, ep: &TestEndpoint<'a>) -> Result<()> {
1031 let status =
1032 self.network.attach_endpoint(&ep.name).await.context("attach_endpoint FIDL error")?;
1033 let () = zx::Status::ok(status).context("attach_endpoint failed")?;
1034 Ok(())
1035 }
1036
1037 pub async fn create_endpoint<S>(&self, name: S) -> Result<TestEndpoint<'a>>
1041 where
1042 S: Into<Cow<'a, str>>,
1043 {
1044 let ep = self
1045 .sandbox
1046 .create_endpoint(name)
1047 .await
1048 .with_context(|| format!("failed to create endpoint for network {}", self.name))?;
1049 let () = self.attach_endpoint(&ep).await.with_context(|| {
1050 format!("failed to attach endpoint {} to network {}", ep.name, self.name)
1051 })?;
1052 Ok(ep)
1053 }
1054
1055 pub async fn create_endpoint_with(
1059 &self,
1060 name: impl Into<Cow<'a, str>>,
1061 config: fnetemul_network::EndpointConfig,
1062 ) -> Result<TestEndpoint<'a>> {
1063 let ep = self
1064 .sandbox
1065 .create_endpoint_with(name, config)
1066 .await
1067 .with_context(|| format!("failed to create endpoint for network {}", self.name))?;
1068 let () = self.attach_endpoint(&ep).await.with_context(|| {
1069 format!("failed to attach endpoint {} to network {}", ep.name, self.name)
1070 })?;
1071 Ok(ep)
1072 }
1073
1074 pub fn create_fake_endpoint(&self) -> Result<TestFakeEndpoint<'a>> {
1076 let (endpoint, server) =
1077 fidl::endpoints::create_proxy::<fnetemul_network::FakeEndpointMarker>();
1078 let () = self.network.create_fake_endpoint(server)?;
1079 return Ok(TestFakeEndpoint { endpoint, _sandbox: self.sandbox });
1080 }
1081
1082 pub async fn start_capture(&self, name: &str) -> Result<PacketCapture> {
1088 let manager = self.sandbox.get_network_manager()?;
1089 let client = manager.get_network(&self.name).await?.expect("network must exist");
1090 zx::ok(self.network.start_capture(name).await?)?;
1091 let sync_proxy = fnetemul_network::NetworkSynchronousProxy::new(client.into_channel());
1092 Ok(PacketCapture { sync_proxy })
1093 }
1094
1095 pub async fn stop_capture(&self) -> Result<()> {
1097 Ok(self.network.stop_capture().await?)
1098 }
1099}
1100
1101pub struct PacketCapture {
1104 sync_proxy: fnetemul_network::NetworkSynchronousProxy,
1105}
1106
1107impl Drop for PacketCapture {
1108 fn drop(&mut self) {
1109 self.sync_proxy
1110 .stop_capture(zx::MonotonicInstant::INFINITE)
1111 .expect("failed to stop packet capture")
1112 }
1113}
1114
1115#[must_use]
1117pub struct TestEndpoint<'a> {
1118 endpoint: fnetemul_network::EndpointProxy,
1119 name: Cow<'a, str>,
1120 _sandbox: &'a TestSandbox,
1121}
1122
1123impl<'a> TestEndpoint<'a> {
1124 pub async fn get_port_identity_koid(&self) -> Result<zx::Koid> {
1127 let (client, server) = fidl::endpoints::create_proxy::<fnetwork::PortMarker>();
1128 self.get_port(server)?;
1129 let identity = client.get_identity().await?;
1130 Ok(identity.get_koid()?)
1131 }
1132}
1133
1134impl<'a> std::fmt::Debug for TestEndpoint<'a> {
1135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
1136 let Self { endpoint: _, name, _sandbox } = self;
1137 f.debug_struct("TestEndpoint").field("name", name).finish_non_exhaustive()
1138 }
1139}
1140
1141impl<'a> std::ops::Deref for TestEndpoint<'a> {
1142 type Target = fnetemul_network::EndpointProxy;
1143
1144 fn deref(&self) -> &Self::Target {
1145 &self.endpoint
1146 }
1147}
1148
1149#[must_use]
1151pub struct TestFakeEndpoint<'a> {
1152 endpoint: fnetemul_network::FakeEndpointProxy,
1153 _sandbox: &'a TestSandbox,
1154}
1155
1156impl<'a> std::ops::Deref for TestFakeEndpoint<'a> {
1157 type Target = fnetemul_network::FakeEndpointProxy;
1158
1159 fn deref(&self) -> &Self::Target {
1160 &self.endpoint
1161 }
1162}
1163
1164impl<'a> TestFakeEndpoint<'a> {
1165 pub fn frame_stream(
1169 &self,
1170 ) -> impl futures::Stream<Item = std::result::Result<(Vec<u8>, u64), fidl::Error>> + '_ {
1171 futures::stream::try_unfold(&self.endpoint, |ep| ep.read().map_ok(move |r| Some((r, ep))))
1172 }
1173}
1174
1175async fn to_netdevice_inner(
1178 port: fidl::endpoints::ClientEnd<fnetwork::PortMarker>,
1179) -> Result<(fidl::endpoints::ClientEnd<fnetwork::DeviceMarker>, fnetwork::PortId)> {
1180 let port = port.into_proxy();
1181 let (device, server_end) = fidl::endpoints::create_endpoints::<fnetwork::DeviceMarker>();
1182 let () = port.get_device(server_end)?;
1183 let port_id = port
1184 .get_info()
1185 .await
1186 .context("get port info")?
1187 .id
1188 .ok_or_else(|| anyhow::anyhow!("missing port id"))?;
1189 Ok((device, port_id))
1190}
1191
1192impl<'a> TestEndpoint<'a> {
1193 pub fn into_proxy(self) -> fnetemul_network::EndpointProxy {
1200 let Self { endpoint, name: _, _sandbox: _ } = self;
1201 endpoint
1202 }
1203
1204 pub async fn get_netdevice(
1209 &self,
1210 ) -> Result<(fidl::endpoints::ClientEnd<fnetwork::DeviceMarker>, fnetwork::PortId)> {
1211 let (port, server_end) = fidl::endpoints::create_endpoints();
1212 self.get_port(server_end)
1213 .with_context(|| format!("failed to get device connection for {}", self.name))?;
1214 to_netdevice_inner(port).await
1215 }
1216
1217 pub async fn install(
1223 &self,
1224 installer: fnet_interfaces_admin::InstallerProxy,
1225 InterfaceConfig {
1226 name,
1227 metric,
1228 ipv4_dad_transmits,
1229 ipv6_dad_transmits,
1230 temporary_addresses,
1231 netstack_managed_routes_designation,
1232 }: InterfaceConfig<'_>,
1233 ) -> Result<(u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
1234 let name = name.map(|n| {
1235 truncate_dropping_front(n.into(), fnet_interfaces::INTERFACE_NAME_LENGTH.into())
1236 .to_string()
1237 });
1238 let (device, port_id) = self.get_netdevice().await?;
1239 let device_control = {
1240 let (control, server_end) =
1241 fidl::endpoints::create_proxy::<fnet_interfaces_admin::DeviceControlMarker>();
1242 let () = installer.install_device(device, server_end).context("install device")?;
1243 control
1244 };
1245 let (control, server_end) = Control::create_endpoints().context("create endpoints")?;
1246 let () = device_control
1247 .create_interface(
1248 &port_id,
1249 server_end,
1250 fnet_interfaces_admin::Options {
1251 name,
1252 metric,
1253 netstack_managed_routes_designation,
1254 __source_breaking: fidl::marker::SourceBreaking,
1255 },
1256 )
1257 .context("create interface")?;
1258 if let Some(ipv4_dad_transmits) = ipv4_dad_transmits {
1259 let _: Option<u16> = set_ipv4_dad_transmits(&control, ipv4_dad_transmits)
1260 .await
1261 .context("set dad transmits")?;
1262 }
1263 if let Some(ipv6_dad_transmits) = ipv6_dad_transmits {
1264 let _: Option<u16> = set_ipv6_dad_transmits(&control, ipv6_dad_transmits)
1265 .await
1266 .context("set dad transmits")?;
1267 }
1268 if let Some(enabled) = temporary_addresses {
1269 set_temporary_address_generation_enabled(&control, enabled)
1270 .await
1271 .context("set temporary addresses")?;
1272 }
1273
1274 let id = control.get_id().await.context("get id")?;
1275 Ok((id, control, device_control))
1276 }
1277
1278 pub async fn add_to_stack(
1283 &self,
1284 realm: &TestRealm<'a>,
1285 config: InterfaceConfig<'a>,
1286 ) -> Result<(u64, Control, fnet_interfaces_admin::DeviceControlProxy)> {
1287 let installer = realm
1288 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
1289 .context("connect to protocol")?;
1290
1291 self.install(installer, config).await
1292 }
1293
1294 pub async fn into_interface_in_realm(self, realm: &TestRealm<'a>) -> Result<TestInterface<'a>> {
1296 self.into_interface_in_realm_with_name(realm, Default::default()).await
1297 }
1298
1299 pub async fn into_interface_in_realm_with_name(
1302 self,
1303 realm: &TestRealm<'a>,
1304 config: InterfaceConfig<'a>,
1305 ) -> Result<TestInterface<'a>> {
1306 let installer = realm
1307 .connect_to_protocol::<fnet_interfaces_admin::InstallerMarker>()
1308 .context("connect to protocol")?;
1309
1310 let (id, control, device_control) =
1311 self.install(installer, config).await.context("failed to install")?;
1312
1313 Ok(TestInterface {
1314 endpoint: self,
1315 id,
1316 realm: realm.clone(),
1317 control,
1318 device_control: Some(device_control),
1319 dhcp_client_task: futures::lock::Mutex::default(),
1320 })
1321 }
1322}
1323
1324#[derive(Copy, Clone, PartialEq, Debug)]
1326pub enum DhcpClientVersion {
1327 InStack,
1329 OutOfStack,
1331}
1332
1333pub trait DhcpClient {
1335 const DHCP_CLIENT_VERSION: DhcpClientVersion;
1337}
1338
1339pub enum InStack {}
1341
1342impl DhcpClient for InStack {
1343 const DHCP_CLIENT_VERSION: DhcpClientVersion = DhcpClientVersion::InStack;
1344}
1345
1346pub enum OutOfStack {}
1348
1349impl DhcpClient for OutOfStack {
1350 const DHCP_CLIENT_VERSION: DhcpClientVersion = DhcpClientVersion::OutOfStack;
1351}
1352
1353#[must_use]
1359pub struct TestInterface<'a> {
1360 endpoint: TestEndpoint<'a>,
1361 realm: TestRealm<'a>,
1362 id: u64,
1363 control: Control,
1364 device_control: Option<fnet_interfaces_admin::DeviceControlProxy>,
1365 dhcp_client_task: futures::lock::Mutex<Option<fnet_dhcp_ext::testutil::DhcpClientTask>>,
1366}
1367
1368impl<'a> std::fmt::Debug for TestInterface<'a> {
1369 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
1370 let Self { endpoint, id, realm: _, control: _, device_control: _, dhcp_client_task: _ } =
1371 self;
1372 f.debug_struct("TestInterface")
1373 .field("endpoint", endpoint)
1374 .field("id", id)
1375 .finish_non_exhaustive()
1376 }
1377}
1378
1379impl<'a> std::ops::Deref for TestInterface<'a> {
1380 type Target = fnetemul_network::EndpointProxy;
1381
1382 fn deref(&self) -> &Self::Target {
1383 &self.endpoint
1384 }
1385}
1386
1387impl<'a> TestInterface<'a> {
1388 pub fn id(&self) -> u64 {
1390 self.id
1391 }
1392
1393 pub fn endpoint(&self) -> &TestEndpoint<'a> {
1395 &self.endpoint
1396 }
1397
1398 pub fn control(&self) -> &Control {
1400 &self.control
1401 }
1402
1403 pub async fn get_authorization(
1405 &self,
1406 ) -> Result<fnet_resources::GrantForInterfaceAuthorization> {
1407 Ok(self.control.get_authorization_for_interface().await?)
1408 }
1409
1410 pub fn connect_stack(&self) -> Result<fnet_stack::StackProxy> {
1412 self.realm.connect_to_protocol::<fnet_stack::StackMarker>()
1413 }
1414
1415 async fn add_route<
1420 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1421 >(
1422 &self,
1423 destination: Subnet<I::Addr>,
1424 next_hop: Option<SpecifiedAddr<I::Addr>>,
1425 metric: fnet_routes::SpecifiedMetric,
1426 ) -> Result<bool> {
1427 let route_set = self.create_authenticated_global_route_set::<I>().await?;
1428 fnet_routes_ext::admin::add_route::<I>(
1429 &route_set,
1430 &fnet_routes_ext::Route::<I>::new_forward(destination, self.id(), next_hop, metric)
1431 .try_into()
1432 .expect("convert to FIDL should succeed"),
1433 )
1434 .await
1435 .context("FIDL error adding route")?
1436 .map_err(|e| anyhow::anyhow!("error adding route: {e:?}"))
1437 }
1438
1439 pub async fn add_route_either(
1445 &self,
1446 destination: fnet::Subnet,
1447 next_hop: Option<fnet::IpAddress>,
1448 metric: fnet_routes::SpecifiedMetric,
1449 ) -> Result<bool> {
1450 let fnet::Subnet { addr: destination_addr, prefix_len } = destination;
1451 match destination_addr {
1452 fnet::IpAddress::Ipv4(destination_addr) => {
1453 let next_hop = match next_hop {
1454 Some(fnet::IpAddress::Ipv4(next_hop)) => Some(
1455 SpecifiedAddr::new(net_types::ip::Ipv4Addr::from_ext(next_hop))
1456 .ok_or_else(|| {
1457 anyhow::anyhow!("next hop must not be unspecified address")
1458 })?,
1459 ),
1460 Some(fnet::IpAddress::Ipv6(_)) => {
1461 return Err(anyhow::anyhow!(
1462 "next hop must be same IP version as destination"
1463 ));
1464 }
1465 None => None,
1466 };
1467 self.add_route::<Ipv4>(
1468 Subnet::new(destination_addr.into_ext(), prefix_len)
1469 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1470 next_hop,
1471 metric,
1472 )
1473 .await
1474 }
1475 fnet::IpAddress::Ipv6(destination_addr) => {
1476 let next_hop = match next_hop {
1477 Some(fnet::IpAddress::Ipv6(next_hop)) => Some(
1478 SpecifiedAddr::new(net_types::ip::Ipv6Addr::from_ext(next_hop))
1479 .ok_or_else(|| {
1480 anyhow::anyhow!("next hop must not be unspecified address")
1481 })?,
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.add_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 async fn remove_route<
1506 I: Ip + fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1507 >(
1508 &self,
1509 destination: Subnet<I::Addr>,
1510 next_hop: Option<SpecifiedAddr<I::Addr>>,
1511 metric: fnet_routes::SpecifiedMetric,
1512 ) -> Result<bool> {
1513 let route_set = self.create_authenticated_global_route_set::<I>().await?;
1514 fnet_routes_ext::admin::remove_route::<I>(
1515 &route_set,
1516 &fnet_routes_ext::Route::<I>::new_forward(destination, self.id(), next_hop, metric)
1517 .try_into()
1518 .expect("convert to FIDL should succeed"),
1519 )
1520 .await
1521 .context("FIDL error removing route")?
1522 .map_err(|e| anyhow::anyhow!("error removing route: {e:?}"))
1523 }
1524
1525 async fn remove_route_either(
1531 &self,
1532 destination: fnet::Subnet,
1533 next_hop: Option<fnet::IpAddress>,
1534 metric: fnet_routes::SpecifiedMetric,
1535 ) -> Result<bool> {
1536 let fnet::Subnet { addr: destination_addr, prefix_len } = destination;
1537 match destination_addr {
1538 fnet::IpAddress::Ipv4(destination_addr) => {
1539 let next_hop = match next_hop {
1540 Some(fnet::IpAddress::Ipv4(next_hop)) => Some(
1541 SpecifiedAddr::new(net_types::ip::Ipv4Addr::from_ext(next_hop))
1542 .ok_or_else(|| {
1543 anyhow::anyhow!("next hop must not be unspecified address")
1544 })?,
1545 ),
1546 Some(fnet::IpAddress::Ipv6(_)) => {
1547 return Err(anyhow::anyhow!(
1548 "next hop must be same IP version as destination"
1549 ));
1550 }
1551 None => None,
1552 };
1553 self.remove_route::<Ipv4>(
1554 Subnet::new(destination_addr.into_ext(), prefix_len)
1555 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1556 next_hop,
1557 metric,
1558 )
1559 .await
1560 }
1561 fnet::IpAddress::Ipv6(destination_addr) => {
1562 let next_hop = match next_hop {
1563 Some(fnet::IpAddress::Ipv6(next_hop)) => Some(
1564 SpecifiedAddr::new(net_types::ip::Ipv6Addr::from_ext(next_hop))
1565 .ok_or_else(|| {
1566 anyhow::anyhow!("next hop must not be unspecified address")
1567 })?,
1568 ),
1569 Some(fnet::IpAddress::Ipv4(_)) => {
1570 return Err(anyhow::anyhow!(
1571 "next hop must be same IP version as destination"
1572 ));
1573 }
1574 None => None,
1575 };
1576 self.remove_route::<Ipv6>(
1577 Subnet::new(destination_addr.into_ext(), prefix_len)
1578 .map_err(|e| anyhow::anyhow!("invalid subnet: {e:?}"))?,
1579 next_hop,
1580 metric,
1581 )
1582 .await
1583 }
1584 }
1585 }
1586
1587 pub async fn add_subnet_route(&self, subnet: fnet::Subnet) -> Result<()> {
1589 let subnet = fnet_ext::apply_subnet_mask(subnet);
1590 let newly_added = self
1591 .add_route_either(
1592 subnet,
1593 None,
1594 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1595 )
1596 .await?;
1597
1598 if !newly_added {
1599 Err(anyhow::anyhow!(
1600 "route to {subnet:?} on {} should not have already existed",
1601 self.id()
1602 ))
1603 } else {
1604 Ok(())
1605 }
1606 }
1607
1608 pub async fn del_subnet_route(&self, subnet: fnet::Subnet) -> Result<()> {
1610 let subnet = fnet_ext::apply_subnet_mask(subnet);
1611 let newly_removed = self
1612 .remove_route_either(
1613 subnet,
1614 None,
1615 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1616 )
1617 .await?;
1618
1619 if !newly_removed {
1620 Err(anyhow::anyhow!(
1621 "route to {subnet:?} on {} should have previously existed before being removed",
1622 self.id()
1623 ))
1624 } else {
1625 Ok(())
1626 }
1627 }
1628
1629 pub async fn add_default_route_with_metric(
1631 &self,
1632 next_hop: fnet::IpAddress,
1633 metric: fnet_routes::SpecifiedMetric,
1634 ) -> Result<()> {
1635 let corresponding_default_subnet = match next_hop {
1636 fnet::IpAddress::Ipv4(_) => net_declare::fidl_subnet!("0.0.0.0/0"),
1637 fnet::IpAddress::Ipv6(_) => net_declare::fidl_subnet!("::/0"),
1638 };
1639
1640 let newly_added =
1641 self.add_route_either(corresponding_default_subnet, Some(next_hop), metric).await?;
1642
1643 if !newly_added {
1644 Err(anyhow::anyhow!(
1645 "default route through {} via {next_hop:?} already exists",
1646 self.id()
1647 ))
1648 } else {
1649 Ok(())
1650 }
1651 }
1652
1653 pub async fn add_default_route_with_explicit_metric(
1655 &self,
1656 next_hop: fnet::IpAddress,
1657 metric: u32,
1658 ) -> Result<()> {
1659 self.add_default_route_with_metric(
1660 next_hop,
1661 fnet_routes::SpecifiedMetric::ExplicitMetric(metric),
1662 )
1663 .await
1664 }
1665
1666 pub async fn add_default_route(&self, next_hop: fnet::IpAddress) -> Result<()> {
1668 self.add_default_route_with_metric(
1669 next_hop,
1670 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1671 )
1672 .await
1673 }
1674
1675 pub async fn remove_default_route(&self, next_hop: fnet::IpAddress) -> Result<()> {
1677 let corresponding_default_subnet = match next_hop {
1678 fnet::IpAddress::Ipv4(_) => net_declare::fidl_subnet!("0.0.0.0/0"),
1679 fnet::IpAddress::Ipv6(_) => net_declare::fidl_subnet!("::/0"),
1680 };
1681
1682 let newly_removed = self
1683 .remove_route_either(
1684 corresponding_default_subnet,
1685 Some(next_hop),
1686 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1687 )
1688 .await?;
1689
1690 if !newly_removed {
1691 Err(anyhow::anyhow!(
1692 "default route through {} via {next_hop:?} does not exist",
1693 self.id()
1694 ))
1695 } else {
1696 Ok(())
1697 }
1698 }
1699
1700 pub async fn add_gateway_route(
1702 &self,
1703 destination: fnet::Subnet,
1704 next_hop: fnet::IpAddress,
1705 ) -> Result<()> {
1706 let newly_added = self
1707 .add_route_either(
1708 destination,
1709 Some(next_hop),
1710 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1711 )
1712 .await?;
1713
1714 if !newly_added {
1715 Err(anyhow::anyhow!(
1716 "should have newly added route to {destination:?} via {next_hop:?} through {}",
1717 self.id()
1718 ))
1719 } else {
1720 Ok(())
1721 }
1722 }
1723
1724 pub async fn create_authenticated_global_route_set<
1726 I: fnet_routes_ext::FidlRouteIpExt + fnet_routes_ext::admin::FidlRouteAdminIpExt,
1727 >(
1728 &self,
1729 ) -> Result<<I::RouteSetMarker as ProtocolMarker>::Proxy> {
1730 #[derive(GenericOverIp)]
1731 #[generic_over_ip(I, Ip)]
1732 struct Out<'a, I: fnet_routes_ext::admin::FidlRouteAdminIpExt>(
1733 LocalBoxFuture<'a, <I::RouteSetMarker as ProtocolMarker>::Proxy>,
1734 );
1735
1736 let Out(proxy_fut) = I::map_ip_out(
1737 self,
1738 |this| {
1739 Out(this
1740 .get_global_route_set_v4()
1741 .map(|result| result.expect("get global route set"))
1742 .boxed_local())
1743 },
1744 |this| {
1745 Out(this
1746 .get_global_route_set_v6()
1747 .map(|result| result.expect("get global route set"))
1748 .boxed_local())
1749 },
1750 );
1751
1752 let route_set = proxy_fut.await;
1753 let fnet_resources::GrantForInterfaceAuthorization { interface_id, token } =
1754 self.get_authorization().await.expect("get interface grant");
1755 fnet_routes_ext::admin::authenticate_for_interface::<I>(
1756 &route_set,
1757 fnet_resources::ProofOfInterfaceAuthorization { interface_id, token },
1758 )
1759 .await
1760 .expect("authentication should not have FIDL error")
1761 .expect("authentication should succeed");
1762 Ok(route_set)
1763 }
1764
1765 async fn get_global_route_set_v4(&self) -> Result<fnet_routes_admin::RouteSetV4Proxy> {
1766 let root_routes = self
1767 .realm
1768 .connect_to_protocol::<fnet_root::RoutesV4Marker>()
1769 .expect("get fuchsia.net.root.RoutesV4");
1770 let (route_set, server_end) =
1771 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteSetV4Marker>();
1772 root_routes.global_route_set(server_end).expect("calling global_route_set should succeed");
1773 Ok(route_set)
1774 }
1775
1776 async fn get_global_route_set_v6(&self) -> Result<fnet_routes_admin::RouteSetV6Proxy> {
1777 let root_routes = self
1778 .realm
1779 .connect_to_protocol::<fnet_root::RoutesV6Marker>()
1780 .expect("get fuchsia.net.root.RoutesV6");
1781 let (route_set, server_end) =
1782 fidl::endpoints::create_proxy::<fnet_routes_admin::RouteSetV6Marker>();
1783 root_routes.global_route_set(server_end).expect("calling global_route_set should succeed");
1784 Ok(route_set)
1785 }
1786
1787 async fn get_properties(
1789 &self,
1790 included_addresses: fnet_interfaces_ext::IncludedAddresses,
1791 ) -> Result<fnet_interfaces_ext::Properties<fnet_interfaces_ext::AllInterest>> {
1792 let interface_state = self.realm.connect_to_protocol::<fnet_interfaces::StateMarker>()?;
1793 let properties = fnet_interfaces_ext::existing(
1794 fnet_interfaces_ext::event_stream_from_state(&interface_state, included_addresses)?,
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(fnet_interfaces_ext::IncludedAddresses::OnlyAssigned).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(fnet_interfaces_ext::IncludedAddresses::OnlyAssigned).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(&self, subnet: fnet::Subnet) -> Result<()> {
1919 let (address_state_provider, server) =
1920 fidl::endpoints::create_proxy::<fnet_interfaces_admin::AddressStateProviderMarker>();
1921 let () = address_state_provider.detach().context("detach address lifetime")?;
1922 let () = self
1923 .control
1924 .add_address(&subnet, &fnet_interfaces_admin::AddressParameters::default(), server)
1925 .context("FIDL error")?;
1926
1927 let mut state_stream =
1928 fnet_interfaces_ext::admin::assignment_state_stream(address_state_provider);
1929 fnet_interfaces_ext::admin::wait_assignment_state(
1930 &mut state_stream,
1931 fnet_interfaces::AddressAssignmentState::Assigned,
1932 )
1933 .await?;
1934 Ok(())
1935 }
1936
1937 pub async fn add_address_and_subnet_route(&self, subnet: fnet::Subnet) -> Result<()> {
1940 let (address_state_provider, server) =
1941 fidl::endpoints::create_proxy::<fnet_interfaces_admin::AddressStateProviderMarker>();
1942 address_state_provider.detach().context("detach address lifetime")?;
1943 self.control
1944 .add_address(
1945 &subnet,
1946 &fnet_interfaces_admin::AddressParameters {
1947 add_subnet_route: Some(true),
1948 ..Default::default()
1949 },
1950 server,
1951 )
1952 .context("FIDL error")?;
1953
1954 let state_stream =
1955 fnet_interfaces_ext::admin::assignment_state_stream(address_state_provider);
1956 let mut state_stream = pin!(state_stream);
1957
1958 fnet_interfaces_ext::admin::wait_assignment_state(
1959 &mut state_stream,
1960 fnet_interfaces::AddressAssignmentState::Assigned,
1961 )
1962 .await
1963 .context("assignment state")?;
1964 Ok(())
1965 }
1966
1967 pub async fn del_address_and_subnet_route(
1969 &self,
1970 addr_with_prefix: fnet::Subnet,
1971 ) -> Result<bool> {
1972 let did_remove =
1973 self.control.remove_address(&addr_with_prefix).await.context("FIDL error").and_then(
1974 |res| {
1975 res.map_err(|e: fnet_interfaces_admin::ControlRemoveAddressError| {
1976 anyhow::anyhow!("{:?}", e)
1977 })
1978 },
1979 )?;
1980
1981 if did_remove {
1982 let destination = fnet_ext::apply_subnet_mask(addr_with_prefix);
1983 let newly_removed_route = self
1984 .remove_route_either(
1985 destination,
1986 None,
1987 fnet_routes::SpecifiedMetric::InheritedFromInterface(fnet_routes::Empty),
1988 )
1989 .await?;
1990
1991 let _: bool = newly_removed_route;
1994 }
1995 Ok(did_remove)
1996 }
1997
1998 pub async fn remove_ipv6_linklocal_addresses(
2002 &self,
2003 ) -> Result<Vec<fnet_interfaces_ext::Address<fnet_interfaces_ext::AllInterest>>> {
2004 let mut result = Vec::new();
2005 for address in self.get_addrs(fnet_interfaces_ext::IncludedAddresses::All).await? {
2006 let fnet_interfaces_ext::Address { addr: fnet::Subnet { addr, prefix_len }, .. } =
2007 &address;
2008 match addr {
2009 fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address { addr: _ }) => {
2010 continue;
2011 }
2012 fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address { addr }) => {
2013 let v6_addr = net_types::ip::Ipv6Addr::from_bytes(*addr);
2014 if !v6_addr.is_unicast_link_local() {
2015 continue;
2016 }
2017 }
2018 }
2019 let _newly_removed: bool = self
2020 .del_address_and_subnet_route(fnet::Subnet { addr: *addr, prefix_len: *prefix_len })
2021 .await?;
2022 result.push(address);
2023 }
2024 Ok(result)
2025 }
2026
2027 async fn set_configuration(&self, config: fnet_interfaces_admin::Configuration) -> Result<()> {
2037 let fnet_interfaces_admin::Configuration {
2038 ipv4: previous_ipv4, ipv6: previous_ipv6, ..
2039 } = self
2040 .control()
2041 .set_configuration(&config.clone())
2042 .await
2043 .context("FIDL error")?
2044 .map_err(|e| anyhow!("set configuration error: {:?}", e))?;
2045
2046 fn verify_config_changed<T: Eq>(previous: Option<T>, current: Option<T>) -> Result<()> {
2047 if let Some(current) = current {
2048 let previous = previous.ok_or_else(|| anyhow!("configuration not supported"))?;
2049 if previous == current {
2050 return Err(anyhow!("configuration change is a no-op"));
2051 }
2052 }
2053 Ok(())
2054 }
2055
2056 let fnet_interfaces_admin::Configuration { ipv4, ipv6, .. } = config;
2057 if let Some(fnet_interfaces_admin::Ipv4Configuration {
2058 unicast_forwarding,
2059 multicast_forwarding,
2060 ..
2061 }) = ipv4
2062 {
2063 let fnet_interfaces_admin::Ipv4Configuration {
2064 unicast_forwarding: previous_unicast_forwarding,
2065 multicast_forwarding: previous_multicast_forwarding,
2066 ..
2067 } = previous_ipv4.ok_or_else(|| anyhow!("IPv4 configuration not supported"))?;
2068 verify_config_changed(previous_unicast_forwarding, unicast_forwarding)
2069 .context("IPv4 unicast forwarding")?;
2070 verify_config_changed(previous_multicast_forwarding, multicast_forwarding)
2071 .context("IPv4 multicast forwarding")?;
2072 }
2073 if let Some(fnet_interfaces_admin::Ipv6Configuration {
2074 unicast_forwarding,
2075 multicast_forwarding,
2076 ..
2077 }) = ipv6
2078 {
2079 let fnet_interfaces_admin::Ipv6Configuration {
2080 unicast_forwarding: previous_unicast_forwarding,
2081 multicast_forwarding: previous_multicast_forwarding,
2082 ..
2083 } = previous_ipv6.ok_or_else(|| anyhow!("IPv6 configuration not supported"))?;
2084 verify_config_changed(previous_unicast_forwarding, unicast_forwarding)
2085 .context("IPv6 unicast forwarding")?;
2086 verify_config_changed(previous_multicast_forwarding, multicast_forwarding)
2087 .context("IPv6 multicast forwarding")?;
2088 }
2089 Ok(())
2090 }
2091
2092 pub async fn set_ipv6_forwarding_enabled(&self, enabled: bool) -> Result<()> {
2094 self.set_configuration(fnet_interfaces_admin::Configuration {
2095 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
2096 unicast_forwarding: Some(enabled),
2097 ..Default::default()
2098 }),
2099 ..Default::default()
2100 })
2101 .await
2102 }
2103
2104 pub async fn set_ipv4_forwarding_enabled(&self, enabled: bool) -> Result<()> {
2106 self.set_configuration(fnet_interfaces_admin::Configuration {
2107 ipv4: Some(fnet_interfaces_admin::Ipv4Configuration {
2108 unicast_forwarding: Some(enabled),
2109 ..Default::default()
2110 }),
2111 ..Default::default()
2112 })
2113 .await
2114 }
2115
2116 pub async fn remove(
2119 self,
2120 ) -> Result<(fnetemul_network::EndpointProxy, Option<fnet_interfaces_admin::DeviceControlProxy>)>
2121 {
2122 let Self {
2123 endpoint: TestEndpoint { endpoint, name: _, _sandbox: _ },
2124 id: _,
2125 realm: _,
2126 control,
2127 device_control,
2128 dhcp_client_task: _,
2129 } = self;
2130 std::mem::drop(control);
2134 Ok((endpoint, device_control))
2135 }
2136
2137 pub fn remove_device(self) -> (Control, Option<fnet_interfaces_admin::DeviceControlProxy>) {
2141 let Self {
2142 endpoint: TestEndpoint { endpoint, name: _, _sandbox: _ },
2143 id: _,
2144 realm: _,
2145 control,
2146 device_control,
2147 dhcp_client_task: _,
2148 } = self;
2149 std::mem::drop(endpoint);
2150 (control, device_control)
2151 }
2152
2153 pub async fn wait_removal(self) -> Result<fnet_interfaces_admin::InterfaceRemovedReason> {
2155 let Self {
2156 endpoint: _endpoint,
2158 id: _,
2159 realm: _,
2160 control,
2161 dhcp_client_task: _,
2162 device_control: _device_control,
2164 } = self;
2165 match control.wait_termination().await {
2166 fnet_interfaces_ext::admin::TerminalError::Fidl(e) => {
2167 Err(e).context("waiting interface control termination")
2168 }
2169 fnet_interfaces_ext::admin::TerminalError::Terminal(reason) => Ok(reason),
2170 }
2171 }
2172
2173 pub async fn set_ipv4_dad_transmits(&self, dad_transmits: u16) -> Result<Option<u16>> {
2177 set_ipv4_dad_transmits(self.control(), dad_transmits).await
2178 }
2179
2180 pub async fn set_ipv6_dad_transmits(&self, dad_transmits: u16) -> Result<Option<u16>> {
2184 set_ipv6_dad_transmits(self.control(), dad_transmits).await
2185 }
2186
2187 pub async fn set_temporary_address_generation_enabled(&self, enabled: bool) -> Result<()> {
2190 set_temporary_address_generation_enabled(self.control(), enabled).await
2191 }
2192}
2193
2194async fn set_ipv4_dad_transmits(control: &Control, dad_transmits: u16) -> Result<Option<u16>> {
2195 control
2196 .set_configuration(&fnet_interfaces_admin::Configuration {
2197 ipv4: Some(fnet_interfaces_admin::Ipv4Configuration {
2198 arp: Some(fnet_interfaces_admin::ArpConfiguration {
2199 dad: Some(fnet_interfaces_admin::DadConfiguration {
2200 transmits: Some(dad_transmits),
2201 ..Default::default()
2202 }),
2203 ..Default::default()
2204 }),
2205 ..Default::default()
2206 }),
2207 ..Default::default()
2208 })
2209 .await?
2210 .map(|config| config.ipv4?.arp?.dad?.transmits)
2211 .map_err(|e| anyhow::anyhow!("set configuration error {e:?}"))
2212}
2213
2214async fn set_ipv6_dad_transmits(control: &Control, dad_transmits: u16) -> Result<Option<u16>> {
2215 control
2216 .set_configuration(&fnet_interfaces_admin::Configuration {
2217 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
2218 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
2219 dad: Some(fnet_interfaces_admin::DadConfiguration {
2220 transmits: Some(dad_transmits),
2221 ..Default::default()
2222 }),
2223 ..Default::default()
2224 }),
2225 ..Default::default()
2226 }),
2227 ..Default::default()
2228 })
2229 .await?
2230 .map(|config| config.ipv6?.ndp?.dad?.transmits)
2231 .map_err(|e| anyhow::anyhow!("set configuration error {e:?}"))
2232}
2233
2234async fn set_temporary_address_generation_enabled(control: &Control, enabled: bool) -> Result<()> {
2235 let _config: fnet_interfaces_admin::Configuration = control
2236 .set_configuration(&fnet_interfaces_admin::Configuration {
2237 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
2238 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
2239 slaac: Some(fnet_interfaces_admin::SlaacConfiguration {
2240 temporary_address: Some(enabled),
2241 ..Default::default()
2242 }),
2243 ..Default::default()
2244 }),
2245 ..Default::default()
2246 }),
2247 ..Default::default()
2248 })
2249 .await
2250 .context("FIDL error")?
2251 .map_err(|e| anyhow::anyhow!("set configuration error {e:?}"))?;
2252 Ok(())
2253}
2254
2255fn get_socket2_domain(addr: &std::net::SocketAddr) -> fposix_socket::Domain {
2257 let domain = match addr {
2258 std::net::SocketAddr::V4(_) => fposix_socket::Domain::Ipv4,
2259 std::net::SocketAddr::V6(_) => fposix_socket::Domain::Ipv6,
2260 };
2261
2262 domain
2263}
2264
2265pub trait RealmUdpSocket: Sized {
2267 fn bind_in_realm<'a>(
2269 realm: &'a TestRealm<'a>,
2270 addr: std::net::SocketAddr,
2271 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2272}
2273
2274impl RealmUdpSocket for std::net::UdpSocket {
2275 fn bind_in_realm<'a>(
2276 realm: &'a TestRealm<'a>,
2277 addr: std::net::SocketAddr,
2278 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2279 async move {
2280 let sock = realm
2281 .datagram_socket(
2282 get_socket2_domain(&addr),
2283 fposix_socket::DatagramSocketProtocol::Udp,
2284 )
2285 .await
2286 .context("failed to create socket")?;
2287
2288 let () = sock.bind(&addr.into()).context("bind failed")?;
2289
2290 Result::Ok(sock.into())
2291 }
2292 .boxed_local()
2293 }
2294}
2295
2296impl RealmUdpSocket for fuchsia_async::net::UdpSocket {
2297 fn bind_in_realm<'a>(
2298 realm: &'a TestRealm<'a>,
2299 addr: std::net::SocketAddr,
2300 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2301 std::net::UdpSocket::bind_in_realm(realm, addr)
2302 .and_then(|udp| {
2303 futures::future::ready(
2304 fuchsia_async::net::UdpSocket::from_socket(udp)
2305 .context("failed to create fuchsia_async socket"),
2306 )
2307 })
2308 .boxed_local()
2309 }
2310}
2311
2312pub trait RealmTcpListener: Sized {
2314 fn listen_in_realm<'a>(
2316 realm: &'a TestRealm<'a>,
2317 addr: std::net::SocketAddr,
2318 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2319 Self::listen_in_realm_with(realm, addr, |_: &socket2::Socket| Ok(()))
2320 }
2321
2322 fn listen_in_realm_with<'a>(
2325 realm: &'a TestRealm<'a>,
2326 addr: std::net::SocketAddr,
2327 setup: impl FnOnce(&socket2::Socket) -> Result<()> + 'a,
2328 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2329}
2330
2331impl RealmTcpListener for std::net::TcpListener {
2332 fn listen_in_realm_with<'a>(
2333 realm: &'a TestRealm<'a>,
2334 addr: std::net::SocketAddr,
2335 setup: impl FnOnce(&socket2::Socket) -> Result<()> + 'a,
2336 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2337 async move {
2338 let sock = realm
2339 .stream_socket(get_socket2_domain(&addr), fposix_socket::StreamSocketProtocol::Tcp)
2340 .await
2341 .context("failed to create server socket")?;
2342 let () = setup(&sock)?;
2343 let () = sock.bind(&addr.into()).context("failed to bind server socket")?;
2344 let () = sock.listen(128).context("failed to listen on server socket")?;
2347
2348 Result::Ok(sock.into())
2349 }
2350 .boxed_local()
2351 }
2352}
2353
2354impl RealmTcpListener for fuchsia_async::net::TcpListener {
2355 fn listen_in_realm_with<'a>(
2356 realm: &'a TestRealm<'a>,
2357 addr: std::net::SocketAddr,
2358 setup: impl FnOnce(&socket2::Socket) -> Result<()> + 'a,
2359 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2360 std::net::TcpListener::listen_in_realm_with(realm, addr, setup)
2361 .and_then(|listener| {
2362 futures::future::ready(
2363 fuchsia_async::net::TcpListener::from_std(listener)
2364 .context("failed to create fuchsia_async socket"),
2365 )
2366 })
2367 .boxed_local()
2368 }
2369}
2370
2371pub trait RealmTcpStream: Sized {
2373 fn connect_in_realm<'a>(
2375 realm: &'a TestRealm<'a>,
2376 addr: std::net::SocketAddr,
2377 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2378
2379 fn bind_and_connect_in_realm<'a>(
2381 realm: &'a TestRealm<'a>,
2382 local: std::net::SocketAddr,
2383 dst: std::net::SocketAddr,
2384 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2385
2386 fn connect_in_realm_with_sock<'a, F: FnOnce(&socket2::Socket) -> Result + 'a>(
2391 realm: &'a TestRealm<'a>,
2392 dst: std::net::SocketAddr,
2393 with_sock: F,
2394 ) -> futures::future::LocalBoxFuture<'a, Result<Self>>;
2395
2396 }
2398
2399impl RealmTcpStream for fuchsia_async::net::TcpStream {
2400 fn connect_in_realm<'a>(
2401 realm: &'a TestRealm<'a>,
2402 addr: std::net::SocketAddr,
2403 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2404 Self::connect_in_realm_with_sock(realm, addr, |_: &socket2::Socket| Ok(()))
2405 }
2406
2407 fn bind_and_connect_in_realm<'a>(
2408 realm: &'a TestRealm<'a>,
2409 local: std::net::SocketAddr,
2410 dst: std::net::SocketAddr,
2411 ) -> futures::future::LocalBoxFuture<'a, Result<Self>> {
2412 Self::connect_in_realm_with_sock(realm, dst, move |sock| {
2413 sock.bind(&local.into()).context("failed to bind")
2414 })
2415 }
2416
2417 fn connect_in_realm_with_sock<'a, F: FnOnce(&socket2::Socket) -> Result + 'a>(
2418 realm: &'a TestRealm<'a>,
2419 dst: std::net::SocketAddr,
2420 with_sock: F,
2421 ) -> futures::future::LocalBoxFuture<'a, Result<fuchsia_async::net::TcpStream>> {
2422 async move {
2423 let sock = realm
2424 .stream_socket(get_socket2_domain(&dst), fposix_socket::StreamSocketProtocol::Tcp)
2425 .await
2426 .context("failed to create socket")?;
2427
2428 with_sock(&sock)?;
2429
2430 let stream = fuchsia_async::net::TcpStream::connect_from_raw(sock, dst)
2431 .context("failed to create client tcp stream")?
2432 .await
2433 .context("failed to connect to server")?;
2434
2435 Result::Ok(stream)
2436 }
2437 .boxed_local()
2438 }
2439}
2440
2441fn truncate_dropping_front(s: Cow<'_, str>, len: usize) -> Cow<'_, str> {
2442 match s.len().checked_sub(len) {
2443 None => s,
2444 Some(start) => {
2445 match s {
2449 Cow::Borrowed(s) => Cow::Borrowed(&s[start..]),
2450 Cow::Owned(mut s) => {
2451 let _: std::string::Drain<'_> = s.drain(..start);
2452 Cow::Owned(s)
2453 }
2454 }
2455 }
2456 }
2457}