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