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(Debug, PartialEq, 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 (_, entry, NetworkId::Fuchsia(_), None) => {
284 let new_dns = changed_dns_servers.unwrap_or_default();
285 let changed_dns = match entry {
286 Entry::Occupied(e) => e.get().dns_servers != new_dns,
287 Entry::Vacant(_) => true,
290 };
291 Ok((
292 NetworkProperties { dns_servers: new_dns, ..Default::default() },
293 added,
294 changed_dns,
295 ))
296 }
297 (_, entry, NetworkId::Delegated(_), Some(socket_marks)) => {
298 let new_dns = changed_dns_servers.unwrap_or_default();
299 let (changed_marks, changed_dns) = match entry {
300 Entry::Occupied(e) => {
301 (e.get().get_marks() != Some(&socket_marks), e.get().dns_servers != new_dns)
302 }
303 Entry::Vacant(_) => (true, true),
306 };
307 Ok((
308 NetworkProperties {
309 socket_marks: Some(socket_marks),
310 dns_servers: new_dns,
311 ..Default::default()
312 },
313 changed_marks,
314 changed_dns,
315 ))
316 }
317 };
318
319 match result {
320 Ok((mut properties, changed_marks, changed_dns)) => {
321 properties.connectivity_state = connectivity_state;
322 properties.network_type = network_type;
323 properties.name = name.clone();
324 let _ = entry.insert_entry(properties);
325 UpdateApplied::NetworkChanged {
326 network_id,
327 added,
328 changed_marks,
329 changed_dns,
330 name,
331 network_type,
332 }
333 }
334 Err(e) => {
335 error!("Cannot {e}. Update ignored.");
336 UpdateApplied::None
337 }
338 }
339 }
340
341 pub fn consolidated_dns_servers(&self) -> Vec<fnet_name::DnsServer_> {
346 if let Some(NetworkId::Fuchsia(if_id)) = self.default_network {
347 self.networks
348 .get(&NetworkId::Fuchsia(if_id))
349 .map(|p| p.dns_servers.clone())
350 .unwrap_or_default()
351 } else {
352 self.networks
353 .iter()
354 .filter(|(id, _)| matches!(id, NetworkId::Delegated(_)))
355 .flat_map(|(_, p)| &p.dns_servers)
356 .cloned()
357 .collect()
358 }
359 }
360
361 fn maybe_respond(
362 &self,
363 network: &NetworkTokenContents,
364 responder: NetworkPropertyResponder,
365 ) -> Option<NetworkPropertyResponder> {
366 let mut updates = Vec::new();
367 updates.add_socket_marks(self, network, &responder);
368 updates.add_dns(self, network, &responder);
369
370 if updates.is_empty() {
371 Some(responder)
372 } else {
373 if let Err(e) = responder.respond(Ok(&updates)) {
374 warn!("Could not send to responder: {e}");
375 }
376 None
377 }
378 }
379}
380
381trait PropertyUpdates {
382 fn add_socket_marks(
383 &mut self,
384 network_registry: &RegisteredNetworks,
385 network: &NetworkTokenContents,
386 responder: &NetworkPropertyResponder,
387 );
388 fn add_dns(
389 &mut self,
390 network_registry: &RegisteredNetworks,
391 network: &NetworkTokenContents,
392 responder: &NetworkPropertyResponder,
393 );
394}
395
396impl PropertyUpdates for Vec<fnp_properties::PropertyUpdate> {
397 fn add_socket_marks(
398 &mut self,
399 network_registry: &RegisteredNetworks,
400 network: &NetworkTokenContents,
401 responder: &NetworkPropertyResponder,
402 ) {
403 if !responder.watched_properties.contains(&fnp_properties::Property::SocketMarks) {
404 return;
405 }
406
407 match network_registry.networks.get(&network.network_id) {
408 Some(network) => {
409 if let Some(socket_marks) = network.get_marks() {
410 self.push(fnp_properties::PropertyUpdate::SocketMarks(socket_marks.clone()));
411 }
412 return;
413 }
414 None => {
415 error!(
416 "State is inconsistent. We attempted to add marks for a \
417 network that is not known: {:?}",
418 network.network_id
419 );
420 }
421 }
422 }
423
424 fn add_dns(
425 &mut self,
426 network_registry: &RegisteredNetworks,
427 network: &NetworkTokenContents,
428 responder: &NetworkPropertyResponder,
429 ) {
430 if !responder.watched_properties.contains(&fnp_properties::Property::DnsConfiguration) {
431 return;
432 }
433
434 let interface_id = network.network_id;
435 self.push(fnp_properties::PropertyUpdate::DnsConfiguration(
436 fnp_properties::DnsConfiguration {
437 servers: Some(
438 network_registry
439 .dns_servers
440 .iter()
441 .filter(|d| {
442 match &d.source {
443 Some(source) => match source {
444 fnet_name::DnsServerSource::StaticSource(_) => true,
445 fnet_name::DnsServerSource::SocketProxy(
449 fnet_name::SocketProxyDnsServerSource {
450 source_interface,
451 ..
452 },
453 ) => match (interface_id, source_interface) {
454 (_, None) => true,
455 (id1, Some(id2)) => {
456 Ok(id1)
457 == InterfaceId::try_from(*id2)
458 .map(|id| NetworkId::delegated(id))
459 }
460 },
461 fnet_name::DnsServerSource::Dhcp(
462 fnet_name::DhcpDnsServerSource { source_interface, .. },
463 )
464 | fnet_name::DnsServerSource::Ndp(
465 fnet_name::NdpDnsServerSource { source_interface, .. },
466 )
467 | fnet_name::DnsServerSource::Dhcpv6(
468 fnet_name::Dhcpv6DnsServerSource {
469 source_interface, ..
470 },
471 ) => match (interface_id, source_interface) {
472 (_, None) => true,
473 (id1, Some(id2)) => {
474 Ok(id1)
475 == InterfaceId::try_from(*id2)
476 .map(|id| NetworkId::fuchsia(id))
477 }
478 },
479
480 _ => {
481 error!("unhandled DnsServerSource: {source:?}");
482 false
483 }
484 },
485
486 None => true,
488 }
489 })
490 .cloned()
491 .collect::<Vec<_>>(),
492 ),
493 ..Default::default()
494 },
495 ));
496 }
497}
498
499#[derive(Clone, Debug, Default)]
501pub struct NetworkPropertiesChange {
502 pub added: bool,
505 pub marks: Option<fnet::Marks>,
507 pub dns_servers: Option<Vec<fnet_name::DnsServer_>>,
509 pub connectivity_state: Option<fnp_socketproxy::ConnectivityState>,
511 pub name: Option<String>,
513 pub network_type: Option<fnp_socketproxy::NetworkType>,
515}
516
517#[derive(Debug, Clone)]
518pub enum NetworkUpdate {
519 Properties(NetworkPropertiesChange),
521 Remove,
522 MakeDefault,
523}
524
525#[derive(Debug, PartialEq, Eq, Clone)]
526struct DefaultChangedEvent {
527 previous_default: Option<NetworkId>,
528}
529
530#[derive(Debug, PartialEq, Eq)]
531struct RegistryUpdateResult {
532 event: UpdateApplied,
533 default_changed: Option<DefaultChangedEvent>,
536}
537
538#[derive(Debug, PartialEq, Eq, Clone)]
539enum UpdateApplied {
540 None,
542
543 DnsChanged,
545
546 NetworkChanged {
548 network_id: NetworkId,
549 added: bool,
550 changed_marks: bool,
551 changed_dns: bool,
552 name: Option<String>,
553 network_type: Option<fnp_socketproxy::NetworkType>,
554 },
555
556 NetworkRemoved(NetworkId),
558}
559
560#[derive(Debug, Clone)]
561pub enum PropertyUpdate {
562 LoseDefaultNetwork,
563 ChangeNetwork(NetworkId, NetworkUpdate),
564 UpdateDns(Vec<fnet_name::DnsServer_>),
565}
566
567impl PropertyUpdate {
568 pub fn default_network_lost() -> Self {
569 PropertyUpdate::LoseDefaultNetwork
570 }
571
572 pub fn dns(dns_servers: &DnsServers) -> Self {
573 PropertyUpdate::UpdateDns(dns_servers.consolidated_dns_servers())
576 }
577}
578
579#[derive(Debug, PartialEq)]
584pub struct DelegatedNetworkUpdateResult {
585 pub dns_servers: Option<Vec<fnet_name::DnsServer_>>,
588}
589
590#[derive(Default)]
591pub struct NetpolNetworksService {
592 current_generation: UpdateGeneration,
594 generations_by_connection: UpdateGenerations,
596 default_network_responders:
598 HashMap<ConnectionId, fnp_properties::NetworksWatchDefaultResponder>,
599 tokens: token_registry::TokenRegistry<NetworkTokenContents>,
600 property_responders: HashMap<ConnectionId, NetworkPropertyResponder>,
602 network_registry: RegisteredNetworks,
604 telemetry: Option<TelemetrySender>,
605}
606
607impl NetpolNetworksService {
608 pub fn set_telemetry(&mut self, telemetry: TelemetrySender) {
609 self.telemetry = Some(telemetry);
610 }
611
612 pub fn consolidated_dns_servers(&self) -> Vec<fnet_name::DnsServer_> {
614 self.network_registry.consolidated_dns_servers()
615 }
616
617 pub async fn handle_network_attributes_request(
618 &mut self,
619 id: ConnectionId,
620 req: Result<fnp_properties::NetworksRequest, fidl::Error>,
621 ) -> Result<(), anyhow::Error> {
622 let req = req.context("network attributes request")?;
623 match req {
624 fnp_properties::NetworksRequest::WatchDefault { responder } => {
625 match self.default_network_responders.entry(id) {
626 std::collections::hash_map::Entry::Occupied(_) => {
627 warn!(
628 "Only one call to fuchsia.net.policy.properties/Networks.WatchDefault \
629 may be active per connection"
630 );
631 responder
632 .control_handle()
633 .shutdown_with_epitaph(zx::Status::CONNECTION_ABORTED)
634 }
635 std::collections::hash_map::Entry::Vacant(vacant_entry) => {
636 let network_id = if self
637 .generations_by_connection
638 .default_network(&id)
639 .unwrap_or_default()
640 < self.current_generation.default_network
641 {
642 self.network_registry.default_network
643 } else {
644 None
645 };
646 if let Some(network_id) = network_id {
647 self.generations_by_connection
648 .set_default_network(id, self.current_generation);
649 let token = self
650 .tokens
651 .ensure_token(NetworkTokenContents { network_id, is_default: true })
652 .get()
653 .duplicate()
654 .context("could not duplicate token")?;
655 responder.send(
656 fnp_properties::NetworksWatchDefaultResponse::Network(token),
657 )?;
658
659 if let Some(responder) = self.property_responders.remove(&id) {
660 let _: Option<_> = self.generations_by_connection.remove(&id);
661 let _: Result<(), fidl::Error> =
662 responder.respond(Err(fnp_properties::WatchError::NetworkGone));
663 }
664 } else {
665 let _: &mut _ = vacant_entry.insert(responder);
666 }
667 }
668 }
669 }
670 fnp_properties::NetworksRequest::WatchProperties {
671 payload: fnp_properties::NetworksWatchPropertiesRequest { network, properties, .. },
672 responder,
673 } => match (network, properties) {
674 (None, _) | (_, None) => {
675 responder.send(Err(fnp_properties::WatchError::MissingRequiredArgument))?
676 }
677 (Some(network), Some(properties)) => {
678 if properties.is_empty() {
679 responder.send(Err(fnp_properties::WatchError::NoProperties))?;
680 } else {
681 match self.property_responders.entry(id) {
682 std::collections::hash_map::Entry::Occupied(_) => {
683 warn!(
684 "Only one call to \
685 fuchsia.net.policy.properties/Networks.WatchProperties may be \
686 active per connection"
687 );
688 responder
689 .control_handle()
690 .shutdown_with_epitaph(zx::Status::CONNECTION_ABORTED)
691 }
692 std::collections::hash_map::Entry::Vacant(vacant_entry) => {
693 match self.tokens.get_contents(&network) {
694 Err(e) => {
695 warn!("Unknown network token. ({network:?}: {e})");
696 responder.send(Err(
697 fnp_properties::WatchError::InvalidNetworkToken,
698 ))?;
699 }
700 Ok(network_contents) => {
701 let responder = NetworkPropertyResponder {
702 token: network,
703 watched_properties: properties,
704 responder,
705 };
706 if self
707 .generations_by_connection
708 .properties(&id)
709 .unwrap_or_default()
710 < self.current_generation.properties
711 {
712 self.generations_by_connection
713 .set_properties(id, self.current_generation);
714 if let Some(responder) = self
715 .network_registry
716 .maybe_respond(&network_contents, responder)
717 {
718 let _: &mut NetworkPropertyResponder =
719 vacant_entry.insert(responder);
720 }
721 } else {
722 let _: &mut NetworkPropertyResponder =
723 vacant_entry.insert(responder);
724 }
725 }
726 }
727 }
728 }
729 }
730 }
731 },
732 _ => {
733 warn!("Received unexpected request {req:?}");
734 }
735 }
736
737 Ok(())
738 }
739
740 pub async fn handle_delegated_networks_update(
749 &mut self,
750 update: Result<fnp_socketproxy::NetworkRegistryRequest, fidl::Error>,
751 ) -> Result<DelegatedNetworkUpdateResult, anyhow::Error> {
752 use fnp_socketproxy::{
753 NetworkInfo, NetworkRegistryAddError, NetworkRegistryRemoveError,
754 NetworkRegistryRequest, NetworkRegistrySetDefaultError, NetworkRegistryUpdateError,
755 };
756
757 let action_result = match update {
758 Err(e) => {
759 error!(
760 "Encountered error watching for delegated network \
761 updates: {e:?}"
762 );
763 return Err(anyhow::anyhow!(e));
764 }
765 Ok(NetworkRegistryRequest::SetDefault { network_id, responder }) => {
766 let update_result = match network_id {
767 fposix_socket::OptionalUint32::Value(interface_id) => {
768 match InterfaceId::try_from(interface_id) {
769 Ok(id) => {
770 let delegated_id = NetworkId::delegated(id);
771 self.update(PropertyUpdate::ChangeNetwork(
772 delegated_id,
773 NetworkUpdate::MakeDefault,
774 ))
775 .await;
776 Ok(())
777 }
778 Err(_) => Err(NetworkRegistrySetDefaultError::NotFound),
779 }
780 }
781 fposix_socket::OptionalUint32::Unset(_) => {
782 self.update(PropertyUpdate::default_network_lost()).await;
783 Ok(())
784 }
785 };
786
787 self.respond_to_delegated_network_update(
788 update_result,
789 |reply| responder.send(reply),
790 "failed to send SetDefault result",
791 )
792 }
793 Ok(NetworkRegistryRequest::Add { network, responder }) => {
794 let extracted_properties = (|| {
795 let raw_network_id =
796 network.network_id.ok_or(NetworkRegistryAddError::MissingNetworkId)?;
797 let network_id = InterfaceId::try_from(raw_network_id)
798 .map(|id| NetworkId::delegated(id))
799 .map_err(|_| NetworkRegistryAddError::MissingNetworkId)?;
800 let NetworkInfo::Starnix(info) =
801 network.info.ok_or(NetworkRegistryAddError::MissingNetworkInfo)?
802 else {
803 return Err(NetworkRegistryAddError::MissingNetworkInfo);
804 };
805
806 let mut marks = fnet::Marks::default();
807 marks.set_mark(fnet::MARK_DOMAIN_SO_MARK, info.mark);
808
809 let dns_servers =
810 Self::extract_dns_servers(&network.dns_servers, raw_network_id.into());
811
812 Ok((network_id, marks, dns_servers))
813 })();
814
815 let update_result = match extracted_properties {
816 Ok((network_id, marks, dns_servers)) => {
817 self.update(PropertyUpdate::ChangeNetwork(
818 network_id,
819 NetworkUpdate::Properties(NetworkPropertiesChange {
820 added: true,
821 marks: Some(marks),
822 dns_servers: Some(dns_servers.clone()),
823 connectivity_state: network.connectivity,
824 name: network.name,
825 network_type: network.network_type,
826 }),
827 ))
828 .await;
829 Ok(())
830 }
831 Err(e) => Err(e),
832 };
833
834 self.respond_to_delegated_network_update(
835 update_result,
836 |reply| responder.send(reply),
837 "failed to send Add result",
838 )
839 }
840 Ok(NetworkRegistryRequest::Update { network, responder }) => {
841 let extracted_properties = (|| {
842 let raw_network_id =
843 network.network_id.ok_or(NetworkRegistryUpdateError::MissingNetworkId)?;
844 let network_id = InterfaceId::try_from(raw_network_id)
845 .map(|id| NetworkId::delegated(id))
846 .map_err(|_| NetworkRegistryUpdateError::MissingNetworkId)?;
847 let NetworkInfo::Starnix(info) =
848 network.info.ok_or(NetworkRegistryUpdateError::MissingNetworkInfo)?
849 else {
850 return Err(NetworkRegistryUpdateError::MissingNetworkInfo);
851 };
852
853 let mut marks = fnet::Marks::default();
854 marks.set_mark(fnet::MARK_DOMAIN_SO_MARK, info.mark);
855
856 let dns_servers =
857 Self::extract_dns_servers(&network.dns_servers, raw_network_id.into());
858
859 Ok((network_id, marks, dns_servers))
860 })();
861
862 let update_result = match extracted_properties {
863 Ok((network_id, marks, dns_servers)) => {
864 self.update(PropertyUpdate::ChangeNetwork(
865 network_id,
866 NetworkUpdate::Properties(NetworkPropertiesChange {
867 added: false,
868 marks: Some(marks),
869 dns_servers: Some(dns_servers.clone()),
870 connectivity_state: network.connectivity,
871 name: network.name,
872 network_type: network.network_type,
873 }),
874 ))
875 .await;
876 Ok(())
877 }
878 Err(e) => Err(e),
879 };
880
881 self.respond_to_delegated_network_update(
882 update_result,
883 |reply| responder.send(reply),
884 "failed to send Update result",
885 )
886 }
887 Ok(NetworkRegistryRequest::Remove { network_id, responder }) => {
888 let update_result = match InterfaceId::try_from(network_id) {
889 Ok(id) => {
890 let delegated_id = NetworkId::delegated(id);
891 self.update(PropertyUpdate::ChangeNetwork(
892 delegated_id,
893 NetworkUpdate::Remove,
894 ))
895 .await;
896 Ok(())
897 }
898 Err(_) => Err(NetworkRegistryRemoveError::NotFound),
899 };
900
901 self.respond_to_delegated_network_update(
902 update_result,
903 |reply| responder.send(reply),
904 "failed to send Remove result",
905 )
906 }
907 };
908
909 Ok(action_result)
910 }
911
912 fn respond_to_delegated_network_update<E, F>(
915 &self,
916 operation_result: Result<(), E>,
917 send_response: F,
918 context_message: &'static str,
919 ) -> DelegatedNetworkUpdateResult
920 where
921 F: FnOnce(Result<(), E>) -> Result<(), fidl::Error>,
922 {
923 let dns_servers = operation_result
925 .as_ref()
926 .ok()
927 .map(|()| self.network_registry.consolidated_dns_servers());
928
929 if let Err(e) = send_response(operation_result) {
931 if !e.is_closed() {
932 error!(
933 "Failed to send delegated network update result \
934 for {context_message}: {e}"
935 );
936 }
937 }
938
939 DelegatedNetworkUpdateResult { dns_servers }
940 }
941
942 fn extract_dns_servers(
947 dns_servers: &Option<fnp_socketproxy::NetworkDnsServers>,
948 network_id: u64,
949 ) -> Vec<fnet_name::DnsServer_> {
950 let make_server = |address| fnet_name::DnsServer_ {
951 address: Some(address),
952 source: Some(fnet_name::DnsServerSource::SocketProxy(
953 fnet_name::SocketProxyDnsServerSource {
954 source_interface: Some(network_id),
955 ..Default::default()
956 },
957 )),
958 ..Default::default()
959 };
960
961 dns_servers
962 .as_ref()
963 .map(|dns| {
964 dns.v4
965 .as_ref()
966 .into_iter()
967 .flatten()
968 .map(|&address| {
969 make_server(fnet::SocketAddress::Ipv4(fnet::Ipv4SocketAddress {
970 address,
971 port: DNS_PORT,
972 }))
973 })
974 .chain(dns.v6.as_ref().into_iter().flatten().map(|&address| {
975 make_server(fnet::SocketAddress::Ipv6(fnet::Ipv6SocketAddress {
976 address,
977 port: DNS_PORT,
978 zone_index: 0,
979 }))
980 }))
981 .collect()
982 })
983 .unwrap_or_default()
984 }
985
986 pub(crate) async fn handle_network_token_resolver_request(
987 &mut self,
988 request: Result<fnp_properties::NetworkTokenResolverRequest, fidl::Error>,
989 ) -> Result<(), anyhow::Error> {
990 use fnp_properties::NetworkTokenResolverResolveTokenError as ResolveTokenError;
991
992 let request = request.context("while handling NetworkTokenResolver request")?;
993 match request {
994 fnp_properties::NetworkTokenResolverRequest::ResolveToken { token, responder } => {
995 let maybe_contents = self.tokens.get_contents(&token).copied();
996 match maybe_contents {
997 Err(e) => {
998 warn!("Unknown network token. ({token:?}: {e})");
999 responder.send(Err(ResolveTokenError::InvalidNetworkToken))?;
1000 }
1001 Ok(contents) => {
1002 if contents.is_default {
1003 let query = NetworkTokenContents { is_default: false, ..contents };
1006 if let Some(tok) = self.tokens.get_token(&query) {
1007 responder.send(tok.duplicate().map_err(|e| {
1008 warn!("Encountered issue duplicating generated token. {e}");
1009 ResolveTokenError::InvalidNetworkToken
1010 }))?;
1011 } else {
1012 warn!("Requested canonical version of unregistered network.");
1013 responder.send(Err(ResolveTokenError::InvalidNetworkToken))?;
1014 }
1015 } else {
1016 responder.send(Ok(token))?;
1017 }
1018 }
1019 }
1020 }
1021 fidl_fuchsia_net_policy_properties::NetworkTokenResolverRequest::_UnknownMethod {
1022 ordinal,
1023 control_handle,
1024 method_type,
1025 ..
1026 } => warn!(
1027 "Encountered unknown method call on NetworkTokenResolver: {ordinal} \
1028 {control_handle:?} {method_type:?}"
1029 ),
1030 }
1031
1032 Ok(())
1033 }
1034
1035 async fn changed_default_network(
1036 &mut self,
1037 previous_default_network: Option<NetworkId>,
1038 responders: &mut HashMap<ConnectionId, NetworkPropertyResponder>,
1039 ) {
1040 let mut r = HashMap::new();
1041 std::mem::swap(&mut r, responders);
1042 r = r
1043 .into_iter()
1044 .filter_map(|(id, responder)| {
1045 match self.tokens.get_contents(&responder.token) {
1046 Ok(contents) => {
1047 if contents.is_default {
1049 let _: Option<_> = self.generations_by_connection.remove(&id);
1050 let _: Result<(), fidl::Error> =
1051 responder.respond(Err(fnp_properties::WatchError::NetworkGone));
1052 return None;
1053 }
1054 }
1055 Err(zx::Status::NOT_FOUND) => {
1056 warn!("Token provided to get_contents is not valid.");
1057 }
1058 Err(e) => {
1059 warn!("Encountered unknown issue while getting contents: {e}");
1060 }
1061 }
1062 Some((id, responder))
1063 })
1064 .collect::<HashMap<_, _>>();
1065 std::mem::swap(&mut r, responders);
1066 self.tokens.drop_if(|&c| {
1067 c.is_default && previous_default_network.is_some_and(|i| i == c.network_id)
1068 });
1069 }
1070
1071 pub(crate) async fn remove_network(&mut self, network_id: NetworkId) {
1072 info!("Removing interface {network_id}. Reporting NETWORK_GONE to all clients.");
1073 let mut responders = HashMap::new();
1074 std::mem::swap(&mut self.property_responders, &mut responders);
1075 for (id, responder) in responders {
1076 let network = match self.tokens.get_contents(&responder.token) {
1077 Ok(network) => network,
1078 Err(e) => {
1079 warn!("Could not fetch network data for responder: {e}");
1080 continue;
1081 }
1082 };
1083 if network.network_id == network_id {
1084 if let Err(e) = responder.respond(Err(fnp_properties::WatchError::NetworkGone)) {
1086 warn!("Could not send to responder: {e}");
1087 }
1088 } else {
1089 if self.property_responders.insert(id, responder).is_some() {
1090 error!("Re-inserted in an existing responder slot. This should be impossible.");
1091 }
1092 }
1093 }
1094 }
1095
1096 pub async fn update(&mut self, update: PropertyUpdate) {
1097 self.current_generation.properties += 1;
1098 let RegistryUpdateResult { event, default_changed } = self.network_registry.apply(update);
1099
1100 if let UpdateApplied::None = event {
1101 if default_changed.is_none() {
1102 return;
1104 }
1105 }
1106
1107 let mut property_responders = HashMap::new();
1108 std::mem::swap(&mut self.property_responders, &mut property_responders);
1109
1110 match event {
1112 UpdateApplied::NetworkChanged { network_id, added: true, .. } => {
1113 let _ = self
1114 .tokens
1115 .ensure_token(NetworkTokenContents { network_id, is_default: false });
1116 }
1117 UpdateApplied::NetworkRemoved(network_id) => {
1118 self.tokens.drop_if(|c| !c.is_default && c.network_id == network_id);
1119 }
1120 UpdateApplied::NetworkChanged { added: false, .. }
1121 | UpdateApplied::DnsChanged
1122 | UpdateApplied::None => {}
1123 }
1124
1125 if let Some(DefaultChangedEvent { previous_default }) = default_changed {
1127 self.notify_default_network_changed(previous_default, &mut property_responders).await;
1128 return;
1129 }
1130
1131 if let UpdateApplied::NetworkChanged { network_id, .. } = event {
1132 if let Some(telemetry) = &self.telemetry {
1133 if let Some(props) = self.network_registry.networks.get(&network_id) {
1134 telemetry.send(TelemetryEvent::NetworkChanged(NetworkEventMetadata {
1135 id: network_id.get().get(),
1136 name: props.name.clone(),
1137 transport: props
1138 .network_type
1139 .unwrap_or(fnp_socketproxy::NetworkType::Unknown),
1140 is_fuchsia_provisioned: network_id.is_fuchsia(),
1141 connectivity_state: props.connectivity_state,
1142 }));
1143 }
1144 }
1145 }
1146
1147 for (id, responder) in property_responders {
1148 let mut updates = Vec::new();
1149 let network = match self.tokens.get_contents(&responder.token) {
1150 Ok(network) => network,
1151 Err(e) => {
1152 warn!("Could not fetch network data for responder: {e}");
1153 continue;
1154 }
1155 };
1156
1157 if let UpdateApplied::NetworkChanged {
1158 network_id, changed_marks, changed_dns, ..
1159 } = event
1160 {
1161 if network.network_id == network_id {
1162 if changed_marks {
1163 updates.add_socket_marks(&self.network_registry, &network, &responder);
1164 }
1165 if changed_dns {
1166 updates.add_dns(&self.network_registry, &network, &responder);
1167 }
1168 }
1169 }
1170 if let UpdateApplied::DnsChanged = event {
1171 updates.add_dns(&self.network_registry, &network, &responder);
1172 }
1173
1174 self.generations_by_connection.set_properties(id, self.current_generation);
1175 if updates.is_empty() {
1176 if self.property_responders.insert(id, responder).is_some() {
1177 warn!("Re-inserted in an existing responder slot. This should be impossible.");
1178 }
1179 } else {
1180 if let Err(e) = responder.respond(Ok(&updates)) {
1181 warn!("Could not send to responder: {e}");
1182 }
1183 }
1184 }
1185 }
1186
1187 async fn notify_default_network_changed(
1188 &mut self,
1189 old_default: Option<NetworkId>,
1190 property_responders: &mut HashMap<ConnectionId, NetworkPropertyResponder>,
1191 ) {
1192 self.changed_default_network(old_default, property_responders).await;
1193 match self.network_registry.default_network {
1194 Some(default_network) => {
1195 if let Some(telemetry) = &self.telemetry {
1196 if let Some(props) = self.network_registry.networks.get(&default_network) {
1197 telemetry.send(TelemetryEvent::DefaultNetworkChanged(
1198 NetworkEventMetadata {
1199 id: default_network.get().get(),
1200 name: props.name.clone(),
1201 transport: props
1202 .network_type
1203 .unwrap_or(fnp_socketproxy::NetworkType::Unknown),
1204 is_fuchsia_provisioned: default_network.is_fuchsia(),
1205 connectivity_state: props.connectivity_state,
1206 },
1207 ));
1208 } else {
1209 warn!("Could not fetch network data for default network.");
1210 }
1211 }
1212 self.current_generation.default_network += 1;
1213 let mut responders = HashMap::new();
1214 std::mem::swap(&mut self.default_network_responders, &mut responders);
1215 for (id, responder) in responders {
1216 self.generations_by_connection.set_default_network(id, self.current_generation);
1217 match self
1218 .tokens
1219 .ensure_token(NetworkTokenContents {
1220 network_id: default_network,
1221 is_default: true,
1222 })
1223 .get()
1224 .duplicate()
1225 {
1226 Ok(token) => {
1227 if let Err(e) = responder
1228 .send(fnp_properties::NetworksWatchDefaultResponse::Network(token))
1229 {
1230 warn!("Could not send to responder: {e}");
1231 }
1232 }
1233 Err(e) => warn!("Could not duplicate token: {e}"),
1234 };
1235 }
1236 }
1237 None => {
1238 if let Some(telemetry) = &self.telemetry {
1239 telemetry.send(TelemetryEvent::DefaultNetworkLost);
1240 }
1241 self.current_generation.default_network += 1;
1243 let mut responders = HashMap::new();
1244 std::mem::swap(&mut self.default_network_responders, &mut responders);
1245 for (id, responder) in responders {
1246 self.generations_by_connection.set_default_network(id, self.current_generation);
1247 if let Err(e) = responder.send(
1248 fnp_properties::NetworksWatchDefaultResponse::NoDefaultNetwork(
1249 fnp_properties::Empty,
1250 ),
1251 ) {
1252 warn!("Could not send to responder: {e}");
1253 }
1254 }
1255 }
1256 }
1257 }
1258}
1259
1260pub struct ConnectionTagged<Stream: futures::Stream + Unpin> {
1261 next_id: ConnectionId,
1262 streams: futures::stream::SelectAll<Tagged<ConnectionId, Stream>>,
1263}
1264
1265impl<Stream: futures::Stream + Unpin> Default for ConnectionTagged<Stream> {
1266 fn default() -> Self {
1267 Self { next_id: Default::default(), streams: Default::default() }
1268 }
1269}
1270
1271impl<Stream: futures::Stream + Unpin> ConnectionTagged<Stream> {
1272 pub fn push(&mut self, stream: Stream) {
1273 self.streams.push(stream.tagged(self.next_id));
1274 self.next_id.0 += 1;
1275 }
1276}
1277
1278impl<Stream: futures::Stream + Unpin> futures::Stream for ConnectionTagged<Stream> {
1279 type Item = (ConnectionId, <Stream as futures::Stream>::Item);
1280
1281 fn poll_next(
1282 mut self: std::pin::Pin<&mut Self>,
1283 cx: &mut std::task::Context<'_>,
1284 ) -> std::task::Poll<Option<Self::Item>> {
1285 std::pin::Pin::new(&mut self.streams).poll_next(cx)
1286 }
1287}
1288
1289impl<Stream: futures::Stream + Unpin> futures::stream::FusedStream for ConnectionTagged<Stream> {
1290 fn is_terminated(&self) -> bool {
1291 self.streams.is_terminated()
1292 }
1293}
1294
1295#[cfg(test)]
1296mod tests {
1297 use super::*;
1298 use assert_matches::assert_matches;
1299 use std::num::NonZeroU64;
1300 use test_case::test_case;
1301
1302 const ID_1: InterfaceId = InterfaceId(NonZeroU64::new(1).unwrap());
1303 const ID_2: InterfaceId = InterfaceId(NonZeroU64::new(2).unwrap());
1304 const NAME_1: &str = "testif1";
1305 const NAME_2: &str = "testif2";
1306
1307 const FUCHSIA_ID_1: NetworkId = NetworkId::Fuchsia(ID_1);
1308 const FUCHSIA_ID_2: NetworkId = NetworkId::Fuchsia(ID_2);
1309 const DELEGATED_ID_1: NetworkId = NetworkId::Delegated(ID_1);
1310 const DELEGATED_ID_2: NetworkId = NetworkId::Delegated(ID_2);
1311
1312 #[derive(Clone, Copy)]
1313 struct TestNetwork {
1314 id: NetworkId,
1315 name: &'static str,
1316 network_type: fnp_socketproxy::NetworkType,
1317 }
1318
1319 #[derive(Default)]
1320 struct AppliedChanges {
1321 added: bool,
1322 changed_marks: bool,
1323 changed_dns: bool,
1324 }
1325
1326 impl TestNetwork {
1327 fn added(&self, added: bool) -> NetworkPropertiesChange {
1328 NetworkPropertiesChange {
1329 added,
1330 name: Some(self.name.to_string()),
1331 network_type: Some(self.network_type),
1332 ..Default::default()
1333 }
1334 }
1335
1336 fn change_with(
1337 &self,
1338 added: bool,
1339 marks: Option<fnet::Marks>,
1340 dns_servers: Option<Vec<fnet_name::DnsServer_>>,
1341 connectivity_state: Option<fnp_socketproxy::ConnectivityState>,
1342 ) -> NetworkPropertiesChange {
1343 NetworkPropertiesChange {
1344 added,
1345 marks,
1346 dns_servers,
1347 connectivity_state,
1348 name: Some(self.name.to_string()),
1349 network_type: Some(self.network_type),
1350 }
1351 }
1352
1353 fn applied(&self, changes: AppliedChanges) -> UpdateApplied {
1354 let AppliedChanges { added, changed_marks, changed_dns } = changes;
1355 UpdateApplied::NetworkChanged {
1356 network_id: self.id,
1357 added,
1358 changed_marks,
1359 changed_dns,
1360 name: Some(self.name.to_string()),
1361 network_type: Some(self.network_type),
1362 }
1363 }
1364 }
1365
1366 const DELEGATED_NET_1: TestNetwork = TestNetwork {
1367 id: DELEGATED_ID_1,
1368 name: NAME_1,
1369 network_type: fnp_socketproxy::NetworkType::Ethernet,
1370 };
1371 const FUCHSIA_NET_1: TestNetwork = TestNetwork {
1372 id: FUCHSIA_ID_1,
1373 name: NAME_1,
1374 network_type: fnp_socketproxy::NetworkType::Ethernet,
1375 };
1376 const FUCHSIA_NET_2: TestNetwork = TestNetwork {
1377 id: FUCHSIA_ID_2,
1378 name: NAME_2,
1379 network_type: fnp_socketproxy::NetworkType::Wifi,
1380 };
1381
1382 fn test_marks() -> fnet::Marks {
1383 fnet::Marks { mark_1: Some(123), ..Default::default() }
1384 }
1385
1386 fn test_marks_updated() -> fnet::Marks {
1387 fnet::Marks { mark_1: Some(456), ..Default::default() }
1388 }
1389
1390 fn test_dns_1() -> Vec<fnet_name::DnsServer_> {
1391 vec![fnet_name::DnsServer_ {
1392 address: Some(net_declare::fidl_socket_addr!("192.0.2.1:53")),
1393 ..Default::default()
1394 }]
1395 }
1396
1397 fn test_dns_2() -> Vec<fnet_name::DnsServer_> {
1398 vec![fnet_name::DnsServer_ {
1399 address: Some(net_declare::fidl_socket_addr!("192.0.2.2:53")),
1400 ..Default::default()
1401 }]
1402 }
1403
1404 fn delegated_properties() -> NetworkProperties {
1405 NetworkProperties { socket_marks: Some(test_marks()), ..Default::default() }
1406 }
1407
1408 fn fuchsia_properties() -> NetworkProperties {
1409 NetworkProperties::default()
1410 }
1411
1412 fn added_properties(name: &str) -> NetworkPropertiesChange {
1413 NetworkPropertiesChange {
1414 added: true,
1415 marks: None,
1416 dns_servers: None,
1417 connectivity_state: None,
1418 name: Some(name.to_string()),
1419 network_type: Some(fnp_socketproxy::NetworkType::Ethernet),
1420 }
1421 }
1422
1423 impl NetpolNetworksService {
1424 pub(crate) fn default_network(&self) -> Option<NetworkId> {
1425 self.network_registry.default_network
1426 }
1427
1428 pub(crate) fn has_network(&self, id: NetworkId) -> bool {
1429 self.network_registry.networks.contains_key(&id)
1430 }
1431
1432 pub(crate) fn has_token(&self, network_id: NetworkId, is_default: bool) -> bool {
1433 self.tokens.get_token(&NetworkTokenContents { network_id, is_default }).is_some()
1434 }
1435
1436 pub(crate) fn ensure_token_for_test(&mut self, network_id: NetworkId, is_default: bool) {
1437 let _token: crate::network::token_registry::TokenEntry<'_, _> =
1438 self.tokens.ensure_token(NetworkTokenContents { network_id, is_default });
1439 }
1440 }
1441
1442 #[test_case(
1443 DELEGATED_NET_1,
1444 Some(test_marks()),
1445 Some(test_marks_updated()); "delegated network"
1446 )]
1447 #[test_case(FUCHSIA_NET_2, None, None; "fuchsia network")]
1448 fn test_handle_changed_network(
1449 net: TestNetwork,
1450 initial_marks: Option<fnet::Marks>,
1451 updated_marks: Option<fnet::Marks>,
1452 ) {
1453 let mut networks = RegisteredNetworks::default();
1454 let dns1 = test_dns_1();
1455 let dns2 = test_dns_2();
1456
1457 let event = net.change_with(
1459 true,
1460 initial_marks.clone(),
1461 Some(dns1.clone()),
1462 Some(fnp_socketproxy::ConnectivityState::FullConnectivity),
1463 );
1464 assert_eq!(
1465 networks.handle_changed_network(net.id, event),
1466 net.applied(AppliedChanges { added: true, changed_marks: true, changed_dns: true }),
1467 );
1468 assert_eq!(
1469 networks.networks.get(&net.id).expect("network should be present"),
1470 &NetworkProperties {
1471 socket_marks: initial_marks.clone(),
1472 dns_servers: dns1,
1473 connectivity_state: Some(fnp_socketproxy::ConnectivityState::FullConnectivity),
1474 name: Some(net.name.to_string()),
1475 network_type: Some(net.network_type),
1476 }
1477 );
1478
1479 let event = net.change_with(
1482 false,
1483 initial_marks.clone(),
1484 Some(dns2.clone()),
1485 Some(fnp_socketproxy::ConnectivityState::NoConnectivity),
1486 );
1487 assert_eq!(
1488 networks.handle_changed_network(net.id, event),
1489 net.applied(AppliedChanges { added: false, changed_marks: false, changed_dns: true }),
1490 );
1491 assert_eq!(
1492 networks.networks.get(&net.id).expect("network should be present"),
1493 &NetworkProperties {
1494 socket_marks: initial_marks.clone(),
1495 dns_servers: dns2.clone(),
1496 connectivity_state: Some(fnp_socketproxy::ConnectivityState::NoConnectivity),
1497 name: Some(net.name.to_string()),
1498 network_type: Some(net.network_type),
1499 }
1500 );
1501
1502 let marks_changed = initial_marks != updated_marks;
1504 let event = net.change_with(
1505 false,
1506 updated_marks.clone(),
1507 Some(dns2.clone()),
1508 Some(fnp_socketproxy::ConnectivityState::NoConnectivity),
1509 );
1510 assert_eq!(
1511 networks.handle_changed_network(net.id, event),
1512 net.applied(AppliedChanges {
1513 added: false,
1514 changed_marks: marks_changed,
1515 changed_dns: false,
1516 }),
1517 );
1518 assert_eq!(
1519 networks.networks.get(&net.id).expect("network should be present"),
1520 &NetworkProperties {
1521 socket_marks: updated_marks,
1522 dns_servers: dns2.clone(),
1523 connectivity_state: Some(fnp_socketproxy::ConnectivityState::NoConnectivity),
1524 name: Some(net.name.to_string()),
1525 network_type: Some(net.network_type),
1526 }
1527 );
1528 }
1529
1530 #[test]
1531 fn test_handle_changed_network_validation() {
1532 let mut networks = RegisteredNetworks::default();
1533 let marks = test_marks();
1534 let net = DELEGATED_NET_1;
1535
1536 let event = NetworkPropertiesChange { marks: Some(marks.clone()), ..net.added(false) };
1538 assert_eq!(networks.handle_changed_network(net.id, event), UpdateApplied::None);
1539
1540 let event = NetworkPropertiesChange { marks: Some(marks.clone()), ..net.added(true) };
1542 assert_eq!(
1543 networks.handle_changed_network(net.id, event),
1544 net.applied(AppliedChanges { added: true, changed_marks: true, changed_dns: true }),
1545 );
1546
1547 let event = NetworkPropertiesChange { marks: Some(marks.clone()), ..net.added(true) };
1549 assert_eq!(networks.handle_changed_network(net.id, event), UpdateApplied::None);
1550
1551 let fuchsia_net = FUCHSIA_NET_1;
1553 let event =
1554 NetworkPropertiesChange { marks: Some(marks.clone()), ..fuchsia_net.added(true) };
1555 assert_eq!(networks.handle_changed_network(fuchsia_net.id, event), UpdateApplied::None);
1556
1557 let event = net.added(true);
1559 assert_eq!(networks.handle_changed_network(net.id, event), UpdateApplied::None);
1560 }
1561
1562 #[test]
1565 fn fallback_election_and_prioritization() {
1566 let mut networks = RegisteredNetworks::default();
1567
1568 assert_eq!(networks.calculate_active_default(), None);
1570
1571 let _ = networks.networks.insert(DELEGATED_ID_1, delegated_properties());
1573 networks.starnix_default = Some(DELEGATED_ID_1);
1574 assert_eq!(
1575 networks.handle_default_network_update(),
1576 Some(DefaultChangedEvent { previous_default: None })
1577 );
1578 assert_eq!(networks.default_network, Some(DELEGATED_ID_1));
1579
1580 let _ = networks.networks.insert(DELEGATED_ID_2, delegated_properties());
1583 networks.starnix_default = Some(DELEGATED_ID_2);
1584 assert_eq!(
1585 networks.handle_default_network_update(),
1586 Some(DefaultChangedEvent { previous_default: Some(DELEGATED_ID_1) })
1587 );
1588 assert_eq!(networks.default_network, Some(DELEGATED_ID_2));
1589
1590 let _ = networks.networks.insert(FUCHSIA_ID_2, fuchsia_properties());
1593 assert_eq!(
1594 networks.handle_default_network_update(),
1595 Some(DefaultChangedEvent { previous_default: Some(DELEGATED_ID_2) })
1596 );
1597 assert_eq!(networks.default_network, Some(FUCHSIA_ID_2));
1598
1599 let _ = networks.networks.insert(FUCHSIA_ID_1, fuchsia_properties());
1602 assert_eq!(
1603 networks.handle_default_network_update(),
1604 Some(DefaultChangedEvent { previous_default: Some(FUCHSIA_ID_2) })
1605 );
1606 assert_eq!(networks.default_network, Some(FUCHSIA_ID_1));
1607
1608 let _ = networks.networks.remove(&FUCHSIA_ID_1);
1610 assert_eq!(
1611 networks.handle_default_network_update(),
1612 Some(DefaultChangedEvent { previous_default: Some(FUCHSIA_ID_1) })
1613 );
1614 assert_eq!(networks.default_network, Some(FUCHSIA_ID_2));
1615
1616 let _ = networks.networks.remove(&FUCHSIA_ID_2);
1619 assert_eq!(
1620 networks.handle_default_network_update(),
1621 Some(DefaultChangedEvent { previous_default: Some(FUCHSIA_ID_2) })
1622 );
1623 assert_eq!(networks.default_network, Some(DELEGATED_ID_2));
1624
1625 assert_eq!(
1627 networks.apply(PropertyUpdate::LoseDefaultNetwork),
1628 RegistryUpdateResult {
1629 event: UpdateApplied::None,
1630 default_changed: Some(DefaultChangedEvent {
1631 previous_default: Some(DELEGATED_ID_2)
1632 })
1633 }
1634 );
1635 assert_eq!(networks.default_network, None);
1636
1637 assert_eq!(
1639 networks.apply(PropertyUpdate::ChangeNetwork(DELEGATED_ID_1, NetworkUpdate::Remove)),
1640 RegistryUpdateResult {
1641 event: UpdateApplied::NetworkRemoved(DELEGATED_ID_1),
1642 default_changed: None
1643 }
1644 );
1645 }
1646
1647 #[test]
1651 fn test_remove_fuchsia_network_fallback() {
1652 let mut networks = RegisteredNetworks::default();
1653 let marks = test_marks();
1654 let fuchsia_added = NetworkPropertiesChange { added: true, ..Default::default() };
1655
1656 let result = networks.apply(PropertyUpdate::ChangeNetwork(
1658 FUCHSIA_ID_1,
1659 NetworkUpdate::Properties(fuchsia_added.clone()),
1660 ));
1661 assert_matches!(
1662 result.event,
1663 UpdateApplied::NetworkChanged { network_id: id, added: true, .. }
1664 if id == FUCHSIA_ID_1
1665 );
1666 assert_eq!(result.default_changed, Some(DefaultChangedEvent { previous_default: None }));
1667
1668 let result = networks.apply(PropertyUpdate::ChangeNetwork(
1670 FUCHSIA_ID_2,
1671 NetworkUpdate::Properties(fuchsia_added),
1672 ));
1673 assert_matches!(
1674 result.event,
1675 UpdateApplied::NetworkChanged { network_id: id, added: true, .. }
1676 if id == FUCHSIA_ID_2
1677 );
1678 assert_eq!(result.default_changed, None);
1679
1680 let result = networks.apply(PropertyUpdate::ChangeNetwork(
1682 DELEGATED_ID_1,
1683 NetworkUpdate::Properties(NetworkPropertiesChange {
1684 added: true,
1685 marks: Some(marks),
1686 ..Default::default()
1687 }),
1688 ));
1689 assert_matches!(
1690 result.event,
1691 UpdateApplied::NetworkChanged { network_id: id, added: true, .. }
1692 if id == DELEGATED_ID_1
1693 );
1694 assert_eq!(result.default_changed, None);
1695
1696 let result = networks
1699 .apply(PropertyUpdate::ChangeNetwork(DELEGATED_ID_1, NetworkUpdate::MakeDefault));
1700 assert_eq!(
1701 result,
1702 RegistryUpdateResult { event: UpdateApplied::None, default_changed: None }
1703 );
1704 assert_eq!(networks.default_network, Some(FUCHSIA_ID_1));
1705
1706 let result =
1709 networks.apply(PropertyUpdate::ChangeNetwork(FUCHSIA_ID_1, NetworkUpdate::Remove));
1710 assert_eq!(result.event, UpdateApplied::NetworkRemoved(FUCHSIA_ID_1));
1711 assert_eq!(
1712 result.default_changed,
1713 Some(DefaultChangedEvent { previous_default: Some(FUCHSIA_ID_1) })
1714 );
1715 assert_eq!(networks.default_network, Some(FUCHSIA_ID_2));
1716
1717 let result =
1720 networks.apply(PropertyUpdate::ChangeNetwork(FUCHSIA_ID_2, NetworkUpdate::Remove));
1721 assert_eq!(result.event, UpdateApplied::NetworkRemoved(FUCHSIA_ID_2));
1722 assert_eq!(
1723 result.default_changed,
1724 Some(DefaultChangedEvent { previous_default: Some(FUCHSIA_ID_2) })
1725 );
1726 assert_eq!(networks.default_network, Some(DELEGATED_ID_1));
1727
1728 let result =
1730 networks.apply(PropertyUpdate::ChangeNetwork(DELEGATED_ID_1, NetworkUpdate::Remove));
1731 assert_eq!(
1732 result,
1733 RegistryUpdateResult { event: UpdateApplied::None, default_changed: None }
1734 );
1735 assert!(networks.networks.contains_key(&DELEGATED_ID_1));
1736 assert_eq!(networks.default_network, Some(DELEGATED_ID_1));
1737 }
1738
1739 #[test]
1740 fn remove_non_default_fuchsia_preserves_default() {
1741 let mut networks = RegisteredNetworks::default();
1742
1743 let _ = networks.networks.insert(FUCHSIA_ID_1, NetworkProperties::default());
1745 let _ = networks.networks.insert(FUCHSIA_ID_2, NetworkProperties::default());
1746
1747 assert_eq!(
1749 networks.handle_default_network_update(),
1750 Some(DefaultChangedEvent { previous_default: None })
1751 );
1752 assert_eq!(networks.default_network, Some(FUCHSIA_ID_1));
1753
1754 assert_eq!(
1756 networks.apply(PropertyUpdate::ChangeNetwork(FUCHSIA_ID_2, NetworkUpdate::Remove)),
1757 RegistryUpdateResult {
1758 event: UpdateApplied::NetworkRemoved(FUCHSIA_ID_2),
1759 default_changed: None
1760 }
1761 );
1762
1763 assert_eq!(networks.default_network, Some(FUCHSIA_ID_1));
1765 }
1766
1767 #[fuchsia::test]
1768 async fn remove_default_network_cleans_up_tokens() {
1769 let mut service = NetpolNetworksService::default();
1770
1771 service
1773 .update(PropertyUpdate::ChangeNetwork(
1774 FUCHSIA_ID_1,
1775 NetworkUpdate::Properties(added_properties(NAME_1)),
1776 ))
1777 .await;
1778
1779 service
1780 .update(PropertyUpdate::ChangeNetwork(
1781 FUCHSIA_ID_2,
1782 NetworkUpdate::Properties(added_properties(NAME_2)),
1783 ))
1784 .await;
1785
1786 assert_eq!(service.default_network(), Some(FUCHSIA_ID_1));
1788
1789 assert!(service.has_token(FUCHSIA_ID_1, false ));
1791 assert!(service.has_token(FUCHSIA_ID_2, false ));
1792
1793 service.ensure_token_for_test(FUCHSIA_ID_1, true );
1795 assert!(service.has_token(FUCHSIA_ID_1, true ));
1796
1797 service.update(PropertyUpdate::ChangeNetwork(FUCHSIA_ID_1, NetworkUpdate::Remove)).await;
1800
1801 assert_eq!(service.default_network(), Some(FUCHSIA_ID_2));
1803
1804 assert!(!service.has_token(FUCHSIA_ID_1, false ));
1806 assert!(!service.has_token(FUCHSIA_ID_1, true ));
1807
1808 assert!(service.has_token(FUCHSIA_ID_2, false ));
1810 }
1811}