1use 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::{ControlHandle as _, Responder as _};
12use log::{error, info, warn};
13use policy_properties::NetworkTokenExt as _;
14use std::collections::HashMap;
15use std::collections::hash_map::Entry;
16
17mod token_registry;
18
19use fidl_fuchsia_net as fnet;
20use fidl_fuchsia_net_name as fnet_name;
21use fidl_fuchsia_net_policy_properties as fnp_properties;
22use fidl_fuchsia_net_policy_socketproxy as fnp_socketproxy;
23use fidl_fuchsia_posix_socket as fposix_socket;
24
25#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
30pub enum NetworkId {
31 Fuchsia(InterfaceId),
32 Delegated(InterfaceId),
33}
34
35impl std::fmt::Display for NetworkId {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 match self {
38 NetworkId::Fuchsia(interface_id) => write!(f, "fuchsia:{interface_id}"),
39 NetworkId::Delegated(interface_id) => write!(f, "delegated:{interface_id}"),
40 }
41 }
42}
43
44impl NetworkId {
45 pub fn get(&self) -> InterfaceId {
46 match self {
47 NetworkId::Fuchsia(interface_id) => *interface_id,
48 NetworkId::Delegated(interface_id) => *interface_id,
49 }
50 }
51
52 pub fn fuchsia<I: Into<InterfaceId>>(id: I) -> Self {
53 NetworkId::Fuchsia(id.into())
54 }
55
56 pub fn delegated<I: Into<InterfaceId>>(id: I) -> Self {
57 NetworkId::Delegated(id.into())
58 }
59
60 pub fn is_fuchsia(&self) -> bool {
61 matches!(self, NetworkId::Fuchsia(_))
62 }
63
64 pub fn is_delegated(&self) -> bool {
65 matches!(self, NetworkId::Delegated(_))
66 }
67}
68
69#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
70pub(crate) struct NetworkTokenContents {
71 network_id: NetworkId,
72 is_default: bool,
73}
74
75#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
76pub struct ConnectionId(usize);
77
78#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
79pub struct UpdateGeneration {
80 default_network: usize,
83
84 properties: usize,
87}
88
89#[derive(Clone, Debug, Default)]
90pub struct UpdateGenerations(HashMap<ConnectionId, UpdateGeneration>);
91
92impl UpdateGenerations {
93 fn default_network(&self, id: &ConnectionId) -> Option<usize> {
94 self.0.get(id).map(|g| g.default_network)
95 }
96
97 fn set_default_network(&mut self, id: ConnectionId, generation: UpdateGeneration) {
98 self.0.entry(id).or_default().default_network = generation.default_network;
99 }
100
101 fn properties(&self, id: &ConnectionId) -> Option<usize> {
102 self.0.get(id).map(|g| g.properties)
103 }
104
105 fn set_properties(&mut self, id: ConnectionId, generation: UpdateGeneration) {
106 self.0.entry(id).or_default().properties = generation.properties;
107 }
108
109 fn remove(&mut self, id: &ConnectionId) -> Option<UpdateGeneration> {
110 self.0.remove(id)
111 }
112}
113
114trait SetMark {
115 fn set_mark(&mut self, domain: fnet::MarkDomain, value: Option<u32>);
116}
117
118impl SetMark for fnet::Marks {
119 fn set_mark(&mut self, domain: fnet::MarkDomain, value: Option<u32>) {
120 match domain {
121 fnet::MarkDomain::Mark1 => self.mark_1 = value,
122 fnet::MarkDomain::Mark2 => self.mark_2 = value,
123 }
124 }
125}
126
127#[derive(Debug)]
128pub(crate) struct NetworkPropertyResponder {
129 token: fnp_properties::NetworkToken,
130 watched_properties: Vec<fnp_properties::Property>,
131 responder: fnp_properties::NetworksWatchPropertiesResponder,
132}
133
134impl NetworkPropertyResponder {
135 fn respond(
136 self,
137 response: Result<&[fnp_properties::PropertyUpdate], fnp_properties::WatchError>,
138 ) -> Result<(), fidl::Error> {
139 self.responder.send(response)
140 }
141}
142
143#[derive(Default, Clone)]
144struct NetworkProperties {
145 socket_marks: Option<fnet::Marks>,
146 dns_servers: Vec<fnet_name::DnsServer_>,
147 #[allow(dead_code)]
149 connectivity_state: Option<fnp_socketproxy::ConnectivityState>,
150 name: Option<String>,
151 network_type: Option<fnp_socketproxy::NetworkType>,
152}
153
154impl NetworkProperties {
155 fn get_marks(&self) -> Option<&fnet::Marks> {
156 self.socket_marks.as_ref()
157 }
158}
159
160#[derive(Default, Clone)]
162struct RegisteredNetworks {
163 default_network: Option<NetworkId>,
166 starnix_default: Option<NetworkId>,
168 networks: HashMap<NetworkId, NetworkProperties>,
169 dns_servers: Vec<fnet_name::DnsServer_>,
170}
171
172impl RegisteredNetworks {
173 fn calculate_active_default(&self) -> Option<NetworkId> {
177 let first_fuchsia = self.networks.keys().filter(|id| id.is_fuchsia()).cloned().min();
180 if let Some(fd) = first_fuchsia {
181 return Some(fd);
182 }
183
184 if let Some(starnix_default) = self.starnix_default {
186 assert!(self.networks.contains_key(&starnix_default));
188 }
189 self.starnix_default
190 }
191
192 fn handle_default_network_update(&mut self) -> Option<DefaultChangedEvent> {
197 let next_default = self.calculate_active_default();
198 if next_default != self.default_network {
199 let old_default = self.default_network;
200 self.default_network = next_default;
201 Some(DefaultChangedEvent { previous_default: old_default })
202 } else {
203 None
204 }
205 }
206
207 fn apply(&mut self, update: PropertyUpdate) -> RegistryUpdateResult {
208 match update {
209 PropertyUpdate::LoseDefaultNetwork => {
210 self.starnix_default = None;
212 RegistryUpdateResult {
213 event: UpdateApplied::None,
214 default_changed: self.handle_default_network_update(),
215 }
216 }
217 PropertyUpdate::ChangeNetwork(network_id, network_change) => match network_change {
218 NetworkUpdate::Properties(event) => RegistryUpdateResult {
219 event: self.handle_changed_network(network_id, event),
220 default_changed: self.handle_default_network_update(),
221 },
222 NetworkUpdate::Remove => {
223 if self.starnix_default == Some(network_id) {
224 error!("Cannot remove the default delegated network. Update ignored.");
225 RegistryUpdateResult { event: UpdateApplied::None, default_changed: None }
226 } else if self.networks.remove(&network_id).is_some() {
227 RegistryUpdateResult {
229 event: UpdateApplied::NetworkRemoved(network_id),
230 default_changed: self.handle_default_network_update(),
231 }
232 } else {
233 error!("Cannot remove a non-existent network. Update ignored.");
234 RegistryUpdateResult { event: UpdateApplied::None, default_changed: None }
235 }
236 }
237 NetworkUpdate::MakeDefault => {
238 match network_id {
239 NetworkId::Fuchsia(_) => {}
242 NetworkId::Delegated(_) => self.starnix_default = Some(network_id),
243 }
244 let default_changed = self.handle_default_network_update();
245 RegistryUpdateResult { event: UpdateApplied::None, default_changed }
246 }
247 },
248 PropertyUpdate::UpdateDns(dns_servers) => {
249 let event = if self.dns_servers != dns_servers {
250 self.dns_servers = dns_servers;
251 UpdateApplied::DnsChanged
252 } else {
253 UpdateApplied::None
254 };
255 RegistryUpdateResult { event, default_changed: None }
256 }
257 }
258 }
259
260 fn handle_changed_network(
265 &mut self,
266 network_id: NetworkId,
267 event: NetworkPropertiesChange,
268 ) -> UpdateApplied {
269 let NetworkPropertiesChange {
270 added,
271 marks: socket_marks,
272 dns_servers: changed_dns_servers,
273 connectivity_state,
274 name,
275 network_type,
276 } = event;
277 let entry = self.networks.entry(network_id);
278 let result = match (added, &entry, network_id, socket_marks) {
279 (true, Entry::Occupied(_), _, _) => Err("add already added network"),
280 (false, Entry::Vacant(_), _, _) => Err("update a non-added network"),
281 (_, _, NetworkId::Fuchsia(_), Some(_)) => Err("have a fuchsia network with marks"),
282 (_, _, NetworkId::Delegated(_), None) => Err("have a delegated network without marks"),
283 (_, _, NetworkId::Fuchsia(_), None) => Ok((
284 NetworkProperties {
285 dns_servers: changed_dns_servers.unwrap_or_default(),
286 ..Default::default()
287 },
288 added,
289 )),
290 (_, entry, NetworkId::Delegated(_), Some(socket_marks)) => {
291 let changed = if let Entry::Occupied(e) = entry {
292 e.get().get_marks() != Some(&socket_marks)
293 } else {
294 true
295 };
296 Ok((
297 NetworkProperties {
298 socket_marks: Some(socket_marks),
299 dns_servers: changed_dns_servers.unwrap_or_default(),
300 ..Default::default()
301 },
302 changed,
303 ))
304 }
305 };
306
307 match result {
308 Ok((mut properties, changed_marks)) => {
309 properties.connectivity_state = connectivity_state;
310 properties.network_type = network_type;
311 properties.name = name.clone();
312 let _ = entry.insert_entry(properties);
313 UpdateApplied::NetworkChanged {
314 network_id,
315 added,
316 changed_marks,
317 name,
318 network_type,
319 }
320 }
321 Err(e) => {
322 error!("Cannot {e}. Update ignored.");
323 UpdateApplied::None
324 }
325 }
326 }
327
328 pub fn consolidated_dns_servers(&self) -> Vec<fnet_name::DnsServer_> {
333 if let Some(NetworkId::Fuchsia(if_id)) = self.default_network {
334 self.networks
335 .get(&NetworkId::Fuchsia(if_id))
336 .map(|p| p.dns_servers.clone())
337 .unwrap_or_default()
338 } else {
339 self.networks
340 .iter()
341 .filter(|(id, _)| matches!(id, NetworkId::Delegated(_)))
342 .flat_map(|(_, p)| &p.dns_servers)
343 .cloned()
344 .collect()
345 }
346 }
347
348 fn maybe_respond(
349 &self,
350 network: &NetworkTokenContents,
351 responder: NetworkPropertyResponder,
352 ) -> Option<NetworkPropertyResponder> {
353 let mut updates = Vec::new();
354 updates.add_socket_marks(self, network, &responder);
355 updates.add_dns(self, network, &responder);
356
357 if updates.is_empty() {
358 Some(responder)
359 } else {
360 if let Err(e) = responder.respond(Ok(&updates)) {
361 warn!("Could not send to responder: {e}");
362 }
363 None
364 }
365 }
366}
367
368trait PropertyUpdates {
369 fn add_socket_marks(
370 &mut self,
371 network_registry: &RegisteredNetworks,
372 network: &NetworkTokenContents,
373 responder: &NetworkPropertyResponder,
374 );
375 fn add_dns(
376 &mut self,
377 network_registry: &RegisteredNetworks,
378 network: &NetworkTokenContents,
379 responder: &NetworkPropertyResponder,
380 );
381}
382
383impl PropertyUpdates for Vec<fnp_properties::PropertyUpdate> {
384 fn add_socket_marks(
385 &mut self,
386 network_registry: &RegisteredNetworks,
387 network: &NetworkTokenContents,
388 responder: &NetworkPropertyResponder,
389 ) {
390 if !responder.watched_properties.contains(&fnp_properties::Property::SocketMarks) {
391 return;
392 }
393
394 match network_registry.networks.get(&network.network_id) {
395 Some(network) => {
396 if let Some(socket_marks) = network.get_marks() {
397 self.push(fnp_properties::PropertyUpdate::SocketMarks(socket_marks.clone()));
398 }
399 return;
400 }
401 None => {
402 error!(
403 "State is inconsistent. We attempted to add marks for a \
404 network that is not known: {:?}",
405 network.network_id
406 );
407 }
408 }
409 }
410
411 fn add_dns(
412 &mut self,
413 network_registry: &RegisteredNetworks,
414 network: &NetworkTokenContents,
415 responder: &NetworkPropertyResponder,
416 ) {
417 if !responder.watched_properties.contains(&fnp_properties::Property::DnsConfiguration) {
418 return;
419 }
420
421 let interface_id = network.network_id;
422 self.push(fnp_properties::PropertyUpdate::DnsConfiguration(
423 fnp_properties::DnsConfiguration {
424 servers: Some(
425 network_registry
426 .dns_servers
427 .iter()
428 .filter(|d| {
429 match &d.source {
430 Some(source) => match source {
431 fnet_name::DnsServerSource::StaticSource(_) => true,
432 fnet_name::DnsServerSource::SocketProxy(
436 fnet_name::SocketProxyDnsServerSource {
437 source_interface,
438 ..
439 },
440 ) => match (interface_id, source_interface) {
441 (_, None) => true,
442 (id1, Some(id2)) => {
443 Ok(id1)
444 == InterfaceId::try_from(*id2)
445 .map(|id| NetworkId::delegated(id))
446 }
447 },
448 fnet_name::DnsServerSource::Dhcp(
449 fnet_name::DhcpDnsServerSource { source_interface, .. },
450 )
451 | fnet_name::DnsServerSource::Ndp(
452 fnet_name::NdpDnsServerSource { source_interface, .. },
453 )
454 | fnet_name::DnsServerSource::Dhcpv6(
455 fnet_name::Dhcpv6DnsServerSource {
456 source_interface, ..
457 },
458 ) => match (interface_id, source_interface) {
459 (_, None) => true,
460 (id1, Some(id2)) => {
461 Ok(id1)
462 == InterfaceId::try_from(*id2)
463 .map(|id| NetworkId::fuchsia(id))
464 }
465 },
466
467 _ => {
468 error!("unhandled DnsServerSource: {source:?}");
469 false
470 }
471 },
472
473 None => true,
475 }
476 })
477 .cloned()
478 .collect::<Vec<_>>(),
479 ),
480 ..Default::default()
481 },
482 ));
483 }
484}
485
486#[derive(Clone, Debug, Default)]
488pub struct NetworkPropertiesChange {
489 pub added: bool,
492 pub marks: Option<fnet::Marks>,
494 pub dns_servers: Option<Vec<fnet_name::DnsServer_>>,
496 pub connectivity_state: Option<fnp_socketproxy::ConnectivityState>,
498 pub name: Option<String>,
500 pub network_type: Option<fnp_socketproxy::NetworkType>,
502}
503
504#[derive(Debug)]
505pub enum NetworkUpdate {
506 Properties(NetworkPropertiesChange),
508 Remove,
509 MakeDefault,
510}
511
512#[derive(Debug, PartialEq, Eq, Clone)]
513struct DefaultChangedEvent {
514 previous_default: Option<NetworkId>,
515}
516
517#[derive(Debug, PartialEq, Eq)]
518struct RegistryUpdateResult {
519 event: UpdateApplied,
520 default_changed: Option<DefaultChangedEvent>,
523}
524
525#[derive(Debug, PartialEq, Eq, Clone)]
526enum UpdateApplied {
527 None,
529
530 DnsChanged,
532
533 NetworkChanged {
535 network_id: NetworkId,
536 added: bool,
537 changed_marks: bool,
538 name: Option<String>,
539 network_type: Option<fnp_socketproxy::NetworkType>,
540 },
541
542 NetworkRemoved(NetworkId),
544}
545
546#[derive(Debug)]
547pub enum PropertyUpdate {
548 LoseDefaultNetwork,
549 ChangeNetwork(NetworkId, NetworkUpdate),
550 UpdateDns(Vec<fnet_name::DnsServer_>),
551}
552
553impl PropertyUpdate {
554 pub fn default_network_lost() -> Self {
555 PropertyUpdate::LoseDefaultNetwork
556 }
557
558 pub fn dns(dns_servers: &DnsServers) -> Self {
559 PropertyUpdate::UpdateDns(dns_servers.consolidated_dns_servers())
562 }
563}
564
565#[derive(Debug, PartialEq)]
570pub struct DelegatedNetworkUpdateResult {
571 pub dns_servers: Option<Vec<fnet_name::DnsServer_>>,
574}
575
576#[derive(Default)]
577pub struct NetpolNetworksService {
578 current_generation: UpdateGeneration,
580 generations_by_connection: UpdateGenerations,
582 default_network_responders:
584 HashMap<ConnectionId, fnp_properties::NetworksWatchDefaultResponder>,
585 tokens: token_registry::TokenRegistry<NetworkTokenContents>,
586 property_responders: HashMap<ConnectionId, NetworkPropertyResponder>,
588 network_registry: RegisteredNetworks,
590 telemetry: Option<TelemetrySender>,
591}
592
593impl NetpolNetworksService {
594 pub fn set_telemetry(&mut self, telemetry: TelemetrySender) {
595 self.telemetry = Some(telemetry);
596 }
597
598 pub fn consolidated_dns_servers(&self) -> Vec<fnet_name::DnsServer_> {
600 self.network_registry.consolidated_dns_servers()
601 }
602
603 pub async fn handle_network_attributes_request(
604 &mut self,
605 id: ConnectionId,
606 req: Result<fnp_properties::NetworksRequest, fidl::Error>,
607 ) -> Result<(), anyhow::Error> {
608 let req = req.context("network attributes request")?;
609 match req {
610 fnp_properties::NetworksRequest::WatchDefault { responder } => {
611 match self.default_network_responders.entry(id) {
612 std::collections::hash_map::Entry::Occupied(_) => {
613 warn!(
614 "Only one call to fuchsia.net.policy.properties/Networks.WatchDefault \
615 may be active per connection"
616 );
617 responder
618 .control_handle()
619 .shutdown_with_epitaph(zx::Status::CONNECTION_ABORTED)
620 }
621 std::collections::hash_map::Entry::Vacant(vacant_entry) => {
622 let network_id = if self
623 .generations_by_connection
624 .default_network(&id)
625 .unwrap_or_default()
626 < self.current_generation.default_network
627 {
628 self.network_registry.default_network
629 } else {
630 None
631 };
632 if let Some(network_id) = network_id {
633 self.generations_by_connection
634 .set_default_network(id, self.current_generation);
635 let token = self
636 .tokens
637 .ensure_token(NetworkTokenContents { network_id, is_default: true })
638 .get()
639 .duplicate()
640 .context("could not duplicate token")?;
641 responder.send(
642 fnp_properties::NetworksWatchDefaultResponse::Network(token),
643 )?;
644
645 if let Some(responder) = self.property_responders.remove(&id) {
646 let _: Option<_> = self.generations_by_connection.remove(&id);
647 let _: Result<(), fidl::Error> =
648 responder.respond(Err(fnp_properties::WatchError::NetworkGone));
649 }
650 } else {
651 let _: &mut _ = vacant_entry.insert(responder);
652 }
653 }
654 }
655 }
656 fnp_properties::NetworksRequest::WatchProperties {
657 payload: fnp_properties::NetworksWatchPropertiesRequest { network, properties, .. },
658 responder,
659 } => match (network, properties) {
660 (None, _) | (_, None) => {
661 responder.send(Err(fnp_properties::WatchError::MissingRequiredArgument))?
662 }
663 (Some(network), Some(properties)) => {
664 if properties.is_empty() {
665 responder.send(Err(fnp_properties::WatchError::NoProperties))?;
666 } else {
667 match self.property_responders.entry(id) {
668 std::collections::hash_map::Entry::Occupied(_) => {
669 warn!(
670 "Only one call to \
671 fuchsia.net.policy.properties/Networks.WatchProperties may be \
672 active per connection"
673 );
674 responder
675 .control_handle()
676 .shutdown_with_epitaph(zx::Status::CONNECTION_ABORTED)
677 }
678 std::collections::hash_map::Entry::Vacant(vacant_entry) => {
679 match self.tokens.get_contents(&network) {
680 Err(e) => {
681 warn!("Unknown network token. ({network:?}: {e})");
682 responder.send(Err(
683 fnp_properties::WatchError::InvalidNetworkToken,
684 ))?;
685 }
686 Ok(network_contents) => {
687 let responder = NetworkPropertyResponder {
688 token: network,
689 watched_properties: properties,
690 responder,
691 };
692 if self
693 .generations_by_connection
694 .properties(&id)
695 .unwrap_or_default()
696 < self.current_generation.properties
697 {
698 self.generations_by_connection
699 .set_properties(id, self.current_generation);
700 if let Some(responder) = self
701 .network_registry
702 .maybe_respond(&network_contents, responder)
703 {
704 let _: &mut NetworkPropertyResponder =
705 vacant_entry.insert(responder);
706 }
707 } else {
708 let _: &mut NetworkPropertyResponder =
709 vacant_entry.insert(responder);
710 }
711 }
712 }
713 }
714 }
715 }
716 }
717 },
718 _ => {
719 warn!("Received unexpected request {req:?}");
720 }
721 }
722
723 Ok(())
724 }
725
726 pub async fn handle_delegated_networks_update(
735 &mut self,
736 update: Result<fnp_socketproxy::NetworkRegistryRequest, fidl::Error>,
737 ) -> Result<DelegatedNetworkUpdateResult, anyhow::Error> {
738 use fnp_socketproxy::{
739 NetworkInfo, NetworkRegistryAddError, NetworkRegistryRemoveError,
740 NetworkRegistryRequest, NetworkRegistrySetDefaultError, NetworkRegistryUpdateError,
741 };
742
743 let action_result = match update {
744 Err(e) => {
745 error!(
746 "Encountered error watching for delegated network \
747 updates: {e:?}"
748 );
749 return Err(anyhow::anyhow!(e));
750 }
751 Ok(NetworkRegistryRequest::SetDefault { network_id, responder }) => {
752 let update_result = match network_id {
753 fposix_socket::OptionalUint32::Value(interface_id) => {
754 match InterfaceId::try_from(interface_id) {
755 Ok(id) => {
756 let delegated_id = NetworkId::delegated(id);
757 self.update(PropertyUpdate::ChangeNetwork(
758 delegated_id,
759 NetworkUpdate::MakeDefault,
760 ))
761 .await;
762 Ok(())
763 }
764 Err(_) => Err(NetworkRegistrySetDefaultError::NotFound),
765 }
766 }
767 fposix_socket::OptionalUint32::Unset(_) => {
768 self.update(PropertyUpdate::default_network_lost()).await;
769 Ok(())
770 }
771 };
772
773 self.respond_to_delegated_network_update(
774 update_result,
775 |reply| responder.send(reply),
776 "failed to send SetDefault result",
777 )
778 }
779 Ok(NetworkRegistryRequest::Add { network, responder }) => {
780 let extracted_properties = (|| {
781 let raw_network_id =
782 network.network_id.ok_or(NetworkRegistryAddError::MissingNetworkId)?;
783 let network_id = InterfaceId::try_from(raw_network_id)
784 .map(|id| NetworkId::delegated(id))
785 .map_err(|_| NetworkRegistryAddError::MissingNetworkId)?;
786 let NetworkInfo::Starnix(info) =
787 network.info.ok_or(NetworkRegistryAddError::MissingNetworkInfo)?
788 else {
789 return Err(NetworkRegistryAddError::MissingNetworkInfo);
790 };
791
792 let mut marks = fnet::Marks::default();
793 marks.set_mark(fnet::MARK_DOMAIN_SO_MARK, info.mark);
794
795 let dns_servers =
796 Self::extract_dns_servers(&network.dns_servers, raw_network_id.into());
797
798 Ok((network_id, marks, dns_servers))
799 })();
800
801 let update_result = match extracted_properties {
802 Ok((network_id, marks, dns_servers)) => {
803 self.update(PropertyUpdate::ChangeNetwork(
804 network_id,
805 NetworkUpdate::Properties(NetworkPropertiesChange {
806 added: true,
807 marks: Some(marks),
808 dns_servers: Some(dns_servers.clone()),
809 connectivity_state: network.connectivity,
810 name: network.name,
811 network_type: network.network_type,
812 }),
813 ))
814 .await;
815 Ok(())
816 }
817 Err(e) => Err(e),
818 };
819
820 self.respond_to_delegated_network_update(
821 update_result,
822 |reply| responder.send(reply),
823 "failed to send Add result",
824 )
825 }
826 Ok(NetworkRegistryRequest::Update { network, responder }) => {
827 let extracted_properties = (|| {
828 let raw_network_id =
829 network.network_id.ok_or(NetworkRegistryUpdateError::MissingNetworkId)?;
830 let network_id = InterfaceId::try_from(raw_network_id)
831 .map(|id| NetworkId::delegated(id))
832 .map_err(|_| NetworkRegistryUpdateError::MissingNetworkId)?;
833 let NetworkInfo::Starnix(info) =
834 network.info.ok_or(NetworkRegistryUpdateError::MissingNetworkInfo)?
835 else {
836 return Err(NetworkRegistryUpdateError::MissingNetworkInfo);
837 };
838
839 let mut marks = fnet::Marks::default();
840 marks.set_mark(fnet::MARK_DOMAIN_SO_MARK, info.mark);
841
842 let dns_servers =
843 Self::extract_dns_servers(&network.dns_servers, raw_network_id.into());
844
845 Ok((network_id, marks, dns_servers))
846 })();
847
848 let update_result = match extracted_properties {
849 Ok((network_id, marks, dns_servers)) => {
850 self.update(PropertyUpdate::ChangeNetwork(
851 network_id,
852 NetworkUpdate::Properties(NetworkPropertiesChange {
853 added: false,
854 marks: Some(marks),
855 dns_servers: Some(dns_servers.clone()),
856 connectivity_state: network.connectivity,
857 name: network.name,
858 network_type: network.network_type,
859 }),
860 ))
861 .await;
862 Ok(())
863 }
864 Err(e) => Err(e),
865 };
866
867 self.respond_to_delegated_network_update(
868 update_result,
869 |reply| responder.send(reply),
870 "failed to send Update result",
871 )
872 }
873 Ok(NetworkRegistryRequest::Remove { network_id, responder }) => {
874 let update_result = match InterfaceId::try_from(network_id) {
875 Ok(id) => {
876 let delegated_id = NetworkId::delegated(id);
877 self.update(PropertyUpdate::ChangeNetwork(
878 delegated_id,
879 NetworkUpdate::Remove,
880 ))
881 .await;
882 Ok(())
883 }
884 Err(_) => Err(NetworkRegistryRemoveError::NotFound),
885 };
886
887 self.respond_to_delegated_network_update(
888 update_result,
889 |reply| responder.send(reply),
890 "failed to send Remove result",
891 )
892 }
893 };
894
895 Ok(action_result)
896 }
897
898 fn respond_to_delegated_network_update<E, F>(
901 &self,
902 operation_result: Result<(), E>,
903 send_response: F,
904 context_message: &'static str,
905 ) -> DelegatedNetworkUpdateResult
906 where
907 F: FnOnce(Result<(), E>) -> Result<(), fidl::Error>,
908 {
909 let dns_servers = operation_result
911 .as_ref()
912 .ok()
913 .map(|()| self.network_registry.consolidated_dns_servers());
914
915 if let Err(e) = send_response(operation_result) {
917 if !e.is_closed() {
918 error!(
919 "Failed to send delegated network update result \
920 for {context_message}: {e}"
921 );
922 }
923 }
924
925 DelegatedNetworkUpdateResult { dns_servers }
926 }
927
928 fn extract_dns_servers(
933 dns_servers: &Option<fnp_socketproxy::NetworkDnsServers>,
934 network_id: u64,
935 ) -> Vec<fnet_name::DnsServer_> {
936 let make_server = |address| fnet_name::DnsServer_ {
937 address: Some(address),
938 source: Some(fnet_name::DnsServerSource::SocketProxy(
939 fnet_name::SocketProxyDnsServerSource {
940 source_interface: Some(network_id),
941 ..Default::default()
942 },
943 )),
944 ..Default::default()
945 };
946
947 dns_servers
948 .as_ref()
949 .map(|dns| {
950 dns.v4
951 .as_ref()
952 .into_iter()
953 .flatten()
954 .map(|&address| {
955 make_server(fnet::SocketAddress::Ipv4(fnet::Ipv4SocketAddress {
956 address,
957 port: DNS_PORT,
958 }))
959 })
960 .chain(dns.v6.as_ref().into_iter().flatten().map(|&address| {
961 make_server(fnet::SocketAddress::Ipv6(fnet::Ipv6SocketAddress {
962 address,
963 port: DNS_PORT,
964 zone_index: 0,
965 }))
966 }))
967 .collect()
968 })
969 .unwrap_or_default()
970 }
971
972 pub(crate) async fn handle_network_token_resolver_request(
973 &mut self,
974 request: Result<fnp_properties::NetworkTokenResolverRequest, fidl::Error>,
975 ) -> Result<(), anyhow::Error> {
976 use fnp_properties::NetworkTokenResolverResolveTokenError as ResolveTokenError;
977
978 let request = request.context("while handling NetworkTokenResolver request")?;
979 match request {
980 fnp_properties::NetworkTokenResolverRequest::ResolveToken { token, responder } => {
981 let maybe_contents = self.tokens.get_contents(&token).copied();
982 match maybe_contents {
983 Err(e) => {
984 warn!("Unknown network token. ({token:?}: {e})");
985 responder.send(Err(ResolveTokenError::InvalidNetworkToken))?;
986 }
987 Ok(contents) => {
988 if contents.is_default {
989 let query = NetworkTokenContents { is_default: false, ..contents };
992 if let Some(tok) = self.tokens.get_token(&query) {
993 responder.send(tok.duplicate().map_err(|e| {
994 warn!("Encountered issue duplicating generated token. {e}");
995 ResolveTokenError::InvalidNetworkToken
996 }))?;
997 } else {
998 warn!("Requested canonical version of unregistered network.");
999 responder.send(Err(ResolveTokenError::InvalidNetworkToken))?;
1000 }
1001 } else {
1002 responder.send(Ok(token))?;
1003 }
1004 }
1005 }
1006 }
1007 fidl_fuchsia_net_policy_properties::NetworkTokenResolverRequest::_UnknownMethod {
1008 ordinal,
1009 control_handle,
1010 method_type,
1011 ..
1012 } => warn!(
1013 "Encountered unknown method call on NetworkTokenResolver: {ordinal} \
1014 {control_handle:?} {method_type:?}"
1015 ),
1016 }
1017
1018 Ok(())
1019 }
1020
1021 async fn changed_default_network(
1022 &mut self,
1023 previous_default_network: Option<NetworkId>,
1024 responders: &mut HashMap<ConnectionId, NetworkPropertyResponder>,
1025 ) {
1026 let mut r = HashMap::new();
1027 std::mem::swap(&mut r, responders);
1028 r = r
1029 .into_iter()
1030 .filter_map(|(id, responder)| {
1031 match self.tokens.get_contents(&responder.token) {
1032 Ok(contents) => {
1033 if contents.is_default {
1035 let _: Option<_> = self.generations_by_connection.remove(&id);
1036 let _: Result<(), fidl::Error> =
1037 responder.respond(Err(fnp_properties::WatchError::NetworkGone));
1038 return None;
1039 }
1040 }
1041 Err(zx::Status::NOT_FOUND) => {
1042 warn!("Token provided to get_contents is not valid.");
1043 }
1044 Err(e) => {
1045 warn!("Encountered unknown issue while getting contents: {e}");
1046 }
1047 }
1048 Some((id, responder))
1049 })
1050 .collect::<HashMap<_, _>>();
1051 std::mem::swap(&mut r, responders);
1052 self.tokens.drop_if(|&c| {
1053 c.is_default && previous_default_network.is_some_and(|i| i == c.network_id)
1054 });
1055 }
1056
1057 pub(crate) async fn remove_network(&mut self, network_id: NetworkId) {
1058 info!("Removing interface {network_id}. Reporting NETWORK_GONE to all clients.");
1059 let mut responders = HashMap::new();
1060 std::mem::swap(&mut self.property_responders, &mut responders);
1061 for (id, responder) in responders {
1062 let network = match self.tokens.get_contents(&responder.token) {
1063 Ok(network) => network,
1064 Err(e) => {
1065 warn!("Could not fetch network data for responder: {e}");
1066 continue;
1067 }
1068 };
1069 if network.network_id == network_id {
1070 if let Err(e) = responder.respond(Err(fnp_properties::WatchError::NetworkGone)) {
1072 warn!("Could not send to responder: {e}");
1073 }
1074 } else {
1075 if self.property_responders.insert(id, responder).is_some() {
1076 error!("Re-inserted in an existing responder slot. This should be impossible.");
1077 }
1078 }
1079 }
1080 }
1081
1082 pub async fn update(&mut self, update: PropertyUpdate) {
1083 self.current_generation.properties += 1;
1084 let RegistryUpdateResult { event, default_changed } = self.network_registry.apply(update);
1085
1086 if let UpdateApplied::None = event {
1087 if default_changed.is_none() {
1088 return;
1090 }
1091 }
1092
1093 let mut property_responders = HashMap::new();
1094 std::mem::swap(&mut self.property_responders, &mut property_responders);
1095
1096 match event {
1098 UpdateApplied::NetworkChanged { network_id, added: true, .. } => {
1099 let _ = self
1100 .tokens
1101 .ensure_token(NetworkTokenContents { network_id, is_default: false });
1102 }
1103 UpdateApplied::NetworkRemoved(network_id) => {
1104 self.tokens.drop_if(|c| !c.is_default && c.network_id == network_id);
1105 }
1106 UpdateApplied::NetworkChanged { added: false, .. }
1107 | UpdateApplied::DnsChanged
1108 | UpdateApplied::None => {}
1109 }
1110
1111 if let Some(DefaultChangedEvent { previous_default }) = default_changed {
1113 self.notify_default_network_changed(previous_default, &mut property_responders).await;
1114 return;
1115 }
1116
1117 if let UpdateApplied::NetworkChanged { network_id, .. } = event {
1118 if let Some(telemetry) = &self.telemetry {
1119 if let Some(props) = self.network_registry.networks.get(&network_id) {
1120 telemetry.send(TelemetryEvent::NetworkChanged(NetworkEventMetadata {
1121 id: network_id.get().get(),
1122 name: props.name.clone(),
1123 transport: props
1124 .network_type
1125 .unwrap_or(fnp_socketproxy::NetworkType::Unknown),
1126 is_fuchsia_provisioned: matches!(network_id, NetworkId::Fuchsia(_)),
1127 connectivity_state: props.connectivity_state,
1128 }));
1129 }
1130 }
1131 }
1132
1133 for (id, responder) in property_responders {
1134 let mut updates = Vec::new();
1135 let network = match self.tokens.get_contents(&responder.token) {
1136 Ok(network) => network,
1137 Err(e) => {
1138 warn!("Could not fetch network data for responder: {e}");
1139 continue;
1140 }
1141 };
1142
1143 if let UpdateApplied::NetworkChanged { network_id, changed_marks: true, .. } = event {
1144 if network.network_id == network_id {
1145 updates.add_socket_marks(&self.network_registry, &network, &responder);
1146 }
1147 }
1148 if let UpdateApplied::DnsChanged = event {
1149 updates.add_dns(&self.network_registry, &network, &responder);
1150 }
1151
1152 self.generations_by_connection.set_properties(id, self.current_generation);
1153 if updates.is_empty() {
1154 if self.property_responders.insert(id, responder).is_some() {
1155 warn!("Re-inserted in an existing responder slot. This should be impossible.");
1156 }
1157 } else {
1158 if let Err(e) = responder.respond(Ok(&updates)) {
1159 warn!("Could not send to responder: {e}");
1160 }
1161 }
1162 }
1163 }
1164
1165 async fn notify_default_network_changed(
1166 &mut self,
1167 old_default: Option<NetworkId>,
1168 property_responders: &mut HashMap<ConnectionId, NetworkPropertyResponder>,
1169 ) {
1170 self.changed_default_network(old_default, property_responders).await;
1171 match self.network_registry.default_network {
1172 Some(default_network) => {
1173 if let Some(telemetry) = &self.telemetry {
1174 if let Some(props) = self.network_registry.networks.get(&default_network) {
1175 telemetry.send(TelemetryEvent::DefaultNetworkChanged(
1176 NetworkEventMetadata {
1177 id: default_network.get().get(),
1178 name: props.name.clone(),
1179 transport: props
1180 .network_type
1181 .unwrap_or(fnp_socketproxy::NetworkType::Unknown),
1182 is_fuchsia_provisioned: matches!(
1183 default_network,
1184 NetworkId::Fuchsia(_)
1185 ),
1186 connectivity_state: props.connectivity_state,
1187 },
1188 ));
1189 } else {
1190 warn!("Could not fetch network data for default network.");
1191 }
1192 }
1193 self.current_generation.default_network += 1;
1194 let mut responders = HashMap::new();
1195 std::mem::swap(&mut self.default_network_responders, &mut responders);
1196 for (id, responder) in responders {
1197 self.generations_by_connection.set_default_network(id, self.current_generation);
1198 match self
1199 .tokens
1200 .ensure_token(NetworkTokenContents {
1201 network_id: default_network,
1202 is_default: true,
1203 })
1204 .get()
1205 .duplicate()
1206 {
1207 Ok(token) => {
1208 if let Err(e) = responder
1209 .send(fnp_properties::NetworksWatchDefaultResponse::Network(token))
1210 {
1211 warn!("Could not send to responder: {e}");
1212 }
1213 }
1214 Err(e) => warn!("Could not duplicate token: {e}"),
1215 };
1216 }
1217 }
1218 None => {
1219 if let Some(telemetry) = &self.telemetry {
1220 telemetry.send(TelemetryEvent::DefaultNetworkLost);
1221 }
1222 self.current_generation.default_network += 1;
1224 let mut responders = HashMap::new();
1225 std::mem::swap(&mut self.default_network_responders, &mut responders);
1226 for (id, responder) in responders {
1227 self.generations_by_connection.set_default_network(id, self.current_generation);
1228 if let Err(e) = responder.send(
1229 fnp_properties::NetworksWatchDefaultResponse::NoDefaultNetwork(
1230 fnp_properties::Empty,
1231 ),
1232 ) {
1233 warn!("Could not send to responder: {e}");
1234 }
1235 }
1236 }
1237 }
1238 }
1239}
1240
1241pub struct ConnectionTagged<Stream: futures::Stream + Unpin> {
1242 next_id: ConnectionId,
1243 streams: futures::stream::SelectAll<Tagged<ConnectionId, Stream>>,
1244}
1245
1246impl<Stream: futures::Stream + Unpin> Default for ConnectionTagged<Stream> {
1247 fn default() -> Self {
1248 Self { next_id: Default::default(), streams: Default::default() }
1249 }
1250}
1251
1252impl<Stream: futures::Stream + Unpin> ConnectionTagged<Stream> {
1253 pub fn push(&mut self, stream: Stream) {
1254 self.streams.push(stream.tagged(self.next_id));
1255 self.next_id.0 += 1;
1256 }
1257}
1258
1259impl<Stream: futures::Stream + Unpin> futures::Stream for ConnectionTagged<Stream> {
1260 type Item = (ConnectionId, <Stream as futures::Stream>::Item);
1261
1262 fn poll_next(
1263 mut self: std::pin::Pin<&mut Self>,
1264 cx: &mut std::task::Context<'_>,
1265 ) -> std::task::Poll<Option<Self::Item>> {
1266 std::pin::Pin::new(&mut self.streams).poll_next(cx)
1267 }
1268}
1269
1270impl<Stream: futures::Stream + Unpin> futures::stream::FusedStream for ConnectionTagged<Stream> {
1271 fn is_terminated(&self) -> bool {
1272 self.streams.is_terminated()
1273 }
1274}
1275
1276#[cfg(test)]
1277mod tests {
1278 use super::*;
1279 use std::num::NonZeroU64;
1280 const ID_1: InterfaceId = InterfaceId(NonZeroU64::new(1).unwrap());
1281 const ID_2: InterfaceId = InterfaceId(NonZeroU64::new(2).unwrap());
1282 const NAME_1: &str = "testif1";
1283 const NAME_2: &str = "testif2";
1284
1285 impl NetpolNetworksService {
1286 pub(crate) fn default_network(&self) -> Option<NetworkId> {
1287 self.network_registry.default_network
1288 }
1289
1290 pub(crate) fn has_network(&self, id: NetworkId) -> bool {
1291 self.network_registry.networks.contains_key(&id)
1292 }
1293 }
1294
1295 #[test]
1296 fn test_handle_changed_network_delegated() {
1297 let mut networks = RegisteredNetworks::default();
1298 let delegated_id = NetworkId::Delegated(ID_1);
1299
1300 let marks = fnet::Marks { mark_1: Some(123), ..Default::default() };
1302 let event = NetworkPropertiesChange {
1303 added: true,
1304 marks: Some(marks.clone()),
1305 dns_servers: None,
1306 connectivity_state: Some(fnp_socketproxy::ConnectivityState::FullConnectivity),
1307 name: Some(NAME_1.to_string()),
1308 network_type: Some(fnp_socketproxy::NetworkType::Ethernet),
1309 };
1310 assert_eq!(
1311 networks.handle_changed_network(delegated_id, event),
1312 UpdateApplied::NetworkChanged {
1313 network_id: delegated_id,
1314 added: true,
1315 changed_marks: true,
1316 name: Some(NAME_1.to_string()),
1317 network_type: Some(fnp_socketproxy::NetworkType::Ethernet),
1318 }
1319 );
1320 let properties = networks.networks.get(&delegated_id).expect("network should be present");
1321 assert_eq!(properties.socket_marks, Some(marks.clone()));
1322 assert_eq!(
1323 properties.connectivity_state,
1324 Some(fnp_socketproxy::ConnectivityState::FullConnectivity)
1325 );
1326
1327 let event = NetworkPropertiesChange {
1329 added: false,
1330 marks: Some(marks.clone()),
1331 dns_servers: None,
1332 connectivity_state: Some(fnp_socketproxy::ConnectivityState::NoConnectivity),
1333 name: Some(NAME_1.to_string()),
1334 network_type: Some(fnp_socketproxy::NetworkType::Ethernet),
1335 };
1336 assert_eq!(
1337 networks.handle_changed_network(delegated_id, event),
1338 UpdateApplied::NetworkChanged {
1339 network_id: delegated_id,
1340 added: false,
1341 changed_marks: false,
1342 name: Some(NAME_1.to_string()),
1343 network_type: Some(fnp_socketproxy::NetworkType::Ethernet),
1344 }
1345 );
1346
1347 let properties = networks.networks.get(&delegated_id).expect("network should be present");
1348 assert_eq!(properties.socket_marks, Some(marks.clone()));
1349 assert_eq!(
1350 properties.connectivity_state,
1351 Some(fnp_socketproxy::ConnectivityState::NoConnectivity)
1352 );
1353
1354 let new_marks = fnet::Marks { mark_1: Some(456), ..Default::default() };
1356 let event = NetworkPropertiesChange {
1357 added: false,
1358 marks: Some(new_marks.clone()),
1359 dns_servers: None,
1360 connectivity_state: Some(fnp_socketproxy::ConnectivityState::NoConnectivity),
1361 name: Some(NAME_1.to_string()),
1362 network_type: Some(fnp_socketproxy::NetworkType::Ethernet),
1363 };
1364 assert_eq!(
1365 networks.handle_changed_network(delegated_id, event),
1366 UpdateApplied::NetworkChanged {
1367 network_id: delegated_id,
1368 added: false,
1369 changed_marks: true,
1370 name: Some(NAME_1.to_string()),
1371 network_type: Some(fnp_socketproxy::NetworkType::Ethernet),
1372 }
1373 );
1374 let properties = networks.networks.get(&delegated_id).expect("network should be present");
1375 assert_eq!(properties.socket_marks, Some(new_marks));
1376 assert_eq!(
1377 properties.connectivity_state,
1378 Some(fnp_socketproxy::ConnectivityState::NoConnectivity)
1379 );
1380 }
1381
1382 #[test]
1383 fn test_handle_changed_network_fuchsia() {
1384 let mut networks = RegisteredNetworks::default();
1385 let fuchsia_id = NetworkId::Fuchsia(ID_2);
1386
1387 let event = NetworkPropertiesChange {
1389 added: true,
1390 marks: None,
1391 dns_servers: None,
1392 connectivity_state: Some(fnp_socketproxy::ConnectivityState::LocalConnectivity),
1393 name: Some(NAME_2.to_string()),
1394 network_type: Some(fnp_socketproxy::NetworkType::Wifi),
1395 };
1396 assert_eq!(
1397 networks.handle_changed_network(fuchsia_id, event),
1398 UpdateApplied::NetworkChanged {
1399 network_id: fuchsia_id,
1400 added: true,
1401 changed_marks: true,
1402 name: Some(NAME_2.to_string()),
1403 network_type: Some(fnp_socketproxy::NetworkType::Wifi),
1404 }
1405 );
1406 let properties = networks.networks.get(&fuchsia_id).expect("network should be present");
1407 assert_eq!(properties.socket_marks, None);
1408 assert_eq!(
1409 properties.connectivity_state,
1410 Some(fnp_socketproxy::ConnectivityState::LocalConnectivity)
1411 );
1412
1413 let event = NetworkPropertiesChange {
1415 added: false,
1416 marks: None,
1417 dns_servers: None,
1418 connectivity_state: Some(fnp_socketproxy::ConnectivityState::FullConnectivity),
1419 name: Some(NAME_2.to_string()),
1420 network_type: Some(fnp_socketproxy::NetworkType::Wifi),
1421 };
1422 assert_eq!(
1423 networks.handle_changed_network(fuchsia_id, event),
1424 UpdateApplied::NetworkChanged {
1425 network_id: fuchsia_id,
1426 added: false,
1427 changed_marks: false,
1428 name: Some(NAME_2.to_string()),
1429 network_type: Some(fnp_socketproxy::NetworkType::Wifi),
1430 }
1431 );
1432
1433 let properties = networks.networks.get(&fuchsia_id).expect("network should be present");
1434 assert_eq!(
1435 properties.connectivity_state,
1436 Some(fnp_socketproxy::ConnectivityState::FullConnectivity)
1437 );
1438 }
1439
1440 #[test]
1441 fn test_handle_changed_network_validation() {
1442 let mut networks = RegisteredNetworks::default();
1443 let fuchsia_id = NetworkId::Fuchsia(ID_1);
1444 let network_id = NetworkId::Delegated(ID_1);
1445 let marks = fnet::Marks { mark_1: Some(123), ..Default::default() };
1446
1447 let event = NetworkPropertiesChange {
1449 added: false,
1450 marks: Some(marks.clone()),
1451 dns_servers: None,
1452 connectivity_state: None,
1453 name: Some(NAME_1.to_string()),
1454 network_type: Some(fnp_socketproxy::NetworkType::Ethernet),
1455 };
1456 assert_eq!(networks.handle_changed_network(network_id, event), UpdateApplied::None);
1457
1458 let event = NetworkPropertiesChange {
1460 added: true,
1461 marks: Some(marks.clone()),
1462 dns_servers: None,
1463 connectivity_state: None,
1464 name: Some(NAME_1.to_string()),
1465 network_type: Some(fnp_socketproxy::NetworkType::Ethernet),
1466 };
1467 assert_eq!(
1468 networks.handle_changed_network(network_id, event),
1469 UpdateApplied::NetworkChanged {
1470 network_id,
1471 added: true,
1472 changed_marks: true,
1473 name: Some(NAME_1.to_string()),
1474 network_type: Some(fnp_socketproxy::NetworkType::Ethernet),
1475 }
1476 );
1477
1478 let event = NetworkPropertiesChange {
1480 added: true,
1481 marks: Some(marks.clone()),
1482 dns_servers: None,
1483 connectivity_state: None,
1484 name: Some(NAME_1.to_string()),
1485 network_type: Some(fnp_socketproxy::NetworkType::Ethernet),
1486 };
1487 assert_eq!(networks.handle_changed_network(network_id, event), UpdateApplied::None);
1488
1489 let event = NetworkPropertiesChange {
1491 added: true,
1492 marks: Some(marks.clone()),
1493 dns_servers: None,
1494 connectivity_state: None,
1495 name: Some(NAME_1.to_string()),
1496 network_type: Some(fnp_socketproxy::NetworkType::Ethernet),
1497 };
1498 assert_eq!(networks.handle_changed_network(fuchsia_id, event), UpdateApplied::None);
1499
1500 let delegated_id = NetworkId::Delegated(ID_1);
1502 let event = NetworkPropertiesChange {
1503 added: true,
1504 marks: None,
1505 dns_servers: None,
1506 connectivity_state: None,
1507 name: Some(NAME_1.to_string()),
1508 network_type: Some(fnp_socketproxy::NetworkType::Ethernet),
1509 };
1510 assert_eq!(networks.handle_changed_network(delegated_id, event), UpdateApplied::None);
1511
1512 assert_eq!(
1514 networks.apply(PropertyUpdate::ChangeNetwork(network_id, NetworkUpdate::MakeDefault)),
1515 RegistryUpdateResult {
1516 event: UpdateApplied::None,
1517 default_changed: Some(DefaultChangedEvent { previous_default: None })
1518 }
1519 );
1520
1521 assert_eq!(
1524 networks.apply(PropertyUpdate::ChangeNetwork(network_id, NetworkUpdate::Remove)),
1525 RegistryUpdateResult { event: UpdateApplied::None, default_changed: None }
1526 );
1527
1528 assert!(networks.networks.contains_key(&network_id));
1530 assert_eq!(networks.default_network, Some(network_id));
1531 }
1532
1533 #[test]
1534 fn test_remove_fuchsia_network_fallback() {
1535 let mut networks = RegisteredNetworks::default();
1536 let fuchsia_id1 = NetworkId::Fuchsia(ID_1);
1537 let fuchsia_id2 = NetworkId::Fuchsia(ID_2);
1538 let delegated_id = NetworkId::Delegated(ID_1);
1539
1540 let marks = fnet::Marks { mark_1: Some(123), ..Default::default() };
1541
1542 let fuchsia_added_network_change =
1544 NetworkPropertiesChange { added: true, ..Default::default() };
1545 assert_eq!(
1546 networks.apply(PropertyUpdate::ChangeNetwork(
1547 fuchsia_id1,
1548 NetworkUpdate::Properties(fuchsia_added_network_change.clone())
1549 )),
1550 RegistryUpdateResult {
1551 event: UpdateApplied::NetworkChanged {
1552 network_id: fuchsia_id1,
1553 added: true,
1554 changed_marks: true,
1555 name: None,
1556 network_type: None,
1557 },
1558 default_changed: Some(DefaultChangedEvent { previous_default: None })
1559 }
1560 );
1561 assert_eq!(
1562 networks.apply(PropertyUpdate::ChangeNetwork(
1563 fuchsia_id2,
1564 NetworkUpdate::Properties(fuchsia_added_network_change)
1565 )),
1566 RegistryUpdateResult {
1567 event: UpdateApplied::NetworkChanged {
1568 network_id: fuchsia_id2,
1569 added: true,
1570 changed_marks: true,
1571 name: None,
1572 network_type: None,
1573 },
1574 default_changed: None
1575 }
1576 );
1577 assert_eq!(
1578 networks.apply(PropertyUpdate::ChangeNetwork(
1579 delegated_id,
1580 NetworkUpdate::Properties(NetworkPropertiesChange {
1581 added: true,
1582 marks: Some(marks),
1583 ..Default::default()
1584 })
1585 )),
1586 RegistryUpdateResult {
1587 event: UpdateApplied::NetworkChanged {
1588 network_id: delegated_id,
1589 added: true,
1590 changed_marks: true,
1591 name: None,
1592 network_type: None,
1593 },
1594 default_changed: None
1595 }
1596 );
1597
1598 assert_eq!(
1602 networks.apply(PropertyUpdate::ChangeNetwork(delegated_id, NetworkUpdate::MakeDefault)),
1603 RegistryUpdateResult { event: UpdateApplied::None, default_changed: None }
1604 );
1605
1606 assert_eq!(networks.default_network, Some(fuchsia_id1));
1608
1609 assert_eq!(
1612 networks.apply(PropertyUpdate::ChangeNetwork(fuchsia_id1, NetworkUpdate::Remove)),
1613 RegistryUpdateResult {
1614 event: UpdateApplied::NetworkRemoved(fuchsia_id1),
1615 default_changed: Some(DefaultChangedEvent { previous_default: Some(fuchsia_id1) })
1616 }
1617 );
1618
1619 assert_eq!(networks.default_network, Some(fuchsia_id2));
1621
1622 assert_eq!(
1624 networks.apply(PropertyUpdate::ChangeNetwork(fuchsia_id2, NetworkUpdate::Remove)),
1625 RegistryUpdateResult {
1626 event: UpdateApplied::NetworkRemoved(fuchsia_id2),
1627 default_changed: Some(DefaultChangedEvent { previous_default: Some(fuchsia_id2) })
1628 }
1629 );
1630
1631 assert_eq!(networks.default_network, Some(delegated_id));
1634
1635 assert_eq!(
1638 networks.apply(PropertyUpdate::ChangeNetwork(delegated_id, NetworkUpdate::Remove)),
1639 RegistryUpdateResult { event: UpdateApplied::None, default_changed: None }
1640 );
1641 assert!(networks.networks.contains_key(&delegated_id));
1642 assert_eq!(networks.default_network, Some(delegated_id));
1643 }
1644}