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
83pub(crate) struct NetworkPropertyResponder {
84 token: fidl::EventPair,
85 watched_properties: Vec<fnp_properties::Property>,
86 responder: fnp_properties::NetworksWatchPropertiesResponder,
87}
88
89#[derive(Default)]
90pub(crate) struct NetpolNetworksService {
91 current_generation: UpdateGeneration,
93 generations_by_connection: UpdateGenerations,
95 default_network_responders:
97 HashMap<ConnectionId, fnp_properties::NetworksWatchDefaultResponder>,
98 tokens: token_map::TokenMap<NetworkTokenContents>,
99 property_responders: HashMap<ConnectionId, NetworkPropertyResponder>,
101 network_properties: NetworkProperties,
103}
104
105#[derive(Default, Clone)]
106struct NetworkProperties {
107 default_network: Option<InterfaceId>,
109 socket_marks: HashMap<InterfaceId, fnet::Marks>,
111 dns_servers: Vec<fnet_name::DnsServer_>,
113}
114
115impl NetworkProperties {
116 fn apply(&mut self, update: PropertyUpdate) -> UpdatesApplied {
117 let mut updates = UpdatesApplied::default();
118
119 if let Some(netid) = update.default_network {
120 updates.default_network = Some(self.default_network);
121 self.default_network = Some(netid);
122 }
123
124 if let Some((netid, marks)) = update.socket_marks {
125 updates.socket_marks_network = Some(netid);
126 let _: Option<_> = self.socket_marks.insert(netid, marks);
127 }
128
129 if let Some(dns) = update.dns {
130 updates.dns_changed = self.dns_servers != dns;
131 self.dns_servers = dns;
132 }
133
134 updates
135 }
136
137 fn maybe_respond(
138 &self,
139 network: &NetworkTokenContents,
140 responder: NetworkPropertyResponder,
141 ) -> Option<NetworkPropertyResponder> {
142 let mut updates = Vec::new();
143 updates.add_socket_marks(self, network, &responder);
144 updates.add_dns(self, network, &responder);
145
146 if updates.is_empty() {
147 Some(responder)
148 } else {
149 if let Err(e) = responder.responder.send(Ok(&updates)) {
150 warn!("Could not send to responder: {e}");
151 }
152 None
153 }
154 }
155}
156
157trait PropertyUpdates {
158 fn add_socket_marks(
159 &mut self,
160 properties: &NetworkProperties,
161 network: &NetworkTokenContents,
162 responder: &NetworkPropertyResponder,
163 );
164 fn add_dns(
165 &mut self,
166 properties: &NetworkProperties,
167 network: &NetworkTokenContents,
168 responder: &NetworkPropertyResponder,
169 );
170}
171
172impl PropertyUpdates for Vec<fnp_properties::PropertyUpdate> {
173 fn add_socket_marks(
174 &mut self,
175 properties: &NetworkProperties,
176 network: &NetworkTokenContents,
177 responder: &NetworkPropertyResponder,
178 ) {
179 if !responder.watched_properties.contains(&fnp_properties::Property::SocketMarks) {
180 return;
181 }
182 match properties.socket_marks.get(&network.interface_id) {
183 Some(marks) => self.push(fnp_properties::PropertyUpdate::SocketMarks(marks.clone())),
184 None => {}
185 }
186 }
187
188 fn add_dns(
189 &mut self,
190 properties: &NetworkProperties,
191 network: &NetworkTokenContents,
192 responder: &NetworkPropertyResponder,
193 ) {
194 if !responder.watched_properties.contains(&fnp_properties::Property::DnsConfiguration) {
195 return;
196 }
197
198 let interface_id = network.interface_id;
199 self.push(fnp_properties::PropertyUpdate::DnsConfiguration(
200 fnp_properties::DnsConfiguration {
201 servers: Some(
202 properties
203 .dns_servers
204 .iter()
205 .filter(|d| {
206 match &d.source {
207 Some(source) => match source {
208 fnet_name::DnsServerSource::StaticSource(_) => true,
209 fnet_name::DnsServerSource::SocketProxy(
210 fnet_name::SocketProxyDnsServerSource {
211 source_interface,
212 ..
213 },
214 )
215 | fnet_name::DnsServerSource::Dhcp(
216 fnet_name::DhcpDnsServerSource { source_interface, .. },
217 )
218 | fnet_name::DnsServerSource::Ndp(
219 fnet_name::NdpDnsServerSource { source_interface, .. },
220 )
221 | fnet_name::DnsServerSource::Dhcpv6(
222 fnet_name::Dhcpv6DnsServerSource {
223 source_interface, ..
224 },
225 ) => match (interface_id, source_interface) {
226 (_, None) => true,
227 (id1, Some(id2)) => id1.get() == *id2,
228 },
229
230 _ => {
231 error!("unhandled DnsServerSource: {source:?}");
232 false
233 }
234 },
235
236 None => true,
238 }
239 })
240 .cloned()
241 .collect::<Vec<_>>(),
242 ),
243 ..Default::default()
244 },
245 ));
246 }
247}
248
249#[derive(Default)]
250pub(crate) struct PropertyUpdate {
251 default_network: Option<InterfaceId>,
252 socket_marks: Option<(InterfaceId, fnet::Marks)>,
253 dns: Option<Vec<fnet_name::DnsServer_>>,
254}
255
256#[derive(Default, Debug)]
257struct UpdatesApplied {
258 default_network: Option<Option<InterfaceId>>,
260
261 socket_marks_network: Option<InterfaceId>,
263
264 dns_changed: bool,
266}
267
268impl PropertyUpdate {
269 pub fn default_network<N: TryInto<InterfaceId>>(
270 mut self,
271 network_id: N,
272 ) -> Result<Self, N::Error> {
273 self.default_network = Some(network_id.try_into()?);
274 Ok(self)
275 }
276
277 pub fn socket_marks<N: TryInto<InterfaceId>, Marks: Into<fnet::Marks>>(
278 mut self,
279 network_id: N,
280 marks: Marks,
281 ) -> Result<Self, N::Error> {
282 self.socket_marks = Some((network_id.try_into()?, marks.into()));
283 Ok(self)
284 }
285
286 pub fn dns(mut self, dns_servers: &DnsServers) -> Self {
287 self.dns = Some(dns_servers.consolidated_dns_servers());
288 self
289 }
290}
291
292impl NetpolNetworksService {
293 pub(crate) async fn handle_network_attributes_request(
294 &mut self,
295 id: ConnectionId,
296 req: Result<fnp_properties::NetworksRequest, fidl::Error>,
297 ) -> Result<(), anyhow::Error> {
298 let req = req.context("network attributes request")?;
299 match req {
300 fnp_properties::NetworksRequest::WatchDefault { responder } => {
301 match self.default_network_responders.entry(id) {
302 std::collections::hash_map::Entry::Occupied(_) => {
303 warn!(
304 "Only one call to fuchsia.net.policy.properties/Networks.WatchDefault \
305 may be active per connection"
306 );
307 responder
308 .control_handle()
309 .shutdown_with_epitaph(zx::Status::CONNECTION_ABORTED)
310 }
311 std::collections::hash_map::Entry::Vacant(vacant_entry) => {
312 let interface_id = if self
313 .generations_by_connection
314 .default_network(&id)
315 .unwrap_or_default()
316 < self.current_generation.default_network
317 {
318 match self.network_properties.default_network {
319 Some(interface_id) => Some(interface_id),
320 None => None,
321 }
322 } else {
323 None
324 };
325 if let Some(interface_id) = interface_id {
326 self.generations_by_connection
327 .set_default_network(id, self.current_generation);
328 let token = self
329 .tokens
330 .insert_data(NetworkTokenContents {
331 connection_id: id,
332 interface_id,
333 })
334 .await;
335 responder.send(fnp_properties::NetworkToken {
336 value: Some(token),
337 ..Default::default()
338 })?;
339 } else {
340 let _ = vacant_entry.insert(responder);
341 }
342
343 if let Some(responder) = self.property_responders.remove(&id) {
344 let _ = self.generations_by_connection.remove(&id);
345 let _ = responder
346 .responder
347 .send(Err(fnp_properties::WatchError::DefaultNetworkChanged));
348 }
349 }
350 }
351 }
352 fnp_properties::NetworksRequest::WatchProperties {
353 payload: fnp_properties::NetworksWatchPropertiesRequest { network, properties, .. },
354 responder,
355 } => match (network, properties) {
356 (None, _) | (_, None) => {
357 responder.send(Err(fnp_properties::WatchError::MissingRequiredArgument))?
358 }
359 (Some(network), Some(properties)) => {
360 if properties.is_empty() {
361 responder.send(Err(fnp_properties::WatchError::NoProperties))?;
362 } else if let Some(token) = network.value {
363 match self.property_responders.entry(id) {
364 std::collections::hash_map::Entry::Occupied(_) => {
365 warn!(
366 "Only one call to fuchsia.net.policy.properties/Networks.WatchProperties \
367 may be active per connection"
368 );
369 responder
370 .control_handle()
371 .shutdown_with_epitaph(zx::Status::CONNECTION_ABORTED)
372 }
373 std::collections::hash_map::Entry::Vacant(vacant_entry) => {
374 match self.tokens.get(&token).await {
375 None => {
376 warn!("Unknown network token. ({token:?}");
377 responder.send(Err(
378 fnp_properties::WatchError::InvalidNetworkToken,
379 ))?;
380 }
381 Some(network_contents) => {
382 if network_contents.connection_id != id {
383 warn!(
384 "Cannot watch a NetworkToken that was not created by \
385 this connection."
386 );
387 responder.send(Err(
388 fnp_properties::WatchError::InvalidNetworkToken,
389 ))?;
390 } else {
391 let responder = NetworkPropertyResponder {
392 token,
393 watched_properties: properties,
394 responder,
395 };
396 if self
397 .generations_by_connection
398 .properties(&id)
399 .unwrap_or_default()
400 < self.current_generation.properties
401 {
402 self.generations_by_connection
403 .set_properties(id, self.current_generation);
404 if let Some(responder) = self
405 .network_properties
406 .maybe_respond(&network_contents, responder)
407 {
408 let _: &mut NetworkPropertyResponder =
409 vacant_entry.insert(responder);
410 }
411 } else {
412 let _: &mut NetworkPropertyResponder =
413 vacant_entry.insert(responder);
414 }
415 }
416 }
417 }
418 }
419 }
420 } else {
421 responder.send(Err(fnp_properties::WatchError::InvalidNetworkToken))?;
422 }
423 }
424 },
425 _ => {
426 warn!("Received unexpected request {req:?}");
427 }
428 }
429
430 Ok(())
431 }
432
433 pub(crate) async fn remove_network<ID: Into<InterfaceId>>(&mut self, interface_id: ID) {
434 let interface_id = interface_id.into();
435 info!("Removing interface {interface_id}. Reporting NETWORK_GONE to all clients.");
436 let mut responders = HashMap::new();
437 std::mem::swap(&mut self.property_responders, &mut responders);
438 for (id, responder) in responders {
439 let network = match self.tokens.get(&responder.token).await {
440 Some(network) => network,
441 None => {
442 warn!("Could not fetch network data for responder");
443 continue;
444 }
445 };
446 if network.interface_id == interface_id {
447 if let Err(e) =
449 responder.responder.send(Err(fnp_properties::WatchError::NetworkGone))
450 {
451 warn!("Could not send to responder: {e}");
452 }
453 } else {
454 if self.property_responders.insert(id, responder).is_some() {
455 error!("Re-inserted in an existing responder slot. This should be impossible.");
456 }
457 }
458 }
459 }
460
461 pub(crate) async fn update(&mut self, update: PropertyUpdate) {
462 self.current_generation.properties += 1;
463 let updates_applied = self.network_properties.apply(update);
464 let mut responders = HashMap::new();
465 std::mem::swap(&mut self.property_responders, &mut responders);
466
467 if updates_applied.default_network.is_some() {
468 if let Some(default_network) = self.network_properties.default_network {
469 self.current_generation.default_network += 1;
470 let mut responders = HashMap::new();
471 std::mem::swap(&mut self.default_network_responders, &mut responders);
472 for (id, responder) in responders {
473 self.generations_by_connection.set_default_network(id, self.current_generation);
474
475 let token = self
476 .tokens
477 .insert_data(NetworkTokenContents {
478 connection_id: id,
479 interface_id: default_network,
480 })
481 .await;
482
483 if let Err(e) = responder.send(fnp_properties::NetworkToken {
484 value: Some(token),
485 ..Default::default()
486 }) {
487 warn!("Could not send to responder: {e}");
488 }
489 }
490 }
491 }
492
493 for (id, responder) in responders {
494 let mut updates = Vec::new();
495 let network = match self.tokens.get(&responder.token).await {
496 Some(network) => network,
497 None => {
498 warn!("Could not fetch network data for responder");
499 continue;
500 }
501 };
502
503 if let Some(network_id) = updates_applied.socket_marks_network {
504 if network.interface_id == network_id {
505 updates.add_socket_marks(&self.network_properties, &network, &responder);
506 }
507 }
508 if updates_applied.dns_changed {
509 updates.add_dns(&self.network_properties, &network, &responder);
510 }
511
512 self.generations_by_connection.set_properties(id, self.current_generation);
513 if updates.is_empty() {
514 if self.property_responders.insert(id, responder).is_some() {
515 warn!("Re-inserted in an existing responder slot. This should be impossible.");
516 }
517 } else {
518 if let Err(e) = responder.responder.send(Ok(&updates)) {
519 warn!("Could not send to responder: {e}");
520 }
521 }
522 }
523 }
524}
525
526#[derive(Default)]
527pub(crate) struct NetworksRequestStreams {
528 next_id: ConnectionId,
529 request_streams:
530 futures::stream::SelectAll<Tagged<ConnectionId, fnp_properties::NetworksRequestStream>>,
531}
532
533impl NetworksRequestStreams {
534 pub fn push(&mut self, stream: fnp_properties::NetworksRequestStream) {
535 self.request_streams.push(stream.tagged(self.next_id));
536 self.next_id.0 += 1;
537 }
538}
539
540impl futures::Stream for NetworksRequestStreams {
541 type Item = (ConnectionId, Result<fnp_properties::NetworksRequest, fidl::Error>);
542
543 fn poll_next(
544 mut self: std::pin::Pin<&mut Self>,
545 cx: &mut std::task::Context<'_>,
546 ) -> std::task::Poll<Option<Self::Item>> {
547 std::pin::Pin::new(&mut self.request_streams).poll_next(cx)
548 }
549}
550
551impl futures::stream::FusedStream for NetworksRequestStreams {
552 fn is_terminated(&self) -> bool {
553 self.request_streams.is_terminated()
554 }
555}