Skip to main content

netcfg/network/
mod.rs

1// Copyright 2025 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::InterfaceId;
6use crate::dns::DNS_PORT;
7use crate::telemetry::{NetworkEventMetadata, TelemetryEvent, TelemetrySender};
8use anyhow::Context as _;
9use async_utils::stream::{Tagged, WithTag as _};
10use dns_server_watcher::DnsServers;
11use fidl::endpoints::Responder as _;
12use futures::StreamExt as _;
13use log::{error, info, warn};
14use policy_properties::NetworkTokenExt as _;
15use std::collections::HashMap;
16use std::collections::hash_map::Entry;
17
18mod token_registry;
19
20use fidl_fuchsia_net as fnet;
21use fidl_fuchsia_net_name as fnet_name;
22use fidl_fuchsia_net_policy_properties as fnp_properties;
23use fidl_fuchsia_net_policy_socketproxy as fnp_socketproxy;
24use fidl_fuchsia_posix_socket as fposix_socket;
25
26// The id for each network, separated by network source.
27//
28// NB: These are separated in the case that the same underlying
29// interface id is used by Fuchsia and a delegated actor.
30#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
31pub enum NetworkId {
32    Fuchsia(InterfaceId),
33    Delegated(InterfaceId),
34}
35
36impl std::fmt::Display for NetworkId {
37    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38        match self {
39            NetworkId::Fuchsia(interface_id) => write!(f, "fuchsia:{interface_id}"),
40            NetworkId::Delegated(interface_id) => write!(f, "delegated:{interface_id}"),
41        }
42    }
43}
44
45impl NetworkId {
46    pub fn get(&self) -> InterfaceId {
47        match self {
48            NetworkId::Fuchsia(interface_id) => *interface_id,
49            NetworkId::Delegated(interface_id) => *interface_id,
50        }
51    }
52
53    pub fn fuchsia<I: Into<InterfaceId>>(id: I) -> Self {
54        NetworkId::Fuchsia(id.into())
55    }
56
57    pub fn delegated<I: Into<InterfaceId>>(id: I) -> Self {
58        NetworkId::Delegated(id.into())
59    }
60
61    pub fn is_fuchsia(&self) -> bool {
62        matches!(self, NetworkId::Fuchsia(_))
63    }
64
65    pub fn is_delegated(&self) -> bool {
66        matches!(self, NetworkId::Delegated(_))
67    }
68}
69
70#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
71pub(crate) struct NetworkTokenContents {
72    network_id: NetworkId,
73    is_default: bool,
74}
75
76#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
77pub struct ConnectionId(usize);
78
79impl ConnectionId {
80    fn increment(&mut self) {
81        self.0 += 1;
82    }
83}
84
85#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
86pub struct UpdateGeneration {
87    /// The current generation for `fuchsia.net.policy.properties.WatchDefault`.
88    /// Incremented each time the default network changes.
89    default_network: usize,
90
91    /// The current generation for `fuchsia.net.policy.properties.WatchProperties`.
92    /// Incremented each time a network property changes.
93    properties: usize,
94}
95
96#[derive(Clone, Debug, Default)]
97pub struct UpdateGenerations(HashMap<ConnectionId, UpdateGeneration>);
98
99impl UpdateGenerations {
100    fn default_network(&self, id: &ConnectionId) -> Option<usize> {
101        self.0.get(id).map(|g| g.default_network)
102    }
103
104    fn set_default_network(&mut self, id: ConnectionId, generation: UpdateGeneration) {
105        self.0.entry(id).or_default().default_network = generation.default_network;
106    }
107
108    fn properties(&self, id: &ConnectionId) -> Option<usize> {
109        self.0.get(id).map(|g| g.properties)
110    }
111
112    fn set_properties(&mut self, id: ConnectionId, generation: UpdateGeneration) {
113        self.0.entry(id).or_default().properties = generation.properties;
114    }
115
116    fn remove(&mut self, id: &ConnectionId) -> Option<UpdateGeneration> {
117        self.0.remove(id)
118    }
119}
120
121trait SetMark {
122    fn set_mark(&mut self, domain: fnet::MarkDomain, value: Option<u32>);
123}
124
125impl SetMark for fnet::Marks {
126    fn set_mark(&mut self, domain: fnet::MarkDomain, value: Option<u32>) {
127        match domain {
128            fnet::MarkDomain::Mark1 => self.mark_1 = value,
129            fnet::MarkDomain::Mark2 => self.mark_2 = value,
130        }
131    }
132}
133
134#[derive(Debug)]
135pub(crate) struct NetworkPropertyResponder {
136    token: fnp_properties::NetworkToken,
137    watched_properties: Vec<fnp_properties::Property>,
138    responder: fnp_properties::NetworksWatchPropertiesResponder,
139}
140
141impl NetworkPropertyResponder {
142    fn respond(
143        self,
144        response: Result<&[fnp_properties::PropertyUpdate], fnp_properties::WatchError>,
145    ) -> Result<(), fidl::Error> {
146        self.responder.send(response)
147    }
148}
149
150#[derive(Debug, PartialEq, Default, Clone)]
151struct NetworkProperties {
152    socket_marks: Option<fnet::Marks>,
153    dns_servers: Vec<fnet_name::DnsServer_>,
154    // TODO(https://fxbug.dev/486892417): Use this field for snapshot metrics.
155    #[allow(dead_code)]
156    connectivity_state: Option<fnp_socketproxy::ConnectivityState>,
157    name: Option<String>,
158    network_type: Option<fnp_socketproxy::NetworkType>,
159}
160
161impl NetworkProperties {
162    fn get_marks(&self) -> Option<&fnet::Marks> {
163        self.socket_marks.as_ref()
164    }
165}
166
167/// The current state of all networks sent to the NetworkRegistry.
168#[derive(Default, Clone)]
169struct RegisteredNetworks {
170    /// The current default network, determined by the priority rules in
171    /// `calculate_active_default`.
172    default_network: Option<NetworkId>,
173    /// The Starnix default network, as determined by Starnix.
174    starnix_default: Option<NetworkId>,
175    networks: HashMap<NetworkId, NetworkProperties>,
176    dns_servers: Vec<fnet_name::DnsServer_>,
177}
178
179impl RegisteredNetworks {
180    // Determine the active default network based on the starnix_default and network registry.
181    // When one or more Fuchsia networks are present, they should be prioritized over Starnix
182    // networks. The 'most prioritized' Fuchsia network is the one with the lowest ID.
183    fn calculate_active_default(&self) -> Option<NetworkId> {
184        // Note: Fuchsia networks are only added to the NetworkRegistry if they meet
185        // certain criteria (ex: have a default route and are online).
186        let first_fuchsia = self.networks.keys().filter(|id| id.is_fuchsia()).cloned().min();
187        if let Some(fd) = first_fuchsia {
188            return Some(fd);
189        }
190
191        // Fallback to starnix_default. If it is unset, no default network is available.
192        if let Some(starnix_default) = self.starnix_default {
193            // Ensure that the network is present in the network registry.
194            assert!(self.networks.contains_key(&starnix_default));
195        }
196        self.starnix_default
197    }
198
199    // Handle updates to the active default network.
200    //
201    // Returns `Some(DefaultChangedEvent)` if the new default network
202    // is different from the old one, otherwise `None`.
203    fn handle_default_network_update(&mut self) -> Option<DefaultChangedEvent> {
204        let next_default = self.calculate_active_default();
205        if next_default != self.default_network {
206            let old_default = self.default_network;
207            self.default_network = next_default;
208            Some(DefaultChangedEvent { previous_default: old_default })
209        } else {
210            None
211        }
212    }
213
214    fn apply(&mut self, update: PropertyUpdate) -> RegistryUpdateResult {
215        match update {
216            PropertyUpdate::LoseDefaultNetwork => {
217                // Handle Starnix unsetting its default network.
218                self.starnix_default = None;
219                RegistryUpdateResult {
220                    event: UpdateApplied::None,
221                    default_changed: self.handle_default_network_update(),
222                }
223            }
224            PropertyUpdate::ChangeNetwork(network_id, network_change) => match network_change {
225                NetworkUpdate::Properties(event) => RegistryUpdateResult {
226                    event: self.handle_changed_network(network_id, event),
227                    default_changed: self.handle_default_network_update(),
228                },
229                NetworkUpdate::Remove => {
230                    if self.starnix_default == Some(network_id) {
231                        error!("Cannot remove the default delegated network. Update ignored.");
232                        RegistryUpdateResult { event: UpdateApplied::None, default_changed: None }
233                    } else if self.networks.remove(&network_id).is_some() {
234                        // Elect fallback default network internally.
235                        RegistryUpdateResult {
236                            event: UpdateApplied::NetworkRemoved(network_id),
237                            default_changed: self.handle_default_network_update(),
238                        }
239                    } else {
240                        error!("Cannot remove a non-existent network. Update ignored.");
241                        RegistryUpdateResult { event: UpdateApplied::None, default_changed: None }
242                    }
243                }
244                NetworkUpdate::MakeDefault => {
245                    match network_id {
246                        // Fuchsia networks are always the default network when present. Netcfg
247                        // does not use this API to set a Fuchsia network as the default.
248                        NetworkId::Fuchsia(_) => {}
249                        NetworkId::Delegated(_) => self.starnix_default = Some(network_id),
250                    }
251                    let default_changed = self.handle_default_network_update();
252                    RegistryUpdateResult { event: UpdateApplied::None, default_changed }
253                }
254            },
255            PropertyUpdate::UpdateDns(dns_servers) => {
256                let event = if self.dns_servers != dns_servers {
257                    self.dns_servers = dns_servers;
258                    UpdateApplied::DnsChanged
259                } else {
260                    UpdateApplied::None
261                };
262                RegistryUpdateResult { event, default_changed: None }
263            }
264        }
265    }
266
267    // Handle the `NetworkPropertiesChange` in a `PropertyUpdate`, determining
268    // whether network properties changed as a result of the update.
269    //
270    // Returns an `UpdateApplied::NetworkChanged` event if this is a valid change.
271    fn handle_changed_network(
272        &mut self,
273        network_id: NetworkId,
274        event: NetworkPropertiesChange,
275    ) -> UpdateApplied {
276        let NetworkPropertiesChange {
277            added,
278            marks: socket_marks,
279            dns_servers: changed_dns_servers,
280            connectivity_state,
281            name,
282            network_type,
283        } = event;
284        let entry = self.networks.entry(network_id);
285        let result = match (added, &entry, network_id, socket_marks) {
286            (true, Entry::Occupied(_), _, _) => Err("add already added network"),
287            (false, Entry::Vacant(_), _, _) => Err("update a non-added network"),
288            (_, _, NetworkId::Fuchsia(_), Some(_)) => Err("have a fuchsia network with marks"),
289            (_, _, NetworkId::Delegated(_), None) => Err("have a delegated network without marks"),
290            (_, entry, NetworkId::Fuchsia(_), None) => {
291                let new_dns = changed_dns_servers.unwrap_or_default();
292                let changed_dns = match entry {
293                    Entry::Occupied(e) => e.get().dns_servers != new_dns,
294                    // When adding a new network, set `changed_dns` to true so responders receive
295                    // an explicit initial state.
296                    Entry::Vacant(_) => true,
297                };
298                Ok((
299                    NetworkProperties { dns_servers: new_dns, ..Default::default() },
300                    added,
301                    changed_dns,
302                ))
303            }
304            (_, entry, NetworkId::Delegated(_), Some(socket_marks)) => {
305                let new_dns = changed_dns_servers.unwrap_or_default();
306                let (changed_marks, changed_dns) = match entry {
307                    Entry::Occupied(e) => {
308                        (e.get().get_marks() != Some(&socket_marks), e.get().dns_servers != new_dns)
309                    }
310                    // When adding a new network, set both `changed_marks` and `changed_dns` to
311                    // true so responders receive an explicit initial state.
312                    Entry::Vacant(_) => (true, true),
313                };
314                Ok((
315                    NetworkProperties {
316                        socket_marks: Some(socket_marks),
317                        dns_servers: new_dns,
318                        ..Default::default()
319                    },
320                    changed_marks,
321                    changed_dns,
322                ))
323            }
324        };
325
326        match result {
327            Ok((mut properties, changed_marks, changed_dns)) => {
328                properties.connectivity_state = connectivity_state;
329                properties.network_type = network_type;
330                properties.name = name.clone();
331                let _ = entry.insert_entry(properties);
332                UpdateApplied::NetworkChanged {
333                    network_id,
334                    added,
335                    changed_marks,
336                    changed_dns,
337                    name,
338                    network_type,
339                }
340            }
341            Err(e) => {
342                error!("Cannot {e}. Update ignored.");
343                UpdateApplied::None
344            }
345        }
346    }
347
348    /// Returns the DNS servers for the default network if it is a Fuchsia network,
349    /// otherwise returns a concatenation of DNS servers from all delegated networks.
350    /// TODO(https://fxbug.dev/428712735): Remove once dns-resolver learns about DNS
351    /// via NetworkProperties.
352    pub fn consolidated_dns_servers(&self) -> Vec<fnet_name::DnsServer_> {
353        if let Some(NetworkId::Fuchsia(if_id)) = self.default_network {
354            self.networks
355                .get(&NetworkId::Fuchsia(if_id))
356                .map(|p| p.dns_servers.clone())
357                .unwrap_or_default()
358        } else {
359            self.networks
360                .iter()
361                .filter(|(id, _)| matches!(id, NetworkId::Delegated(_)))
362                .flat_map(|(_, p)| &p.dns_servers)
363                .cloned()
364                .collect()
365        }
366    }
367
368    fn maybe_respond(
369        &self,
370        network: &NetworkTokenContents,
371        responder: NetworkPropertyResponder,
372    ) -> Option<NetworkPropertyResponder> {
373        let mut updates = Vec::new();
374        updates.add_socket_marks(self, network, &responder);
375        updates.add_dns(self, network, &responder);
376
377        if updates.is_empty() {
378            Some(responder)
379        } else {
380            if let Err(e) = responder.respond(Ok(&updates)) {
381                warn!("Could not send to responder: {e}");
382            }
383            None
384        }
385    }
386}
387
388trait PropertyUpdates {
389    fn add_socket_marks(
390        &mut self,
391        network_registry: &RegisteredNetworks,
392        network: &NetworkTokenContents,
393        responder: &NetworkPropertyResponder,
394    );
395    fn add_dns(
396        &mut self,
397        network_registry: &RegisteredNetworks,
398        network: &NetworkTokenContents,
399        responder: &NetworkPropertyResponder,
400    );
401}
402
403impl PropertyUpdates for Vec<fnp_properties::PropertyUpdate> {
404    fn add_socket_marks(
405        &mut self,
406        network_registry: &RegisteredNetworks,
407        network: &NetworkTokenContents,
408        responder: &NetworkPropertyResponder,
409    ) {
410        if !responder.watched_properties.contains(&fnp_properties::Property::SocketMarks) {
411            return;
412        }
413
414        match network_registry.networks.get(&network.network_id) {
415            Some(network) => {
416                if let Some(socket_marks) = network.get_marks() {
417                    self.push(fnp_properties::PropertyUpdate::SocketMarks(socket_marks.clone()));
418                }
419                return;
420            }
421            None => {
422                error!(
423                    "State is inconsistent. We attempted to add marks for a \
424            network that is not known: {:?}",
425                    network.network_id
426                );
427            }
428        }
429    }
430
431    fn add_dns(
432        &mut self,
433        network_registry: &RegisteredNetworks,
434        network: &NetworkTokenContents,
435        responder: &NetworkPropertyResponder,
436    ) {
437        if !responder.watched_properties.contains(&fnp_properties::Property::DnsConfiguration) {
438            return;
439        }
440
441        let interface_id = network.network_id;
442        self.push(fnp_properties::PropertyUpdate::DnsConfiguration(
443            fnp_properties::DnsConfiguration {
444                servers: Some(
445                    network_registry
446                        .dns_servers
447                        .iter()
448                        .filter(|d| {
449                            match &d.source {
450                                Some(source) => match source {
451                                    fnet_name::DnsServerSource::StaticSource(_) => true,
452                                    // `extract_dns_servers` prefers IPv4 DNS
453                                    // over IPv6 DNS when DNS servers are
454                                    // provided by the SocketProxy.
455                                    fnet_name::DnsServerSource::SocketProxy(
456                                        fnet_name::SocketProxyDnsServerSource {
457                                            source_interface,
458                                            ..
459                                        },
460                                    ) => match (interface_id, source_interface) {
461                                        (_, None) => true,
462                                        (id1, Some(id2)) => {
463                                            Ok(id1)
464                                                == InterfaceId::try_from(*id2)
465                                                    .map(|id| NetworkId::delegated(id))
466                                        }
467                                    },
468                                    fnet_name::DnsServerSource::Dhcp(
469                                        fnet_name::DhcpDnsServerSource { source_interface, .. },
470                                    )
471                                    | fnet_name::DnsServerSource::Ndp(
472                                        fnet_name::NdpDnsServerSource { source_interface, .. },
473                                    )
474                                    | fnet_name::DnsServerSource::Dhcpv6(
475                                        fnet_name::Dhcpv6DnsServerSource {
476                                            source_interface, ..
477                                        },
478                                    ) => match (interface_id, source_interface) {
479                                        (_, None) => true,
480                                        (id1, Some(id2)) => {
481                                            Ok(id1)
482                                                == InterfaceId::try_from(*id2)
483                                                    .map(|id| NetworkId::fuchsia(id))
484                                        }
485                                    },
486
487                                    _ => {
488                                        error!("unhandled DnsServerSource: {source:?}");
489                                        false
490                                    }
491                                },
492
493                                // No source, assume static source, so include it.
494                                None => true,
495                            }
496                        })
497                        .cloned()
498                        .collect::<Vec<_>>(),
499                ),
500                ..Default::default()
501            },
502        ));
503    }
504}
505
506/// An event representing the properties that changed for a network.
507#[derive(Clone, Debug, Default)]
508pub struct NetworkPropertiesChange {
509    /// When true, this is a new network being added. Otherwise, this is an
510    /// update to an existing network.
511    pub added: bool,
512    /// The new marks for the network.
513    pub marks: Option<fnet::Marks>,
514    /// If present, contains the new DNS servers for this network.
515    pub dns_servers: Option<Vec<fnet_name::DnsServer_>>,
516    /// The new connectivity state of the network.
517    pub connectivity_state: Option<fnp_socketproxy::ConnectivityState>,
518    /// The name of the network.
519    pub name: Option<String>,
520    /// The transport type of the network.
521    pub network_type: Option<fnp_socketproxy::NetworkType>,
522}
523
524#[derive(Debug, Clone)]
525pub enum NetworkUpdate {
526    /// Change a network's properties.
527    Properties(NetworkPropertiesChange),
528    Remove,
529    MakeDefault,
530}
531
532#[derive(Debug, PartialEq, Eq, Clone)]
533struct DefaultChangedEvent {
534    previous_default: Option<NetworkId>,
535}
536
537#[derive(Debug, PartialEq, Eq)]
538struct RegistryUpdateResult {
539    event: UpdateApplied,
540    /// Stores whether the default network has changed, and the previous default
541    /// network, if any.
542    default_changed: Option<DefaultChangedEvent>,
543}
544
545#[derive(Debug, PartialEq, Eq, Clone)]
546enum UpdateApplied {
547    /// No update was performed.
548    None,
549
550    /// Whether the DNS servers changed.
551    DnsChanged,
552
553    /// Network was added or updated, contains the NetworkId of the added network.
554    NetworkChanged {
555        network_id: NetworkId,
556        added: bool,
557        changed_marks: bool,
558        changed_dns: bool,
559        name: Option<String>,
560        network_type: Option<fnp_socketproxy::NetworkType>,
561    },
562
563    /// Network was removed, contains the NetworkId of the removed network.
564    NetworkRemoved(NetworkId),
565}
566
567#[derive(Debug, Clone)]
568pub enum PropertyUpdate {
569    LoseDefaultNetwork,
570    ChangeNetwork(NetworkId, NetworkUpdate),
571    UpdateDns(Vec<fnet_name::DnsServer_>),
572}
573
574impl PropertyUpdate {
575    pub fn default_network_lost() -> Self {
576        PropertyUpdate::LoseDefaultNetwork
577    }
578
579    pub fn dns(dns_servers: &DnsServers) -> Self {
580        // TODO(https://fxbug.dev/477980011): Switch to deriving dns servers from
581        // NetworkRegistry updates.
582        PropertyUpdate::UpdateDns(dns_servers.consolidated_dns_servers())
583    }
584}
585
586/// The result of a delegated network update.
587///
588/// Returned to the main event loop to propagate system-wide configuration
589/// changes (such as DNS server updates) and notify active watchers.
590#[derive(Debug, PartialEq)]
591pub struct DelegatedNetworkUpdateResult {
592    /// If present, contains the new consolidated DNS servers known by the
593    /// network registry.
594    pub dns_servers: Option<Vec<fnet_name::DnsServer_>>,
595}
596
597pub enum NetworkRequestStream {
598    Networks(fnp_properties::NetworksRequestStream),
599    NetworkTokenResolver(fnp_properties::NetworkTokenResolverRequestStream),
600    DelegatedNetworks(fnp_socketproxy::NetworkRegistryRequestStream),
601}
602
603impl From<fnp_properties::NetworksRequestStream> for NetworkRequestStream {
604    fn from(s: fnp_properties::NetworksRequestStream) -> Self {
605        Self::Networks(s)
606    }
607}
608impl From<fnp_properties::NetworkTokenResolverRequestStream> for NetworkRequestStream {
609    fn from(s: fnp_properties::NetworkTokenResolverRequestStream) -> Self {
610        Self::NetworkTokenResolver(s)
611    }
612}
613impl From<fnp_socketproxy::NetworkRegistryRequestStream> for NetworkRequestStream {
614    fn from(s: fnp_socketproxy::NetworkRegistryRequestStream) -> Self {
615        Self::DelegatedNetworks(s)
616    }
617}
618
619pub struct NetworkAttributesRequest {
620    pub id: ConnectionId,
621    pub request: Result<fnp_properties::NetworksRequest, fidl::Error>,
622}
623
624pub struct NetworkTokenResolverRequest {
625    pub request: Result<fnp_properties::NetworkTokenResolverRequest, fidl::Error>,
626}
627
628pub struct DelegatedNetworksRequest {
629    pub request: Result<fnp_socketproxy::NetworkRegistryRequest, fidl::Error>,
630}
631
632pub enum NetworkRequest {
633    NetworkAttributes(NetworkAttributesRequest),
634    NetworkTokenResolver(NetworkTokenResolverRequest),
635    DelegatedNetworks(DelegatedNetworksRequest),
636}
637
638enum NetworkRequestStreamInner {
639    Networks(Tagged<ConnectionId, fnp_properties::NetworksRequestStream>),
640    NetworkTokenResolver(fnp_properties::NetworkTokenResolverRequestStream),
641    DelegatedNetworks(fnp_socketproxy::NetworkRegistryRequestStream),
642}
643
644impl futures::Stream for NetworkRequestStreamInner {
645    type Item = NetworkRequest;
646
647    fn poll_next(
648        mut self: std::pin::Pin<&mut Self>,
649        cx: &mut std::task::Context<'_>,
650    ) -> std::task::Poll<Option<Self::Item>> {
651        match *self {
652            NetworkRequestStreamInner::Networks(ref mut stream) => {
653                stream.poll_next_unpin(cx).map(|o| {
654                    o.map(|(id, request)| NetworkAttributesRequest { id, request })
655                        .map(NetworkRequest::NetworkAttributes)
656                })
657            }
658            NetworkRequestStreamInner::NetworkTokenResolver(ref mut stream) => {
659                stream.poll_next_unpin(cx).map(|o| {
660                    o.map(|request| NetworkTokenResolverRequest { request })
661                        .map(NetworkRequest::NetworkTokenResolver)
662                })
663            }
664            NetworkRequestStreamInner::DelegatedNetworks(ref mut stream) => {
665                stream.poll_next_unpin(cx).map(|o| {
666                    o.map(|request| DelegatedNetworksRequest { request })
667                        .map(NetworkRequest::DelegatedNetworks)
668                })
669            }
670        }
671    }
672}
673
674impl futures::Stream for NetpolNetworksService {
675    type Item = NetworkRequest;
676
677    fn poll_next(
678        mut self: std::pin::Pin<&mut Self>,
679        cx: &mut std::task::Context<'_>,
680    ) -> std::task::Poll<Option<Self::Item>> {
681        self.streams.poll_next_unpin(cx)
682    }
683}
684
685impl futures::stream::FusedStream for NetpolNetworksService {
686    fn is_terminated(&self) -> bool {
687        self.streams.is_terminated()
688    }
689}
690
691#[derive(Default)]
692pub struct NetpolNetworksService {
693    // The current generation
694    current_generation: UpdateGeneration,
695    // The last generation sent per connection
696    generations_by_connection: UpdateGenerations,
697    // Default Network Watchers
698    default_network_responders:
699        HashMap<ConnectionId, fnp_properties::NetworksWatchDefaultResponder>,
700    tokens: token_registry::TokenRegistry<NetworkTokenContents>,
701    // NetworkProperty Watchers
702    property_responders: HashMap<ConnectionId, NetworkPropertyResponder>,
703    // The networks known to the system
704    network_registry: RegisteredNetworks,
705    telemetry: Option<TelemetrySender>,
706    next_networks_id: ConnectionId,
707    streams: futures::stream::SelectAll<NetworkRequestStreamInner>,
708}
709
710impl NetpolNetworksService {
711    pub fn set_telemetry(&mut self, telemetry: TelemetrySender) {
712        self.telemetry = Some(telemetry);
713    }
714
715    pub fn add_stream<S: Into<NetworkRequestStream>>(&mut self, s: S) {
716        match s.into() {
717            NetworkRequestStream::Networks(stream) => {
718                self.streams.push(NetworkRequestStreamInner::Networks(
719                    stream.tagged(self.next_networks_id),
720                ));
721                self.next_networks_id.increment();
722            }
723            NetworkRequestStream::NetworkTokenResolver(stream) => {
724                self.streams.push(NetworkRequestStreamInner::NetworkTokenResolver(stream));
725            }
726            NetworkRequestStream::DelegatedNetworks(stream) => {
727                self.streams.push(NetworkRequestStreamInner::DelegatedNetworks(stream));
728            }
729        }
730    }
731
732    pub async fn handle_event(
733        &mut self,
734        event: NetworkRequest,
735    ) -> Result<DelegatedNetworkUpdateResult, anyhow::Error> {
736        match event {
737            NetworkRequest::NetworkAttributes(NetworkAttributesRequest { id, request }) => {
738                self.handle_network_attributes_request(id, request).await?;
739                Ok(DelegatedNetworkUpdateResult { dns_servers: None })
740            }
741            NetworkRequest::NetworkTokenResolver(NetworkTokenResolverRequest { request }) => {
742                self.handle_network_token_resolver_request(request).await?;
743                Ok(DelegatedNetworkUpdateResult { dns_servers: None })
744            }
745            NetworkRequest::DelegatedNetworks(DelegatedNetworksRequest { request }) => {
746                self.handle_delegated_networks_update(request).await
747            }
748        }
749    }
750
751    /// Returns the consolidated DNS servers from the Network Registry.
752    pub fn consolidated_dns_servers(&self) -> Vec<fnet_name::DnsServer_> {
753        self.network_registry.consolidated_dns_servers()
754    }
755
756    pub async fn handle_network_attributes_request(
757        &mut self,
758        id: ConnectionId,
759        req: Result<fnp_properties::NetworksRequest, fidl::Error>,
760    ) -> Result<(), anyhow::Error> {
761        let req = req.context("network attributes request")?;
762        match req {
763            fnp_properties::NetworksRequest::WatchDefault { responder } => {
764                match self.default_network_responders.entry(id) {
765                    std::collections::hash_map::Entry::Occupied(_) => {
766                        warn!(
767                            "Only one call to fuchsia.net.policy.properties/Networks.WatchDefault \
768                             may be active per connection"
769                        );
770                        responder
771                            .control_handle()
772                            .shutdown_with_epitaph(zx::Status::CONNECTION_ABORTED)
773                    }
774                    std::collections::hash_map::Entry::Vacant(vacant_entry) => {
775                        let network_id = if self
776                            .generations_by_connection
777                            .default_network(&id)
778                            .unwrap_or_default()
779                            < self.current_generation.default_network
780                        {
781                            self.network_registry.default_network
782                        } else {
783                            None
784                        };
785                        if let Some(network_id) = network_id {
786                            self.generations_by_connection
787                                .set_default_network(id, self.current_generation);
788                            let token = self
789                                .tokens
790                                .ensure_token(NetworkTokenContents { network_id, is_default: true })
791                                .get()
792                                .duplicate()
793                                .context("could not duplicate token")?;
794                            responder.send(
795                                fnp_properties::NetworksWatchDefaultResponse::Network(token),
796                            )?;
797
798                            if let Some(responder) = self.property_responders.remove(&id) {
799                                let _: Option<_> = self.generations_by_connection.remove(&id);
800                                let _: Result<(), fidl::Error> =
801                                    responder.respond(Err(fnp_properties::WatchError::NetworkGone));
802                            }
803                        } else {
804                            let _: &mut _ = vacant_entry.insert(responder);
805                        }
806                    }
807                }
808            }
809            fnp_properties::NetworksRequest::WatchProperties {
810                payload: fnp_properties::NetworksWatchPropertiesRequest { network, properties, .. },
811                responder,
812            } => match (network, properties) {
813                (None, _) | (_, None) => {
814                    responder.send(Err(fnp_properties::WatchError::MissingRequiredArgument))?
815                }
816                (Some(network), Some(properties)) => {
817                    if properties.is_empty() {
818                        responder.send(Err(fnp_properties::WatchError::NoProperties))?;
819                    } else {
820                        match self.property_responders.entry(id) {
821                            std::collections::hash_map::Entry::Occupied(_) => {
822                                warn!(
823                                    "Only one call to \
824                                    fuchsia.net.policy.properties/Networks.WatchProperties may be \
825                                    active per connection"
826                                );
827                                responder
828                                    .control_handle()
829                                    .shutdown_with_epitaph(zx::Status::CONNECTION_ABORTED)
830                            }
831                            std::collections::hash_map::Entry::Vacant(vacant_entry) => {
832                                match self.tokens.get_contents(&network) {
833                                    Err(e) => {
834                                        warn!("Unknown network token. ({network:?}: {e})");
835                                        responder.send(Err(
836                                            fnp_properties::WatchError::InvalidNetworkToken,
837                                        ))?;
838                                    }
839                                    Ok(network_contents) => {
840                                        let responder = NetworkPropertyResponder {
841                                            token: network,
842                                            watched_properties: properties,
843                                            responder,
844                                        };
845                                        if self
846                                            .generations_by_connection
847                                            .properties(&id)
848                                            .unwrap_or_default()
849                                            < self.current_generation.properties
850                                        {
851                                            self.generations_by_connection
852                                                .set_properties(id, self.current_generation);
853                                            if let Some(responder) = self
854                                                .network_registry
855                                                .maybe_respond(&network_contents, responder)
856                                            {
857                                                let _: &mut NetworkPropertyResponder =
858                                                    vacant_entry.insert(responder);
859                                            }
860                                        } else {
861                                            let _: &mut NetworkPropertyResponder =
862                                                vacant_entry.insert(responder);
863                                        }
864                                    }
865                                }
866                            }
867                        }
868                    }
869                }
870            },
871            _ => {
872                warn!("Received unexpected request {req:?}");
873            }
874        }
875
876        Ok(())
877    }
878
879    /// Handles delegated network updates coming from Starnix.
880    ///
881    /// Resolves network events requested through the `NetworkRegistry` interface, applies the
882    /// corresponding properties changes, and yields the computed DNS configuration targets for
883    /// output updates.
884    ///
885    /// TODO(https://fxbug.dev/428712735): Stop returning DnsServer list once
886    /// dns-resolver learns about DNS via NetworkProperties.
887    pub async fn handle_delegated_networks_update(
888        &mut self,
889        update: Result<fnp_socketproxy::NetworkRegistryRequest, fidl::Error>,
890    ) -> Result<DelegatedNetworkUpdateResult, anyhow::Error> {
891        use fnp_socketproxy::{
892            NetworkInfo, NetworkRegistryAddError, NetworkRegistryRemoveError,
893            NetworkRegistryRequest, NetworkRegistrySetDefaultError, NetworkRegistryUpdateError,
894        };
895
896        let action_result = match update {
897            Err(e) => {
898                error!(
899                    "Encountered error watching for delegated network \
900                                    updates: {e:?}"
901                );
902                return Err(anyhow::anyhow!(e));
903            }
904            Ok(NetworkRegistryRequest::SetDefault { network_id, responder }) => {
905                let update_result = match network_id {
906                    fposix_socket::OptionalUint32::Value(interface_id) => {
907                        match InterfaceId::try_from(interface_id) {
908                            Ok(id) => {
909                                let delegated_id = NetworkId::delegated(id);
910                                self.update(PropertyUpdate::ChangeNetwork(
911                                    delegated_id,
912                                    NetworkUpdate::MakeDefault,
913                                ))
914                                .await;
915                                Ok(())
916                            }
917                            Err(_) => Err(NetworkRegistrySetDefaultError::NotFound),
918                        }
919                    }
920                    fposix_socket::OptionalUint32::Unset(_) => {
921                        self.update(PropertyUpdate::default_network_lost()).await;
922                        Ok(())
923                    }
924                };
925
926                self.respond_to_delegated_network_update(
927                    update_result,
928                    |reply| responder.send(reply),
929                    "failed to send SetDefault result",
930                )
931            }
932            Ok(NetworkRegistryRequest::Add { network, responder }) => {
933                let extracted_properties = (|| {
934                    let raw_network_id =
935                        network.network_id.ok_or(NetworkRegistryAddError::MissingNetworkId)?;
936                    let network_id = InterfaceId::try_from(raw_network_id)
937                        .map(|id| NetworkId::delegated(id))
938                        .map_err(|_| NetworkRegistryAddError::MissingNetworkId)?;
939                    let NetworkInfo::Starnix(info) =
940                        network.info.ok_or(NetworkRegistryAddError::MissingNetworkInfo)?
941                    else {
942                        return Err(NetworkRegistryAddError::MissingNetworkInfo);
943                    };
944
945                    let mut marks = fnet::Marks::default();
946                    marks.set_mark(fnet::MARK_DOMAIN_SO_MARK, info.mark);
947
948                    let dns_servers =
949                        Self::extract_dns_servers(&network.dns_servers, raw_network_id.into());
950
951                    Ok((network_id, marks, dns_servers))
952                })();
953
954                let update_result = match extracted_properties {
955                    Ok((network_id, marks, dns_servers)) => {
956                        self.update(PropertyUpdate::ChangeNetwork(
957                            network_id,
958                            NetworkUpdate::Properties(NetworkPropertiesChange {
959                                added: true,
960                                marks: Some(marks),
961                                dns_servers: Some(dns_servers.clone()),
962                                connectivity_state: network.connectivity,
963                                name: network.name,
964                                network_type: network.network_type,
965                            }),
966                        ))
967                        .await;
968                        Ok(())
969                    }
970                    Err(e) => Err(e),
971                };
972
973                self.respond_to_delegated_network_update(
974                    update_result,
975                    |reply| responder.send(reply),
976                    "failed to send Add result",
977                )
978            }
979            Ok(NetworkRegistryRequest::Update { network, responder }) => {
980                let extracted_properties = (|| {
981                    let raw_network_id =
982                        network.network_id.ok_or(NetworkRegistryUpdateError::MissingNetworkId)?;
983                    let network_id = InterfaceId::try_from(raw_network_id)
984                        .map(|id| NetworkId::delegated(id))
985                        .map_err(|_| NetworkRegistryUpdateError::MissingNetworkId)?;
986                    let NetworkInfo::Starnix(info) =
987                        network.info.ok_or(NetworkRegistryUpdateError::MissingNetworkInfo)?
988                    else {
989                        return Err(NetworkRegistryUpdateError::MissingNetworkInfo);
990                    };
991
992                    let mut marks = fnet::Marks::default();
993                    marks.set_mark(fnet::MARK_DOMAIN_SO_MARK, info.mark);
994
995                    let dns_servers =
996                        Self::extract_dns_servers(&network.dns_servers, raw_network_id.into());
997
998                    Ok((network_id, marks, dns_servers))
999                })();
1000
1001                let update_result = match extracted_properties {
1002                    Ok((network_id, marks, dns_servers)) => {
1003                        self.update(PropertyUpdate::ChangeNetwork(
1004                            network_id,
1005                            NetworkUpdate::Properties(NetworkPropertiesChange {
1006                                added: false,
1007                                marks: Some(marks),
1008                                dns_servers: Some(dns_servers.clone()),
1009                                connectivity_state: network.connectivity,
1010                                name: network.name,
1011                                network_type: network.network_type,
1012                            }),
1013                        ))
1014                        .await;
1015                        Ok(())
1016                    }
1017                    Err(e) => Err(e),
1018                };
1019
1020                self.respond_to_delegated_network_update(
1021                    update_result,
1022                    |reply| responder.send(reply),
1023                    "failed to send Update result",
1024                )
1025            }
1026            Ok(NetworkRegistryRequest::Remove { network_id, responder }) => {
1027                let update_result = match InterfaceId::try_from(network_id) {
1028                    Ok(id) => {
1029                        let delegated_id = NetworkId::delegated(id);
1030                        self.update(PropertyUpdate::ChangeNetwork(
1031                            delegated_id,
1032                            NetworkUpdate::Remove,
1033                        ))
1034                        .await;
1035                        Ok(())
1036                    }
1037                    Err(_) => Err(NetworkRegistryRemoveError::NotFound),
1038                };
1039
1040                self.respond_to_delegated_network_update(
1041                    update_result,
1042                    |reply| responder.send(reply),
1043                    "failed to send Remove result",
1044                )
1045            }
1046        };
1047
1048        Ok(action_result)
1049    }
1050
1051    // Resolves the operation result, sends the success or failure status to
1052    // the FIDL responder, and returns the updated network registry settings.
1053    fn respond_to_delegated_network_update<E, F>(
1054        &self,
1055        operation_result: Result<(), E>,
1056        send_response: F,
1057        context_message: &'static str,
1058    ) -> DelegatedNetworkUpdateResult
1059    where
1060        F: FnOnce(Result<(), E>) -> Result<(), fidl::Error>,
1061    {
1062        // Return consolidated DNS servers if the operation was successful.
1063        let dns_servers = operation_result
1064            .as_ref()
1065            .ok()
1066            .map(|()| self.network_registry.consolidated_dns_servers());
1067
1068        // Send success or failure status to the FIDL responder.
1069        if let Err(e) = send_response(operation_result) {
1070            if !e.is_closed() {
1071                error!(
1072                    "Failed to send delegated network update result \
1073                for {context_message}: {e}"
1074                );
1075            }
1076        }
1077
1078        DelegatedNetworkUpdateResult { dns_servers }
1079    }
1080
1081    // Converts `NetworkDnsServers` to `Vec<DnsServer_>` for a given network.
1082    //
1083    // Note: We prioritize IPv4 servers over IPv6 servers. This is impactful
1084    // when sending DNS servers through NetworkProperties or to dns-resolver.
1085    fn extract_dns_servers(
1086        dns_servers: &Option<fnp_socketproxy::NetworkDnsServers>,
1087        network_id: u64,
1088    ) -> Vec<fnet_name::DnsServer_> {
1089        let make_server = |address| fnet_name::DnsServer_ {
1090            address: Some(address),
1091            source: Some(fnet_name::DnsServerSource::SocketProxy(
1092                fnet_name::SocketProxyDnsServerSource {
1093                    source_interface: Some(network_id),
1094                    ..Default::default()
1095                },
1096            )),
1097            ..Default::default()
1098        };
1099
1100        dns_servers
1101            .as_ref()
1102            .map(|dns| {
1103                dns.v4
1104                    .as_ref()
1105                    .into_iter()
1106                    .flatten()
1107                    .map(|&address| {
1108                        make_server(fnet::SocketAddress::Ipv4(fnet::Ipv4SocketAddress {
1109                            address,
1110                            port: DNS_PORT,
1111                        }))
1112                    })
1113                    .chain(dns.v6.as_ref().into_iter().flatten().map(|&address| {
1114                        make_server(fnet::SocketAddress::Ipv6(fnet::Ipv6SocketAddress {
1115                            address,
1116                            port: DNS_PORT,
1117                            zone_index: 0,
1118                        }))
1119                    }))
1120                    .collect()
1121            })
1122            .unwrap_or_default()
1123    }
1124
1125    pub(crate) async fn handle_network_token_resolver_request(
1126        &mut self,
1127        request: Result<fnp_properties::NetworkTokenResolverRequest, fidl::Error>,
1128    ) -> Result<(), anyhow::Error> {
1129        use fnp_properties::NetworkTokenResolverResolveTokenError as ResolveTokenError;
1130
1131        let request = request.context("while handling NetworkTokenResolver request")?;
1132        match request {
1133            fnp_properties::NetworkTokenResolverRequest::ResolveToken { token, responder } => {
1134                let maybe_contents = self.tokens.get_contents(&token).copied();
1135                match maybe_contents {
1136                    Err(e) => {
1137                        warn!("Unknown network token. ({token:?}: {e})");
1138                        responder.send(Err(ResolveTokenError::InvalidNetworkToken))?;
1139                    }
1140                    Ok(contents) => {
1141                        if contents.is_default {
1142                            // This is a default network token, we need to grab
1143                            // the non-default variant.
1144                            let query = NetworkTokenContents { is_default: false, ..contents };
1145                            if let Some(tok) = self.tokens.get_token(&query) {
1146                                responder.send(tok.duplicate().map_err(|e| {
1147                                    warn!("Encountered issue duplicating generated token. {e}");
1148                                    ResolveTokenError::InvalidNetworkToken
1149                                }))?;
1150                            } else {
1151                                warn!("Requested canonical version of unregistered network.");
1152                                responder.send(Err(ResolveTokenError::InvalidNetworkToken))?;
1153                            }
1154                        } else {
1155                            responder.send(Ok(token))?;
1156                        }
1157                    }
1158                }
1159            }
1160            fidl_fuchsia_net_policy_properties::NetworkTokenResolverRequest::_UnknownMethod {
1161                ordinal,
1162                control_handle,
1163                method_type,
1164                ..
1165            } => warn!(
1166                "Encountered unknown method call on NetworkTokenResolver: {ordinal} \
1167                {control_handle:?} {method_type:?}"
1168            ),
1169        }
1170
1171        Ok(())
1172    }
1173
1174    async fn changed_default_network(
1175        &mut self,
1176        previous_default_network: Option<NetworkId>,
1177        responders: &mut HashMap<ConnectionId, NetworkPropertyResponder>,
1178    ) {
1179        let mut r = HashMap::new();
1180        std::mem::swap(&mut r, responders);
1181        r = r
1182            .into_iter()
1183            .filter_map(|(id, responder)| {
1184                match self.tokens.get_contents(&responder.token) {
1185                    Ok(contents) => {
1186                        // We only want to remove when watching a default token.
1187                        if contents.is_default {
1188                            let _: Option<_> = self.generations_by_connection.remove(&id);
1189                            let _: Result<(), fidl::Error> =
1190                                responder.respond(Err(fnp_properties::WatchError::NetworkGone));
1191                            return None;
1192                        }
1193                    }
1194                    Err(zx::Status::NOT_FOUND) => {
1195                        warn!("Token provided to get_contents is not valid.");
1196                    }
1197                    Err(e) => {
1198                        warn!("Encountered unknown issue while getting contents: {e}");
1199                    }
1200                }
1201                Some((id, responder))
1202            })
1203            .collect::<HashMap<_, _>>();
1204        std::mem::swap(&mut r, responders);
1205        self.tokens.drop_if(|&c| {
1206            c.is_default && previous_default_network.is_some_and(|i| i == c.network_id)
1207        });
1208    }
1209
1210    pub(crate) async fn remove_network(&mut self, network_id: NetworkId) {
1211        info!("Removing interface {network_id}. Reporting NETWORK_GONE to all clients.");
1212        let mut responders = HashMap::new();
1213        std::mem::swap(&mut self.property_responders, &mut responders);
1214        for (id, responder) in responders {
1215            let network = match self.tokens.get_contents(&responder.token) {
1216                Ok(network) => network,
1217                Err(e) => {
1218                    warn!("Could not fetch network data for responder: {e}");
1219                    continue;
1220                }
1221            };
1222            if network.network_id == network_id {
1223                // Report that this interface was removed
1224                if let Err(e) = responder.respond(Err(fnp_properties::WatchError::NetworkGone)) {
1225                    warn!("Could not send to responder: {e}");
1226                }
1227            } else {
1228                if self.property_responders.insert(id, responder).is_some() {
1229                    error!("Re-inserted in an existing responder slot. This should be impossible.");
1230                }
1231            }
1232        }
1233    }
1234
1235    pub async fn update(&mut self, update: PropertyUpdate) {
1236        self.current_generation.properties += 1;
1237        let RegistryUpdateResult { event, default_changed } = self.network_registry.apply(update);
1238
1239        if let UpdateApplied::None = event {
1240            if default_changed.is_none() {
1241                // Return early if there were absolutely no changes and the default stayed the same.
1242                return;
1243            }
1244        }
1245
1246        let mut property_responders = HashMap::new();
1247        std::mem::swap(&mut self.property_responders, &mut property_responders);
1248
1249        // Clean up or register tokens based on whether the network was added or removed.
1250        match event {
1251            UpdateApplied::NetworkChanged { network_id, added: true, .. } => {
1252                let _ = self
1253                    .tokens
1254                    .ensure_token(NetworkTokenContents { network_id, is_default: false });
1255            }
1256            UpdateApplied::NetworkRemoved(network_id) => {
1257                self.tokens.drop_if(|c| !c.is_default && c.network_id == network_id);
1258            }
1259            UpdateApplied::NetworkChanged { added: false, .. }
1260            | UpdateApplied::DnsChanged
1261            | UpdateApplied::None => {}
1262        }
1263
1264        // Notify watchers of default network changes if one occurred.
1265        if let Some(DefaultChangedEvent { previous_default }) = default_changed {
1266            self.notify_default_network_changed(previous_default, &mut property_responders).await;
1267            return;
1268        }
1269
1270        if let UpdateApplied::NetworkChanged { network_id, .. } = event {
1271            if let Some(telemetry) = &self.telemetry {
1272                if let Some(props) = self.network_registry.networks.get(&network_id) {
1273                    telemetry.send(TelemetryEvent::NetworkChanged(NetworkEventMetadata {
1274                        id: network_id.get().get(),
1275                        name: props.name.clone(),
1276                        transport: props
1277                            .network_type
1278                            .unwrap_or(fnp_socketproxy::NetworkType::Unknown),
1279                        is_fuchsia_provisioned: network_id.is_fuchsia(),
1280                        connectivity_state: props.connectivity_state,
1281                    }));
1282                }
1283            }
1284        }
1285
1286        for (id, responder) in property_responders {
1287            let mut updates = Vec::new();
1288            let network = match self.tokens.get_contents(&responder.token) {
1289                Ok(network) => network,
1290                Err(e) => {
1291                    warn!("Could not fetch network data for responder: {e}");
1292                    continue;
1293                }
1294            };
1295
1296            if let UpdateApplied::NetworkChanged {
1297                network_id, changed_marks, changed_dns, ..
1298            } = event
1299            {
1300                if network.network_id == network_id {
1301                    if changed_marks {
1302                        updates.add_socket_marks(&self.network_registry, &network, &responder);
1303                    }
1304                    if changed_dns {
1305                        updates.add_dns(&self.network_registry, &network, &responder);
1306                    }
1307                }
1308            }
1309            if let UpdateApplied::DnsChanged = event {
1310                updates.add_dns(&self.network_registry, &network, &responder);
1311            }
1312
1313            self.generations_by_connection.set_properties(id, self.current_generation);
1314            if updates.is_empty() {
1315                if self.property_responders.insert(id, responder).is_some() {
1316                    warn!("Re-inserted in an existing responder slot. This should be impossible.");
1317                }
1318            } else {
1319                if let Err(e) = responder.respond(Ok(&updates)) {
1320                    warn!("Could not send to responder: {e}");
1321                }
1322            }
1323        }
1324    }
1325
1326    async fn notify_default_network_changed(
1327        &mut self,
1328        old_default: Option<NetworkId>,
1329        property_responders: &mut HashMap<ConnectionId, NetworkPropertyResponder>,
1330    ) {
1331        self.changed_default_network(old_default, property_responders).await;
1332        match self.network_registry.default_network {
1333            Some(default_network) => {
1334                if let Some(telemetry) = &self.telemetry {
1335                    if let Some(props) = self.network_registry.networks.get(&default_network) {
1336                        telemetry.send(TelemetryEvent::DefaultNetworkChanged(
1337                            NetworkEventMetadata {
1338                                id: default_network.get().get(),
1339                                name: props.name.clone(),
1340                                transport: props
1341                                    .network_type
1342                                    .unwrap_or(fnp_socketproxy::NetworkType::Unknown),
1343                                is_fuchsia_provisioned: default_network.is_fuchsia(),
1344                                connectivity_state: props.connectivity_state,
1345                            },
1346                        ));
1347                    } else {
1348                        warn!("Could not fetch network data for default network.");
1349                    }
1350                }
1351                self.current_generation.default_network += 1;
1352                let mut responders = HashMap::new();
1353                std::mem::swap(&mut self.default_network_responders, &mut responders);
1354                for (id, responder) in responders {
1355                    self.generations_by_connection.set_default_network(id, self.current_generation);
1356                    match self
1357                        .tokens
1358                        .ensure_token(NetworkTokenContents {
1359                            network_id: default_network,
1360                            is_default: true,
1361                        })
1362                        .get()
1363                        .duplicate()
1364                    {
1365                        Ok(token) => {
1366                            if let Err(e) = responder
1367                                .send(fnp_properties::NetworksWatchDefaultResponse::Network(token))
1368                            {
1369                                warn!("Could not send to responder: {e}");
1370                            }
1371                        }
1372                        Err(e) => warn!("Could not duplicate token: {e}"),
1373                    };
1374                }
1375            }
1376            None => {
1377                if let Some(telemetry) = &self.telemetry {
1378                    telemetry.send(TelemetryEvent::DefaultNetworkLost);
1379                }
1380                // The default network has been lost.
1381                self.current_generation.default_network += 1;
1382                let mut responders = HashMap::new();
1383                std::mem::swap(&mut self.default_network_responders, &mut responders);
1384                for (id, responder) in responders {
1385                    self.generations_by_connection.set_default_network(id, self.current_generation);
1386                    if let Err(e) = responder.send(
1387                        fnp_properties::NetworksWatchDefaultResponse::NoDefaultNetwork(
1388                            fnp_properties::Empty,
1389                        ),
1390                    ) {
1391                        warn!("Could not send to responder: {e}");
1392                    }
1393                }
1394            }
1395        }
1396    }
1397}
1398
1399pub struct ConnectionTagged<Stream: futures::Stream + Unpin> {
1400    next_id: ConnectionId,
1401    streams: futures::stream::SelectAll<Tagged<ConnectionId, Stream>>,
1402}
1403
1404impl<Stream: futures::Stream + Unpin> Default for ConnectionTagged<Stream> {
1405    fn default() -> Self {
1406        Self { next_id: Default::default(), streams: Default::default() }
1407    }
1408}
1409
1410impl<Stream: futures::Stream + Unpin> ConnectionTagged<Stream> {
1411    pub fn push(&mut self, stream: Stream) {
1412        self.streams.push(stream.tagged(self.next_id));
1413        self.next_id.0 += 1;
1414    }
1415}
1416
1417impl<Stream: futures::Stream + Unpin> futures::Stream for ConnectionTagged<Stream> {
1418    type Item = (ConnectionId, <Stream as futures::Stream>::Item);
1419
1420    fn poll_next(
1421        mut self: std::pin::Pin<&mut Self>,
1422        cx: &mut std::task::Context<'_>,
1423    ) -> std::task::Poll<Option<Self::Item>> {
1424        std::pin::Pin::new(&mut self.streams).poll_next(cx)
1425    }
1426}
1427
1428impl<Stream: futures::Stream + Unpin> futures::stream::FusedStream for ConnectionTagged<Stream> {
1429    fn is_terminated(&self) -> bool {
1430        self.streams.is_terminated()
1431    }
1432}
1433
1434#[cfg(test)]
1435mod tests {
1436    use super::*;
1437    use assert_matches::assert_matches;
1438    use std::num::NonZeroU64;
1439    use test_case::test_case;
1440
1441    const ID_1: InterfaceId = InterfaceId(NonZeroU64::new(1).unwrap());
1442    const ID_2: InterfaceId = InterfaceId(NonZeroU64::new(2).unwrap());
1443    const NAME_1: &str = "testif1";
1444    const NAME_2: &str = "testif2";
1445
1446    const FUCHSIA_ID_1: NetworkId = NetworkId::Fuchsia(ID_1);
1447    const FUCHSIA_ID_2: NetworkId = NetworkId::Fuchsia(ID_2);
1448    const DELEGATED_ID_1: NetworkId = NetworkId::Delegated(ID_1);
1449    const DELEGATED_ID_2: NetworkId = NetworkId::Delegated(ID_2);
1450
1451    #[derive(Clone, Copy)]
1452    struct TestNetwork {
1453        id: NetworkId,
1454        name: &'static str,
1455        network_type: fnp_socketproxy::NetworkType,
1456    }
1457
1458    #[derive(Default)]
1459    struct AppliedChanges {
1460        added: bool,
1461        changed_marks: bool,
1462        changed_dns: bool,
1463    }
1464
1465    impl TestNetwork {
1466        fn added(&self, added: bool) -> NetworkPropertiesChange {
1467            NetworkPropertiesChange {
1468                added,
1469                name: Some(self.name.to_string()),
1470                network_type: Some(self.network_type),
1471                ..Default::default()
1472            }
1473        }
1474
1475        fn change_with(
1476            &self,
1477            added: bool,
1478            marks: Option<fnet::Marks>,
1479            dns_servers: Option<Vec<fnet_name::DnsServer_>>,
1480            connectivity_state: Option<fnp_socketproxy::ConnectivityState>,
1481        ) -> NetworkPropertiesChange {
1482            NetworkPropertiesChange {
1483                added,
1484                marks,
1485                dns_servers,
1486                connectivity_state,
1487                name: Some(self.name.to_string()),
1488                network_type: Some(self.network_type),
1489            }
1490        }
1491
1492        fn applied(&self, changes: AppliedChanges) -> UpdateApplied {
1493            let AppliedChanges { added, changed_marks, changed_dns } = changes;
1494            UpdateApplied::NetworkChanged {
1495                network_id: self.id,
1496                added,
1497                changed_marks,
1498                changed_dns,
1499                name: Some(self.name.to_string()),
1500                network_type: Some(self.network_type),
1501            }
1502        }
1503    }
1504
1505    const DELEGATED_NET_1: TestNetwork = TestNetwork {
1506        id: DELEGATED_ID_1,
1507        name: NAME_1,
1508        network_type: fnp_socketproxy::NetworkType::Ethernet,
1509    };
1510    const FUCHSIA_NET_1: TestNetwork = TestNetwork {
1511        id: FUCHSIA_ID_1,
1512        name: NAME_1,
1513        network_type: fnp_socketproxy::NetworkType::Ethernet,
1514    };
1515    const FUCHSIA_NET_2: TestNetwork = TestNetwork {
1516        id: FUCHSIA_ID_2,
1517        name: NAME_2,
1518        network_type: fnp_socketproxy::NetworkType::Wifi,
1519    };
1520
1521    fn test_marks() -> fnet::Marks {
1522        fnet::Marks { mark_1: Some(123), ..Default::default() }
1523    }
1524
1525    fn test_marks_updated() -> fnet::Marks {
1526        fnet::Marks { mark_1: Some(456), ..Default::default() }
1527    }
1528
1529    fn test_dns_1() -> Vec<fnet_name::DnsServer_> {
1530        vec![fnet_name::DnsServer_ {
1531            address: Some(net_declare::fidl_socket_addr!("192.0.2.1:53")),
1532            ..Default::default()
1533        }]
1534    }
1535
1536    fn test_dns_2() -> Vec<fnet_name::DnsServer_> {
1537        vec![fnet_name::DnsServer_ {
1538            address: Some(net_declare::fidl_socket_addr!("192.0.2.2:53")),
1539            ..Default::default()
1540        }]
1541    }
1542
1543    fn delegated_properties() -> NetworkProperties {
1544        NetworkProperties { socket_marks: Some(test_marks()), ..Default::default() }
1545    }
1546
1547    fn fuchsia_properties() -> NetworkProperties {
1548        NetworkProperties::default()
1549    }
1550
1551    fn added_properties(name: &str) -> NetworkPropertiesChange {
1552        NetworkPropertiesChange {
1553            added: true,
1554            marks: None,
1555            dns_servers: None,
1556            connectivity_state: None,
1557            name: Some(name.to_string()),
1558            network_type: Some(fnp_socketproxy::NetworkType::Ethernet),
1559        }
1560    }
1561
1562    impl NetpolNetworksService {
1563        pub(crate) fn default_network(&self) -> Option<NetworkId> {
1564            self.network_registry.default_network
1565        }
1566
1567        pub(crate) fn has_network(&self, id: NetworkId) -> bool {
1568            self.network_registry.networks.contains_key(&id)
1569        }
1570
1571        pub(crate) fn has_token(&self, network_id: NetworkId, is_default: bool) -> bool {
1572            self.tokens.get_token(&NetworkTokenContents { network_id, is_default }).is_some()
1573        }
1574
1575        pub(crate) fn ensure_token_for_test(&mut self, network_id: NetworkId, is_default: bool) {
1576            let _token: crate::network::token_registry::TokenEntry<'_, _> =
1577                self.tokens.ensure_token(NetworkTokenContents { network_id, is_default });
1578        }
1579    }
1580
1581    #[test_case(
1582        DELEGATED_NET_1,
1583        Some(test_marks()),
1584        Some(test_marks_updated()); "delegated network"
1585    )]
1586    #[test_case(FUCHSIA_NET_2, None, None; "fuchsia network")]
1587    fn test_handle_changed_network(
1588        net: TestNetwork,
1589        initial_marks: Option<fnet::Marks>,
1590        updated_marks: Option<fnet::Marks>,
1591    ) {
1592        let mut networks = RegisteredNetworks::default();
1593        let dns1 = test_dns_1();
1594        let dns2 = test_dns_2();
1595
1596        // Adding network with DNS servers should have changed_marks=true and changed_dns=true.
1597        let event = net.change_with(
1598            true,
1599            initial_marks.clone(),
1600            Some(dns1.clone()),
1601            Some(fnp_socketproxy::ConnectivityState::FullConnectivity),
1602        );
1603        assert_eq!(
1604            networks.handle_changed_network(net.id, event),
1605            net.applied(AppliedChanges { added: true, changed_marks: true, changed_dns: true }),
1606        );
1607        assert_eq!(
1608            networks.networks.get(&net.id).expect("network should be present"),
1609            &NetworkProperties {
1610                socket_marks: initial_marks.clone(),
1611                dns_servers: dns1,
1612                connectivity_state: Some(fnp_socketproxy::ConnectivityState::FullConnectivity),
1613                name: Some(net.name.to_string()),
1614                network_type: Some(net.network_type),
1615            }
1616        );
1617
1618        // Updating with same marks and different DNS should have changed_marks=false and
1619        // changed_dns=true.
1620        let event = net.change_with(
1621            false,
1622            initial_marks.clone(),
1623            Some(dns2.clone()),
1624            Some(fnp_socketproxy::ConnectivityState::NoConnectivity),
1625        );
1626        assert_eq!(
1627            networks.handle_changed_network(net.id, event),
1628            net.applied(AppliedChanges { added: false, changed_marks: false, changed_dns: true }),
1629        );
1630        assert_eq!(
1631            networks.networks.get(&net.id).expect("network should be present"),
1632            &NetworkProperties {
1633                socket_marks: initial_marks.clone(),
1634                dns_servers: dns2.clone(),
1635                connectivity_state: Some(fnp_socketproxy::ConnectivityState::NoConnectivity),
1636                name: Some(net.name.to_string()),
1637                network_type: Some(net.network_type),
1638            }
1639        );
1640
1641        // Updating marks (if delegated) with same DNS should have changed_dns=false.
1642        let marks_changed = initial_marks != updated_marks;
1643        let event = net.change_with(
1644            false,
1645            updated_marks.clone(),
1646            Some(dns2.clone()),
1647            Some(fnp_socketproxy::ConnectivityState::NoConnectivity),
1648        );
1649        assert_eq!(
1650            networks.handle_changed_network(net.id, event),
1651            net.applied(AppliedChanges {
1652                added: false,
1653                changed_marks: marks_changed,
1654                changed_dns: false,
1655            }),
1656        );
1657        assert_eq!(
1658            networks.networks.get(&net.id).expect("network should be present"),
1659            &NetworkProperties {
1660                socket_marks: updated_marks,
1661                dns_servers: dns2.clone(),
1662                connectivity_state: Some(fnp_socketproxy::ConnectivityState::NoConnectivity),
1663                name: Some(net.name.to_string()),
1664                network_type: Some(net.network_type),
1665            }
1666        );
1667    }
1668
1669    #[test]
1670    fn test_handle_changed_network_validation() {
1671        let mut networks = RegisteredNetworks::default();
1672        let marks = test_marks();
1673        let net = DELEGATED_NET_1;
1674
1675        // Update a non-added network
1676        let event = NetworkPropertiesChange { marks: Some(marks.clone()), ..net.added(false) };
1677        assert_eq!(networks.handle_changed_network(net.id, event), UpdateApplied::None);
1678
1679        // Add the network
1680        let event = NetworkPropertiesChange { marks: Some(marks.clone()), ..net.added(true) };
1681        assert_eq!(
1682            networks.handle_changed_network(net.id, event),
1683            net.applied(AppliedChanges { added: true, changed_marks: true, changed_dns: true }),
1684        );
1685
1686        // Add already added network
1687        let event = NetworkPropertiesChange { marks: Some(marks.clone()), ..net.added(true) };
1688        assert_eq!(networks.handle_changed_network(net.id, event), UpdateApplied::None);
1689
1690        // Fuchsia network with marks
1691        let fuchsia_net = FUCHSIA_NET_1;
1692        let event =
1693            NetworkPropertiesChange { marks: Some(marks.clone()), ..fuchsia_net.added(true) };
1694        assert_eq!(networks.handle_changed_network(fuchsia_net.id, event), UpdateApplied::None);
1695
1696        // Delegated network without marks
1697        let event = net.added(true);
1698        assert_eq!(networks.handle_changed_network(net.id, event), UpdateApplied::None);
1699    }
1700
1701    // Unit tests the election algorithm directly by manipulating internal state.
1702    // Verifies prioritization and intermediate fallback election logic.
1703    #[test]
1704    fn fallback_election_and_prioritization() {
1705        let mut networks = RegisteredNetworks::default();
1706
1707        // Initial State: Empty, no default network.
1708        assert_eq!(networks.calculate_active_default(), None);
1709
1710        // Add a delegated network and set as the Starnix default.
1711        let _ = networks.networks.insert(DELEGATED_ID_1, delegated_properties());
1712        networks.starnix_default = Some(DELEGATED_ID_1);
1713        assert_eq!(
1714            networks.handle_default_network_update(),
1715            Some(DefaultChangedEvent { previous_default: None })
1716        );
1717        assert_eq!(networks.default_network, Some(DELEGATED_ID_1));
1718
1719        // Replace the delegated network with another delegated network.
1720        // The new network should take over.
1721        let _ = networks.networks.insert(DELEGATED_ID_2, delegated_properties());
1722        networks.starnix_default = Some(DELEGATED_ID_2);
1723        assert_eq!(
1724            networks.handle_default_network_update(),
1725            Some(DefaultChangedEvent { previous_default: Some(DELEGATED_ID_1) })
1726        );
1727        assert_eq!(networks.default_network, Some(DELEGATED_ID_2));
1728
1729        // Add a Fuchsia network. This Fuchsia network should take over because of
1730        // Fuchsia network priority.
1731        let _ = networks.networks.insert(FUCHSIA_ID_2, fuchsia_properties());
1732        assert_eq!(
1733            networks.handle_default_network_update(),
1734            Some(DefaultChangedEvent { previous_default: Some(DELEGATED_ID_2) })
1735        );
1736        assert_eq!(networks.default_network, Some(FUCHSIA_ID_2));
1737
1738        // Add a Fuchsia network with a smaller ID. The smaller ID Fuchsia network
1739        // should take over.
1740        let _ = networks.networks.insert(FUCHSIA_ID_1, fuchsia_properties());
1741        assert_eq!(
1742            networks.handle_default_network_update(),
1743            Some(DefaultChangedEvent { previous_default: Some(FUCHSIA_ID_2) })
1744        );
1745        assert_eq!(networks.default_network, Some(FUCHSIA_ID_1));
1746
1747        // Remove FUCHSIA_ID_1. The next Fuchsia network should take over.
1748        let _ = networks.networks.remove(&FUCHSIA_ID_1);
1749        assert_eq!(
1750            networks.handle_default_network_update(),
1751            Some(DefaultChangedEvent { previous_default: Some(FUCHSIA_ID_1) })
1752        );
1753        assert_eq!(networks.default_network, Some(FUCHSIA_ID_2));
1754
1755        // Remove FUCHSIA_ID_2. There are no more Fuchsia networks, so the default
1756        // should fallback to the delegated network.
1757        let _ = networks.networks.remove(&FUCHSIA_ID_2);
1758        assert_eq!(
1759            networks.handle_default_network_update(),
1760            Some(DefaultChangedEvent { previous_default: Some(FUCHSIA_ID_2) })
1761        );
1762        assert_eq!(networks.default_network, Some(DELEGATED_ID_2));
1763
1764        // Unset the default delegated network prior to removal.
1765        assert_eq!(
1766            networks.apply(PropertyUpdate::LoseDefaultNetwork),
1767            RegistryUpdateResult {
1768                event: UpdateApplied::None,
1769                default_changed: Some(DefaultChangedEvent {
1770                    previous_default: Some(DELEGATED_ID_2)
1771                })
1772            }
1773        );
1774        assert_eq!(networks.default_network, None);
1775
1776        // Remove the delegated network.
1777        assert_eq!(
1778            networks.apply(PropertyUpdate::ChangeNetwork(DELEGATED_ID_1, NetworkUpdate::Remove)),
1779            RegistryUpdateResult {
1780                event: UpdateApplied::NetworkRemoved(DELEGATED_ID_1),
1781                default_changed: None
1782            }
1783        );
1784    }
1785
1786    // Tests the integration of `RegisteredNetworks::apply` updates, verifying
1787    // fallback priority from Fuchsia to Delegated networks and ensuring that
1788    // active default delegated networks cannot be removed.
1789    #[test]
1790    fn test_remove_fuchsia_network_fallback() {
1791        let mut networks = RegisteredNetworks::default();
1792        let marks = test_marks();
1793        let fuchsia_added = NetworkPropertiesChange { added: true, ..Default::default() };
1794
1795        // Add a Fuchsia network. This should become the default network.
1796        let result = networks.apply(PropertyUpdate::ChangeNetwork(
1797            FUCHSIA_ID_1,
1798            NetworkUpdate::Properties(fuchsia_added.clone()),
1799        ));
1800        assert_matches!(
1801            result.event,
1802            UpdateApplied::NetworkChanged { network_id: id, added: true, .. }
1803            if id == FUCHSIA_ID_1
1804        );
1805        assert_eq!(result.default_changed, Some(DefaultChangedEvent { previous_default: None }));
1806
1807        // Add a second Fuchsia network. This should not change the default network.
1808        let result = networks.apply(PropertyUpdate::ChangeNetwork(
1809            FUCHSIA_ID_2,
1810            NetworkUpdate::Properties(fuchsia_added),
1811        ));
1812        assert_matches!(
1813            result.event,
1814            UpdateApplied::NetworkChanged { network_id: id, added: true, .. }
1815            if id == FUCHSIA_ID_2
1816        );
1817        assert_eq!(result.default_changed, None);
1818
1819        // Add a delegated network. This should not change the default network.
1820        let result = networks.apply(PropertyUpdate::ChangeNetwork(
1821            DELEGATED_ID_1,
1822            NetworkUpdate::Properties(NetworkPropertiesChange {
1823                added: true,
1824                marks: Some(marks),
1825                ..Default::default()
1826            }),
1827        ));
1828        assert_matches!(
1829            result.event,
1830            UpdateApplied::NetworkChanged { network_id: id, added: true, .. }
1831            if id == DELEGATED_ID_1
1832        );
1833        assert_eq!(result.default_changed, None);
1834
1835        // Make the delegated network default (ignored because a Fuchsia
1836        // network is present).
1837        let result = networks
1838            .apply(PropertyUpdate::ChangeNetwork(DELEGATED_ID_1, NetworkUpdate::MakeDefault));
1839        assert_eq!(
1840            result,
1841            RegistryUpdateResult { event: UpdateApplied::None, default_changed: None }
1842        );
1843        assert_eq!(networks.default_network, Some(FUCHSIA_ID_1));
1844
1845        // Remove the first Fuchsia network (fallback to the second Fuchsia
1846        // network).
1847        let result =
1848            networks.apply(PropertyUpdate::ChangeNetwork(FUCHSIA_ID_1, NetworkUpdate::Remove));
1849        assert_eq!(result.event, UpdateApplied::NetworkRemoved(FUCHSIA_ID_1));
1850        assert_eq!(
1851            result.default_changed,
1852            Some(DefaultChangedEvent { previous_default: Some(FUCHSIA_ID_1) })
1853        );
1854        assert_eq!(networks.default_network, Some(FUCHSIA_ID_2));
1855
1856        // Remove the second Fuchsia network (fallback to the
1857        // delegated network since it is default).
1858        let result =
1859            networks.apply(PropertyUpdate::ChangeNetwork(FUCHSIA_ID_2, NetworkUpdate::Remove));
1860        assert_eq!(result.event, UpdateApplied::NetworkRemoved(FUCHSIA_ID_2));
1861        assert_eq!(
1862            result.default_changed,
1863            Some(DefaultChangedEvent { previous_default: Some(FUCHSIA_ID_2) })
1864        );
1865        assert_eq!(networks.default_network, Some(DELEGATED_ID_1));
1866
1867        // Remove the delegated network (rejected because it is default).
1868        let result =
1869            networks.apply(PropertyUpdate::ChangeNetwork(DELEGATED_ID_1, NetworkUpdate::Remove));
1870        assert_eq!(
1871            result,
1872            RegistryUpdateResult { event: UpdateApplied::None, default_changed: None }
1873        );
1874        assert!(networks.networks.contains_key(&DELEGATED_ID_1));
1875        assert_eq!(networks.default_network, Some(DELEGATED_ID_1));
1876    }
1877
1878    #[test]
1879    fn remove_non_default_fuchsia_preserves_default() {
1880        let mut networks = RegisteredNetworks::default();
1881
1882        // Add both networks.
1883        let _ = networks.networks.insert(FUCHSIA_ID_1, NetworkProperties::default());
1884        let _ = networks.networks.insert(FUCHSIA_ID_2, NetworkProperties::default());
1885
1886        // On election, FUCHSIA_ID_1 (the smaller ID) is elected as default.
1887        assert_eq!(
1888            networks.handle_default_network_update(),
1889            Some(DefaultChangedEvent { previous_default: None })
1890        );
1891        assert_eq!(networks.default_network, Some(FUCHSIA_ID_1));
1892
1893        // Remove the non-default network.
1894        assert_eq!(
1895            networks.apply(PropertyUpdate::ChangeNetwork(FUCHSIA_ID_2, NetworkUpdate::Remove)),
1896            RegistryUpdateResult {
1897                event: UpdateApplied::NetworkRemoved(FUCHSIA_ID_2),
1898                default_changed: None
1899            }
1900        );
1901
1902        // Verify that FUCHSIA_ID_1 is still the active default.
1903        assert_eq!(networks.default_network, Some(FUCHSIA_ID_1));
1904    }
1905
1906    #[fuchsia::test]
1907    async fn remove_default_network_cleans_up_tokens() {
1908        let mut service = NetpolNetworksService::default();
1909
1910        // Add two Fuchsia networks via ChangeNetwork updates.
1911        service
1912            .update(PropertyUpdate::ChangeNetwork(
1913                FUCHSIA_ID_1,
1914                NetworkUpdate::Properties(added_properties(NAME_1)),
1915            ))
1916            .await;
1917
1918        service
1919            .update(PropertyUpdate::ChangeNetwork(
1920                FUCHSIA_ID_2,
1921                NetworkUpdate::Properties(added_properties(NAME_2)),
1922            ))
1923            .await;
1924
1925        // On election, FUCHSIA_ID_1 (the smaller ID) is elected as default.
1926        assert_eq!(service.default_network(), Some(FUCHSIA_ID_1));
1927
1928        // Ensure non-default tokens exist for both networks.
1929        assert!(service.has_token(FUCHSIA_ID_1, false /* is_default */));
1930        assert!(service.has_token(FUCHSIA_ID_2, false /* is_default */));
1931
1932        // Manually create default token for FUCHSIA_ID_1 to simulate a client WatchDefault call.
1933        service.ensure_token_for_test(FUCHSIA_ID_1, true /* is_default */);
1934        assert!(service.has_token(FUCHSIA_ID_1, true /* is_default */));
1935
1936        // Remove FUCHSIA_ID_1 (the default network). This should trigger fallback to FUCHSIA_ID_2
1937        // and clean up FUCHSIA_ID_1's tokens.
1938        service.update(PropertyUpdate::ChangeNetwork(FUCHSIA_ID_1, NetworkUpdate::Remove)).await;
1939
1940        // Verify fallback happened.
1941        assert_eq!(service.default_network(), Some(FUCHSIA_ID_2));
1942
1943        // Verify FUCHSIA_ID_1 tokens are gone.
1944        assert!(!service.has_token(FUCHSIA_ID_1, false /* is_default */));
1945        assert!(!service.has_token(FUCHSIA_ID_1, true /* is_default */));
1946
1947        // Verify FUCHSIA_ID_2 tokens still exist.
1948        assert!(service.has_token(FUCHSIA_ID_2, false /* is_default */));
1949    }
1950}