1use crate::InterfaceId;
6use anyhow::Context as _;
7use async_utils::stream::{Tagged, WithTag as _};
8use dns_server_watcher::DnsServers;
9use fidl::endpoints::{ControlHandle as _, Responder as _};
10use fidl::HandleBased as _;
11use log::{error, info, warn};
12use std::collections::HashMap;
13
14mod token_map;
15
16use {
17 fidl_fuchsia_net as fnet, fidl_fuchsia_net_name as fnet_name,
18 fidl_fuchsia_net_policy_properties as fnp_properties,
19};
20
21pub trait NetworkTokenExt: Sized {
22 fn duplicate(&self) -> Result<Self, zx::Status>;
23}
24
25impl NetworkTokenExt for fnp_properties::NetworkToken {
26 fn duplicate(&self) -> Result<fnp_properties::NetworkToken, zx::Status> {
27 Ok(fnp_properties::NetworkToken {
28 value: Some(
29 self.value
30 .as_ref()
31 .ok_or(zx::Status::NOT_FOUND)?
32 .duplicate_handle(zx::Rights::SAME_RIGHTS)?,
33 ),
34 ..Default::default()
35 })
36 }
37}
38
39pub(crate) struct NetworkTokenContents {
40 connection_id: ConnectionId,
41 interface_id: InterfaceId,
42}
43
44#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
45pub(crate) struct ConnectionId(usize);
46
47#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
48pub(crate) struct UpdateGeneration {
49 default_network: usize,
52
53 properties: usize,
56}
57
58#[derive(Clone, Debug, Default)]
59pub(crate) struct UpdateGenerations(HashMap<ConnectionId, UpdateGeneration>);
60
61impl UpdateGenerations {
62 fn default_network(&self, id: &ConnectionId) -> Option<usize> {
63 self.0.get(id).map(|g| g.default_network)
64 }
65
66 fn set_default_network(&mut self, id: ConnectionId, generation: UpdateGeneration) {
67 self.0.entry(id).or_default().default_network = generation.default_network;
68 }
69
70 fn properties(&self, id: &ConnectionId) -> Option<usize> {
71 self.0.get(id).map(|g| g.properties)
72 }
73
74 fn set_properties(&mut self, id: ConnectionId, generation: UpdateGeneration) {
75 self.0.entry(id).or_default().properties = generation.properties;
76 }
77
78 fn remove(&mut self, id: &ConnectionId) -> Option<UpdateGeneration> {
79 self.0.remove(id)
80 }
81}
82
83#[derive(Debug)]
84pub(crate) struct NetworkPropertyResponder {
85 token: fidl::EventPair,
86 watched_properties: Vec<fnp_properties::Property>,
87 responder: fnp_properties::NetworksWatchPropertiesResponder,
88}
89
90#[derive(Default)]
91pub(crate) struct NetpolNetworksService {
92 current_generation: UpdateGeneration,
94 generations_by_connection: UpdateGenerations,
96 default_network_responders:
98 HashMap<ConnectionId, fnp_properties::NetworksWatchDefaultResponder>,
99 tokens: token_map::TokenMap<NetworkTokenContents>,
100 property_responders: HashMap<ConnectionId, NetworkPropertyResponder>,
102 network_properties: NetworkProperties,
104}
105
106#[derive(Default, Clone)]
107struct NetworkProperties {
108 default_network: Option<InterfaceId>,
110 socket_marks: HashMap<InterfaceId, fnet::Marks>,
112 dns_servers: Vec<fnet_name::DnsServer_>,
114}
115
116impl NetworkProperties {
117 fn apply(&mut self, update: PropertyUpdate) -> UpdatesApplied {
118 let mut updates = UpdatesApplied::default();
119
120 if let Some(new_default_network) = update.default_network {
121 updates.default_network = self.handle_default_network_update(new_default_network);
122 }
123
124 if let Some((netid, marks)) = update.socket_marks {
125 updates.socket_marks_network = self.handle_socket_marks_update(netid, marks);
126 }
127
128 if let Some(dns) = update.dns {
129 updates.dns_changed = self.dns_servers != dns;
130 self.dns_servers = dns;
131 }
132
133 updates
134 }
135
136 fn handle_default_network_update(
142 &mut self,
143 new_default_network: Option<InterfaceId>,
144 ) -> Option<Option<InterfaceId>> {
145 if new_default_network == self.default_network {
147 return None;
148 }
149
150 let old_default_network = self.default_network;
151 if let (None, Some(old_default_network_id)) = (new_default_network, old_default_network) {
152 let _: Option<_> = self.socket_marks.remove(&old_default_network_id);
153 }
154 self.default_network = new_default_network;
155 return Some(old_default_network);
156 }
157
158 fn handle_socket_marks_update(
164 &mut self,
165 netid: InterfaceId,
166 marks: fnet::Marks,
167 ) -> Option<InterfaceId> {
168 if self.socket_marks.contains_key(&netid)
171 && self
172 .socket_marks
173 .get(&netid)
174 .and_then(|old_marks| Some(*old_marks == marks))
175 .unwrap_or_default()
176 {
177 return None;
178 }
179
180 let _: Option<_> = self.socket_marks.insert(netid, marks);
181 return Some(netid);
182 }
183
184 fn maybe_respond(
185 &self,
186 network: &NetworkTokenContents,
187 responder: NetworkPropertyResponder,
188 ) -> Option<NetworkPropertyResponder> {
189 let mut updates = Vec::new();
190 updates.add_socket_marks(self, network, &responder);
191 updates.add_dns(self, network, &responder);
192
193 if updates.is_empty() {
194 Some(responder)
195 } else {
196 if let Err(e) = responder.responder.send(Ok(&updates)) {
197 warn!("Could not send to responder: {e}");
198 }
199 None
200 }
201 }
202}
203
204trait PropertyUpdates {
205 fn add_socket_marks(
206 &mut self,
207 properties: &NetworkProperties,
208 network: &NetworkTokenContents,
209 responder: &NetworkPropertyResponder,
210 );
211 fn add_dns(
212 &mut self,
213 properties: &NetworkProperties,
214 network: &NetworkTokenContents,
215 responder: &NetworkPropertyResponder,
216 );
217}
218
219impl PropertyUpdates for Vec<fnp_properties::PropertyUpdate> {
220 fn add_socket_marks(
221 &mut self,
222 properties: &NetworkProperties,
223 network: &NetworkTokenContents,
224 responder: &NetworkPropertyResponder,
225 ) {
226 if !responder.watched_properties.contains(&fnp_properties::Property::SocketMarks) {
227 return;
228 }
229 match properties.socket_marks.get(&network.interface_id) {
230 Some(marks) => self.push(fnp_properties::PropertyUpdate::SocketMarks(marks.clone())),
231 None => {}
232 }
233 }
234
235 fn add_dns(
236 &mut self,
237 properties: &NetworkProperties,
238 network: &NetworkTokenContents,
239 responder: &NetworkPropertyResponder,
240 ) {
241 if !responder.watched_properties.contains(&fnp_properties::Property::DnsConfiguration) {
242 return;
243 }
244
245 let interface_id = network.interface_id;
246 self.push(fnp_properties::PropertyUpdate::DnsConfiguration(
247 fnp_properties::DnsConfiguration {
248 servers: Some(
249 properties
250 .dns_servers
251 .iter()
252 .filter(|d| {
253 match &d.source {
254 Some(source) => match source {
255 fnet_name::DnsServerSource::StaticSource(_) => true,
256 fnet_name::DnsServerSource::SocketProxy(
257 fnet_name::SocketProxyDnsServerSource {
258 source_interface,
259 ..
260 },
261 )
262 | fnet_name::DnsServerSource::Dhcp(
263 fnet_name::DhcpDnsServerSource { source_interface, .. },
264 )
265 | fnet_name::DnsServerSource::Ndp(
266 fnet_name::NdpDnsServerSource { source_interface, .. },
267 )
268 | fnet_name::DnsServerSource::Dhcpv6(
269 fnet_name::Dhcpv6DnsServerSource {
270 source_interface, ..
271 },
272 ) => match (interface_id, source_interface) {
273 (_, None) => true,
274 (id1, Some(id2)) => id1.get() == *id2,
275 },
276
277 _ => {
278 error!("unhandled DnsServerSource: {source:?}");
279 false
280 }
281 },
282
283 None => true,
285 }
286 })
287 .cloned()
288 .collect::<Vec<_>>(),
289 ),
290 ..Default::default()
291 },
292 ));
293 }
294}
295
296#[derive(Default)]
297pub(crate) struct PropertyUpdate {
298 default_network: Option<Option<InterfaceId>>,
299 socket_marks: Option<(InterfaceId, fnet::Marks)>,
300 dns: Option<Vec<fnet_name::DnsServer_>>,
301}
302
303#[derive(Default, Debug)]
304struct UpdatesApplied {
305 default_network: Option<Option<InterfaceId>>,
307
308 socket_marks_network: Option<InterfaceId>,
310
311 dns_changed: bool,
313}
314
315impl PropertyUpdate {
316 pub fn default_network<N: TryInto<InterfaceId>>(
317 mut self,
318 network_id: N,
319 ) -> Result<Self, N::Error> {
320 self.default_network = Some(Some(network_id.try_into()?));
321 Ok(self)
322 }
323
324 pub fn default_network_lost(mut self) -> Self {
325 self.default_network = Some(None);
326 self
327 }
328
329 pub fn socket_marks<N: TryInto<InterfaceId>, Marks: Into<fnet::Marks>>(
330 mut self,
331 network_id: N,
332 marks: Marks,
333 ) -> Result<Self, N::Error> {
334 self.socket_marks = Some((network_id.try_into()?, marks.into()));
335 Ok(self)
336 }
337
338 pub fn dns(mut self, dns_servers: &DnsServers) -> Self {
339 self.dns = Some(dns_servers.consolidated_dns_servers());
340 self
341 }
342}
343
344impl NetpolNetworksService {
345 pub(crate) async fn handle_network_attributes_request(
346 &mut self,
347 id: ConnectionId,
348 req: Result<fnp_properties::NetworksRequest, fidl::Error>,
349 ) -> Result<(), anyhow::Error> {
350 let req = req.context("network attributes request")?;
351 match req {
352 fnp_properties::NetworksRequest::WatchDefault { responder } => {
353 match self.default_network_responders.entry(id) {
354 std::collections::hash_map::Entry::Occupied(_) => {
355 warn!(
356 "Only one call to fuchsia.net.policy.properties/Networks.WatchDefault \
357 may be active per connection"
358 );
359 responder
360 .control_handle()
361 .shutdown_with_epitaph(zx::Status::CONNECTION_ABORTED)
362 }
363 std::collections::hash_map::Entry::Vacant(vacant_entry) => {
364 let interface_id = if self
365 .generations_by_connection
366 .default_network(&id)
367 .unwrap_or_default()
368 < self.current_generation.default_network
369 {
370 match self.network_properties.default_network {
371 Some(interface_id) => Some(interface_id),
372 None => None,
373 }
374 } else {
375 None
376 };
377 if let Some(interface_id) = interface_id {
378 self.generations_by_connection
379 .set_default_network(id, self.current_generation);
380 let token = self
381 .tokens
382 .insert_data(NetworkTokenContents {
383 connection_id: id,
384 interface_id,
385 })
386 .await;
387 responder.send(
388 fnp_properties::NetworksWatchDefaultResponse::Network(
389 fnp_properties::NetworkToken {
390 value: Some(token),
391 ..Default::default()
392 },
393 ),
394 )?;
395
396 if let Some(responder) = self.property_responders.remove(&id) {
397 let _ = self.generations_by_connection.remove(&id);
398 let _ = responder
399 .responder
400 .send(Err(fnp_properties::WatchError::DefaultNetworkChanged));
401 }
402 } else {
403 let _ = vacant_entry.insert(responder);
404 }
405 }
406 }
407 }
408 fnp_properties::NetworksRequest::WatchProperties {
409 payload: fnp_properties::NetworksWatchPropertiesRequest { network, properties, .. },
410 responder,
411 } => match (network, properties) {
412 (None, _) | (_, None) => {
413 responder.send(Err(fnp_properties::WatchError::MissingRequiredArgument))?
414 }
415 (Some(network), Some(properties)) => {
416 if properties.is_empty() {
417 responder.send(Err(fnp_properties::WatchError::NoProperties))?;
418 } else if let Some(token) = network.value {
419 match self.property_responders.entry(id) {
420 std::collections::hash_map::Entry::Occupied(_) => {
421 warn!(
422 "Only one call to fuchsia.net.policy.properties/Networks.WatchProperties \
423 may be active per connection"
424 );
425 responder
426 .control_handle()
427 .shutdown_with_epitaph(zx::Status::CONNECTION_ABORTED)
428 }
429 std::collections::hash_map::Entry::Vacant(vacant_entry) => {
430 match self.tokens.get(&token).await {
431 None => {
432 warn!("Unknown network token. ({token:?}");
433 responder.send(Err(
434 fnp_properties::WatchError::InvalidNetworkToken,
435 ))?;
436 }
437 Some(network_contents) => {
438 if network_contents.connection_id != id {
439 warn!(
440 "Cannot watch a NetworkToken that was not created by \
441 this connection."
442 );
443 responder.send(Err(
444 fnp_properties::WatchError::InvalidNetworkToken,
445 ))?;
446 } else {
447 let responder = NetworkPropertyResponder {
448 token,
449 watched_properties: properties,
450 responder,
451 };
452 if self
453 .generations_by_connection
454 .properties(&id)
455 .unwrap_or_default()
456 < self.current_generation.properties
457 {
458 self.generations_by_connection
459 .set_properties(id, self.current_generation);
460 if let Some(responder) = self
461 .network_properties
462 .maybe_respond(&network_contents, responder)
463 {
464 let _: &mut NetworkPropertyResponder =
465 vacant_entry.insert(responder);
466 }
467 } else {
468 let _: &mut NetworkPropertyResponder =
469 vacant_entry.insert(responder);
470 }
471 }
472 }
473 }
474 }
475 }
476 } else {
477 responder.send(Err(fnp_properties::WatchError::InvalidNetworkToken))?;
478 }
479 }
480 },
481 _ => {
482 warn!("Received unexpected request {req:?}");
483 }
484 }
485
486 Ok(())
487 }
488
489 async fn changed_default_network(
490 error: fnp_properties::WatchError,
491 responders: &mut HashMap<ConnectionId, NetworkPropertyResponder>,
492 generations: &mut UpdateGenerations,
493 ) {
494 let mut r = HashMap::new();
495 std::mem::swap(&mut r, responders);
496 r = r
497 .into_iter()
498 .filter_map(|(id, responder)| {
499 let _ = generations.remove(&id);
501 let _ = responder.responder.send(Err(error));
502 None
503 })
504 .collect::<HashMap<_, _>>();
505 std::mem::swap(&mut r, responders);
506 }
507
508 pub(crate) async fn remove_network<ID: Into<InterfaceId>>(&mut self, interface_id: ID) {
509 let interface_id = interface_id.into();
510 info!("Removing interface {interface_id}. Reporting NETWORK_GONE to all clients.");
511 let mut responders = HashMap::new();
512 std::mem::swap(&mut self.property_responders, &mut responders);
513 for (id, responder) in responders {
514 let network = match self.tokens.get(&responder.token).await {
515 Some(network) => network,
516 None => {
517 warn!("Could not fetch network data for responder");
518 continue;
519 }
520 };
521 if network.interface_id == interface_id {
522 if let Err(e) =
524 responder.responder.send(Err(fnp_properties::WatchError::NetworkGone))
525 {
526 warn!("Could not send to responder: {e}");
527 }
528 } else {
529 if self.property_responders.insert(id, responder).is_some() {
530 error!("Re-inserted in an existing responder slot. This should be impossible.");
531 }
532 }
533 }
534 }
535
536 pub(crate) async fn update(&mut self, update: PropertyUpdate) {
537 self.current_generation.properties += 1;
538 let updates_applied = self.network_properties.apply(update);
539 let mut property_responders = HashMap::new();
540 std::mem::swap(&mut self.property_responders, &mut property_responders);
541
542 if updates_applied.default_network.is_some() {
543 if let Some(default_network) = self.network_properties.default_network {
544 self.current_generation.default_network += 1;
545 let mut responders = HashMap::new();
546 std::mem::swap(&mut self.default_network_responders, &mut responders);
547 for (id, responder) in responders {
548 self.generations_by_connection.set_default_network(id, self.current_generation);
549
550 let token = self
551 .tokens
552 .insert_data(NetworkTokenContents {
553 connection_id: id,
554 interface_id: default_network,
555 })
556 .await;
557
558 if let Err(e) =
559 responder.send(fnp_properties::NetworksWatchDefaultResponse::Network(
560 fnp_properties::NetworkToken {
561 value: Some(token),
562 ..Default::default()
563 },
564 ))
565 {
566 warn!("Could not send to responder: {e}");
567 }
568 }
569
570 NetpolNetworksService::changed_default_network(
571 fnp_properties::WatchError::DefaultNetworkChanged,
572 &mut property_responders,
573 &mut self.generations_by_connection,
574 )
575 .await;
576 } else {
577 self.current_generation.default_network += 1;
579 let mut responders = HashMap::new();
580 std::mem::swap(&mut self.default_network_responders, &mut responders);
581 for (id, responder) in responders {
582 self.generations_by_connection.set_default_network(id, self.current_generation);
583 if let Err(e) = responder.send(
584 fnp_properties::NetworksWatchDefaultResponse::NoDefaultNetwork(
585 fnp_properties::Empty,
586 ),
587 ) {
588 warn!("Could not send to responder: {e}");
589 }
590 }
591
592 NetpolNetworksService::changed_default_network(
593 fnp_properties::WatchError::DefaultNetworkLost,
594 &mut property_responders,
595 &mut self.generations_by_connection,
596 )
597 .await;
598 }
599 }
600
601 for (id, responder) in property_responders {
602 let mut updates = Vec::new();
603 let network = match self.tokens.get(&responder.token).await {
604 Some(network) => network,
605 None => {
606 warn!("Could not fetch network data for responder");
607 continue;
608 }
609 };
610
611 if let Some(network_id) = updates_applied.socket_marks_network {
612 if network.interface_id == network_id {
613 updates.add_socket_marks(&self.network_properties, &network, &responder);
614 }
615 }
616 if updates_applied.dns_changed {
617 updates.add_dns(&self.network_properties, &network, &responder);
618 }
619
620 self.generations_by_connection.set_properties(id, self.current_generation);
621 if updates.is_empty() {
622 if self.property_responders.insert(id, responder).is_some() {
623 warn!("Re-inserted in an existing responder slot. This should be impossible.");
624 }
625 } else {
626 if let Err(e) = responder.responder.send(Ok(&updates)) {
627 warn!("Could not send to responder: {e}");
628 }
629 }
630 }
631 }
632}
633
634#[derive(Default)]
635pub(crate) struct NetworksRequestStreams {
636 next_id: ConnectionId,
637 request_streams:
638 futures::stream::SelectAll<Tagged<ConnectionId, fnp_properties::NetworksRequestStream>>,
639}
640
641impl NetworksRequestStreams {
642 pub fn push(&mut self, stream: fnp_properties::NetworksRequestStream) {
643 self.request_streams.push(stream.tagged(self.next_id));
644 self.next_id.0 += 1;
645 }
646}
647
648impl futures::Stream for NetworksRequestStreams {
649 type Item = (ConnectionId, Result<fnp_properties::NetworksRequest, fidl::Error>);
650
651 fn poll_next(
652 mut self: std::pin::Pin<&mut Self>,
653 cx: &mut std::task::Context<'_>,
654 ) -> std::task::Poll<Option<Self::Item>> {
655 std::pin::Pin::new(&mut self.request_streams).poll_next(cx)
656 }
657}
658
659impl futures::stream::FusedStream for NetworksRequestStreams {
660 fn is_terminated(&self) -> bool {
661 self.request_streams.is_terminated()
662 }
663}