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