Skip to main content

starnix_modules_nmfs/
manager.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::{NetworkMessage, fuchsia_network_monitor_fs};
6use bstr::BString;
7use fuchsia_component::client::connect_to_protocol_sync;
8use fuchsia_inspect_derive::{IValue, Inspect, Unit, WithInspect};
9use starnix_core::task::Kernel;
10use starnix_core::vfs::fs_registry::FsRegistry;
11use starnix_logging::{log_error, log_info};
12use starnix_sync::{LockDepGuard, LockDepMutex, NmfsNetworkManagerLock};
13use starnix_uapi::error;
14use starnix_uapi::errors::Errno;
15use starnix_uapi::fs_type::FileSystemTypeFlags;
16use std::collections::HashMap;
17use std::collections::hash_map::Entry;
18use thiserror::Error;
19
20use fidl_fuchsia_net_policy_socketproxy as fnp_socketproxy;
21
22/// Manager for communicating network properties.
23#[derive(Inspect)]
24pub(crate) struct NetworkManager {
25    network_registry: fnp_socketproxy::NetworkRegistrySynchronousProxy,
26    #[inspect(forward)]
27    inner: LockDepMutex<IValue<NetworkManagerInner>, NmfsNetworkManagerLock>,
28}
29
30#[derive(Unit, Default)]
31struct NetworkManagerInner {
32    // Keeps track of networks and their [`NetworkMessage`].
33    #[inspect(skip)]
34    default_id: Option<u32>,
35    #[inspect(skip)]
36    networks: HashMap<u32, Option<NetworkMessage>>,
37
38    default_ids_set: SeenSentData,
39    added_networks: SeenSentData,
40    updated_networks: SeenSentData,
41    removed_networks: SeenSentData,
42}
43
44#[derive(Unit, Default)]
45struct SeenSentData {
46    // The number of event occurrences witnessed
47    // by the NetworkManager.
48    seen: u64,
49    // The number of event occurrences that have been
50    // sent successfully to netcfg.
51    sent: u64,
52}
53
54/// Initialize the connection to the NetworkRegistry protocol.
55pub fn nmfs_init(kernel: &Kernel) -> Result<(), anyhow::Error> {
56    // Register the fuchsia_network_monitor_fs in the FsRegistry.
57    let registry = kernel.expando.get::<FsRegistry>();
58    registry.register(
59        b"fuchsia_network_monitor_fs".into(),
60        FileSystemTypeFlags::empty(),
61        fuchsia_network_monitor_fs,
62    );
63
64    // Register the NetworkManager.
65    let network_registry = connect_to_protocol_sync::<fnp_socketproxy::NetworkRegistryMarker>()?;
66    kernel
67        .expando
68        .get_or_init(|| NetworkManager::new_with_proxy(network_registry, &kernel.inspect_node));
69    Ok(())
70}
71
72// The functions that propagate calls to netcfg prioritize maintaining
73// a correct version of local state and logging an error if the netcfg
74// state is not aligned.
75impl NetworkManager {
76    // Create a NetworkManager with a NetworkRegistry protocol connection and `nmfs` inspect node.
77    pub(crate) fn new_with_proxy(
78        proxy: fnp_socketproxy::NetworkRegistrySynchronousProxy,
79        node: &fuchsia_inspect::Node,
80    ) -> Self {
81        Self { network_registry: proxy, inner: Default::default() }
82            .with_inspect(node, "nmfs")
83            .expect("Failed to attach 'nmfs' node")
84    }
85
86    // Locks and returns the inner state of the manager.
87    fn lock(&self) -> LockDepGuard<'_, IValue<NetworkManagerInner>> {
88        self.inner.lock()
89    }
90
91    pub(crate) fn get_default_network_id(&self) -> Option<u32> {
92        self.lock().default_id
93    }
94
95    pub(crate) fn get_network(&self, network_id: &u32) -> Option<Option<NetworkMessage>> {
96        self.lock().networks.get(network_id).cloned()
97    }
98
99    pub(crate) fn get_default_id_as_bytes(&self) -> BString {
100        let default_id = match self.get_default_network_id() {
101            Some(id) => id.to_string(),
102            None => "".to_string(),
103        };
104        default_id.into_bytes().into()
105    }
106
107    pub(crate) fn get_network_by_id_as_bytes(&self, network_id: u32) -> BString {
108        let network_info = match self.get_network(&network_id) {
109            Some(Some(network)) => {
110                serde_json::to_string(&network).unwrap_or_else(|_| "{}".to_string())
111            }
112            // A network with that was created but hasn't yet
113            // been populated with network properties.
114            Some(None) | None => "{}".to_string(),
115        };
116        network_info.into_bytes().into()
117    }
118
119    // Set the default network identifier. Propagate the change
120    // to netcfg.
121    pub(crate) fn set_default_network_id(&self, network_id: Option<u32>) {
122        {
123            let mut inner_guard = self.lock();
124            let mut inner = inner_guard.as_mut();
125            inner.default_id = network_id;
126            inner.default_ids_set.seen += 1;
127        }
128
129        // Only log when there is an internal proxy error.
130        match self.fidl_set_default_network_id(network_id) {
131            Ok(()) => {
132                log_info!("Successfully set network with id {network_id:?} as default in netcfg",);
133                let mut inner_guard = self.lock();
134                inner_guard.as_mut().default_ids_set.sent += 1;
135            }
136            Err(e) => {
137                log_error!(
138                    "Failed to set network with id {network_id:?} as default in netcfg; {e:?}"
139                );
140            }
141        }
142    }
143
144    // Populate a new element in the Map. This does not
145    // propagate to netcfg.
146    //
147    // An error will be returned if a network with the id
148    // exists in the local state.
149    pub(crate) fn add_empty_network(&self, network_id: u32) -> Result<(), Errno> {
150        let mut inner_guard = self.lock();
151        match inner_guard.as_mut().networks.entry(network_id) {
152            Entry::Occupied(_) => {
153                log_error!(
154                    "Failed to add empty network to HashMap, was present for id: {}",
155                    network_id
156                );
157                return error!(EEXIST);
158            }
159            Entry::Vacant(entry) => entry.insert(None),
160        };
161        Ok(())
162    }
163
164    // Add a new network. Propagate the change to netcfg.
165    //
166    // An error will be returned if a network with the id
167    // exists in the local state.
168    pub(crate) fn add_network(&self, network: NetworkMessage) -> Result<(), Errno> {
169        {
170            let mut inner_guard = self.lock();
171            let mut inner = inner_guard.as_mut();
172            match inner.networks.entry(network.netid) {
173                Entry::Occupied(mut entry) => {
174                    // This is deliberately before any Map manipulation because we
175                    // should not modify the Map state if we return an error.
176                    if let Some(network) = entry.get() {
177                        log_error!(
178                            "Failed to add network with id {} to HashMap, already existed",
179                            network.netid
180                        );
181                        return error!(EEXIST);
182                    }
183                    let _ = entry.insert(Some(network.clone()));
184                }
185                Entry::Vacant(entry) => {
186                    let _ = entry.insert(Some(network.clone()));
187                }
188            }
189            inner.added_networks.seen += 1;
190        }
191
192        // Only log when there is an internal proxy error.
193        match self.fidl_add_network(&fnp_socketproxy::Network::from(&network)) {
194            Ok(()) => {
195                log_info!("Successfully added network with id {} to netcfg", network.netid);
196                let mut inner_guard = self.lock();
197                inner_guard.as_mut().added_networks.sent += 1;
198            }
199            Err(e) => {
200                log_error!("Failed to add network with id {:?} to netcfg; {e:?}", network.netid);
201            }
202        }
203
204        Ok(())
205    }
206
207    // Update an existing network. Propagate the change to netcfg.
208    //
209    // An error will be returned if a network with the id does not
210    // exist in the local state.
211    pub(crate) fn update_network(&self, network: NetworkMessage) -> Result<(), Errno> {
212        {
213            let mut inner_guard = self.lock();
214            let mut inner = inner_guard.as_mut();
215            // Ensure that there is a network already present at that netid
216            // prior to modifying the map.
217            let _old_network = match inner.networks.entry(network.netid) {
218                Entry::Occupied(mut entry) => {
219                    if let None = entry.get() {
220                        return error!(ENOENT);
221                    }
222                    entry.insert(Some(network.clone()))
223                }
224                Entry::Vacant(_) => {
225                    return error!(ENOENT);
226                }
227            };
228            inner.updated_networks.seen += 1;
229        };
230
231        // Only log when there is an internal proxy error.
232        match self.fidl_update_network(&fnp_socketproxy::Network::from(&network)) {
233            Ok(()) => {
234                log_info!("Successfully updated network with id {} in netcfg", network.netid);
235                let mut inner_guard = self.lock();
236                inner_guard.as_mut().updated_networks.sent += 1;
237            }
238            Err(e) => {
239                log_error!("Failed to update network with id {} in netcfg; {e:?}", network.netid);
240            }
241        }
242
243        Ok(())
244    }
245
246    // Remove an existing network. Propagate the change to netcfg.
247    //
248    // An error will be returned if a network with the id does not
249    // exist in the local state.
250    pub(crate) fn remove_network(&self, network_id: u32) -> Result<(), Errno> {
251        // Surface an error if the network is the current default
252        // network or if the network is not found.
253        let default_network_id = self.get_default_network_id();
254        if let Some(id) = default_network_id {
255            if id == network_id {
256                return error!(EPERM);
257            }
258        }
259        {
260            let mut inner_guard = self.lock();
261            let mut inner = inner_guard.as_mut();
262            if let None = inner.networks.remove(&network_id) {
263                return error!(ENOENT);
264            }
265            inner.removed_networks.seen += 1;
266        }
267
268        // Only log when there is an internal proxy error.
269        match self.fidl_remove_network(&network_id) {
270            Ok(()) => {
271                log_info!("Successfully removed network with id {network_id} from netcfg",);
272                let mut inner_guard = self.lock();
273                inner_guard.as_mut().removed_networks.sent += 1;
274            }
275            Err(e) => {
276                log_error!("Failed to remove network with id {network_id} in netcfg; {e:?}");
277            }
278        }
279        Ok(())
280    }
281
282    // Call `set_default` on `NetworkRegistry`.
283    fn fidl_set_default_network_id(
284        &self,
285        network_id: Option<u32>,
286    ) -> Result<(), NetworkManagerError> {
287        let network_id = match network_id {
288            Some(id) => fidl_fuchsia_posix_socket::OptionalUint32::Value(id),
289            None => {
290                fidl_fuchsia_posix_socket::OptionalUint32::Unset(fidl_fuchsia_posix_socket::Empty)
291            }
292        };
293        Ok(self.network_registry.set_default(&network_id, zx::MonotonicInstant::INFINITE)??)
294    }
295
296    // Call `add` on `NetworkRegistry`.
297    fn fidl_add_network(
298        &self,
299        network: &fnp_socketproxy::Network,
300    ) -> Result<(), NetworkManagerError> {
301        Ok(self.network_registry.add(&network, zx::MonotonicInstant::INFINITE)??)
302    }
303
304    // Call `update` on `NetworkRegistry`.
305    fn fidl_update_network(
306        &self,
307        network: &fnp_socketproxy::Network,
308    ) -> Result<(), NetworkManagerError> {
309        Ok(self.network_registry.update(&network, zx::MonotonicInstant::INFINITE)??)
310    }
311
312    // Call `remove` on `NetworkRegistry`.
313    fn fidl_remove_network(&self, network_id: &u32) -> Result<(), NetworkManagerError> {
314        Ok(self.network_registry.remove(*network_id, zx::MonotonicInstant::INFINITE)??)
315    }
316}
317
318// Errors produced when communicating updates to
319// netcfg.
320#[derive(Clone, Debug, Error)]
321pub(crate) enum NetworkManagerError {
322    #[error("Error during netcfg Add: {0:?}")]
323    Add(fnp_socketproxy::NetworkRegistryAddError),
324    #[error("Error calling FIDL on netcfg: {0:?}")]
325    Fidl(#[from] fidl::Error),
326    #[error("Error during netcfg Remove: {0:?}")]
327    Remove(fnp_socketproxy::NetworkRegistryRemoveError),
328    #[error("Error during netcfg SetDefault: {0:?}")]
329    SetDefault(fnp_socketproxy::NetworkRegistrySetDefaultError),
330    #[error("Error during netcfg Update: {0:?}")]
331    Update(fnp_socketproxy::NetworkRegistryUpdateError),
332}
333
334impl From<fnp_socketproxy::NetworkRegistryAddError> for NetworkManagerError {
335    fn from(error: fnp_socketproxy::NetworkRegistryAddError) -> Self {
336        NetworkManagerError::Add(error)
337    }
338}
339
340impl From<fnp_socketproxy::NetworkRegistryRemoveError> for NetworkManagerError {
341    fn from(error: fnp_socketproxy::NetworkRegistryRemoveError) -> Self {
342        NetworkManagerError::Remove(error)
343    }
344}
345
346impl From<fnp_socketproxy::NetworkRegistrySetDefaultError> for NetworkManagerError {
347    fn from(error: fnp_socketproxy::NetworkRegistrySetDefaultError) -> Self {
348        NetworkManagerError::SetDefault(error)
349    }
350}
351
352impl From<fnp_socketproxy::NetworkRegistryUpdateError> for NetworkManagerError {
353    fn from(error: fnp_socketproxy::NetworkRegistryUpdateError) -> Self {
354        NetworkManagerError::Update(error)
355    }
356}
357
358#[cfg(test)]
359mod tests {
360    use super::*;
361
362    use assert_matches::assert_matches;
363    use diagnostics_assertions::assert_data_tree;
364    use futures::StreamExt as _;
365    use starnix_uapi::{EEXIST, ENOENT};
366    use test_case::test_case;
367
368    fn test_network_message_from_id(netid: u32) -> NetworkMessage {
369        NetworkMessage { netid, ..Default::default() }
370    }
371
372    #[::fuchsia::test]
373    async fn test_add_empty_network() {
374        let inspector = fuchsia_inspect::Inspector::default();
375        let manager = &setup_proxy(inspector.root(), vec![]);
376
377        let network_id = 1;
378        assert_matches!(manager.add_empty_network(network_id), Ok(()));
379
380        // Ensure we cannot add an empty network with the same id twice.
381        assert_matches!(
382            manager.add_empty_network(network_id),
383            Err(errno) if errno.code.error_code() == EEXIST
384        );
385        assert_matches!(manager.get_network(&network_id), Some(None));
386        // Empty networks don't get sent to netcfg, so they are
387        // ignored in SeenSentData.
388        assert_data_tree!(inspector, root: {
389            nmfs: contains {
390                added_networks: {
391                    seen: 0u64,
392                    sent: 0u64,
393                },
394            },
395        });
396    }
397
398    // Set the `NetworkRegistryMarker` in the NetworkManager and mock out
399    // the responses to `NetworkRegistryRequest`s with provided results.
400    fn setup_proxy(
401        inspect_node: &fuchsia_inspect::Node,
402        results: Vec<Result<(), NetworkManagerError>>,
403    ) -> NetworkManager {
404        let (proxy, mut stream) = fidl::endpoints::create_sync_proxy_and_stream::<
405            fnp_socketproxy::NetworkRegistryMarker,
406        >();
407        let manager = NetworkManager::new_with_proxy(proxy, inspect_node);
408
409        let mut results = results.into_iter();
410        fuchsia_async::Task::spawn(async move {
411            while let Some(item) = stream.next().await {
412                let result = results
413                    .next()
414                    .expect("there should be an equivalent # of results and requests");
415                match item.expect("receive request") {
416                    fnp_socketproxy::NetworkRegistryRequest::SetDefault {
417                        network_id: _,
418                        responder,
419                    } => {
420                        let res = result.map_err(|e| match e {
421                            NetworkManagerError::SetDefault(err) => err,
422                            _ => unreachable!("should have been SetDefault error variant"),
423                        });
424                        responder.send(res).expect("respond to SetDefault");
425                    }
426                    fnp_socketproxy::NetworkRegistryRequest::Add { network: _, responder } => {
427                        let res = result.map_err(|e| match e {
428                            NetworkManagerError::Add(err) => err,
429                            _ => unreachable!("should have been Add error variant"),
430                        });
431                        responder.send(res).expect("respond to Add");
432                    }
433                    fnp_socketproxy::NetworkRegistryRequest::Update { network: _, responder } => {
434                        let res = result.map_err(|e| match e {
435                            NetworkManagerError::Update(err) => err,
436                            _ => unreachable!("should have been Update error variant"),
437                        });
438                        responder.send(res).expect("respond to Update");
439                    }
440                    fnp_socketproxy::NetworkRegistryRequest::Remove {
441                        network_id: _,
442                        responder,
443                    } => {
444                        let res = result.map_err(|e| match e {
445                            NetworkManagerError::Remove(err) => err,
446                            _ => unreachable!("should have been Remove error variant"),
447                        });
448                        responder.send(res).expect("respond to Remove");
449                    }
450                }
451            }
452        })
453        .detach();
454        manager
455    }
456
457    // Mock of private `manager::SeenSentData` to use for
458    // improved test case readability.
459    struct SeenSentData {
460        seen: u64,
461        sent: u64,
462    }
463
464    #[test_case(vec![Ok(()), Ok(())], SeenSentData { seen: 2, sent: 2 }; "all_success")]
465    #[test_case(
466        vec![
467            Ok(()),
468            Err(NetworkManagerError::SetDefault(fnp_socketproxy::NetworkRegistrySetDefaultError::NotFound)),
469        ],
470        SeenSentData { seen: 2, sent: 1 };
471        "one_error"
472    )]
473    #[test_case(
474        vec![
475            Err(NetworkManagerError::SetDefault(fnp_socketproxy::NetworkRegistrySetDefaultError::NotFound)),
476            Err(NetworkManagerError::SetDefault(fnp_socketproxy::NetworkRegistrySetDefaultError::NotFound)),
477        ],
478        SeenSentData { seen: 2, sent: 0 };
479        "all_errors"
480    )]
481    #[::fuchsia::test(threads = 2)]
482    async fn test_set_default_network_id_with_proxy(
483        results: Vec<Result<(), NetworkManagerError>>,
484        expected_data: SeenSentData,
485    ) {
486        let inspector = fuchsia_inspect::Inspector::default();
487        let manager = &setup_proxy(inspector.root(), results);
488
489        manager.set_default_network_id(Some(1));
490        assert_eq!(manager.get_default_network_id(), Some(1));
491
492        manager.set_default_network_id(Some(2));
493        assert_eq!(manager.get_default_network_id(), Some(2));
494
495        assert_data_tree!(inspector, root: {
496            nmfs: contains {
497                default_ids_set: {
498                    seen: expected_data.seen,
499                    sent: expected_data.sent,
500                },
501                added_networks: {
502                    seen: 0u64,
503                    sent: 0u64,
504                },
505                updated_networks: {
506                    seen: 0u64,
507                    sent: 0u64,
508                },
509                removed_networks: {
510                    seen: 0u64,
511                    sent: 0u64,
512                },
513            },
514        });
515    }
516
517    #[test_case(vec![Ok(()), Ok(())], SeenSentData { seen: 2, sent: 2 }; "all_success")]
518    #[test_case(
519        vec![
520            Ok(()),
521            Err(NetworkManagerError::Add(fnp_socketproxy::NetworkRegistryAddError::DuplicateNetworkId)),
522        ],
523        SeenSentData { seen: 2, sent: 1 };
524        "one_error"
525    )]
526    #[test_case(
527        vec![
528            Err(NetworkManagerError::Add(fnp_socketproxy::NetworkRegistryAddError::MissingNetworkId)),
529            Err(NetworkManagerError::Add(fnp_socketproxy::NetworkRegistryAddError::MissingNetworkInfo)),
530        ],
531        SeenSentData { seen: 2, sent: 0 };
532        "all_errors"
533    )]
534    #[::fuchsia::test(threads = 2)]
535    async fn test_add_network_with_proxy(
536        results: Vec<Result<(), NetworkManagerError>>,
537        expected_data: SeenSentData,
538    ) {
539        let inspector = fuchsia_inspect::Inspector::default();
540        let manager = &setup_proxy(inspector.root(), results);
541
542        // `add_network` returns Ok(()) as long as the network
543        // addition is applied locally, regardless of if the call
544        // is sent to netcfg successfully.
545        let network1 = test_network_message_from_id(1);
546        assert_matches!(manager.add_network(network1.clone()), Ok(()));
547
548        let network2 = test_network_message_from_id(2);
549        assert_matches!(manager.add_network(network2.clone()), Ok(()));
550
551        // Ensure we cannot add a network with the same id twice. This is
552        // observed fully within the NetworkManager and not netcfg.
553        assert_matches!(
554            manager.add_network(network2.clone()),
555            Err(errno) if errno.code.error_code() == EEXIST
556        );
557
558        assert_data_tree!(inspector, root: {
559            nmfs: contains {
560                default_ids_set: {
561                    seen: 0u64,
562                    sent: 0u64,
563                },
564                added_networks: {
565                    seen: expected_data.seen,
566                    sent: expected_data.sent,
567                },
568                updated_networks: {
569                    seen: 0u64,
570                    sent: 0u64,
571                },
572                removed_networks: {
573                    seen: 0u64,
574                    sent: 0u64,
575                },
576            },
577        });
578    }
579
580    #[test_case(vec![Ok(()), Ok(())], SeenSentData { seen: 2, sent: 2 }; "all_success")]
581    #[test_case(
582        vec![
583            Ok(()),
584            Err(NetworkManagerError::Update(fnp_socketproxy::NetworkRegistryUpdateError::MissingNetworkId)),
585        ],
586        SeenSentData { seen: 2, sent: 1 };
587        "one_error"
588    )]
589    #[test_case(
590        vec![
591            Err(NetworkManagerError::Update(fnp_socketproxy::NetworkRegistryUpdateError::NotFound)),
592            Err(NetworkManagerError::Update(fnp_socketproxy::NetworkRegistryUpdateError::MissingNetworkInfo)),
593        ],
594        SeenSentData { seen: 2, sent: 0 };
595        "all_errors"
596    )]
597    #[::fuchsia::test(threads = 2)]
598    async fn test_update_network_with_proxy(
599        results: Vec<Result<(), NetworkManagerError>>,
600        expected_data: SeenSentData,
601    ) {
602        let inspector = fuchsia_inspect::Inspector::default();
603        let manager = &setup_proxy(inspector.root(), results);
604
605        let network_id = 1;
606        let network = test_network_message_from_id(network_id);
607
608        // Ensure we cannot update a network that doesn't exist.
609        assert_matches!(
610            manager.update_network(network.clone()),
611            Err(errno) if errno.code.error_code() == ENOENT
612        );
613
614        // Insert the network manually and then update the network.
615        {
616            let mut inner_guard = manager.lock();
617            let _ = inner_guard.as_mut().networks.insert(network_id, Some(network.clone()));
618        }
619
620        // `update_network` returns Ok(()) as long as the network
621        // update is applied locally, regardless of if the call
622        // is sent to netcfg successfully. Use the same
623        // network information -- another test verifies that the
624        // change is applied successfully.
625        assert_matches!(manager.update_network(network.clone()), Ok(()));
626        assert_matches!(manager.update_network(network), Ok(()));
627
628        assert_data_tree!(inspector, root: {
629            nmfs: contains {
630                default_ids_set: {
631                    seen: 0u64,
632                    sent: 0u64,
633                },
634                added_networks: {
635                    seen: 0u64,
636                    sent: 0u64,
637                },
638                updated_networks: {
639                    seen: expected_data.seen,
640                    sent: expected_data.sent,
641                },
642                removed_networks: {
643                    seen: 0u64,
644                    sent: 0u64,
645                },
646            },
647        });
648    }
649
650    #[test_case(vec![Ok(())], SeenSentData { seen: 1, sent: 1 }; "success")]
651    #[test_case(
652        vec![
653            Err(NetworkManagerError::Remove(fnp_socketproxy::NetworkRegistryRemoveError::NotFound)),
654        ],
655        SeenSentData { seen: 1, sent: 0 };
656        "error"
657    )]
658    #[::fuchsia::test(threads = 2)]
659    async fn test_remove_network_with_proxy(
660        results: Vec<Result<(), NetworkManagerError>>,
661        expected_data: SeenSentData,
662    ) {
663        let inspector = fuchsia_inspect::Inspector::default();
664        let manager = &setup_proxy(inspector.root(), results);
665
666        let network_id = 1;
667        let network = test_network_message_from_id(network_id);
668
669        // Ensure we cannot remove a network that doesn't exist.
670        assert_matches!(
671            manager.remove_network(network_id),
672            Err(errno) if errno.code.error_code() == ENOENT
673        );
674
675        // Insert the network manually and then remove it.
676        {
677            let mut inner_guard = manager.lock();
678            let _ = inner_guard.as_mut().networks.insert(network_id, Some(network.clone()));
679        }
680
681        // `remove_network` returns Ok(()) as long as the network
682        // removal is applied locally, regardless of if the call
683        // is sent to netcfg successfully.
684        assert_matches!(manager.remove_network(network_id), Ok(()));
685
686        assert_data_tree!(inspector, root: {
687            nmfs: contains {
688                default_ids_set: {
689                    seen: 0u64,
690                    sent: 0u64,
691                },
692                added_networks: {
693                    seen: 0u64,
694                    sent: 0u64,
695                },
696                updated_networks: {
697                    seen: 0u64,
698                    sent: 0u64,
699                },
700                removed_networks: {
701                    seen: expected_data.seen,
702                    sent: expected_data.sent,
703                },
704            },
705        });
706    }
707
708    #[::fuchsia::test(threads = 2)]
709    async fn test_multiple_operations_with_proxy() {
710        let inspector = fuchsia_inspect::Inspector::default();
711        let results = vec![
712            // Network added to Manager, but not added to netcfg.
713            Err(NetworkManagerError::Add(
714                fnp_socketproxy::NetworkRegistryAddError::MissingNetworkId,
715            )),
716            // Network added to Manager and to netcfg.
717            Ok(()),
718            // Network set as default in Manager, but not in netcfg.
719            Err(NetworkManagerError::SetDefault(
720                fnp_socketproxy::NetworkRegistrySetDefaultError::NotFound,
721            )),
722            // Network set as default in Manager and in netcfg.
723            Ok(()),
724            // Network updated in Manager, but not in netcfg.
725            Err(NetworkManagerError::Update(fnp_socketproxy::NetworkRegistryUpdateError::NotFound)),
726            // Network updated in Manager and in netcfg.
727            Ok(()),
728            // Unset the default network so the network is eligible to be removed.
729            Ok(()),
730            // Network removed from Manager, but not from netcfg.
731            Err(NetworkManagerError::Remove(fnp_socketproxy::NetworkRegistryRemoveError::NotFound)),
732            // Network removed from Manager and from netcfg.
733            Ok(()),
734        ];
735        let manager = &setup_proxy(inspector.root(), results);
736
737        // Add a network that doesn't get sent to netcfg.
738        let network1 = test_network_message_from_id(1);
739        assert_matches!(manager.add_network(network1.clone()), Ok(()));
740        assert_data_tree!(inspector, root: {
741            nmfs: contains {
742                added_networks: {
743                    seen: 1u64,
744                    sent: 0u64,
745                },
746            },
747        });
748
749        // Add a network that gets sent to netcfg.
750        let network2 = test_network_message_from_id(2);
751        assert_matches!(manager.add_network(network2.clone()), Ok(()));
752        assert_data_tree!(inspector, root: {
753            nmfs: contains {
754                added_networks: {
755                    seen: 2u64,
756                    sent: 1u64,
757                },
758            },
759        });
760
761        // Set the default network that isn't known to netcfg.
762        manager.set_default_network_id(Some(1));
763        assert_eq!(manager.get_default_network_id(), Some(1));
764        assert_data_tree!(inspector, root: {
765            nmfs: contains {
766                default_ids_set: {
767                    seen: 1u64,
768                    sent: 0u64,
769                },
770            },
771        });
772
773        // Set the default network that is known to netcfg.
774        manager.set_default_network_id(Some(2));
775        assert_eq!(manager.get_default_network_id(), Some(2));
776        assert_data_tree!(inspector, root: {
777            nmfs: contains {
778                default_ids_set: {
779                    seen: 2u64,
780                    sent: 1u64,
781                },
782            },
783        });
784
785        // Update a network not known to netcfg.
786        let mut network1_updated = network1.clone();
787        network1_updated.mark = 1;
788        assert_matches!(manager.update_network(network1_updated.clone()), Ok(()));
789        assert_data_tree!(inspector, root: {
790            nmfs: contains {
791                updated_networks: {
792                    seen: 1u64,
793                    sent: 0u64,
794                },
795            },
796        });
797
798        // Update a network that is known to netcfg.
799        let mut network2_updated = network2.clone();
800        network2_updated.mark = 2;
801        assert_matches!(manager.update_network(network2_updated.clone()), Ok(()));
802        assert_data_tree!(inspector, root: {
803            nmfs: contains {
804                updated_networks: {
805                    seen: 2u64,
806                    sent: 1u64,
807                },
808            },
809        });
810
811        // The default network must be unset first to remove this network.
812        manager.set_default_network_id(None);
813        assert_data_tree!(inspector, root: {
814            nmfs: contains {
815                default_ids_set: {
816                    seen: 3u64,
817                    sent: 2u64,
818                },
819            },
820        });
821
822        // Remove a network that doesn't get sent to netcfg.
823        assert_matches!(manager.remove_network(1), Ok(()));
824        assert_data_tree!(inspector, root: {
825            nmfs: contains {
826                removed_networks: {
827                    seen: 1u64,
828                    sent: 0u64,
829                },
830            },
831        });
832
833        // Remove a network that gets sent to netcfg.
834        assert_matches!(manager.remove_network(2), Ok(()));
835        assert_data_tree!(inspector, root: {
836            nmfs: contains {
837                default_ids_set: {
838                    seen: 3u64,
839                    sent: 2u64,
840                },
841                added_networks: {
842                    seen: 2u64,
843                    sent: 1u64,
844                },
845                updated_networks: {
846                    seen: 2u64,
847                    sent: 1u64,
848                },
849                removed_networks: {
850                    seen: 2u64,
851                    sent: 1u64,
852                },
853            },
854        });
855    }
856}