1use anyhow::{Context, Error};
8use fidl::endpoints::{ControlHandle, RequestStream};
9use fidl_fuchsia_net_policy_socketproxy::{
10 self as fnp_socketproxy, FuchsiaNetworkInfo, FuchsiaNetworksRequest, Network,
11 NetworkDnsServers, NetworkInfo, NetworkRegistryAddError, NetworkRegistryRemoveError,
12 NetworkRegistrySetDefaultError, StarnixNetworksRequest,
13};
14use fuchsia_inspect_derive::{IValue, Inspect, Unit};
15use futures::channel::mpsc;
16use futures::lock::Mutex;
17use futures::{SinkExt as _, StreamExt as _, TryStreamExt as _};
18use log::{error, info, warn};
19use std::collections::HashMap;
20use std::sync::Arc;
21use thiserror::Error;
22
23use {
24 fidl_fuchsia_net as fnet, fidl_fuchsia_net_interfaces_ext as fnet_interfaces_ext,
25 fidl_fuchsia_net_policy_properties as fnp_properties,
26 fidl_fuchsia_posix_socket as fposix_socket,
27};
28
29const DEFAULT_DNS_PORT: u16 = 53;
31
32pub(crate) const DEFAULT_SOCKET_MARK: u32 = 0;
35
36enum CommonErrors {
37 MissingNetworkId,
38 MissingNetworkInfo,
39 MissingNetworkDnsServers,
40}
41
42trait IpAddressExt {
43 fn to_dns_socket_address(self) -> fnet::SocketAddress;
44}
45
46impl<T: IpAddressExt + Copy> IpAddressExt for &T {
47 fn to_dns_socket_address(self) -> fnet::SocketAddress {
48 (*self).to_dns_socket_address()
49 }
50}
51
52impl IpAddressExt for fnet::Ipv4Address {
53 fn to_dns_socket_address(self) -> fnet::SocketAddress {
54 fnet::SocketAddress::Ipv4(fnet::Ipv4SocketAddress { address: self, port: DEFAULT_DNS_PORT })
55 }
56}
57
58impl IpAddressExt for fnet::Ipv6Address {
59 fn to_dns_socket_address(self) -> fnet::SocketAddress {
60 fnet::SocketAddress::Ipv6(fnet::Ipv6SocketAddress {
61 address: self,
62 port: DEFAULT_DNS_PORT,
63 zone_index: 0,
64 })
65 }
66}
67
68trait IntoOptionalUint32 {
69 fn into_optional_uint32(self) -> fposix_socket::OptionalUint32;
70}
71
72impl IntoOptionalUint32 for Option<u32> {
73 fn into_optional_uint32(self) -> fposix_socket::OptionalUint32 {
74 match self {
75 Some(value) => fposix_socket::OptionalUint32::Value(value),
76 None => fposix_socket::OptionalUint32::Unset(fposix_socket::Empty),
77 }
78 }
79}
80
81trait NetworkInfoExt {
82 fn mark(&self) -> Option<u32>;
83}
84
85impl NetworkInfoExt for NetworkInfo {
86 fn mark(&self) -> Option<u32> {
87 match self {
88 NetworkInfo::Starnix(s) => s.mark,
89 NetworkInfo::Fuchsia(_) | _ => None,
92 }
93 }
94}
95
96#[derive(Clone, Debug, Error)]
99pub enum NetworkRegistryError {
100 #[error("Error during socketproxy Add: {0:?}")]
101 Add(NetworkRegistryAddError),
102 #[error("Error during socketproxy Remove: {0:?}")]
103 Remove(NetworkRegistryRemoveError),
104 #[error("Error during socketproxy SetDefault: {0:?}")]
105 SetDefault(NetworkRegistrySetDefaultError),
106}
107
108impl From<NetworkRegistryAddError> for NetworkRegistryError {
109 fn from(error: NetworkRegistryAddError) -> Self {
110 NetworkRegistryError::Add(error)
111 }
112}
113
114impl From<NetworkRegistryRemoveError> for NetworkRegistryError {
115 fn from(error: NetworkRegistryRemoveError) -> Self {
116 NetworkRegistryError::Remove(error)
117 }
118}
119
120impl From<NetworkRegistrySetDefaultError> for NetworkRegistryError {
121 fn from(error: NetworkRegistrySetDefaultError) -> Self {
122 NetworkRegistryError::SetDefault(error)
123 }
124}
125
126#[derive(Clone, Debug, Error)]
127pub enum NetworkConversionError {
128 #[error("Could not convert id ({0}) to u32")]
129 InvalidInterfaceId(u64),
130}
131
132pub trait NetworkExt<I: fnet_interfaces_ext::FieldInterests> {
133 fn from_watcher_properties(
134 properties: &fnet_interfaces_ext::Properties<I>,
135 ) -> Result<Self, NetworkConversionError>
136 where
137 Self: Sized;
138}
139
140impl<I: fnet_interfaces_ext::FieldInterests> NetworkExt<I> for Network {
141 fn from_watcher_properties(
142 properties: &fnet_interfaces_ext::Properties<I>,
143 ) -> Result<Self, NetworkConversionError> {
144 let network_id: u32 =
146 properties.id.get().try_into().or_else(|_| {
147 Err(NetworkConversionError::InvalidInterfaceId(properties.id.into()))
148 })?;
149 let network = Self {
150 network_id: Some(network_id),
151 info: Some(NetworkInfo::Fuchsia(FuchsiaNetworkInfo {
152 ..Default::default()
154 })),
155 dns_servers: Some(fnp_socketproxy::NetworkDnsServers {
159 v4: Some(vec![]),
160 v6: Some(vec![]),
161 ..Default::default()
162 }),
163 ..Default::default()
164 };
165 Ok(network)
166 }
167}
168
169#[derive(Debug, Clone)]
171pub(crate) struct ValidatedNetwork {
172 network_id: u32,
173 info: NetworkInfo,
174 dns_servers: NetworkDnsServers,
175}
176
177impl ValidatedNetwork {
178 fn dns_servers(&self) -> Vec<fnet::SocketAddress> {
179 self.dns_servers
180 .v4
181 .iter()
182 .flat_map(|a| a.iter().map(IpAddressExt::to_dns_socket_address))
183 .chain(
184 self.dns_servers
185 .v6
186 .iter()
187 .flat_map(|a| a.iter().map(IpAddressExt::to_dns_socket_address)),
188 )
189 .collect()
190 }
191}
192
193trait ValidateNetworkExt {
194 fn validate(self) -> Result<ValidatedNetwork, CommonErrors>;
195}
196
197impl ValidateNetworkExt for Network {
198 fn validate(self) -> Result<ValidatedNetwork, CommonErrors> {
199 match self {
200 Network { network_id: None, .. } => Err(CommonErrors::MissingNetworkId),
201 Network { info: None, .. } => Err(CommonErrors::MissingNetworkInfo),
202 Network { dns_servers: None, .. } => Err(CommonErrors::MissingNetworkDnsServers),
203 Network {
204 network_id: Some(network_id),
205 info: Some(info),
206 dns_servers: Some(dns_servers),
207 ..
208 } => Ok(ValidatedNetwork { network_id, info, dns_servers }),
209 }
210 }
211}
212
213macro_rules! common_errors_impl {
214 ($($p:ty),+) => {
215 $(
216 impl From<CommonErrors> for $p {
217 fn from(value: CommonErrors) -> Self {
218 use CommonErrors::*;
219 match value {
220 MissingNetworkId => <$p>::MissingNetworkId,
221 MissingNetworkInfo => <$p>::MissingNetworkInfo,
222 MissingNetworkDnsServers => <$p>::MissingNetworkDnsServers,
223 }
224 }
225 }
226 )+
227 }
228}
229
230common_errors_impl!(
231 fnp_socketproxy::NetworkRegistryAddError,
232 fnp_socketproxy::NetworkRegistryUpdateError
233);
234
235#[derive(Inspect, Debug, Default)]
237struct NetworkRegistry {
238 networks: IValue<RegisteredNetworks>,
239
240 inspect_node: fuchsia_inspect::Node,
241}
242
243impl NetworkRegistry {
244 pub(crate) fn dns_servers(&self) -> Vec<fnp_socketproxy::DnsServerList> {
246 self.networks.dns_servers()
247 }
248
249 pub(crate) fn has_default_network(&self) -> bool {
251 self.networks.default_network_id.is_some()
252 }
253
254 pub(crate) fn current_mark(&self) -> Option<u32> {
256 self.networks.current_mark()
257 }
258
259 pub(crate) fn default_network_update(&self) -> fnp_properties::DefaultNetworkUpdate {
262 self.networks.default_network_update()
263 }
264}
265
266#[derive(Unit, Debug, Default)]
267struct MethodInspect {
268 successes: u32,
269 errors: u32,
270}
271
272#[derive(Unit, Default, Debug)]
273struct RegisteredNetworks {
274 default_network_id: Option<u32>,
275
276 #[inspect(skip)]
277 networks: HashMap<u32, ValidatedNetwork>,
279
280 adds: MethodInspect,
281 removes: MethodInspect,
282 set_defaults: MethodInspect,
283 updates: MethodInspect,
284}
285
286impl RegisteredNetworks {
287 fn add_network(&mut self, network: Network) -> fnp_socketproxy::NetworkRegistryAddResult {
288 let network = network.validate()?;
289 #[allow(clippy::map_entry, reason = "mass allow for https://fxbug.dev/381896734")]
290 if self.networks.contains_key(&network.network_id) {
291 self.adds.errors += 1;
292 Err(fnp_socketproxy::NetworkRegistryAddError::DuplicateNetworkId)
293 } else {
294 let _: Option<_> = self.networks.insert(network.network_id, network);
295 self.adds.successes += 1;
296 Ok(())
297 }
298 }
299
300 pub(crate) fn clear(&mut self) {
302 self.default_network_id = None;
303 self.networks.clear();
304 }
305
306 fn update_network(&mut self, network: Network) -> fnp_socketproxy::NetworkRegistryUpdateResult {
307 let network = network.validate()?;
308 let network_id = network.network_id;
309 *self
310 .networks
311 .get_mut(&network_id)
312 .ok_or(fnp_socketproxy::NetworkRegistryUpdateError::NotFound)
313 .inspect(|_| self.updates.successes += 1)
314 .inspect_err(|_| self.updates.errors += 1)? = network;
315 Ok(())
316 }
317
318 fn remove_network(&mut self, network_id: u32) -> fnp_socketproxy::NetworkRegistryRemoveResult {
319 if self.default_network_id == Some(network_id) {
320 self.removes.errors += 1;
321 return Err(fnp_socketproxy::NetworkRegistryRemoveError::CannotRemoveDefaultNetwork);
322 }
323 match self.networks.remove(&network_id) {
324 Some(_) => {
325 self.removes.successes += 1;
326 Ok(())
327 }
328 None => {
329 self.removes.errors += 1;
330 Err(fnp_socketproxy::NetworkRegistryRemoveError::NotFound)
331 }
332 }
333 }
334
335 fn set_default_network(
339 &mut self,
340 network_id: Option<u32>,
341 ) -> fnp_socketproxy::NetworkRegistrySetDefaultResult {
342 if let Some(network_id) = network_id {
343 if !self.networks.contains_key(&network_id) {
344 self.set_defaults.errors += 1;
345 return Err(fnp_socketproxy::NetworkRegistrySetDefaultError::NotFound);
346 }
347 }
348 self.set_defaults.successes += 1;
349 self.default_network_id = network_id;
350
351 Ok(())
352 }
353
354 pub(crate) fn dns_servers(&self) -> Vec<fnp_socketproxy::DnsServerList> {
356 self.networks
357 .iter()
358 .map(|(id, network)| fnp_socketproxy::DnsServerList {
359 source_network_id: Some(*id),
360 addresses: Some(network.dns_servers()),
361 ..Default::default()
362 })
363 .collect()
364 }
365
366 fn current_mark(&self) -> Option<u32> {
367 match (self.default_network_id, self.networks.is_empty()) {
368 (None, false) => Some(DEFAULT_SOCKET_MARK),
369 (id, _) => id.and_then(|id| self.networks[&id].info.mark()),
370 }
371 }
372
373 fn default_network_update(&self) -> fnp_properties::DefaultNetworkUpdate {
374 fnp_properties::DefaultNetworkUpdate {
375 interface_id: self.default_network_id.map(u64::from),
376 socket_marks: Some(fnet::Marks {
377 mark_1: self.current_mark(),
378 mark_2: None,
379 ..Default::default()
380 }),
381 ..Default::default()
382 }
383 }
384
385 fn len(&self) -> usize {
386 self.networks.len()
387 }
388}
389
390#[derive(Inspect, Clone, Debug, Default)]
391pub struct NetworkRegistries {
392 starnix: Arc<Mutex<NetworkRegistry>>,
393 fuchsia: Arc<Mutex<NetworkRegistry>>,
394}
395
396impl NetworkRegistries {
397 async fn current_mark(&self) -> Option<u32> {
401 {
402 let fuchsia = self.fuchsia.lock().await;
403 if fuchsia.has_default_network() {
404 return fuchsia.current_mark();
405 }
406 }
407
408 return self.starnix.lock().await.networks.current_mark();
409 }
410
411 async fn current_dns_servers(&self) -> Vec<fnp_socketproxy::DnsServerList> {
415 {
416 let fuchsia = self.fuchsia.lock().await;
417 if fuchsia.has_default_network() {
418 return fuchsia.dns_servers();
419 }
420 }
421
422 return self.starnix.lock().await.dns_servers();
423 }
424
425 async fn default_network_update(&self) -> fnp_properties::DefaultNetworkUpdate {
426 {
427 let fuchsia = self.fuchsia.lock().await;
428 if fuchsia.has_default_network() {
429 info!("FuchsiaNetworks has a default network, preferring Fuchsia network.");
430 return fuchsia.default_network_update();
431 }
432 }
433
434 {
435 let starnix = self.starnix.lock().await;
436 if starnix.has_default_network() {
437 return starnix.default_network_update();
438 }
439 }
440
441 Default::default()
443 }
444}
445
446#[derive(Debug)]
447enum RegistryType {
448 Starnix,
449 Fuchsia,
450}
451
452#[derive(Inspect, Clone, Debug)]
453pub struct Registry {
454 #[inspect(forward)]
455 networks: NetworkRegistries,
456 marks: Arc<Mutex<crate::SocketMarks>>,
459 dns_tx: mpsc::Sender<Vec<fnp_socketproxy::DnsServerList>>,
460 default_network_tx: mpsc::Sender<fnp_properties::DefaultNetworkUpdate>,
461
462 starnix_occupant: Arc<Mutex<()>>,
463 fuchsia_occupant: Arc<Mutex<()>>,
464}
465
466macro_rules! handle_registry_request {
467 ($request_type:ident, $request:expr, $network_registry:expr, $registry_type:expr) => {{
468 let mut networks = $network_registry.networks.as_mut();
469 let (op, send): (_, Box<dyn FnOnce() -> Result<(), _> + Send + Sync + 'static>) =
470 match $request {
471 $request_type::SetDefault { network_id, responder } => {
472 let result = networks.set_default_network(match network_id {
473 fposix_socket::OptionalUint32::Value(value) => Some(value),
474 fposix_socket::OptionalUint32::Unset(_) => None,
475 });
476 ("set default", Box::new(move || responder.send(result)))
477 }
478 $request_type::Add { network, responder } => {
479 let result = networks.add_network(network);
480 ("add", Box::new(move || responder.send(result)))
481 }
482 $request_type::Update { network, responder } => {
483 let result = networks.update_network(network);
484 ("update", Box::new(move || responder.send(result)))
485 }
486 $request_type::Remove { network_id, responder } => {
487 let result = networks.remove_network(network_id);
488 ("remove", Box::new(move || responder.send(result)))
489 }
490 };
491 let new_mark = networks.current_mark();
492 info!(
493 "{:?} registry {op}. mark: {new_mark:?}, networks count: {}",
494 $registry_type,
495 networks.len()
496 );
497 std::mem::drop(networks);
498 send
499 }};
500}
501
502impl Registry {
503 pub(crate) fn new(
504 marks: Arc<Mutex<crate::SocketMarks>>,
505 dns_tx: mpsc::Sender<Vec<fnp_socketproxy::DnsServerList>>,
506 default_network_tx: mpsc::Sender<fnp_properties::DefaultNetworkUpdate>,
507 ) -> Result<Self, anyhow::Error> {
508 Ok(Self {
509 networks: Default::default(),
510 marks,
511 dns_tx,
512 default_network_tx,
513 starnix_occupant: Default::default(),
514 fuchsia_occupant: Default::default(),
515 })
516 }
517}
518
519impl Registry {
520 pub(crate) async fn run_starnix(
521 &self,
522 stream: fnp_socketproxy::StarnixNetworksRequestStream,
523 ) -> Result<(), Error> {
524 let _occupant = match self.starnix_occupant.try_lock() {
525 Some(o) => o,
526 None => {
527 warn!("Only one connection to StarnixNetworks is allowed at a time");
528 stream.control_handle().shutdown_with_epitaph(fidl::Status::ACCESS_DENIED);
529 return Ok(());
530 }
531 };
532
533 info!("Starting fuchsia.net.policy.socketproxy.StarnixNetworks server");
534 self.networks.starnix.lock().await.networks.as_mut().clear();
535 stream
536 .map(|result| result.context("failed request"))
537 .try_for_each(|request| {
538 async {
539 let mut network_registry = self.networks.starnix.lock().await;
540 let send: Box<dyn FnOnce() -> Result<(), _> + Send + Sync + 'static> = handle_registry_request!(
541 StarnixNetworksRequest,
542 request,
543 network_registry,
544 RegistryType::Starnix
545 );
546 std::mem::drop(network_registry);
547
548 self.handle_state_changed().await?;
549 self.default_network_tx
550 .clone()
551 .feed(self.networks.default_network_update().await)
552 .await?;
553 send().context("error sending response")?;
554 Ok(())
555 }
556 })
557 .await
558 }
559
560 pub(crate) async fn run_fuchsia(
561 &self,
562 stream: fnp_socketproxy::FuchsiaNetworksRequestStream,
563 ) -> Result<(), Error> {
564 let _occupant = match self.fuchsia_occupant.try_lock() {
565 Some(o) => o,
566 None => {
567 warn!("Only one connection to FuchsiaNetworks is allowed at a time");
568 stream.control_handle().shutdown_with_epitaph(fidl::Status::ACCESS_DENIED);
569 return Ok(());
570 }
571 };
572
573 info!("Starting fuchsia.net.policy.socketproxy.FuchsiaNetworks server");
574 self.networks.fuchsia.lock().await.networks.as_mut().clear();
575 stream
576 .map(|result| result.context("failed request"))
577 .try_for_each(|request| {
578 async {
579 let mut network_registry = self.networks.fuchsia.lock().await;
580 let send: Box<dyn FnOnce() -> Result<(), _> + Send + Sync + 'static> = handle_registry_request!(
581 FuchsiaNetworksRequest,
582 request,
583 network_registry,
584 RegistryType::Fuchsia
585 );
586 std::mem::drop(network_registry);
587
588 self.handle_state_changed().await?;
589 self.default_network_tx
590 .clone()
591 .feed(self.networks.default_network_update().await)
592 .await?;
593 send().context("error sending response")?;
594 Ok(())
595 }
596 })
597 .await
598 }
599
600 async fn handle_state_changed(&self) -> Result<(), Error> {
601 self.dns_tx.clone().feed(self.networks.current_dns_servers().await).await.unwrap_or_else(
604 |e| {
605 if !e.is_disconnected() {
606 error!("Unable to feed DNS update: {e:?}")
608 }
609 },
610 );
611
612 self.marks.lock().await.mark_1 = self.networks.current_mark().await.into_optional_uint32();
617 Ok(())
618 }
619}
620
621#[cfg(test)]
622mod test {
623 use super::*;
624 use fuchsia_component::server::ServiceFs;
625 use fuchsia_component_test::{
626 Capability, ChildOptions, LocalComponentHandles, RealmBuilder, RealmInstance, Ref, Route,
627 };
628 use futures::channel::mpsc::Receiver;
629 use futures::future;
630 use net_declare::{fidl_ip, fidl_socket_addr};
631 use pretty_assertions::assert_eq;
632 use socket_proxy_testing::{RegistryType, ToDnsServerList as _, ToNetwork};
633 use test_case::test_case;
634
635 #[derive(Clone, Debug)]
636 enum Op<N: ToNetwork> {
637 SetDefault {
638 network_id: Option<u32>,
639 result: Result<(), fnp_socketproxy::NetworkRegistrySetDefaultError>,
640 },
641 Add {
642 network: N,
643 result: Result<(), fnp_socketproxy::NetworkRegistryAddError>,
644 },
645 Update {
646 network: N,
647 result: Result<(), fnp_socketproxy::NetworkRegistryUpdateError>,
648 },
649 Remove {
650 network_id: u32,
651 result: Result<(), fnp_socketproxy::NetworkRegistryRemoveError>,
652 },
653 }
654
655 macro_rules! execute {
656 ($self:ident, $proxy:ident, $registry:expr) => {{
657 match $self {
658 Op::SetDefault { network_id, result } => {
659 assert_eq!(
660 $proxy
661 .set_default(&match network_id {
662 Some(value) => fposix_socket::OptionalUint32::Value(*value),
663 None => fposix_socket::OptionalUint32::Unset(fposix_socket::Empty),
664 })
665 .await?,
666 *result
667 )
668 }
669 Op::Add { network, result } => {
670 assert_eq!($proxy.add(&network.to_network($registry)).await?, *result)
671 }
672 Op::Update { network, result } => {
673 assert_eq!($proxy.update(&network.to_network($registry)).await?, *result)
674 }
675 Op::Remove { network_id, result } => {
676 assert_eq!($proxy.remove(*network_id).await?, *result)
677 }
678 }
679 Ok(())
680 }};
681 }
682
683 impl<N: ToNetwork + Clone> Op<N> {
684 async fn execute_starnix(
685 &self,
686 starnix: &fnp_socketproxy::StarnixNetworksProxy,
687 ) -> Result<(), Error> {
688 execute!(self, starnix, RegistryType::Starnix)
689 }
690
691 async fn execute_fuchsia(
692 &self,
693 fuchsia: &fnp_socketproxy::FuchsiaNetworksProxy,
694 ) -> Result<(), Error> {
695 execute!(self, fuchsia, RegistryType::Fuchsia)
696 }
697
698 fn is_err(&self) -> bool {
699 match &self {
700 Op::SetDefault { network_id: _, result } => result.is_err(),
701 Op::Add { network: _, result } => result.is_err(),
702 Op::Update { network: _, result } => result.is_err(),
703 Op::Remove { network_id: _, result } => result.is_err(),
704 }
705 }
706 }
707
708 enum IncomingService {
709 StarnixNetworks(fnp_socketproxy::StarnixNetworksRequestStream),
710 FuchsiaNetworks(fnp_socketproxy::FuchsiaNetworksRequestStream),
711 }
712
713 async fn run_registry(
714 handles: LocalComponentHandles,
715 starnix_networks: Arc<Mutex<NetworkRegistry>>,
716 fuchsia_networks: Arc<Mutex<NetworkRegistry>>,
717 marks: Arc<Mutex<crate::SocketMarks>>,
718 dns_tx: mpsc::Sender<Vec<fnp_socketproxy::DnsServerList>>,
719 default_network_tx: mpsc::Sender<fnp_properties::DefaultNetworkUpdate>,
720 ) -> Result<(), Error> {
721 let mut fs = ServiceFs::new();
722 let _ = fs
723 .dir("svc")
724 .add_fidl_service(IncomingService::StarnixNetworks)
725 .add_fidl_service(IncomingService::FuchsiaNetworks);
726 let _ = fs.serve_connection(handles.outgoing_dir)?;
727
728 let registry = Registry {
729 networks: NetworkRegistries { starnix: starnix_networks, fuchsia: fuchsia_networks },
730 marks,
731 dns_tx,
732 default_network_tx,
733 starnix_occupant: Default::default(),
734 fuchsia_occupant: Default::default(),
735 };
736
737 fs.for_each_concurrent(0, |service| async {
738 match service {
739 IncomingService::StarnixNetworks(stream) => registry.run_starnix(stream).await,
740 IncomingService::FuchsiaNetworks(stream) => registry.run_fuchsia(stream).await,
741 }
742 .unwrap_or_else(|e| error!("{e:?}"))
743 })
744 .await;
745
746 Ok(())
747 }
748
749 async fn setup_test() -> Result<
750 (
751 RealmInstance,
752 Receiver<Vec<fnp_socketproxy::DnsServerList>>,
753 Receiver<fnp_properties::DefaultNetworkUpdate>,
754 ),
755 Error,
756 > {
757 let builder = RealmBuilder::new().await?;
758 let starnix_networks = Arc::new(Mutex::new(Default::default()));
759 let fuchsia_networks = Arc::new(Mutex::new(Default::default()));
760 let (dns_tx, dns_rx) = mpsc::channel(1);
761 let (default_network_tx, default_network_rx) = mpsc::channel(1);
762 let marks = Arc::new(Mutex::new(crate::SocketMarks::default()));
763 let registry = builder
764 .add_local_child(
765 "registry",
766 {
767 let starnix_networks = starnix_networks.clone();
768 let fuchsia_networks = fuchsia_networks.clone();
769 let marks = marks.clone();
770 let dns_tx = dns_tx.clone();
771 let default_network_tx = default_network_tx.clone();
772 move |handles: LocalComponentHandles| {
773 Box::pin(run_registry(
774 handles,
775 starnix_networks.clone(),
776 fuchsia_networks.clone(),
777 marks.clone(),
778 dns_tx.clone(),
779 default_network_tx.clone(),
780 ))
781 }
782 },
783 ChildOptions::new(),
784 )
785 .await?;
786
787 builder
788 .add_route(
789 Route::new()
790 .capability(Capability::protocol::<fnp_socketproxy::StarnixNetworksMarker>())
791 .from(®istry)
792 .to(Ref::parent()),
793 )
794 .await?;
795
796 builder
797 .add_route(
798 Route::new()
799 .capability(Capability::protocol::<fnp_socketproxy::FuchsiaNetworksMarker>())
800 .from(®istry)
801 .to(Ref::parent()),
802 )
803 .await?;
804
805 let realm = builder.build().await?;
806
807 Ok((realm, dns_rx, default_network_rx))
808 }
809
810 #[test_case(&[
811 Op::Add { network: 1, result: Ok(()) },
812 Op::Update { network: 1, result: Ok(()) },
813 Op::Remove { network_id: 1, result: Ok(()) },
814 ]; "normal operation")]
815 #[test_case(&[
816 Op::Add { network: 1, result: Ok(()) },
817 Op::Add { network: 1, result: Err(fnp_socketproxy::NetworkRegistryAddError::DuplicateNetworkId) },
818 ]; "duplicate add")]
819 #[test_case(&[
820 Op::Update { network: 1, result: Err(fnp_socketproxy::NetworkRegistryUpdateError::NotFound) },
821 ]; "update missing")]
822 #[test_case(&[
823 Op::<u32>::Remove { network_id: 1, result: Err(fnp_socketproxy::NetworkRegistryRemoveError::NotFound) },
824 ]; "remove missing")]
825 #[test_case(&[
826 Op::<u32>::SetDefault { network_id: Some(1), result: Err(fnp_socketproxy::NetworkRegistrySetDefaultError::NotFound) },
827 ]; "set default missing")]
828 #[test_case(&[
829 Op::Add { network: 1, result: Ok(()) },
830 Op::SetDefault { network_id: Some(1), result: Ok(()) },
831 Op::Remove { network_id: 1, result: Err(fnp_socketproxy::NetworkRegistryRemoveError::CannotRemoveDefaultNetwork)},
832 ]; "remove default network")]
833 #[test_case(&[
834 Op::Add { network: 1, result: Ok(()) },
835 Op::SetDefault { network_id: Some(1), result: Ok(()) },
836 Op::Remove { network_id: 1, result: Err(fnp_socketproxy::NetworkRegistryRemoveError::CannotRemoveDefaultNetwork)},
837 Op::Add { network: 2, result: Ok(()) },
838 Op::SetDefault { network_id: Some(2), result: Ok(()) },
839 Op::Remove { network_id: 1, result: Ok(()) },
840 ]; "remove formerly default network")]
841 #[test_case(&[
842 Op::Add { network: 1, result: Ok(()) },
843 Op::SetDefault { network_id: Some(1), result: Ok(()) },
844 Op::Remove { network_id: 1, result: Err(fnp_socketproxy::NetworkRegistryRemoveError::CannotRemoveDefaultNetwork)},
845 Op::SetDefault { network_id: None, result: Ok(()) },
846 Op::Remove { network_id: 1, result: Ok(()) },
847 ]; "remove last network")]
848 #[test_case(&[
849 Op::Add { network: 1, result: Ok(()) },
850 Op::Update { network: 1, result: Ok(()) },
851 Op::Add { network: 2, result: Ok(()) },
852 Op::Add { network: 3, result: Ok(()) },
853 Op::Add { network: 4, result: Ok(()) },
854 Op::Update { network: 4, result: Ok(()) },
855 Op::Update { network: 2, result: Ok(()) },
856 Op::Update { network: 3, result: Ok(()) },
857 Op::Add { network: 5, result: Ok(()) },
858 Op::Update { network: 5, result: Ok(()) },
859 Op::Add { network: 6, result: Ok(()) },
860 Op::Add { network: 7, result: Ok(()) },
861 Op::Add { network: 8, result: Ok(()) },
862 Op::Update { network: 8, result: Ok(()) },
863 Op::Update { network: 6, result: Ok(()) },
864 Op::Add { network: 9, result: Ok(()) },
865 Op::Update { network: 9, result: Ok(()) },
866 Op::Update { network: 7, result: Ok(()) },
867 Op::Add { network: 10, result: Ok(()) },
868 Op::Update { network: 10, result: Ok(()) },
869 ]; "many updates")]
870 #[fuchsia::test]
871 async fn test_operations<N: ToNetwork + Clone>(operations: &[Op<N>]) -> Result<(), Error> {
872 let (realm, _, _default_network_rx) = setup_test().await?;
873 let starnix_networks = realm
874 .root
875 .connect_to_protocol_at_exposed_dir()
876 .context("While connecting to StarnixNetworks")?;
877 let fuchsia_networks = realm
878 .root
879 .connect_to_protocol_at_exposed_dir()
880 .context("While connecting to FuchsiaNetworks")?;
881
882 for op in operations {
883 op.execute_starnix(&starnix_networks).await?;
886 op.execute_fuchsia(&fuchsia_networks).await?;
887 }
888
889 Ok(())
890 }
891
892 #[test_case(&[
893 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
894 ], vec![(1, vec![fidl_socket_addr!("192.0.2.0:53")]).to_dns_server_list()]
895 ; "normal operation (v4)")]
896 #[test_case(&[
897 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
898 Op::Update { network: (1, vec![fidl_ip!("192.0.2.1")]), result: Ok(()) },
899 ], vec![(1, vec![fidl_socket_addr!("192.0.2.1:53")]).to_dns_server_list()]
900 ; "update server list (v4)")]
901 #[test_case(&[
902 Op::Add { network: (1, vec![fidl_ip!("2001:db8::1")]), result: Ok(()) },
903 ], vec![(1, vec![fidl_socket_addr!("[2001:db8::1]:53")]).to_dns_server_list()]
904 ; "normal operation (v6)")]
905 #[test_case(&[
906 Op::Add { network: (1, vec![fidl_ip!("2001:db8::1")]), result: Ok(()) },
907 Op::Update { network: (1, vec![fidl_ip!("2001:db8::2")]), result: Ok(()) },
908 ], vec![(1, vec![fidl_socket_addr!("[2001:db8::2]:53")]).to_dns_server_list()]
909 ; "update server list (v6)")]
910 #[test_case(&[
911 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0"), fidl_ip!("2001:db8::1")]), result: Ok(()) },
912 ], vec![(1, vec![fidl_socket_addr!("192.0.2.0:53"), fidl_socket_addr!("[2001:db8::1]:53")]).to_dns_server_list()]
913 ; "normal operation (mixed)")]
914 #[test_case(&[
915 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0"), fidl_ip!("2001:db8::1")]), result: Ok(()) },
916 Op::Update { network: (1, vec![fidl_ip!("192.0.2.1"), fidl_ip!("2001:db8::2")]), result: Ok(()) },
917 ], vec![(1, vec![fidl_socket_addr!("192.0.2.1:53"), fidl_socket_addr!("[2001:db8::2]:53")]).to_dns_server_list()]
918 ; "update server list (mixed)")]
919 #[test_case(&[
920 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0"), fidl_ip!("2001:db8::1")]), result: Ok(()) },
921 Op::Add { network: (2, vec![fidl_ip!("192.0.2.1"), fidl_ip!("2001:db8::2")]), result: Ok(()) },
922 Op::Add { network: (3, vec![fidl_ip!("192.0.2.2"), fidl_ip!("2001:db8::3")]), result: Ok(()) },
923 ], vec![
924 (1, vec![fidl_socket_addr!("192.0.2.0:53"), fidl_socket_addr!("[2001:db8::1]:53")]).to_dns_server_list(),
925 (2, vec![fidl_socket_addr!("192.0.2.1:53"), fidl_socket_addr!("[2001:db8::2]:53")]).to_dns_server_list(),
926 (3, vec![fidl_socket_addr!("192.0.2.2:53"), fidl_socket_addr!("[2001:db8::3]:53")]).to_dns_server_list(),
927 ]; "multiple networks")]
928 #[fuchsia::test]
929 async fn test_dns_tracking<N: ToNetwork + Clone>(
930 operations: &[Op<N>],
931 dns_servers: Vec<fnp_socketproxy::DnsServerList>,
932 ) -> Result<(), Error> {
933 let (realm, mut dns_rx, _default_network_rx) = setup_test().await?;
934 let starnix_networks = realm
935 .root
936 .connect_to_protocol_at_exposed_dir()
937 .context("While connecting to StarnixNetworks")?;
938
939 let mut last_dns = None;
940 for op in operations {
941 op.execute_starnix(&starnix_networks).await?;
944 last_dns = Some(dns_rx.next().await.expect("dns update expected after each operation"));
945 }
946
947 let mut last_dns = last_dns.expect("there should be at least one dns update");
948 last_dns.sort_by_key(|a| a.source_network_id);
949 assert_eq!(last_dns, dns_servers);
950
951 Ok(())
952 }
953
954 #[test_case(&[
955 (RegistryType::Fuchsia, Op::Add { network: (1, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) }),
956 (RegistryType::Fuchsia, Op::SetDefault { network_id: Some(1), result: Ok(()) }),
957 ], vec![(1, vec![fidl_socket_addr!("192.0.2.0:53")]).to_dns_server_list()]
958 ; "normal operation Fuchsia (v4)")]
959 #[test_case(&[
960 (RegistryType::Fuchsia, Op::Add { network: (1, vec![fidl_ip!("2001:db8::1")]), result: Ok(()) }),
961 (RegistryType::Fuchsia, Op::SetDefault { network_id: Some(1), result: Ok(()) }),
962 ], vec![(1, vec![fidl_socket_addr!("[2001:db8::1]:53")]).to_dns_server_list()]
963 ; "normal operation Fuchsia (v6)")]
964 #[test_case(&[
965 (RegistryType::Starnix, Op::Add { network: (1, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) }),
966 (RegistryType::Fuchsia, Op::Remove { network_id: 1, result: Err(fnp_socketproxy::NetworkRegistryRemoveError::NotFound) }),
967 ], vec![(1, vec![fidl_socket_addr!("192.0.2.0:53")]).to_dns_server_list()]
968 ; "attempt remove in wrong registry")]
969 #[test_case(&[
970 (RegistryType::Starnix, Op::Add { network: (1, vec![fidl_ip!("192.0.2.0"), fidl_ip!("2001:db8::1")]), result: Ok(()) }),
971 (RegistryType::Fuchsia, Op::Add { network: (2, vec![fidl_ip!("192.0.2.1"), fidl_ip!("2001:db8::2")]), result: Ok(()) }),
972 ], vec![
973 (1, vec![fidl_socket_addr!("192.0.2.0:53"), fidl_socket_addr!("[2001:db8::1]:53")]).to_dns_server_list(),
974 ]; "Fuchsia default absent, use Starnix")]
975 #[test_case(&[
976 (RegistryType::Starnix, Op::Add { network: (1, vec![fidl_ip!("192.0.2.0"), fidl_ip!("2001:db8::1")]), result: Ok(()) }),
977 (RegistryType::Fuchsia, Op::Add { network: (2, vec![fidl_ip!("192.0.2.1"), fidl_ip!("2001:db8::2")]), result: Ok(()) }),
978 (RegistryType::Fuchsia, Op::SetDefault { network_id: Some(2), result: Ok(()) }),
979 ], vec![
980 (2, vec![fidl_socket_addr!("192.0.2.1:53"), fidl_socket_addr!("[2001:db8::2]:53")]).to_dns_server_list(),
981 ]; "Fuchsia default present, use Fuchsia")]
982 #[test_case(&[
983 (RegistryType::Starnix, Op::Add { network: (1, vec![fidl_ip!("192.0.2.0"), fidl_ip!("2001:db8::1")]), result: Ok(()) }),
984 (RegistryType::Fuchsia, Op::Add { network: (2, vec![fidl_ip!("192.0.2.1"), fidl_ip!("2001:db8::2")]), result: Ok(()) }),
985 (RegistryType::Fuchsia, Op::SetDefault { network_id: Some(2), result: Ok(()) }),
986 (RegistryType::Fuchsia, Op::SetDefault { network_id: None, result: Ok(()) }),
987 ], vec![
988 (1, vec![fidl_socket_addr!("192.0.2.0:53"), fidl_socket_addr!("[2001:db8::1]:53")]).to_dns_server_list(),
989 ]; "Fallback to Starnix network")]
990 #[test_case(&[
991 (RegistryType::Starnix, Op::Add { network: (1, vec![fidl_ip!("192.0.2.0"), fidl_ip!("2001:db8::1")]), result: Ok(()) }),
992 (RegistryType::Fuchsia, Op::Add { network: (2, vec![fidl_ip!("192.0.2.1"), fidl_ip!("2001:db8::2")]), result: Ok(()) }),
993 (RegistryType::Fuchsia, Op::SetDefault { network_id: Some(2), result: Ok(()) }),
994 (RegistryType::Fuchsia, Op::Update { network: (2, vec![fidl_ip!("192.0.2.2"), fidl_ip!("2001:db8::3")]), result: Ok(()) }),
995 ], vec![
996 (2, vec![fidl_socket_addr!("192.0.2.2:53"), fidl_socket_addr!("[2001:db8::3]:53")]).to_dns_server_list(),
997 ]; "Fuchsia default present then updated")]
998 #[fuchsia::test]
999 async fn test_dns_tracking_across_registries<N: ToNetwork + Clone>(
1000 operations: &[(RegistryType, Op<N>)],
1001 dns_servers: Vec<fnp_socketproxy::DnsServerList>,
1002 ) -> Result<(), Error> {
1003 let (realm, mut dns_rx, _default_network_rx) = setup_test().await?;
1004 let starnix_networks = realm
1005 .root
1006 .connect_to_protocol_at_exposed_dir()
1007 .context("While connecting to StarnixNetworks")?;
1008 let fuchsia_networks = realm
1009 .root
1010 .connect_to_protocol_at_exposed_dir()
1011 .context("While connecting to FuchsiaNetworks")?;
1012
1013 let mut last_dns = None;
1014 for (registry, op) in operations {
1015 match registry {
1016 RegistryType::Starnix => {
1017 op.execute_starnix(&starnix_networks).await?;
1018 }
1019 RegistryType::Fuchsia => {
1020 op.execute_fuchsia(&fuchsia_networks).await?;
1021 }
1022 }
1023 if !op.is_err() {
1026 last_dns =
1027 Some(dns_rx.next().await.expect("dns update expected after each operation"));
1028 }
1029 }
1030
1031 let mut last_dns = last_dns.expect("there should be at least one dns update");
1032 last_dns.sort_by_key(|a| a.source_network_id);
1033 assert_eq!(last_dns, dns_servers);
1034
1035 Ok(())
1036 }
1037
1038 trait ToDefaultNetworkUpdate {
1039 fn to_default_network_update_request(self) -> fnp_properties::DefaultNetworkUpdate;
1040 }
1041
1042 impl ToDefaultNetworkUpdate for (u64, (u32, Option<u32>)) {
1043 fn to_default_network_update_request(self) -> fnp_properties::DefaultNetworkUpdate {
1044 fnp_properties::DefaultNetworkUpdate {
1045 interface_id: Some(self.0),
1046 socket_marks: Some(fnet::Marks {
1047 mark_1: Some(self.1.0),
1048 mark_2: self.1.1,
1049 __source_breaking: fidl::marker::SourceBreaking,
1050 }),
1051 __source_breaking: fidl::marker::SourceBreaking,
1052 }
1053 }
1054 }
1055
1056 #[test_case(&[
1057 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
1058 ], vec![
1059 Default::default(),
1060 ]
1061 ; "Add but no default")]
1062 #[test_case(&[
1063 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
1064 Op::SetDefault { network_id: Some(1), result: Ok(()) },
1065 ], vec![
1066 Default::default(),
1067 (1, (1, None)).to_default_network_update_request()
1068 ]
1069 ; "Add and set default")]
1070 #[test_case(&[
1071 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
1072 Op::Add { network: (2, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
1073 Op::SetDefault { network_id: Some(1), result: Ok(()) },
1074 Op::SetDefault { network_id: Some(2), result: Ok(()) },
1075 ], vec![
1076 Default::default(),
1077 Default::default(),
1078 (1, (1, None)).to_default_network_update_request(),
1079 (2, (2, None)).to_default_network_update_request(),
1080 ]
1081 ; "Add two and set default")]
1082 #[test_case(&[
1083 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
1084 Op::SetDefault { network_id: Some(1), result: Ok(()) },
1085 Op::SetDefault { network_id: None, result: Ok(()) },
1086 Op::Remove { network_id: 1, result: Ok(()) },
1087 ], vec![
1088 Default::default(),
1089 (1, (1, None)).to_default_network_update_request(),
1090 Default::default(),
1092 Default::default(),
1093 ]
1094 ; "Add default and delete")]
1095 #[fuchsia::test]
1096 async fn test_default_network_update<N: ToNetwork + Clone>(
1097 operations: &[Op<N>],
1098 default_server_updates: Vec<fnp_properties::DefaultNetworkUpdate>,
1099 ) -> Result<(), Error> {
1100 let (realm, _, mut default_networks_rx) = setup_test().await?;
1101 let starnix_networks = realm
1102 .root
1103 .connect_to_protocol_at_exposed_dir()
1104 .context("While connecting to StarnixNetworks")?;
1105
1106 let (_, default_networks) = future::join(
1107 async move {
1108 for op in operations {
1109 op.execute_starnix(&starnix_networks).await?;
1110 }
1111 std::mem::drop(realm);
1112 Ok::<(), anyhow::Error>(())
1113 },
1114 async move {
1115 let mut default_networks = Vec::new();
1116 while let Some(upd) = default_networks_rx.next().await {
1117 default_networks.push(upd);
1118 }
1119 default_networks
1120 },
1121 )
1122 .await;
1123
1124 assert_eq!(*default_networks, default_server_updates);
1125
1126 Ok(())
1127 }
1128}