1use super::*;
2
3fn traffic_detail(stats: &crate::interface::InterfaceStats) -> TrafficDetail {
4 TrafficDetail {
5 rxs: stats.traffic_rates.rxs,
6 txs: stats.traffic_rates.txs,
7 arxb: stats.arxb,
8 atxb: stats.atxb,
9 arxc: stats.arxc,
10 atxc: stats.atxc,
11 arxs: stats.traffic_rates.arxs,
12 atxs: stats.traffic_rates.atxs,
13 prxb: stats.prxb,
14 ptxb: stats.ptxb,
15 prxc: stats.prxc,
16 ptxc: stats.ptxc,
17 prxs: stats.traffic_rates.prxs,
18 ptxs: stats.traffic_rates.ptxs,
19 arxf: stats.incoming_announce_freq(),
20 atxf: stats.outgoing_announce_freq(),
21 prxf: stats.incoming_path_request_freq(),
22 ptxf: stats.outgoing_path_request_freq(),
23 }
24}
25
26fn add_traffic(total: &mut TrafficDetail, detail: TrafficDetail) {
27 total.rxs += detail.rxs;
28 total.txs += detail.txs;
29 total.arxb += detail.arxb;
30 total.atxb += detail.atxb;
31 total.arxc += detail.arxc;
32 total.atxc += detail.atxc;
33 total.arxs += detail.arxs;
34 total.atxs += detail.atxs;
35 total.prxb += detail.prxb;
36 total.ptxb += detail.ptxb;
37 total.prxc += detail.prxc;
38 total.ptxc += detail.ptxc;
39 total.prxs += detail.prxs;
40 total.ptxs += detail.ptxs;
41 total.arxf += detail.arxf;
42 total.atxf += detail.atxf;
43 total.prxf += detail.prxf;
44 total.ptxf += detail.ptxf;
45}
46
47impl Driver {
48 pub(super) fn dynamic_burst_counts(&self, parent_id: InterfaceId) -> (usize, usize) {
49 self.dynamic_interface_parents
50 .iter()
51 .filter(|(_, candidate_parent)| **candidate_parent == parent_id)
52 .fold((0, 0), |(announce_count, path_request_count), (id, _)| {
53 (
54 announce_count + usize::from(self.engine.burst_active(id)),
55 path_request_count + usize::from(self.engine.pr_burst_active(id)),
56 )
57 })
58 }
59
60 #[cfg(feature = "iface-backbone")]
62 pub fn backbone_burst_counts(&self, parent_id: InterfaceId) -> Option<(usize, usize)> {
63 self.backbone_peer_state
64 .values()
65 .any(|handle| handle.interface_id == parent_id)
66 .then(|| self.dynamic_burst_counts(parent_id))
67 }
68
69 pub(crate) fn handle_interface_stats_query(&self) -> QueryResponse {
70 let now = time::now();
71 let mut interfaces = Vec::new();
72 let mut total_rxb: u64 = 0;
73 let mut total_txb: u64 = 0;
74 let mut rx_pps = 0.0;
75 let mut tx_pps = 0.0;
76 let mut total_traffic = TrafficDetail::default();
77 for entry in self.interfaces.values() {
78 let traffic = traffic_detail(&entry.stats);
79 if !entry.info.is_local_client {
80 total_rxb += entry.stats.rxb;
81 total_txb += entry.stats.txb;
82 rx_pps += entry.stats.traffic_rates.rxpps;
83 tx_pps += entry.stats.traffic_rates.txpps;
84 add_traffic(&mut total_traffic, traffic);
85 }
86 let interface_id = entry.info.id;
87 interfaces.push(SingleInterfaceStat {
88 id: interface_id.0,
89 name: entry.info.name.clone(),
90 status: entry.online && entry.enabled,
91 mode: entry.info.mode,
92 gravity: entry.info.gravity,
93 announces_to_internal: entry.info.announces_to_internal,
94 rxb: entry.stats.rxb,
95 txb: entry.stats.txb,
96 tx_drops: entry
97 .async_writer_metrics
98 .as_ref()
99 .map(crate::interface::AsyncWriterMetrics::tx_drops)
100 .unwrap_or(0),
101 tx_dropped_bytes: entry
102 .async_writer_metrics
103 .as_ref()
104 .map(crate::interface::AsyncWriterMetrics::tx_dropped_bytes)
105 .unwrap_or(0),
106 tx_stalled: entry
107 .async_writer_metrics
108 .as_ref()
109 .is_some_and(crate::interface::AsyncWriterMetrics::tx_stalled),
110 tx_buffered: entry
111 .async_writer_metrics
112 .as_ref()
113 .map(crate::interface::AsyncWriterMetrics::tx_buffered)
114 .unwrap_or(0),
115 traffic,
116 protocol_violations: entry.stats.protocol_violations,
117 ifac_violations: entry.stats.ifac_violations,
118 packet_filter_hits: entry.stats.packet_filter_hits,
119 rx_packets: entry.stats.rx_packets,
120 tx_packets: entry.stats.tx_packets,
121 cpu_load: entry.stats.cpu_load,
122 mem_load: entry.stats.mem_load,
123 switch_id: entry.stats.switch_id,
124 endpoint_id: entry.stats.endpoint_id,
125 via_switch_id: entry.stats.via_switch_id,
126 peers: entry.stats.peers,
127 bitrate: entry.info.bitrate,
128 mtu: Some(entry.info.mtu),
129 ifac_size: entry.ifac.as_ref().map(|s| s.size),
130 started: entry.stats.started,
131 ia_freq: entry.stats.incoming_announce_freq(),
132 oa_freq: entry.stats.outgoing_announce_freq(),
133 ip_freq: entry.stats.incoming_path_request_freq(),
134 op_freq: entry.stats.outgoing_path_request_freq(),
135 op_samples: entry.stats.outgoing_path_request_samples(),
136 burst_active: self.engine.burst_active(&interface_id),
137 burst_activated: self.engine.burst_activated(&interface_id),
138 pr_burst_active: self.engine.pr_burst_active(&interface_id),
139 pr_burst_activated: self.engine.pr_burst_activated(&interface_id),
140 clients: None,
141 blocked_ips: None,
142 blocked_ip_list: None,
143 announce_rate_target: entry.info.announce_rate_target,
144 announce_rate_grace: entry.info.announce_rate_grace,
145 announce_rate_penalty: entry.info.announce_rate_penalty,
146 interface_type: entry.interface_type.clone(),
147 });
148 }
149 #[cfg(feature = "iface-backbone")]
150 for handle in self.backbone_peer_state.values() {
151 let clients = recover_mutex_guard(&handle.peer_state, "backbone peer state")
152 .list(&handle.interface_name)
153 .into_iter()
154 .map(|entry| entry.connected_count as u64)
155 .sum();
156 let blocked_ip_list =
157 recover_mutex_guard(&handle.fast_flap_state, "backbone fast-flap state")
158 .blocked_ip_list(&handle.fast_flap, std::time::Instant::now())
159 .into_iter()
160 .map(|peer_ip| peer_ip.to_string())
161 .collect::<Vec<_>>();
162 let blocked_ips = blocked_ip_list.len() as u64;
163 let mut traffic = TrafficDetail::default();
164 let mut protocol_violations = 0;
165 let mut ifac_violations = 0;
166 let mut packet_filter_hits = 0;
167 let mut tx_drops = 0;
168 let mut tx_dropped_bytes = 0;
169 let mut tx_stalled = false;
170 let mut tx_buffered = 0;
171 for (child_id, parent_id) in &self.dynamic_interface_parents {
172 if *parent_id == handle.interface_id {
173 if let Some(child) = self.interfaces.get(child_id) {
174 add_traffic(&mut traffic, traffic_detail(&child.stats));
175 protocol_violations += child.stats.protocol_violations;
176 ifac_violations += child.stats.ifac_violations;
177 packet_filter_hits += child.stats.packet_filter_hits;
178 if let Some(metrics) = &child.async_writer_metrics {
179 tx_drops += metrics.tx_drops();
180 tx_dropped_bytes += metrics.tx_dropped_bytes();
181 tx_stalled |= metrics.tx_stalled();
182 tx_buffered += metrics.tx_buffered();
183 }
184 }
185 }
186 }
187 interfaces.push(SingleInterfaceStat {
188 id: handle.interface_id.0,
189 name: handle.interface_name.clone(),
190 status: true,
191 mode: handle.mode,
192 gravity: handle.gravity,
193 announces_to_internal: handle.announces_to_internal,
194 rxb: 0,
195 txb: 0,
196 tx_drops,
197 tx_dropped_bytes,
198 tx_stalled,
199 tx_buffered,
200 traffic,
201 protocol_violations,
202 ifac_violations,
203 packet_filter_hits,
204 rx_packets: 0,
205 tx_packets: 0,
206 cpu_load: None,
207 mem_load: None,
208 switch_id: None,
209 endpoint_id: None,
210 via_switch_id: None,
211 peers: None,
212 bitrate: Some(crate::interface::backbone::SERVER_BITRATE_GUESS),
213 mtu: Some(crate::interface::backbone::HW_MTU as u32),
214 ifac_size: None,
215 started: self.started,
216 ia_freq: 0.0,
217 oa_freq: 0.0,
218 ip_freq: 0.0,
219 op_freq: 0.0,
220 op_samples: 0,
221 burst_active: false,
222 burst_activated: 0.0,
223 pr_burst_active: false,
224 pr_burst_activated: 0.0,
225 clients: Some(clients),
226 blocked_ips: Some(blocked_ips),
227 blocked_ip_list: Some(blocked_ip_list),
228 announce_rate_target: None,
229 announce_rate_grace: 0,
230 announce_rate_penalty: 0.0,
231 interface_type: "BackboneInterface".into(),
232 });
233 }
234 interfaces.sort_by(|a, b| a.name.cmp(&b.name));
235 QueryResponse::InterfaceStats(InterfaceStatsResponse {
236 interfaces,
237 transport_id: self.engine.identity_hash().copied(),
238 transport_enabled: self.engine.transport_enabled(),
239 transport_uptime: now - self.started,
240 total_rxb,
241 total_txb,
242 rx_pps,
243 tx_pps,
244 traffic: total_traffic,
245 probe_responder: self.probe_responder_hash,
246 #[cfg(feature = "iface-backbone")]
247 backbone_peer_pool: self.backbone_peer_pool_status(),
248 #[cfg(not(feature = "iface-backbone"))]
249 backbone_peer_pool: None,
250 })
251 }
252
253 pub(crate) fn handle_path_table_query(&self, max_hops: Option<u8>) -> QueryResponse {
254 let entries: Vec<PathTableEntry> = self
255 .engine
256 .path_table_entries()
257 .filter(|(_, entry)| max_hops.is_none_or(|max| entry.hops <= max))
258 .map(|(hash, entry)| {
259 let iface_name = self
260 .interfaces
261 .get(&entry.receiving_interface)
262 .map(|e| e.info.name.clone())
263 .or_else(|| {
264 self.engine
265 .interface_info(&entry.receiving_interface)
266 .map(|i| i.name.clone())
267 })
268 .unwrap_or_default();
269 PathTableEntry {
270 hash: *hash,
271 timestamp: entry.timestamp,
272 via: entry.next_hop,
273 hops: entry.hops,
274 expires: entry.expires,
275 interface: entry.receiving_interface,
276 interface_name: iface_name,
277 }
278 })
279 .collect();
280 QueryResponse::PathTable(entries)
281 }
282
283 pub(crate) fn handle_runtime_config_query(
284 &self,
285 request: QueryRequest,
286 ) -> Option<QueryResponse> {
287 match request {
288 QueryRequest::ListRuntimeConfig => {
289 Some(QueryResponse::RuntimeConfigList(self.list_runtime_config()))
290 }
291 QueryRequest::GetRuntimeConfig { key } => Some(QueryResponse::RuntimeConfigEntry(
292 self.runtime_config_entry(&key),
293 )),
294 QueryRequest::BackbonePeerState { interface_name } => {
295 Some(QueryResponse::BackbonePeerState(
296 self.list_backbone_peer_state(interface_name.as_deref()),
297 ))
298 }
299 QueryRequest::SetRuntimeConfig { .. } => {
300 Some(QueryResponse::RuntimeConfigSet(Err(RuntimeConfigError {
301 code: RuntimeConfigErrorCode::Unsupported,
302 message: "mutating runtime config is handled separately".to_string(),
303 })))
304 }
305 QueryRequest::ResetRuntimeConfig { .. } => {
306 Some(QueryResponse::RuntimeConfigReset(Err(RuntimeConfigError {
307 code: RuntimeConfigErrorCode::Unsupported,
308 message: "mutating runtime config is handled separately".to_string(),
309 })))
310 }
311 QueryRequest::ClearBackbonePeerState { .. } => {
312 Some(QueryResponse::ClearBackbonePeerState(false))
313 }
314 QueryRequest::BlacklistBackbonePeer { .. } => {
315 Some(QueryResponse::BlacklistBackbonePeer(false))
316 }
317 _ => None,
318 }
319 }
320
321 pub(crate) fn handle_mutation_query(&mut self, request: QueryRequest) -> Option<QueryResponse> {
322 match request {
323 QueryRequest::BlackholeIdentity {
324 identity_hash,
325 duration_hours,
326 reason,
327 } => {
328 let now = time::now();
329 self.engine
330 .blackhole_identity(identity_hash, now, duration_hours, reason);
331 Some(QueryResponse::BlackholeResult(true))
332 }
333 QueryRequest::UnblackholeIdentity { identity_hash } => Some(
334 QueryResponse::UnblackholeResult(self.engine.unblackhole_identity(&identity_hash)),
335 ),
336 QueryRequest::DropPath { dest_hash } => {
337 Some(QueryResponse::DropPath(self.engine.drop_path(&dest_hash)))
338 }
339 QueryRequest::DropAllVia { transport_hash } => Some(QueryResponse::DropAllVia(
340 self.engine.drop_all_via(&transport_hash),
341 )),
342 QueryRequest::DropAnnounceQueues => {
343 self.engine.drop_announce_queues();
344 Some(QueryResponse::DropAnnounceQueues)
345 }
346 QueryRequest::ClearBackbonePeerState {
347 interface_name,
348 peer_ip,
349 } => Some(QueryResponse::ClearBackbonePeerState(
350 self.clear_backbone_peer_state(&interface_name, peer_ip),
351 )),
352 QueryRequest::BlacklistBackbonePeer {
353 interface_name,
354 peer_ip,
355 duration,
356 reason,
357 penalty_level,
358 } => Some(QueryResponse::BlacklistBackbonePeer(
359 self.blacklist_backbone_peer(
360 &interface_name,
361 peer_ip,
362 duration,
363 reason,
364 penalty_level,
365 ),
366 )),
367 QueryRequest::InjectPath {
368 dest_hash,
369 next_hop,
370 hops,
371 expires,
372 interface_name,
373 packet_hash,
374 } => {
375 let iface_id = self
376 .interfaces
377 .iter()
378 .find(|(_, entry)| entry.info.name == interface_name)
379 .map(|(id, _)| *id);
380 Some(match iface_id {
381 Some(id) => {
382 let entry = PathEntry {
383 timestamp: time::now(),
384 next_hop,
385 hops,
386 expires,
387 random_blobs: Vec::new(),
388 receiving_interface: id,
389 packet_hash,
390 announce_raw: None,
391 };
392 self.engine.inject_path(dest_hash, entry);
393 QueryResponse::InjectPath(true)
394 }
395 None => QueryResponse::InjectPath(false),
396 })
397 }
398 QueryRequest::InjectIdentity {
399 dest_hash,
400 identity_hash,
401 public_key,
402 app_data,
403 hops,
404 received_at,
405 } => {
406 self.upsert_known_destination(
407 dest_hash,
408 crate::destination::AnnouncedIdentity {
409 dest_hash: rns_core::types::DestHash(dest_hash),
410 identity_hash: rns_core::types::IdentityHash(identity_hash),
411 public_key,
412 app_data,
413 hops,
414 received_at,
415 receiving_interface: rns_core::transport::types::InterfaceId(0),
416 rssi: None,
417 snr: None,
418 },
419 );
420 Some(QueryResponse::InjectIdentity(true))
421 }
422 QueryRequest::RestoreKnownDestination(entry) => {
423 self.known_destinations.insert(
424 entry.dest_hash,
425 KnownDestinationState {
426 announced: crate::destination::AnnouncedIdentity {
427 dest_hash: rns_core::types::DestHash(entry.dest_hash),
428 identity_hash: rns_core::types::IdentityHash(entry.identity_hash),
429 public_key: entry.public_key,
430 app_data: entry.app_data,
431 hops: entry.hops,
432 received_at: entry.received_at,
433 receiving_interface: entry.receiving_interface,
434 rssi: None,
435 snr: None,
436 },
437 was_used: entry.was_used,
438 last_used_at: entry.last_used_at,
439 retained: entry.retained,
440 },
441 );
442 Some(QueryResponse::RestoreKnownDestination(true))
443 }
444 QueryRequest::RetainKnownDestination { dest_hash } => Some(
445 QueryResponse::RetainKnownDestination(self.retain_known_destination(&dest_hash)),
446 ),
447 QueryRequest::RetainIdentity { identity_hash } => Some(QueryResponse::RetainIdentity(
448 self.retain_identity(&identity_hash),
449 )),
450 QueryRequest::UnretainKnownDestination { dest_hash } => {
451 Some(QueryResponse::UnretainKnownDestination(
452 self.unretain_known_destination(&dest_hash),
453 ))
454 }
455 QueryRequest::MarkKnownDestinationUsed { dest_hash } => {
456 Some(QueryResponse::MarkKnownDestinationUsed(
457 self.mark_known_destination_used(&dest_hash),
458 ))
459 }
460 _ => None,
461 }
462 }
463
464 pub(crate) fn runtime_config_query_fallback(request: &QueryRequest) -> QueryResponse {
465 match request {
466 QueryRequest::ListRuntimeConfig => QueryResponse::RuntimeConfigList(Vec::new()),
467 QueryRequest::GetRuntimeConfig { .. } => QueryResponse::RuntimeConfigEntry(None),
468 QueryRequest::BackbonePeerState { .. } => QueryResponse::BackbonePeerState(Vec::new()),
469 QueryRequest::SetRuntimeConfig { key, .. } => {
470 QueryResponse::RuntimeConfigSet(Err(RuntimeConfigError {
471 code: RuntimeConfigErrorCode::ApplyFailed,
472 message: format!(
473 "internal error: no response generated for runtime-config set '{}'",
474 key
475 ),
476 }))
477 }
478 QueryRequest::ResetRuntimeConfig { key } => {
479 QueryResponse::RuntimeConfigReset(Err(RuntimeConfigError {
480 code: RuntimeConfigErrorCode::ApplyFailed,
481 message: format!(
482 "internal error: no response generated for runtime-config reset '{}'",
483 key
484 ),
485 }))
486 }
487 QueryRequest::ClearBackbonePeerState { .. } => {
488 QueryResponse::ClearBackbonePeerState(false)
489 }
490 QueryRequest::BlacklistBackbonePeer { .. } => {
491 QueryResponse::BlacklistBackbonePeer(false)
492 }
493 _ => QueryResponse::RuntimeConfigEntry(None),
494 }
495 }
496
497 pub(crate) fn mutation_query_fallback(request: &QueryRequest) -> QueryResponse {
498 match request {
499 QueryRequest::BlackholeIdentity { .. } => QueryResponse::BlackholeResult(false),
500 QueryRequest::UnblackholeIdentity { .. } => QueryResponse::UnblackholeResult(false),
501 QueryRequest::DropPath { .. } => QueryResponse::DropPath(false),
502 QueryRequest::DropAllVia { .. } => QueryResponse::DropAllVia(0),
503 QueryRequest::DropAnnounceQueues => QueryResponse::DropAnnounceQueues,
504 QueryRequest::ClearBackbonePeerState { .. } => {
505 QueryResponse::ClearBackbonePeerState(false)
506 }
507 QueryRequest::BlacklistBackbonePeer { .. } => {
508 QueryResponse::BlacklistBackbonePeer(false)
509 }
510 QueryRequest::InjectPath { .. } => QueryResponse::InjectPath(false),
511 QueryRequest::InjectIdentity { .. } => QueryResponse::InjectIdentity(false),
512 QueryRequest::RestoreKnownDestination(..) => {
513 QueryResponse::RestoreKnownDestination(false)
514 }
515 QueryRequest::RetainKnownDestination { .. } => {
516 QueryResponse::RetainKnownDestination(false)
517 }
518 QueryRequest::RetainIdentity { .. } => QueryResponse::RetainIdentity(false),
519 QueryRequest::UnretainKnownDestination { .. } => {
520 QueryResponse::UnretainKnownDestination(false)
521 }
522 QueryRequest::MarkKnownDestinationUsed { .. } => {
523 QueryResponse::MarkKnownDestinationUsed(false)
524 }
525 _ => QueryResponse::InjectIdentity(false),
526 }
527 }
528
529 pub(crate) fn handle_query(&self, request: QueryRequest) -> QueryResponse {
531 match request {
532 QueryRequest::InterfaceStats => self.handle_interface_stats_query(),
533 QueryRequest::BackboneInterfaces => {
534 QueryResponse::BackboneInterfaces(self.list_backbone_interfaces())
535 }
536 QueryRequest::ProviderBridgeStats => {
537 #[cfg(feature = "hooks")]
538 {
539 QueryResponse::ProviderBridgeStats(
540 self.provider_bridge.as_ref().map(|bridge| bridge.stats()),
541 )
542 }
543 #[cfg(not(feature = "hooks"))]
544 {
545 QueryResponse::ProviderBridgeStats(None::<crate::event::ProviderBridgeStats>)
546 }
547 }
548 QueryRequest::DrainStatus => QueryResponse::DrainStatus(self.drain_status()),
549 QueryRequest::PathTable { max_hops } => self.handle_path_table_query(max_hops),
550 QueryRequest::RateTable => {
551 let entries: Vec<RateTableEntry> = self
552 .engine
553 .rate_limiter()
554 .entries()
555 .map(|(hash, entry)| RateTableEntry {
556 hash: *hash,
557 last: entry.last,
558 rate_violations: entry.rate_violations,
559 blocked_until: entry.blocked_until,
560 timestamps: entry.timestamps.clone(),
561 })
562 .collect();
563 QueryResponse::RateTable(entries)
564 }
565 QueryRequest::LowestInterfaceBitrate => {
566 QueryResponse::LowestInterfaceBitrate(self.engine.lowest_interface_bitrate())
567 }
568 QueryRequest::MediumPathTimeout => {
569 QueryResponse::MediumPathTimeout(self.engine.medium_path_timeout())
570 }
571 QueryRequest::NextHop { dest_hash } => {
572 let resp = self
573 .engine
574 .next_hop(&dest_hash)
575 .map(|next_hop| NextHopResponse {
576 next_hop,
577 hops: self.engine.hops_to(&dest_hash).unwrap_or(0),
578 interface: self
579 .engine
580 .next_hop_interface(&dest_hash)
581 .unwrap_or(InterfaceId(0)),
582 });
583 QueryResponse::NextHop(resp)
584 }
585 QueryRequest::NextHopIfName { dest_hash } => {
586 let name = self
587 .engine
588 .next_hop_interface(&dest_hash)
589 .and_then(|id| self.interfaces.get(&id))
590 .map(|entry| entry.info.name.clone());
591 QueryResponse::NextHopIfName(name)
592 }
593 QueryRequest::LinkCount => QueryResponse::LinkCount(
594 self.engine.link_table_count() + self.link_manager.link_count(),
595 ),
596 QueryRequest::ActiveLinkCount => QueryResponse::ActiveLinkCount(
597 self.engine.active_link_count() + self.link_manager.active_link_count(),
598 ),
599 QueryRequest::DropPath { .. } => {
600 QueryResponse::DropPath(false)
602 }
603 QueryRequest::DropAllVia { .. } => QueryResponse::DropAllVia(0),
604 QueryRequest::DropAnnounceQueues => QueryResponse::DropAnnounceQueues,
605 QueryRequest::TransportIdentity => {
606 QueryResponse::TransportIdentity(self.engine.identity_hash().copied())
607 }
608 QueryRequest::TransportStateSnapshot => {
609 QueryResponse::TransportStateSnapshot(self.engine.persistence_snapshot())
610 }
611 QueryRequest::GetBlackholed => {
612 let now = time::now();
613 let entries: Vec<BlackholeInfo> = self
614 .engine
615 .blackholed_entries()
616 .filter(|(_, e)| e.expires == 0.0 || e.expires > now)
617 .map(|(hash, entry)| BlackholeInfo {
618 identity_hash: *hash,
619 created: entry.created,
620 expires: entry.expires,
621 reason: entry.reason.clone(),
622 })
623 .collect();
624 QueryResponse::Blackholed(entries)
625 }
626 QueryRequest::BlackholeIdentity { .. } | QueryRequest::UnblackholeIdentity { .. } => {
627 QueryResponse::BlackholeResult(false)
629 }
630 QueryRequest::InjectPath { .. } => {
631 QueryResponse::InjectPath(false)
633 }
634 QueryRequest::InjectIdentity { .. } => {
635 QueryResponse::InjectIdentity(false)
637 }
638 QueryRequest::RestoreKnownDestination(..) => {
639 QueryResponse::RestoreKnownDestination(false)
640 }
641 QueryRequest::RetainKnownDestination { .. } => {
642 QueryResponse::RetainKnownDestination(false)
643 }
644 QueryRequest::RetainIdentity { .. } => QueryResponse::RetainIdentity(false),
645 QueryRequest::UnretainKnownDestination { .. } => {
646 QueryResponse::UnretainKnownDestination(false)
647 }
648 QueryRequest::MarkKnownDestinationUsed { .. } => {
649 QueryResponse::MarkKnownDestinationUsed(false)
650 }
651 QueryRequest::HasPath { dest_hash } => {
652 QueryResponse::HasPath(self.engine.has_path(&dest_hash))
653 }
654 QueryRequest::HopsTo { dest_hash } => {
655 QueryResponse::HopsTo(self.engine.hops_to(&dest_hash))
656 }
657 QueryRequest::RecallIdentity { .. } => QueryResponse::RecallIdentity(None),
658 QueryRequest::KnownDestinations => {
659 QueryResponse::KnownDestinations(self.known_destination_entries())
660 }
661 QueryRequest::LocalDestinations => {
662 let entries: Vec<LocalDestinationEntry> = self
663 .local_destinations
664 .iter()
665 .map(|(hash, dest_type)| LocalDestinationEntry {
666 hash: *hash,
667 dest_type: *dest_type,
668 })
669 .collect();
670 QueryResponse::LocalDestinations(entries)
671 }
672 QueryRequest::Links => QueryResponse::Links(self.link_manager.link_entries()),
673 QueryRequest::Resources => {
674 QueryResponse::Resources(self.link_manager.resource_entries())
675 }
676 QueryRequest::DiscoveredInterfaces {
677 only_available,
678 only_transport,
679 } => {
680 let mut interfaces = self.discovered_interfaces.list().unwrap_or_default();
681 crate::discovery::filter_and_sort_interfaces(
682 &mut interfaces,
683 only_available,
684 only_transport,
685 );
686 QueryResponse::DiscoveredInterfaces(interfaces)
687 }
688 request @ (QueryRequest::ListRuntimeConfig
689 | QueryRequest::GetRuntimeConfig { .. }
690 | QueryRequest::BackbonePeerState { .. }
691 | QueryRequest::SetRuntimeConfig { .. }
692 | QueryRequest::ResetRuntimeConfig { .. }
693 | QueryRequest::ClearBackbonePeerState { .. }
694 | QueryRequest::BlacklistBackbonePeer { .. }) => {
695 let fallback = Self::runtime_config_query_fallback(&request);
696 self.handle_runtime_config_query(request)
697 .unwrap_or_else(|| {
698 log::error!(
699 "runtime-config query branch unexpectedly returned no response"
700 );
701 fallback
702 })
703 }
704 QueryRequest::SendProbe { .. } => QueryResponse::SendProbe(None),
706 QueryRequest::CheckProof { .. } => QueryResponse::CheckProof(None),
707 }
708 }
709
710 pub(crate) fn handle_query_mut(&mut self, request: QueryRequest) -> QueryResponse {
712 match request {
713 request @ (QueryRequest::BlackholeIdentity { .. }
714 | QueryRequest::UnblackholeIdentity { .. }
715 | QueryRequest::DropPath { .. }
716 | QueryRequest::DropAllVia { .. }
717 | QueryRequest::DropAnnounceQueues
718 | QueryRequest::ClearBackbonePeerState { .. }
719 | QueryRequest::BlacklistBackbonePeer { .. }
720 | QueryRequest::InjectPath { .. }
721 | QueryRequest::InjectIdentity { .. }
722 | QueryRequest::RestoreKnownDestination(..)
723 | QueryRequest::RetainKnownDestination { .. }
724 | QueryRequest::RetainIdentity { .. }
725 | QueryRequest::UnretainKnownDestination { .. }
726 | QueryRequest::MarkKnownDestinationUsed { .. }) => {
727 let fallback = Self::mutation_query_fallback(&request);
728 self.handle_mutation_query(request).unwrap_or_else(|| {
729 log::error!("mutation query branch unexpectedly returned no response");
730 fallback
731 })
732 }
733 QueryRequest::RecallIdentity { dest_hash } => {
734 let recalled = self.known_destination_announced(&dest_hash);
735 if recalled.is_some() {
736 let _ = self.mark_known_destination_used(&dest_hash);
737 }
738 QueryResponse::RecallIdentity(recalled)
739 }
740 QueryRequest::DrainStatus => QueryResponse::DrainStatus(self.drain_status()),
741 QueryRequest::SendProbe {
742 dest_hash,
743 payload_size,
744 } => {
745 let announced = self.known_destination_announced(&dest_hash);
747 match announced {
748 Some(recalled) => {
749 let _ = self.mark_known_destination_used(&dest_hash);
750 let remote_id =
752 rns_crypto::identity::Identity::from_public_key(&recalled.public_key);
753 let mut payload = vec![0u8; payload_size];
754 self.rng.fill_bytes(&mut payload);
755 match remote_id.encrypt(&payload, &mut self.rng) {
756 Ok(ciphertext) => {
757 let flags = rns_core::packet::PacketFlags {
759 header_type: rns_core::constants::HEADER_1,
760 context_flag: rns_core::constants::FLAG_UNSET,
761 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
762 destination_type: rns_core::constants::DESTINATION_SINGLE,
763 packet_type: rns_core::constants::PACKET_TYPE_DATA,
764 };
765 match RawPacket::pack(
766 flags,
767 0,
768 &dest_hash,
769 None,
770 rns_core::constants::CONTEXT_NONE,
771 &ciphertext,
772 ) {
773 Ok(packet) => {
774 let packet_hash = packet.packet_hash;
775 let hops = self.engine.hops_to(&dest_hash).unwrap_or(0);
776 self.sent_packets
778 .insert(packet_hash, (dest_hash, time::now()));
779 let actions = self.engine.handle_outbound(
781 &packet,
782 rns_core::constants::DESTINATION_SINGLE,
783 None,
784 time::now(),
785 );
786 self.dispatch_all(actions);
787 log::debug!(
788 "Sent probe ({} bytes) to {:02x?}",
789 payload_size,
790 &dest_hash[..4],
791 );
792 QueryResponse::SendProbe(Some((packet_hash, hops)))
793 }
794 Err(_) => {
795 log::warn!("Failed to pack probe packet");
796 QueryResponse::SendProbe(None)
797 }
798 }
799 }
800 Err(_) => {
801 log::warn!("Failed to encrypt probe payload");
802 QueryResponse::SendProbe(None)
803 }
804 }
805 }
806 None => {
807 log::debug!("No known identity for probe dest {:02x?}", &dest_hash[..4]);
808 QueryResponse::SendProbe(None)
809 }
810 }
811 }
812 QueryRequest::CheckProof { packet_hash } => {
813 match self.completed_proofs.remove(&packet_hash) {
814 Some((rtt, _received)) => QueryResponse::CheckProof(Some(rtt)),
815 None => QueryResponse::CheckProof(None),
816 }
817 }
818 QueryRequest::SetRuntimeConfig { key, value } => {
819 let result = match key.as_str() {
820 "global.tick_interval_ms" => match Self::expect_u64(value, &key) {
821 Ok(value) => {
822 let clamped = value.clamp(100, 10_000);
823 self.tick_interval_ms.store(clamped, Ordering::Relaxed);
824 Ok(())
825 }
826 Err(err) => Err(err),
827 },
828 "global.known_destinations_ttl_secs" => match Self::expect_f64(value, &key) {
829 Ok(value) => {
830 self.known_destinations_ttl = value;
831 Ok(())
832 }
833 Err(err) => Err(err),
834 },
835 "global.rate_limiter_ttl_secs" => match Self::expect_f64(value, &key) {
836 Ok(value) if value >= 0.0 => {
837 self.rate_limiter_ttl_secs = value;
838 Ok(())
839 }
840 Ok(_) => Err(RuntimeConfigError {
841 code: RuntimeConfigErrorCode::InvalidValue,
842 message: format!("{} must be >= 0", key),
843 }),
844 Err(err) => Err(err),
845 },
846 "global.known_destinations_cleanup_interval_ticks" => {
847 match Self::expect_u64(value, &key) {
848 Ok(value) if value > 0 => {
849 self.known_destinations_cleanup_interval_ticks = value as u32;
850 Ok(())
851 }
852 Ok(_) => Err(RuntimeConfigError {
853 code: RuntimeConfigErrorCode::InvalidValue,
854 message: format!("{} must be >= 1", key),
855 }),
856 Err(err) => Err(err),
857 }
858 }
859 "global.announce_cache_cleanup_interval_ticks" => {
860 match Self::expect_u64(value, &key) {
861 Ok(value) if value > 0 => {
862 self.announce_cache_cleanup_interval_ticks = value as u32;
863 Ok(())
864 }
865 Ok(_) => Err(RuntimeConfigError {
866 code: RuntimeConfigErrorCode::InvalidValue,
867 message: format!("{} must be >= 1", key),
868 }),
869 Err(err) => Err(err),
870 }
871 }
872 "global.announce_cache_cleanup_batch_size" => {
873 match Self::expect_u64(value, &key) {
874 Ok(value) if value > 0 => {
875 self.announce_cache_cleanup_batch_size = value as usize;
876 Ok(())
877 }
878 Ok(_) => Err(RuntimeConfigError {
879 code: RuntimeConfigErrorCode::InvalidValue,
880 message: format!("{} must be >= 1", key),
881 }),
882 Err(err) => Err(err),
883 }
884 }
885 "global.discovery_cleanup_interval_ticks" => {
886 match Self::expect_u64(value, &key) {
887 Ok(value) if value > 0 => {
888 self.discovery_cleanup_interval_ticks = value as u32;
889 Ok(())
890 }
891 Ok(_) => Err(RuntimeConfigError {
892 code: RuntimeConfigErrorCode::InvalidValue,
893 message: format!("{} must be >= 1", key),
894 }),
895 Err(err) => Err(err),
896 }
897 }
898 "global.management_announce_interval_secs" => {
899 match Self::expect_f64(value, &key) {
900 Ok(value) => {
901 self.management_announce_interval_secs = value;
902 Ok(())
903 }
904 Err(err) => Err(err),
905 }
906 }
907 "global.direct_connect_policy" => {
908 let policy = match Self::parse_holepunch_policy(&value) {
909 Some(policy) => policy,
910 None => {
911 return QueryResponse::RuntimeConfigSet(Err(RuntimeConfigError {
912 code: RuntimeConfigErrorCode::InvalidValue,
913 message: format!(
914 "{} must be one of: reject, accept_all, identified_only, ask_app",
915 key
916 ),
917 }))
918 }
919 };
920 self.holepunch_manager.set_policy(policy);
921 Ok(())
922 }
923 #[cfg(feature = "hooks")]
924 "provider.queue_max_events" => match Self::expect_u64(value, &key) {
925 Ok(v) if v > 0 => {
926 if let Some(ref bridge) = self.provider_bridge {
927 bridge.set_queue_max_events(v as usize);
928 }
929 Ok(())
930 }
931 Ok(_) => Err(RuntimeConfigError {
932 code: RuntimeConfigErrorCode::InvalidValue,
933 message: format!("{} must be >= 1", key),
934 }),
935 Err(err) => Err(err),
936 },
937 #[cfg(feature = "hooks")]
938 "provider.queue_max_bytes" => match Self::expect_u64(value, &key) {
939 Ok(v) if v > 0 => {
940 if let Some(ref bridge) = self.provider_bridge {
941 bridge.set_queue_max_bytes(v as usize);
942 }
943 Ok(())
944 }
945 Ok(_) => Err(RuntimeConfigError {
946 code: RuntimeConfigErrorCode::InvalidValue,
947 message: format!("{} must be >= 1", key),
948 }),
949 Err(err) => Err(err),
950 },
951 _ => match Self::runtime_config_family_for_key(&key) {
952 Some(family) => self.set_runtime_config_family_value(family, &key, value),
953 None => {
954 return QueryResponse::RuntimeConfigSet(Err(RuntimeConfigError {
955 code: RuntimeConfigErrorCode::UnknownKey,
956 message: format!("unknown runtime-config key '{}'", key),
957 }))
958 }
959 },
960 };
961
962 QueryResponse::RuntimeConfigSet(match result {
963 Ok(()) => self.runtime_config_entry(&key).ok_or(RuntimeConfigError {
964 code: RuntimeConfigErrorCode::ApplyFailed,
965 message: format!("failed to read back runtime-config key '{}'", key),
966 }),
967 Err(err) => Err(err),
968 })
969 }
970 QueryRequest::ResetRuntimeConfig { key } => {
971 let defaults = self.runtime_config_defaults;
972 let result = match key.as_str() {
973 "global.tick_interval_ms" => {
974 self.tick_interval_ms
975 .store(defaults.tick_interval_ms, Ordering::Relaxed);
976 Ok(())
977 }
978 "global.known_destinations_ttl_secs" => {
979 self.known_destinations_ttl = defaults.known_destinations_ttl;
980 Ok(())
981 }
982 "global.rate_limiter_ttl_secs" => {
983 self.rate_limiter_ttl_secs = defaults.rate_limiter_ttl_secs;
984 Ok(())
985 }
986 "global.known_destinations_cleanup_interval_ticks" => {
987 self.known_destinations_cleanup_interval_ticks =
988 defaults.known_destinations_cleanup_interval_ticks;
989 Ok(())
990 }
991 "global.announce_cache_cleanup_interval_ticks" => {
992 self.announce_cache_cleanup_interval_ticks =
993 defaults.announce_cache_cleanup_interval_ticks;
994 Ok(())
995 }
996 "global.announce_cache_cleanup_batch_size" => {
997 self.announce_cache_cleanup_batch_size =
998 defaults.announce_cache_cleanup_batch_size;
999 Ok(())
1000 }
1001 "global.discovery_cleanup_interval_ticks" => {
1002 self.discovery_cleanup_interval_ticks =
1003 defaults.discovery_cleanup_interval_ticks;
1004 Ok(())
1005 }
1006 "global.management_announce_interval_secs" => {
1007 self.management_announce_interval_secs =
1008 defaults.management_announce_interval_secs;
1009 Ok(())
1010 }
1011 "global.direct_connect_policy" => {
1012 self.holepunch_manager
1013 .set_policy(defaults.direct_connect_policy);
1014 Ok(())
1015 }
1016 #[cfg(feature = "hooks")]
1017 "provider.queue_max_events" => {
1018 if let Some(ref bridge) = self.provider_bridge {
1019 bridge.set_queue_max_events(defaults.provider_queue_max_events);
1020 }
1021 Ok(())
1022 }
1023 #[cfg(feature = "hooks")]
1024 "provider.queue_max_bytes" => {
1025 if let Some(ref bridge) = self.provider_bridge {
1026 bridge.set_queue_max_bytes(defaults.provider_queue_max_bytes);
1027 }
1028 Ok(())
1029 }
1030 _ => match Self::runtime_config_family_for_key(&key) {
1031 Some(family) => self.reset_runtime_config_family_value(family, &key),
1032 None => {
1033 return QueryResponse::RuntimeConfigReset(Err(RuntimeConfigError {
1034 code: RuntimeConfigErrorCode::UnknownKey,
1035 message: format!("unknown runtime-config key '{}'", key),
1036 }))
1037 }
1038 },
1039 };
1040
1041 QueryResponse::RuntimeConfigReset(match result {
1042 Ok(()) => self.runtime_config_entry(&key).ok_or(RuntimeConfigError {
1043 code: RuntimeConfigErrorCode::ApplyFailed,
1044 message: format!("failed to read back runtime-config key '{}'", key),
1045 }),
1046 Err(err) => Err(err),
1047 })
1048 }
1049 other => self.handle_query(other),
1050 }
1051 }
1052}