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