1use anyhow::{Context, Error, anyhow};
8use fidl::endpoints::RequestStream;
9use fidl_fuchsia_net_policy_socketproxy::{
10 self as fnp_socketproxy, FuchsiaNetworkInfo, FuchsiaNetworksRequest, Network, NetworkInfo,
11 NetworkRegistryAddError, NetworkRegistryRemoveError, NetworkRegistrySetDefaultError,
12 NetworkRegistryUpdateError, StarnixNetworksRequest,
13};
14use fuchsia_component::client::connect_to_protocol;
15use fuchsia_inspect_derive::{IValue, Inspect, Unit};
16use futures::channel::mpsc;
17use futures::lock::Mutex;
18use futures::{SinkExt as _, StreamExt as _, TryStreamExt as _};
19use log::{error, info, warn};
20use std::collections::HashMap;
21use std::sync::Arc;
22use thiserror::Error;
23
24use fidl_fuchsia_net as fnet;
25use fidl_fuchsia_net_interfaces_ext as fnet_interfaces_ext;
26use fidl_fuchsia_posix_socket as fposix_socket;
27
28pub(crate) const DEFAULT_SOCKET_MARK: u32 = 0;
31
32pub(crate) struct RequestForwarder {
33 forwarder_rx: mpsc::Receiver<NetworkRegistryRequest>,
34 registry: fnp_socketproxy::NetworkRegistryProxy,
35}
36
37impl RequestForwarder {
38 pub(crate) fn new(
39 forwarder_rx: mpsc::Receiver<NetworkRegistryRequest>,
40 ) -> Result<Self, anyhow::Error> {
41 Ok(Self {
42 forwarder_rx,
43 registry: connect_to_protocol::<fnp_socketproxy::NetworkRegistryMarker>()
44 .context("error connecting to network registry")?,
45 })
46 }
47
48 pub(crate) async fn run(&mut self) -> Result<(), anyhow::Error> {
49 while let Some(request) = self.forwarder_rx.next().await {
50 match self.forward_request(request).await {
51 Err(e) => {
52 info!(
53 "FIDL error while forwarding request to delegated networks. \
54 Protocol likely not available: {e:?}"
55 );
56 return Err(e);
57 }
58 Ok(Err(e)) => {
59 error!(
60 "Failed to forward request to delegated networks. Future updates \
61 will be ignored, and netcfg will not have complete state: {e:?}"
62 );
63 return Err(anyhow!("Delegated registry has inconsistent state: {e:?}"));
64 }
65 Ok(Ok(_)) => continue,
66 }
67 }
68 error!(
69 "RequestForwarder stream ended. No more requests will be \
70 forwarded to netcfg"
71 );
72 Ok(())
73 }
74
75 async fn forward_request(
76 &self,
77 request: NetworkRegistryRequest,
78 ) -> Result<Result<(), NetworkRegistryError>, anyhow::Error> {
79 info!("forwarding Starnix NetworkRegistry change to netcfg: {request:?}");
80 let res = match request {
81 NetworkRegistryRequest::SetDefault { network_id } => self
82 .registry
83 .set_default(&match network_id {
84 Some(id) => fposix_socket::OptionalUint32::Value(id),
85 None => fposix_socket::OptionalUint32::Unset(fposix_socket::Empty),
86 })
87 .await
88 .context("fidl error forwarding set_default")?
89 .map_err(|e| e.into()),
90 NetworkRegistryRequest::Add { network } => self
91 .registry
92 .add(&network)
93 .await
94 .context("fidl error forwarding add")?
95 .map_err(|e| e.into()),
96 NetworkRegistryRequest::Update { network } => self
97 .registry
98 .update(&network)
99 .await
100 .context("fidl error forwarding update")?
101 .map_err(|e| e.into()),
102 NetworkRegistryRequest::Remove { network_id } => self
103 .registry
104 .remove(network_id)
105 .await
106 .context("fidl error forwarding remove")?
107 .map_err(|e| e.into()),
108 };
109 Ok(res)
110 }
111}
112
113enum CommonErrors {
114 MissingNetworkId,
115 MissingNetworkInfo,
116}
117
118trait NetworkInfoExt {
119 fn mark(&self) -> Option<u32>;
120}
121
122impl NetworkInfoExt for NetworkInfo {
123 fn mark(&self) -> Option<u32> {
124 match self {
125 NetworkInfo::Starnix(s) => s.mark,
126 NetworkInfo::Fuchsia(_) | _ => None,
129 }
130 }
131}
132
133#[derive(Clone, Debug, Error)]
136pub enum NetworkRegistryError {
137 #[error("Error during socketproxy Add: {0:?}")]
138 Add(NetworkRegistryAddError),
139 #[error("Error during socketproxy Remove: {0:?}")]
140 Remove(NetworkRegistryRemoveError),
141 #[error("Error during socketproxy SetDefault: {0:?}")]
142 SetDefault(NetworkRegistrySetDefaultError),
143 #[error("Error during socketproxy Update: {0:?}")]
144 Update(NetworkRegistryUpdateError),
145}
146
147impl From<NetworkRegistryAddError> for NetworkRegistryError {
148 fn from(error: NetworkRegistryAddError) -> Self {
149 NetworkRegistryError::Add(error)
150 }
151}
152
153impl From<NetworkRegistryRemoveError> for NetworkRegistryError {
154 fn from(error: NetworkRegistryRemoveError) -> Self {
155 NetworkRegistryError::Remove(error)
156 }
157}
158
159impl From<NetworkRegistrySetDefaultError> for NetworkRegistryError {
160 fn from(error: NetworkRegistrySetDefaultError) -> Self {
161 NetworkRegistryError::SetDefault(error)
162 }
163}
164
165impl From<NetworkRegistryUpdateError> for NetworkRegistryError {
166 fn from(error: NetworkRegistryUpdateError) -> Self {
167 NetworkRegistryError::Update(error)
168 }
169}
170
171#[derive(Clone, Debug, Error)]
172pub enum NetworkConversionError {
173 #[error("Could not convert id ({0}) to u32")]
174 InvalidInterfaceId(u64),
175}
176
177pub trait NetworkExt<I: fnet_interfaces_ext::FieldInterests> {
178 fn from_watcher_properties(
179 properties: &fnet_interfaces_ext::Properties<I>,
180 ) -> Result<Self, NetworkConversionError>
181 where
182 Self: Sized;
183}
184
185impl<I: fnet_interfaces_ext::FieldInterests> NetworkExt<I> for Network {
186 fn from_watcher_properties(
187 properties: &fnet_interfaces_ext::Properties<I>,
188 ) -> Result<Self, NetworkConversionError> {
189 let network_id: u32 =
191 properties.id.get().try_into().or_else(|_| {
192 Err(NetworkConversionError::InvalidInterfaceId(properties.id.into()))
193 })?;
194 let network = Self {
195 network_id: Some(network_id),
196 info: Some(NetworkInfo::Fuchsia(FuchsiaNetworkInfo {
197 ..Default::default()
199 })),
200 dns_servers: Some(fnp_socketproxy::NetworkDnsServers {
204 v4: Some(vec![]),
205 v6: Some(vec![]),
206 ..Default::default()
207 }),
208 ..Default::default()
209 };
210 Ok(network)
211 }
212}
213
214#[derive(Debug, Clone, PartialEq)]
215pub enum NetworkRegistryRequest {
217 SetDefault { network_id: Option<u32> },
221 Add { network: Network },
226 Update { network: Network },
231 Remove { network_id: u32 },
236}
237
238impl From<&fnp_socketproxy::NetworkRegistryRequest> for NetworkRegistryRequest {
239 fn from(value: &fnp_socketproxy::NetworkRegistryRequest) -> Self {
240 match *value {
241 fnp_socketproxy::NetworkRegistryRequest::SetDefault { network_id, responder: _ } => {
242 NetworkRegistryRequest::SetDefault {
243 network_id: match network_id {
244 fposix_socket::OptionalUint32::Value(v) => Some(v),
245 fposix_socket::OptionalUint32::Unset(_) => None,
246 },
247 }
248 }
249 fnp_socketproxy::NetworkRegistryRequest::Add { ref network, responder: _ } => {
250 NetworkRegistryRequest::Add { network: network.clone() }
251 }
252 fnp_socketproxy::NetworkRegistryRequest::Update { ref network, responder: _ } => {
253 NetworkRegistryRequest::Update { network: network.clone() }
254 }
255 fnp_socketproxy::NetworkRegistryRequest::Remove { network_id, responder: _ } => {
256 NetworkRegistryRequest::Remove { network_id }
257 }
258 }
259 }
260}
261impl From<&StarnixNetworksRequest> for NetworkRegistryRequest {
262 fn from(value: &StarnixNetworksRequest) -> Self {
263 match *value {
264 StarnixNetworksRequest::SetDefault { network_id, responder: _ } => {
265 NetworkRegistryRequest::SetDefault {
266 network_id: match network_id {
267 fposix_socket::OptionalUint32::Value(v) => Some(v),
268 fposix_socket::OptionalUint32::Unset(_) => None,
269 },
270 }
271 }
272 StarnixNetworksRequest::Add { ref network, responder: _ } => {
273 NetworkRegistryRequest::Add { network: network.clone() }
274 }
275 StarnixNetworksRequest::Update { ref network, responder: _ } => {
276 NetworkRegistryRequest::Update { network: network.clone() }
277 }
278 StarnixNetworksRequest::Remove { network_id, responder: _ } => {
279 NetworkRegistryRequest::Remove { network_id }
280 }
281 }
282 }
283}
284impl From<&FuchsiaNetworksRequest> for NetworkRegistryRequest {
285 fn from(value: &FuchsiaNetworksRequest) -> Self {
286 match *value {
287 FuchsiaNetworksRequest::SetDefault { network_id, responder: _ } => {
288 NetworkRegistryRequest::SetDefault {
289 network_id: match network_id {
290 fposix_socket::OptionalUint32::Value(v) => Some(v),
291 fposix_socket::OptionalUint32::Unset(_) => None,
292 },
293 }
294 }
295 FuchsiaNetworksRequest::Add { ref network, responder: _ } => {
296 NetworkRegistryRequest::Add { network: network.clone() }
297 }
298 FuchsiaNetworksRequest::Update { ref network, responder: _ } => {
299 NetworkRegistryRequest::Update { network: network.clone() }
300 }
301 FuchsiaNetworksRequest::Remove { network_id, responder: _ } => {
302 NetworkRegistryRequest::Remove { network_id }
303 }
304 }
305 }
306}
307
308#[derive(Debug, Clone)]
310pub(crate) struct ValidatedNetwork {
311 network_id: u32,
312 info: NetworkInfo,
313}
314
315trait ValidateNetworkExt {
316 fn validate(self) -> Result<ValidatedNetwork, CommonErrors>;
317}
318
319impl ValidateNetworkExt for Network {
320 fn validate(self) -> Result<ValidatedNetwork, CommonErrors> {
321 match self {
322 Network { network_id: None, .. } => Err(CommonErrors::MissingNetworkId),
323 Network { info: None, .. } => Err(CommonErrors::MissingNetworkInfo),
324 Network { network_id: Some(network_id), info: Some(info), .. } => {
325 Ok(ValidatedNetwork { network_id, info })
326 }
327 }
328 }
329}
330
331macro_rules! common_errors_impl {
332 ($($p:ty),+) => {
333 $(
334 impl From<CommonErrors> for $p {
335 fn from(value: CommonErrors) -> Self {
336 use CommonErrors::*;
337 match value {
338 MissingNetworkId => <$p>::MissingNetworkId,
339 MissingNetworkInfo => <$p>::MissingNetworkInfo,
340 }
341 }
342 }
343 )+
344 }
345}
346
347common_errors_impl!(
348 fnp_socketproxy::NetworkRegistryAddError,
349 fnp_socketproxy::NetworkRegistryUpdateError
350);
351
352#[derive(Inspect, Debug, Default)]
354struct NetworkRegistry {
355 networks: IValue<RegisteredNetworks>,
356
357 inspect_node: fuchsia_inspect::Node,
358}
359
360impl NetworkRegistry {
361 pub(crate) fn has_default_network(&self) -> bool {
363 self.networks.default_network_id.is_some()
364 }
365
366 pub(crate) fn current_mark(&self) -> Option<u32> {
368 self.networks.current_mark()
369 }
370}
371
372#[derive(Unit, Debug, Default)]
373struct MethodInspect {
374 successes: u32,
375 errors: u32,
376}
377
378#[derive(Unit, Default, Debug)]
379struct RegisteredNetworks {
380 default_network_id: Option<u32>,
381
382 #[inspect(skip)]
383 networks: HashMap<u32, ValidatedNetwork>,
385
386 adds: MethodInspect,
387 removes: MethodInspect,
388 set_defaults: MethodInspect,
389 updates: MethodInspect,
390}
391
392impl RegisteredNetworks {
393 fn add_network(&mut self, network: Network) -> fnp_socketproxy::NetworkRegistryAddResult {
394 let network = network.validate()?;
395 #[allow(clippy::map_entry, reason = "mass allow for https://fxbug.dev/381896734")]
396 if self.networks.contains_key(&network.network_id) {
397 self.adds.errors += 1;
398 Err(fnp_socketproxy::NetworkRegistryAddError::DuplicateNetworkId)
399 } else {
400 let _: Option<_> = self.networks.insert(network.network_id, network);
401 self.adds.successes += 1;
402 Ok(())
403 }
404 }
405
406 pub(crate) fn clear(&mut self) {
408 self.default_network_id = None;
409 self.networks.clear();
410 }
411
412 fn update_network(&mut self, network: Network) -> fnp_socketproxy::NetworkRegistryUpdateResult {
413 let network = network.validate()?;
414 let network_id = network.network_id;
415 *self
416 .networks
417 .get_mut(&network_id)
418 .ok_or(fnp_socketproxy::NetworkRegistryUpdateError::NotFound)
419 .inspect(|_| self.updates.successes += 1)
420 .inspect_err(|_| self.updates.errors += 1)? = network;
421 Ok(())
422 }
423
424 fn remove_network(&mut self, network_id: u32) -> fnp_socketproxy::NetworkRegistryRemoveResult {
425 if self.default_network_id == Some(network_id) {
426 self.removes.errors += 1;
427 return Err(fnp_socketproxy::NetworkRegistryRemoveError::CannotRemoveDefaultNetwork);
428 }
429 match self.networks.remove(&network_id) {
430 Some(_) => {
431 self.removes.successes += 1;
432 Ok(())
433 }
434 None => {
435 self.removes.errors += 1;
436 Err(fnp_socketproxy::NetworkRegistryRemoveError::NotFound)
437 }
438 }
439 }
440
441 fn set_default_network(
445 &mut self,
446 network_id: Option<u32>,
447 ) -> fnp_socketproxy::NetworkRegistrySetDefaultResult {
448 if let Some(network_id) = network_id {
449 if !self.networks.contains_key(&network_id) {
450 self.set_defaults.errors += 1;
451 return Err(fnp_socketproxy::NetworkRegistrySetDefaultError::NotFound);
452 }
453 }
454 self.set_defaults.successes += 1;
455 self.default_network_id = network_id;
456
457 Ok(())
458 }
459
460 fn current_mark(&self) -> Option<u32> {
461 match (self.default_network_id, self.networks.is_empty()) {
462 (None, false) => Some(DEFAULT_SOCKET_MARK),
463 (id, _) => id.and_then(|id| self.networks[&id].info.mark()),
464 }
465 }
466
467 fn len(&self) -> usize {
468 self.networks.len()
469 }
470}
471
472#[derive(Inspect, Clone, Debug, Default)]
473pub struct NetworkRegistries {
474 starnix: Arc<Mutex<NetworkRegistry>>,
475 fuchsia: Arc<Mutex<NetworkRegistry>>,
476}
477
478impl NetworkRegistries {
479 async fn current_mark(&self) -> Option<u32> {
483 {
484 let fuchsia = self.fuchsia.lock().await;
485 if fuchsia.has_default_network() {
486 return fuchsia.current_mark();
487 }
488 }
489
490 return self.starnix.lock().await.networks.current_mark();
491 }
492}
493
494#[derive(Debug)]
495enum RegistryType {
496 Starnix,
497 Fuchsia,
498}
499
500#[derive(Inspect, Debug)]
501pub struct Registry {
502 #[inspect(forward)]
503 networks: NetworkRegistries,
504 marks: Arc<Mutex<crate::SocketMarks>>,
507 forwarder_tx: mpsc::Sender<NetworkRegistryRequest>,
508 starnix_occupant: Mutex<()>,
509 fuchsia_occupant: Mutex<()>,
510}
511
512macro_rules! handle_registry_request {
513 ($request_type:ident, $request:expr, $network_registry:expr, $registry_type:expr) => {{
514 let mut networks = $network_registry.networks.as_mut();
515 let (op, send): (_, Box<dyn FnOnce() -> Result<(), _> + Send + Sync + 'static>) =
516 match $request {
517 $request_type::SetDefault { network_id, responder } => {
518 let result = networks.set_default_network(match network_id {
519 fposix_socket::OptionalUint32::Value(value) => Some(value),
520 fposix_socket::OptionalUint32::Unset(_) => None,
521 });
522 ("set default", Box::new(move || responder.send(result)))
523 }
524 $request_type::Add { network, responder } => {
525 let result = networks.add_network(network);
526 ("add", Box::new(move || responder.send(result)))
527 }
528 $request_type::Update { network, responder } => {
529 let result = networks.update_network(network);
530 ("update", Box::new(move || responder.send(result)))
531 }
532 $request_type::Remove { network_id, responder } => {
533 let result = networks.remove_network(network_id);
534 ("remove", Box::new(move || responder.send(result)))
535 }
536 };
537 let new_mark = networks.current_mark();
538 info!(
539 "{:?} registry {op}. mark: {new_mark:?}, networks count: {}",
540 $registry_type,
541 networks.len()
542 );
543 std::mem::drop(networks);
544 send
545 }};
546}
547
548impl Registry {
549 pub(crate) fn new(
550 marks: Arc<Mutex<crate::SocketMarks>>,
551 forwarder_tx: mpsc::Sender<NetworkRegistryRequest>,
552 ) -> Result<Self, anyhow::Error> {
553 Ok(Self {
554 networks: Default::default(),
555 marks,
556 forwarder_tx,
557 starnix_occupant: Default::default(),
558 fuchsia_occupant: Default::default(),
559 })
560 }
561}
562
563impl Registry {
564 pub(crate) async fn run_starnix(
565 &self,
566 stream: fnp_socketproxy::StarnixNetworksRequestStream,
567 ) -> Result<(), Error> {
568 let _occupant = match self.starnix_occupant.try_lock() {
569 Some(o) => o,
570 None => {
571 warn!("Only one connection to StarnixNetworks is allowed at a time");
572 stream.control_handle().shutdown_with_epitaph(fidl::Status::ACCESS_DENIED);
573 return Ok(());
574 }
575 };
576
577 info!("Starting fuchsia.net.policy.socketproxy.StarnixNetworks server");
578 self.networks.starnix.lock().await.networks.as_mut().clear();
579 stream
580 .map(|result| result.context("failed request"))
581 .try_for_each(|request| {
582 async {
583 self.forwarder_tx.clone().feed((&request).into()).await.unwrap_or_else(
584 |e| {
585 if !e.is_disconnected() {
586 error!("Unable to feed request forward: {e:?}")
588 }
589 },
590 );
591 let mut network_registry = self.networks.starnix.lock().await;
592 let send: Box<dyn FnOnce() -> Result<(), _> + Send + Sync + 'static> =
593 handle_registry_request!(
594 StarnixNetworksRequest,
595 request,
596 network_registry,
597 RegistryType::Starnix
598 );
599 std::mem::drop(network_registry);
600
601 self.handle_state_changed().await?;
602 send().context("error sending response")?;
603 Ok(())
604 }
605 })
606 .await
607 }
608
609 pub(crate) async fn run_fuchsia(
610 &self,
611 stream: fnp_socketproxy::FuchsiaNetworksRequestStream,
612 ) -> Result<(), Error> {
613 let _occupant = match self.fuchsia_occupant.try_lock() {
614 Some(o) => o,
615 None => {
616 warn!("Only one connection to FuchsiaNetworks is allowed at a time");
617 stream.control_handle().shutdown_with_epitaph(fidl::Status::ACCESS_DENIED);
618 return Ok(());
619 }
620 };
621
622 info!("Starting fuchsia.net.policy.socketproxy.FuchsiaNetworks server");
623 self.networks.fuchsia.lock().await.networks.as_mut().clear();
624 stream
625 .map(|result| result.context("failed request"))
626 .try_for_each(|request| {
627 async {
628 let mut network_registry = self.networks.fuchsia.lock().await;
629 let send: Box<dyn FnOnce() -> Result<(), _> + Send + Sync + 'static> =
630 handle_registry_request!(
631 FuchsiaNetworksRequest,
632 request,
633 network_registry,
634 RegistryType::Fuchsia
635 );
636 std::mem::drop(network_registry);
637
638 self.handle_state_changed().await?;
639 send().context("error sending response")?;
640 Ok(())
641 }
642 })
643 .await
644 }
645
646 async fn handle_state_changed(&self) -> Result<(), Error> {
647 let mark = self.networks.current_mark().await;
650 self.marks.lock().await.set_mark(fnet::MARK_DOMAIN_SO_MARK, mark);
651 Ok(())
652 }
653}
654
655#[cfg(test)]
656mod test {
657 use super::*;
658 use fuchsia_component::server::ServiceFs;
659 use fuchsia_component_test::{
660 Capability, ChildOptions, LocalComponentHandles, RealmBuilder, RealmInstance, Ref, Route,
661 };
662 use futures::channel::mpsc::Receiver;
663 use futures::future;
664 use net_declare::fidl_ip;
665 use pretty_assertions::assert_eq;
666 use socket_proxy_testing::{RegistryType, ToNetwork};
667 use test_case::test_case;
668
669 #[derive(Clone, Debug)]
670 enum Op<N: ToNetwork> {
671 SetDefault {
672 network_id: Option<u32>,
673 result: Result<(), fnp_socketproxy::NetworkRegistrySetDefaultError>,
674 },
675 Add {
676 network: N,
677 result: Result<(), fnp_socketproxy::NetworkRegistryAddError>,
678 },
679 Update {
680 network: N,
681 result: Result<(), fnp_socketproxy::NetworkRegistryUpdateError>,
682 },
683 Remove {
684 network_id: u32,
685 result: Result<(), fnp_socketproxy::NetworkRegistryRemoveError>,
686 },
687 }
688
689 impl<N: ToNetwork + Clone> From<&Op<N>> for NetworkRegistryRequest {
690 fn from(value: &Op<N>) -> Self {
691 match value {
692 Op::SetDefault { network_id, result: _ } => {
693 NetworkRegistryRequest::SetDefault { network_id: *network_id }
694 }
695 Op::Add { network, result: _ } => NetworkRegistryRequest::Add {
696 network: network.clone().to_network(RegistryType::Starnix),
697 },
698 Op::Update { network, result: _ } => NetworkRegistryRequest::Update {
699 network: network.clone().to_network(RegistryType::Starnix),
700 },
701 Op::Remove { network_id, result: _ } => {
702 NetworkRegistryRequest::Remove { network_id: *network_id }
703 }
704 }
705 }
706 }
707
708 macro_rules! execute {
709 ($self:ident, $proxy:ident, $registry:expr) => {{
710 match $self {
711 Op::SetDefault { network_id, result } => {
712 assert_eq!(
713 $proxy
714 .set_default(&match network_id {
715 Some(value) => fposix_socket::OptionalUint32::Value(*value),
716 None => fposix_socket::OptionalUint32::Unset(fposix_socket::Empty),
717 })
718 .await?,
719 *result
720 )
721 }
722 Op::Add { network, result } => {
723 assert_eq!($proxy.add(&network.to_network($registry)).await?, *result)
724 }
725 Op::Update { network, result } => {
726 assert_eq!($proxy.update(&network.to_network($registry)).await?, *result)
727 }
728 Op::Remove { network_id, result } => {
729 assert_eq!($proxy.remove(*network_id).await?, *result)
730 }
731 }
732 Ok(())
733 }};
734 }
735
736 impl<N: ToNetwork + Clone> Op<N> {
737 async fn execute_starnix(
738 &self,
739 starnix: &fnp_socketproxy::StarnixNetworksProxy,
740 ) -> Result<(), Error> {
741 execute!(self, starnix, RegistryType::Starnix)
742 }
743
744 async fn execute_fuchsia(
745 &self,
746 fuchsia: &fnp_socketproxy::FuchsiaNetworksProxy,
747 ) -> Result<(), Error> {
748 execute!(self, fuchsia, RegistryType::Fuchsia)
749 }
750 }
751
752 enum IncomingService {
753 StarnixNetworks(fnp_socketproxy::StarnixNetworksRequestStream),
754 FuchsiaNetworks(fnp_socketproxy::FuchsiaNetworksRequestStream),
755 }
756
757 async fn run_registry(
758 handles: LocalComponentHandles,
759 starnix_networks: Arc<Mutex<NetworkRegistry>>,
760 fuchsia_networks: Arc<Mutex<NetworkRegistry>>,
761 marks: Arc<Mutex<crate::SocketMarks>>,
762 forwarder_tx: mpsc::Sender<NetworkRegistryRequest>,
763 ) -> Result<(), Error> {
764 let mut fs = ServiceFs::new();
765 let _ = fs
766 .dir("svc")
767 .add_fidl_service(IncomingService::StarnixNetworks)
768 .add_fidl_service(IncomingService::FuchsiaNetworks);
769 let _ = fs.serve_connection(handles.outgoing_dir)?;
770
771 let registry = Registry {
772 networks: NetworkRegistries { starnix: starnix_networks, fuchsia: fuchsia_networks },
773 marks,
774 forwarder_tx,
775 starnix_occupant: Default::default(),
776 fuchsia_occupant: Default::default(),
777 };
778
779 fs.for_each_concurrent(0, |service| async {
780 match service {
781 IncomingService::StarnixNetworks(stream) => registry.run_starnix(stream).await,
782 IncomingService::FuchsiaNetworks(stream) => registry.run_fuchsia(stream).await,
783 }
784 .unwrap_or_else(|e| error!("{e:?}"))
785 })
786 .await;
787
788 Ok(())
789 }
790
791 async fn setup_test() -> Result<(RealmInstance, Receiver<NetworkRegistryRequest>), Error> {
792 let builder = RealmBuilder::new().await?;
793 let starnix_networks = Arc::new(Mutex::new(Default::default()));
794 let fuchsia_networks = Arc::new(Mutex::new(Default::default()));
795 let (forwarder_tx, forwarder_rx) = mpsc::channel(1);
796 let marks = Arc::new(Mutex::new(crate::SocketMarks::default()));
797 let registry = builder
798 .add_local_child(
799 "registry",
800 {
801 let starnix_networks = starnix_networks.clone();
802 let fuchsia_networks = fuchsia_networks.clone();
803 let marks = marks.clone();
804 move |handles: LocalComponentHandles| {
805 Box::pin(run_registry(
806 handles,
807 starnix_networks.clone(),
808 fuchsia_networks.clone(),
809 marks.clone(),
810 forwarder_tx.clone(),
811 ))
812 }
813 },
814 ChildOptions::new(),
815 )
816 .await?;
817
818 builder
819 .add_route(
820 Route::new()
821 .capability(Capability::protocol::<fnp_socketproxy::StarnixNetworksMarker>())
822 .from(®istry)
823 .to(Ref::parent()),
824 )
825 .await?;
826
827 builder
828 .add_route(
829 Route::new()
830 .capability(Capability::protocol::<fnp_socketproxy::FuchsiaNetworksMarker>())
831 .from(®istry)
832 .to(Ref::parent()),
833 )
834 .await?;
835
836 let realm = builder.build().await?;
837
838 Ok((realm, forwarder_rx))
839 }
840
841 #[test_case(&[
842 Op::Add { network: 1, result: Ok(()) },
843 Op::Update { network: 1, result: Ok(()) },
844 Op::Remove { network_id: 1, result: Ok(()) },
845 ]; "normal operation")]
846 #[test_case(&[
847 Op::Add { network: 1, result: Ok(()) },
848 Op::Add { network: 1, result: Err(fnp_socketproxy::NetworkRegistryAddError::DuplicateNetworkId) },
849 ]; "duplicate add")]
850 #[test_case(&[
851 Op::Update { network: 1, result: Err(fnp_socketproxy::NetworkRegistryUpdateError::NotFound) },
852 ]; "update missing")]
853 #[test_case(&[
854 Op::<u32>::Remove { network_id: 1, result: Err(fnp_socketproxy::NetworkRegistryRemoveError::NotFound) },
855 ]; "remove missing")]
856 #[test_case(&[
857 Op::<u32>::SetDefault { network_id: Some(1), result: Err(fnp_socketproxy::NetworkRegistrySetDefaultError::NotFound) },
858 ]; "set default missing")]
859 #[test_case(&[
860 Op::Add { network: 1, result: Ok(()) },
861 Op::SetDefault { network_id: Some(1), result: Ok(()) },
862 Op::Remove { network_id: 1, result: Err(fnp_socketproxy::NetworkRegistryRemoveError::CannotRemoveDefaultNetwork)},
863 ]; "remove default network")]
864 #[test_case(&[
865 Op::Add { network: 1, result: Ok(()) },
866 Op::SetDefault { network_id: Some(1), result: Ok(()) },
867 Op::Remove { network_id: 1, result: Err(fnp_socketproxy::NetworkRegistryRemoveError::CannotRemoveDefaultNetwork)},
868 Op::Add { network: 2, result: Ok(()) },
869 Op::SetDefault { network_id: Some(2), result: Ok(()) },
870 Op::Remove { network_id: 1, result: Ok(()) },
871 ]; "remove formerly default network")]
872 #[test_case(&[
873 Op::Add { network: 1, result: Ok(()) },
874 Op::SetDefault { network_id: Some(1), result: Ok(()) },
875 Op::Remove { network_id: 1, result: Err(fnp_socketproxy::NetworkRegistryRemoveError::CannotRemoveDefaultNetwork)},
876 Op::SetDefault { network_id: None, result: Ok(()) },
877 Op::Remove { network_id: 1, result: Ok(()) },
878 ]; "remove last network")]
879 #[test_case(&[
880 Op::Add { network: 1, result: Ok(()) },
881 Op::Update { network: 1, result: Ok(()) },
882 Op::Add { network: 2, result: Ok(()) },
883 Op::Add { network: 3, result: Ok(()) },
884 Op::Add { network: 4, result: Ok(()) },
885 Op::Update { network: 4, result: Ok(()) },
886 Op::Update { network: 2, result: Ok(()) },
887 Op::Update { network: 3, result: Ok(()) },
888 Op::Add { network: 5, result: Ok(()) },
889 Op::Update { network: 5, result: Ok(()) },
890 Op::Add { network: 6, result: Ok(()) },
891 Op::Add { network: 7, result: Ok(()) },
892 Op::Add { network: 8, result: Ok(()) },
893 Op::Update { network: 8, result: Ok(()) },
894 Op::Update { network: 6, result: Ok(()) },
895 Op::Add { network: 9, result: Ok(()) },
896 Op::Update { network: 9, result: Ok(()) },
897 Op::Update { network: 7, result: Ok(()) },
898 Op::Add { network: 10, result: Ok(()) },
899 Op::Update { network: 10, result: Ok(()) },
900 ]; "many updates")]
901 #[fuchsia::test]
902 async fn test_operations<N: ToNetwork + Clone>(operations: &[Op<N>]) -> Result<(), Error> {
903 let (realm, _) = setup_test().await?;
904 let starnix_networks = realm
905 .root
906 .connect_to_protocol_at_exposed_dir()
907 .context("While connecting to StarnixNetworks")?;
908 let fuchsia_networks = realm
909 .root
910 .connect_to_protocol_at_exposed_dir()
911 .context("While connecting to FuchsiaNetworks")?;
912
913 for op in operations {
914 op.execute_starnix(&starnix_networks).await?;
917 op.execute_fuchsia(&fuchsia_networks).await?;
918 }
919
920 Ok(())
921 }
922
923 #[test_case(&[
924 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
925 ]
926 ; "Add but no default")]
927 #[test_case(&[
928 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
929 Op::SetDefault { network_id: Some(1), result: Ok(()) },
930 ]
931 ; "Add and set default")]
932 #[test_case(&[
933 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
934 Op::Add { network: (2, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
935 Op::SetDefault { network_id: Some(1), result: Ok(()) },
936 Op::SetDefault { network_id: Some(2), result: Ok(()) },
937 ]
938 ; "Add two and set default")]
939 #[test_case(&[
940 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
941 Op::SetDefault { network_id: Some(1), result: Ok(()) },
942 Op::SetDefault { network_id: None, result: Ok(()) },
943 Op::Remove { network_id: 1, result: Ok(()) },
944 ]
945 ; "Add default and delete")]
946 #[fuchsia::test]
947 async fn test_forward_network_update<N: ToNetwork + Clone + std::fmt::Debug>(
948 operations: &[Op<N>],
949 ) -> Result<(), Error> {
950 let (realm, mut forwarder_rx) = setup_test().await?;
951 let starnix_networks = realm
952 .root
953 .connect_to_protocol_at_exposed_dir()
954 .context("While connecting to StarnixNetworks")?;
955
956 let (_, seen_updates) = future::join(
957 async move {
958 for op in operations {
959 op.execute_starnix(&starnix_networks).await?;
960 }
961 std::mem::drop(realm);
962 Ok::<(), anyhow::Error>(())
963 },
964 async move {
965 let mut forwarded_requests = Vec::new();
966 while let Some(req) = forwarder_rx.next().await {
967 forwarded_requests.push(req);
968 }
969 forwarded_requests
970 },
971 )
972 .await;
973
974 let expected_updates =
975 operations.iter().map(NetworkRegistryRequest::from).collect::<Vec<_>>();
976 assert_eq!(expected_updates, seen_updates);
977
978 Ok(())
979 }
980}