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