1use anyhow::{Context, Error, anyhow};
8use fidl::endpoints::RequestStream;
9use fidl_fuchsia_net_policy_socketproxy::{
10 self as fnp_socketproxy, Network, NetworkInfo, NetworkRegistryAddError,
11 NetworkRegistryRemoveError, NetworkRegistrySetDefaultError, NetworkRegistryUpdateError,
12 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_posix_socket as fposix_socket;
26
27pub(crate) const DEFAULT_SOCKET_MARK: u32 = 0;
30
31pub(crate) struct RequestForwarder {
32 forwarder_rx: mpsc::Receiver<NetworkRegistryRequest>,
33 registry: Option<fnp_socketproxy::NetworkRegistryProxy>,
34}
35
36impl RequestForwarder {
37 pub(crate) fn new(
38 forwarder_rx: mpsc::Receiver<NetworkRegistryRequest>,
39 ) -> Result<Self, anyhow::Error> {
40 Ok(Self { forwarder_rx, registry: None })
41 }
42
43 fn get_registry(&mut self) -> Result<&fnp_socketproxy::NetworkRegistryProxy, anyhow::Error> {
44 match &self.registry {
45 Some(registry) => Ok(registry),
46 None => {
47 let registry = connect_to_protocol::<fnp_socketproxy::NetworkRegistryMarker>()
48 .context("error connecting to network registry")?;
49 Ok(self.registry.insert(registry))
50 }
51 }
52 }
53
54 pub(crate) async fn run(&mut self) -> Result<(), anyhow::Error> {
55 while let Some(request) = self.forwarder_rx.next().await {
56 match self.forward_request(request).await {
57 Err(e) => {
58 info!(
59 "FIDL error while forwarding request to delegated networks. \
60 Protocol likely not available: {e:?}"
61 );
62 return Err(e);
63 }
64 Ok(Err(e)) => {
65 error!(
66 "Failed to forward request to delegated networks. Future updates \
67 will be ignored, and netcfg will not have complete state: {e:?}"
68 );
69 return Err(anyhow!("Delegated registry has inconsistent state: {e:?}"));
70 }
71 Ok(Ok(_)) => continue,
72 }
73 }
74 error!(
75 "RequestForwarder stream ended. No more requests will be \
76 forwarded to netcfg"
77 );
78 Ok(())
79 }
80
81 async fn forward_request(
82 &mut self,
83 request: NetworkRegistryRequest,
84 ) -> Result<Result<(), NetworkRegistryError>, anyhow::Error> {
85 info!("forwarding Starnix NetworkRegistry change to netcfg: {request:?}");
86 let registry = self.get_registry()?;
87 let res = match request {
88 NetworkRegistryRequest::SetDefault { network_id } => registry
89 .set_default(&match network_id {
90 Some(id) => fposix_socket::OptionalUint32::Value(id),
91 None => fposix_socket::OptionalUint32::Unset(fposix_socket::Empty),
92 })
93 .await
94 .context("fidl error forwarding set_default")?
95 .map_err(|e| e.into()),
96 NetworkRegistryRequest::Add { network } => registry
97 .add(&network)
98 .await
99 .context("fidl error forwarding add")?
100 .map_err(|e| e.into()),
101 NetworkRegistryRequest::Update { network } => registry
102 .update(&network)
103 .await
104 .context("fidl error forwarding update")?
105 .map_err(|e| e.into()),
106 NetworkRegistryRequest::Remove { network_id } => registry
107 .remove(network_id)
108 .await
109 .context("fidl error forwarding remove")?
110 .map_err(|e| e.into()),
111 };
112 Ok(res)
113 }
114}
115
116enum CommonErrors {
117 MissingNetworkId,
118 MissingNetworkInfo,
119}
120
121trait NetworkInfoExt {
122 fn mark(&self) -> Option<u32>;
123}
124
125impl NetworkInfoExt for NetworkInfo {
126 fn mark(&self) -> Option<u32> {
127 match self {
128 NetworkInfo::Starnix(s) => s.mark,
129 NetworkInfo::Fuchsia(_) | _ => None,
132 }
133 }
134}
135
136#[derive(Clone, Debug, Error)]
139pub enum NetworkRegistryError {
140 #[error("Error during socketproxy Add: {0:?}")]
141 Add(NetworkRegistryAddError),
142 #[error("Error during socketproxy Remove: {0:?}")]
143 Remove(NetworkRegistryRemoveError),
144 #[error("Error during socketproxy SetDefault: {0:?}")]
145 SetDefault(NetworkRegistrySetDefaultError),
146 #[error("Error during socketproxy Update: {0:?}")]
147 Update(NetworkRegistryUpdateError),
148}
149
150impl From<NetworkRegistryAddError> for NetworkRegistryError {
151 fn from(error: NetworkRegistryAddError) -> Self {
152 NetworkRegistryError::Add(error)
153 }
154}
155
156impl From<NetworkRegistryRemoveError> for NetworkRegistryError {
157 fn from(error: NetworkRegistryRemoveError) -> Self {
158 NetworkRegistryError::Remove(error)
159 }
160}
161
162impl From<NetworkRegistrySetDefaultError> for NetworkRegistryError {
163 fn from(error: NetworkRegistrySetDefaultError) -> Self {
164 NetworkRegistryError::SetDefault(error)
165 }
166}
167
168impl From<NetworkRegistryUpdateError> for NetworkRegistryError {
169 fn from(error: NetworkRegistryUpdateError) -> Self {
170 NetworkRegistryError::Update(error)
171 }
172}
173
174#[derive(Debug, Clone, PartialEq)]
175pub enum NetworkRegistryRequest {
177 SetDefault { network_id: Option<u32> },
181 Add { network: Network },
186 Update { network: Network },
191 Remove { network_id: u32 },
196}
197
198impl From<&fnp_socketproxy::NetworkRegistryRequest> for NetworkRegistryRequest {
199 fn from(value: &fnp_socketproxy::NetworkRegistryRequest) -> Self {
200 match *value {
201 fnp_socketproxy::NetworkRegistryRequest::SetDefault { network_id, responder: _ } => {
202 NetworkRegistryRequest::SetDefault {
203 network_id: match network_id {
204 fposix_socket::OptionalUint32::Value(v) => Some(v),
205 fposix_socket::OptionalUint32::Unset(_) => None,
206 },
207 }
208 }
209 fnp_socketproxy::NetworkRegistryRequest::Add { ref network, responder: _ } => {
210 NetworkRegistryRequest::Add { network: network.clone() }
211 }
212 fnp_socketproxy::NetworkRegistryRequest::Update { ref network, responder: _ } => {
213 NetworkRegistryRequest::Update { network: network.clone() }
214 }
215 fnp_socketproxy::NetworkRegistryRequest::Remove { network_id, responder: _ } => {
216 NetworkRegistryRequest::Remove { network_id }
217 }
218 }
219 }
220}
221impl From<&StarnixNetworksRequest> for NetworkRegistryRequest {
222 fn from(value: &StarnixNetworksRequest) -> Self {
223 match *value {
224 StarnixNetworksRequest::SetDefault { network_id, responder: _ } => {
225 NetworkRegistryRequest::SetDefault {
226 network_id: match network_id {
227 fposix_socket::OptionalUint32::Value(v) => Some(v),
228 fposix_socket::OptionalUint32::Unset(_) => None,
229 },
230 }
231 }
232 StarnixNetworksRequest::Add { ref network, responder: _ } => {
233 NetworkRegistryRequest::Add { network: network.clone() }
234 }
235 StarnixNetworksRequest::Update { ref network, responder: _ } => {
236 NetworkRegistryRequest::Update { network: network.clone() }
237 }
238 StarnixNetworksRequest::Remove { network_id, responder: _ } => {
239 NetworkRegistryRequest::Remove { network_id }
240 }
241 }
242 }
243}
244
245#[derive(Debug, Clone)]
247pub(crate) struct ValidatedNetwork {
248 network_id: u32,
249 info: NetworkInfo,
250}
251
252trait ValidateNetworkExt {
253 fn validate(self) -> Result<ValidatedNetwork, CommonErrors>;
254}
255
256impl ValidateNetworkExt for Network {
257 fn validate(self) -> Result<ValidatedNetwork, CommonErrors> {
258 match self {
259 Network { network_id: None, .. } => Err(CommonErrors::MissingNetworkId),
260 Network { info: None, .. } => Err(CommonErrors::MissingNetworkInfo),
261 Network { network_id: Some(network_id), info: Some(info), .. } => {
262 Ok(ValidatedNetwork { network_id, info })
263 }
264 }
265 }
266}
267
268macro_rules! common_errors_impl {
269 ($($p:ty),+) => {
270 $(
271 impl From<CommonErrors> for $p {
272 fn from(value: CommonErrors) -> Self {
273 use CommonErrors::*;
274 match value {
275 MissingNetworkId => <$p>::MissingNetworkId,
276 MissingNetworkInfo => <$p>::MissingNetworkInfo,
277 }
278 }
279 }
280 )+
281 }
282}
283
284common_errors_impl!(
285 fnp_socketproxy::NetworkRegistryAddError,
286 fnp_socketproxy::NetworkRegistryUpdateError
287);
288
289#[derive(Inspect, Debug, Default)]
291struct NetworkRegistry {
292 networks: IValue<RegisteredNetworks>,
293
294 inspect_node: fuchsia_inspect::Node,
295}
296
297impl NetworkRegistry {
298 pub(crate) fn current_mark(&self) -> Option<u32> {
300 self.networks.current_mark()
301 }
302}
303
304#[derive(Unit, Debug, Default)]
305struct MethodInspect {
306 successes: u32,
307 errors: u32,
308}
309
310#[derive(Unit, Default, Debug)]
311struct RegisteredNetworks {
312 default_network_id: Option<u32>,
313
314 #[inspect(skip)]
315 networks: HashMap<u32, ValidatedNetwork>,
317
318 adds: MethodInspect,
319 removes: MethodInspect,
320 set_defaults: MethodInspect,
321 updates: MethodInspect,
322}
323
324impl RegisteredNetworks {
325 fn add_network(&mut self, network: Network) -> fnp_socketproxy::NetworkRegistryAddResult {
326 let network = network.validate()?;
327 #[allow(clippy::map_entry, reason = "mass allow for https://fxbug.dev/381896734")]
328 if self.networks.contains_key(&network.network_id) {
329 self.adds.errors += 1;
330 Err(fnp_socketproxy::NetworkRegistryAddError::DuplicateNetworkId)
331 } else {
332 let _: Option<_> = self.networks.insert(network.network_id, network);
333 self.adds.successes += 1;
334 Ok(())
335 }
336 }
337
338 pub(crate) fn clear(&mut self) {
340 self.default_network_id = None;
341 self.networks.clear();
342 }
343
344 fn update_network(&mut self, network: Network) -> fnp_socketproxy::NetworkRegistryUpdateResult {
345 let network = network.validate()?;
346 let network_id = network.network_id;
347 *self
348 .networks
349 .get_mut(&network_id)
350 .ok_or(fnp_socketproxy::NetworkRegistryUpdateError::NotFound)
351 .inspect(|_| self.updates.successes += 1)
352 .inspect_err(|_| self.updates.errors += 1)? = network;
353 Ok(())
354 }
355
356 fn remove_network(&mut self, network_id: u32) -> fnp_socketproxy::NetworkRegistryRemoveResult {
357 if self.default_network_id == Some(network_id) {
358 self.removes.errors += 1;
359 return Err(fnp_socketproxy::NetworkRegistryRemoveError::CannotRemoveDefaultNetwork);
360 }
361 match self.networks.remove(&network_id) {
362 Some(_) => {
363 self.removes.successes += 1;
364 Ok(())
365 }
366 None => {
367 self.removes.errors += 1;
368 Err(fnp_socketproxy::NetworkRegistryRemoveError::NotFound)
369 }
370 }
371 }
372
373 fn set_default_network(
377 &mut self,
378 network_id: Option<u32>,
379 ) -> fnp_socketproxy::NetworkRegistrySetDefaultResult {
380 if let Some(network_id) = network_id {
381 if !self.networks.contains_key(&network_id) {
382 self.set_defaults.errors += 1;
383 return Err(fnp_socketproxy::NetworkRegistrySetDefaultError::NotFound);
384 }
385 }
386 self.set_defaults.successes += 1;
387 self.default_network_id = network_id;
388
389 Ok(())
390 }
391
392 fn current_mark(&self) -> Option<u32> {
393 match (self.default_network_id, self.networks.is_empty()) {
394 (None, false) => Some(DEFAULT_SOCKET_MARK),
395 (id, _) => id.and_then(|id| self.networks[&id].info.mark()),
396 }
397 }
398
399 fn len(&self) -> usize {
400 self.networks.len()
401 }
402}
403
404#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
406pub(crate) enum NetcfgMarkState {
407 #[default]
409 NoDefault,
410 Default(Option<u32>),
412}
413
414#[derive(Inspect, Clone, Debug, Default)]
415pub struct NetworkRegistries {
416 starnix: Arc<Mutex<NetworkRegistry>>,
417 #[inspect(skip)]
418 netcfg: Arc<Mutex<NetcfgMarkState>>,
419}
420
421impl NetworkRegistries {
422 async fn current_mark(&self) -> Option<u32> {
426 {
427 let netcfg = self.netcfg.lock().await;
428 match *netcfg {
429 NetcfgMarkState::Default(mark) => return mark,
430 NetcfgMarkState::NoDefault => {}
431 }
432 }
433 self.starnix.lock().await.current_mark()
434 }
435}
436
437#[derive(Inspect, Debug)]
438pub struct Registry {
439 #[inspect(forward)]
440 networks: NetworkRegistries,
441 marks: Arc<Mutex<crate::SocketMarks>>,
444 forwarder_tx: mpsc::Sender<NetworkRegistryRequest>,
445 starnix_occupant: Mutex<()>,
446}
447
448impl Registry {
449 pub(crate) fn new(
450 marks: Arc<Mutex<crate::SocketMarks>>,
451 forwarder_tx: mpsc::Sender<NetworkRegistryRequest>,
452 ) -> Result<Self, anyhow::Error> {
453 Ok(Self {
454 networks: Default::default(),
455 marks,
456 forwarder_tx,
457 starnix_occupant: Default::default(),
458 })
459 }
460}
461
462impl Registry {
463 pub(crate) async fn run_starnix(
464 &self,
465 stream: fnp_socketproxy::StarnixNetworksRequestStream,
466 ) -> Result<(), Error> {
467 let _occupant = match self.starnix_occupant.try_lock() {
468 Some(o) => o,
469 None => {
470 warn!("Only one connection to StarnixNetworks is allowed at a time");
471 stream.control_handle().shutdown_with_epitaph(fidl::Status::ACCESS_DENIED);
472 return Ok(());
473 }
474 };
475
476 info!("Starting fuchsia.net.policy.socketproxy.StarnixNetworks server");
477 self.networks.starnix.lock().await.networks.as_mut().clear();
478 stream
479 .map(|result| result.context("failed request"))
480 .try_for_each(|request| {
481 async {
482 self.forwarder_tx.clone().feed((&request).into()).await.unwrap_or_else(|e| {
483 if !e.is_disconnected() {
484 error!("Unable to feed request forward: {e:?}")
486 }
487 });
488 let mut network_registry = self.networks.starnix.lock().await;
489 let mut networks = network_registry.networks.as_mut();
490 let (op, send): (
491 _,
492 Box<dyn FnOnce() -> Result<(), _> + Send + Sync + 'static>,
493 ) = match request {
494 StarnixNetworksRequest::SetDefault { network_id, responder } => {
495 let result = networks.set_default_network(match network_id {
496 fposix_socket::OptionalUint32::Value(value) => Some(value),
497 fposix_socket::OptionalUint32::Unset(_) => None,
498 });
499 ("set default", Box::new(move || responder.send(result)))
500 }
501 StarnixNetworksRequest::Add { network, responder } => {
502 let result = networks.add_network(network);
503 ("add", Box::new(move || responder.send(result)))
504 }
505 StarnixNetworksRequest::Update { network, responder } => {
506 let result = networks.update_network(network);
507 ("update", Box::new(move || responder.send(result)))
508 }
509 StarnixNetworksRequest::Remove { network_id, responder } => {
510 let result = networks.remove_network(network_id);
511 ("remove", Box::new(move || responder.send(result)))
512 }
513 };
514 let new_mark = networks.current_mark();
515 info!(
516 "Starnix registry {op}. mark: {new_mark:?}, networks count: {}",
517 networks.len()
518 );
519 std::mem::drop(networks);
520 std::mem::drop(network_registry);
521
522 self.handle_state_changed().await;
523 send().context("error sending response")?;
524 Ok(())
525 }
526 })
527 .await
528 }
529
530 pub(crate) async fn set_netcfg_mark(&self, state: NetcfgMarkState) {
531 *self.networks.netcfg.lock().await = state;
532 self.handle_state_changed().await;
533 }
534
535 pub(crate) async fn handle_state_changed(&self) {
536 let mark = self.networks.current_mark().await;
539 self.marks.lock().await.set_mark(fnet::MARK_DOMAIN_SO_MARK, mark);
540 }
541}
542
543#[cfg(test)]
544mod test {
545 use super::*;
546 use fuchsia_component::server::ServiceFs;
547 use fuchsia_component_test::{
548 Capability, ChildOptions, LocalComponentHandles, RealmBuilder, RealmInstance, Ref, Route,
549 };
550 use futures::channel::mpsc::Receiver;
551 use futures::future;
552 use net_declare::fidl_ip;
553 use pretty_assertions::assert_eq;
554 use socket_proxy_testing::ToNetwork;
555 use test_case::test_case;
556
557 #[derive(Clone, Debug)]
558 enum Op<N: ToNetwork> {
559 SetDefault {
560 network_id: Option<u32>,
561 result: Result<(), fnp_socketproxy::NetworkRegistrySetDefaultError>,
562 },
563 Add {
564 network: N,
565 result: Result<(), fnp_socketproxy::NetworkRegistryAddError>,
566 },
567 Update {
568 network: N,
569 result: Result<(), fnp_socketproxy::NetworkRegistryUpdateError>,
570 },
571 Remove {
572 network_id: u32,
573 result: Result<(), fnp_socketproxy::NetworkRegistryRemoveError>,
574 },
575 }
576
577 impl<N: ToNetwork + Clone> From<&Op<N>> for NetworkRegistryRequest {
578 fn from(value: &Op<N>) -> Self {
579 match value {
580 Op::SetDefault { network_id, result: _ } => {
581 NetworkRegistryRequest::SetDefault { network_id: *network_id }
582 }
583 Op::Add { network, result: _ } => {
584 NetworkRegistryRequest::Add { network: network.clone().to_network() }
585 }
586 Op::Update { network, result: _ } => {
587 NetworkRegistryRequest::Update { network: network.clone().to_network() }
588 }
589 Op::Remove { network_id, result: _ } => {
590 NetworkRegistryRequest::Remove { network_id: *network_id }
591 }
592 }
593 }
594 }
595
596 impl<N: ToNetwork + Clone> Op<N> {
597 async fn execute_starnix(
598 &self,
599 starnix: &fnp_socketproxy::StarnixNetworksProxy,
600 ) -> Result<(), Error> {
601 match self {
602 Op::SetDefault { network_id, result } => {
603 assert_eq!(
604 starnix
605 .set_default(&match network_id {
606 Some(value) => fposix_socket::OptionalUint32::Value(*value),
607 None => fposix_socket::OptionalUint32::Unset(fposix_socket::Empty),
608 })
609 .await?,
610 *result
611 )
612 }
613 Op::Add { network, result } => {
614 assert_eq!(starnix.add(&network.to_network()).await?, *result)
615 }
616 Op::Update { network, result } => {
617 assert_eq!(starnix.update(&network.to_network()).await?, *result)
618 }
619 Op::Remove { network_id, result } => {
620 assert_eq!(starnix.remove(*network_id).await?, *result)
621 }
622 }
623 Ok(())
624 }
625 }
626
627 enum IncomingService {
628 StarnixNetworks(fnp_socketproxy::StarnixNetworksRequestStream),
629 }
630
631 async fn run_registry(
632 handles: LocalComponentHandles,
633 starnix_networks: Arc<Mutex<NetworkRegistry>>,
634 marks: Arc<Mutex<crate::SocketMarks>>,
635 forwarder_tx: mpsc::Sender<NetworkRegistryRequest>,
636 ) -> Result<(), Error> {
637 let mut fs = ServiceFs::new();
638 let _ = fs.dir("svc").add_fidl_service(IncomingService::StarnixNetworks);
639 let _ = fs.serve_connection(handles.outgoing_dir)?;
640
641 let registry = Registry {
642 networks: NetworkRegistries { starnix: starnix_networks, netcfg: Default::default() },
643 marks,
644 forwarder_tx,
645 starnix_occupant: Default::default(),
646 };
647
648 fs.for_each_concurrent(0, |service| async {
649 match service {
650 IncomingService::StarnixNetworks(stream) => registry.run_starnix(stream).await,
651 }
652 .unwrap_or_else(|e| error!("{e:?}"))
653 })
654 .await;
655
656 Ok(())
657 }
658
659 async fn setup_test() -> Result<(RealmInstance, Receiver<NetworkRegistryRequest>), Error> {
660 let builder = RealmBuilder::new().await?;
661 let starnix_networks = Arc::new(Mutex::new(Default::default()));
662 let (forwarder_tx, forwarder_rx) = mpsc::channel(1);
663 let marks = Arc::new(Mutex::new(crate::SocketMarks::default()));
664 let registry = builder
665 .add_local_child(
666 "registry",
667 {
668 let starnix_networks = starnix_networks.clone();
669 let marks = marks.clone();
670 move |handles: LocalComponentHandles| {
671 Box::pin(run_registry(
672 handles,
673 starnix_networks.clone(),
674 marks.clone(),
675 forwarder_tx.clone(),
676 ))
677 }
678 },
679 ChildOptions::new(),
680 )
681 .await?;
682
683 builder
684 .add_route(
685 Route::new()
686 .capability(Capability::protocol::<fnp_socketproxy::StarnixNetworksMarker>())
687 .from(®istry)
688 .to(Ref::parent()),
689 )
690 .await?;
691
692 let realm = builder.build().await?;
693
694 Ok((realm, forwarder_rx))
695 }
696
697 #[test_case(&[
698 Op::Add { network: 1, result: Ok(()) },
699 Op::Update { network: 1, result: Ok(()) },
700 Op::Remove { network_id: 1, result: Ok(()) },
701 ]; "normal operation")]
702 #[test_case(&[
703 Op::Add { network: 1, result: Ok(()) },
704 Op::Add { network: 1, result: Err(fnp_socketproxy::NetworkRegistryAddError::DuplicateNetworkId) },
705 ]; "duplicate add")]
706 #[test_case(&[
707 Op::Update { network: 1, result: Err(fnp_socketproxy::NetworkRegistryUpdateError::NotFound) },
708 ]; "update missing")]
709 #[test_case(&[
710 Op::<u32>::Remove { network_id: 1, result: Err(fnp_socketproxy::NetworkRegistryRemoveError::NotFound) },
711 ]; "remove missing")]
712 #[test_case(&[
713 Op::<u32>::SetDefault { network_id: Some(1), result: Err(fnp_socketproxy::NetworkRegistrySetDefaultError::NotFound) },
714 ]; "set default missing")]
715 #[test_case(&[
716 Op::Add { network: 1, result: Ok(()) },
717 Op::SetDefault { network_id: Some(1), result: Ok(()) },
718 Op::Remove { network_id: 1, result: Err(fnp_socketproxy::NetworkRegistryRemoveError::CannotRemoveDefaultNetwork)},
719 ]; "remove default network")]
720 #[test_case(&[
721 Op::Add { network: 1, result: Ok(()) },
722 Op::SetDefault { network_id: Some(1), result: Ok(()) },
723 Op::Remove { network_id: 1, result: Err(fnp_socketproxy::NetworkRegistryRemoveError::CannotRemoveDefaultNetwork)},
724 Op::Add { network: 2, result: Ok(()) },
725 Op::SetDefault { network_id: Some(2), result: Ok(()) },
726 Op::Remove { network_id: 1, result: Ok(()) },
727 ]; "remove formerly default network")]
728 #[test_case(&[
729 Op::Add { network: 1, result: Ok(()) },
730 Op::SetDefault { network_id: Some(1), result: Ok(()) },
731 Op::Remove { network_id: 1, result: Err(fnp_socketproxy::NetworkRegistryRemoveError::CannotRemoveDefaultNetwork)},
732 Op::SetDefault { network_id: None, result: Ok(()) },
733 Op::Remove { network_id: 1, result: Ok(()) },
734 ]; "remove last network")]
735 #[test_case(&[
736 Op::Add { network: 1, result: Ok(()) },
737 Op::Update { network: 1, result: Ok(()) },
738 Op::Add { network: 2, result: Ok(()) },
739 Op::Add { network: 3, result: Ok(()) },
740 Op::Add { network: 4, result: Ok(()) },
741 Op::Update { network: 4, result: Ok(()) },
742 Op::Update { network: 2, result: Ok(()) },
743 Op::Update { network: 3, result: Ok(()) },
744 Op::Add { network: 5, result: Ok(()) },
745 Op::Update { network: 5, result: Ok(()) },
746 Op::Add { network: 6, result: Ok(()) },
747 Op::Add { network: 7, result: Ok(()) },
748 Op::Add { network: 8, result: Ok(()) },
749 Op::Update { network: 8, result: Ok(()) },
750 Op::Update { network: 6, result: Ok(()) },
751 Op::Add { network: 9, result: Ok(()) },
752 Op::Update { network: 9, result: Ok(()) },
753 Op::Update { network: 7, result: Ok(()) },
754 Op::Add { network: 10, result: Ok(()) },
755 Op::Update { network: 10, result: Ok(()) },
756 ]; "many updates")]
757 #[fuchsia::test]
758 async fn test_operations<N: ToNetwork + Clone>(operations: &[Op<N>]) -> Result<(), Error> {
759 let (realm, _) = setup_test().await?;
760 let starnix_networks = realm
761 .root
762 .connect_to_protocol_at_exposed_dir()
763 .context("While connecting to StarnixNetworks")?;
764 for op in operations {
765 op.execute_starnix(&starnix_networks).await?;
766 }
767
768 Ok(())
769 }
770
771 #[test_case(&[
772 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
773 ]
774 ; "Add but no default")]
775 #[test_case(&[
776 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
777 Op::SetDefault { network_id: Some(1), result: Ok(()) },
778 ]
779 ; "Add and set default")]
780 #[test_case(&[
781 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
782 Op::Add { network: (2, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
783 Op::SetDefault { network_id: Some(1), result: Ok(()) },
784 Op::SetDefault { network_id: Some(2), result: Ok(()) },
785 ]
786 ; "Add two and set default")]
787 #[test_case(&[
788 Op::Add { network: (1, vec![fidl_ip!("192.0.2.0")]), result: Ok(()) },
789 Op::SetDefault { network_id: Some(1), result: Ok(()) },
790 Op::SetDefault { network_id: None, result: Ok(()) },
791 Op::Remove { network_id: 1, result: Ok(()) },
792 ]
793 ; "Add default and delete")]
794 #[fuchsia::test]
795 async fn test_forward_network_update<N: ToNetwork + Clone + std::fmt::Debug>(
796 operations: &[Op<N>],
797 ) -> Result<(), Error> {
798 let (realm, mut forwarder_rx) = setup_test().await?;
799 let starnix_networks = realm
800 .root
801 .connect_to_protocol_at_exposed_dir()
802 .context("While connecting to StarnixNetworks")?;
803
804 let (_, seen_updates) = future::join(
805 async move {
806 for op in operations {
807 op.execute_starnix(&starnix_networks).await?;
808 }
809 std::mem::drop(realm);
810 Ok::<(), anyhow::Error>(())
811 },
812 async move {
813 let mut forwarded_requests = Vec::new();
814 while let Some(req) = forwarder_rx.next().await {
815 forwarded_requests.push(req);
816 }
817 forwarded_requests
818 },
819 )
820 .await;
821
822 let expected_updates =
823 operations.iter().map(NetworkRegistryRequest::from).collect::<Vec<_>>();
824 assert_eq!(expected_updates, seen_updates);
825
826 Ok(())
827 }
828
829 #[fuchsia::test]
830 async fn test_mark_resolution_precedence() {
831 let starnix = Arc::new(Mutex::new(NetworkRegistry::default()));
832 let netcfg = Arc::new(Mutex::new(NetcfgMarkState::NoDefault));
833
834 let registries = NetworkRegistries { starnix: starnix.clone(), netcfg: netcfg.clone() };
835
836 assert_eq!(registries.current_mark().await, None);
838
839 starnix.lock().await.networks.as_mut().add_network(1.to_network()).unwrap();
841 starnix.lock().await.networks.as_mut().set_default_network(Some(1)).unwrap();
842 assert_eq!(registries.current_mark().await, Some(1));
843
844 *netcfg.lock().await = NetcfgMarkState::Default(None);
846 assert_eq!(registries.current_mark().await, None);
847
848 *netcfg.lock().await = NetcfgMarkState::Default(Some(456));
850 assert_eq!(registries.current_mark().await, Some(456));
851
852 *netcfg.lock().await = NetcfgMarkState::NoDefault;
854 assert_eq!(registries.current_mark().await, Some(1));
855 }
856}