1use std::collections::HashMap;
4use std::sync::atomic::{AtomicU64, Ordering};
5use std::sync::{Arc, Mutex};
6use std::time::{Duration, Instant};
7
8use rns_core::packet::RawPacket;
9use rns_core::transport::announce_verify_queue::{AnnounceVerifyQueue, OverflowPolicy};
10use rns_core::transport::tables::PathEntry;
11use rns_core::transport::types::{InterfaceId, TransportAction, TransportConfig};
12use rns_core::transport::TransportEngine;
13use rns_crypto::{OsRng, Rng};
14
15#[cfg(feature = "rns-hooks")]
16use crate::provider_bridge::ProviderBridge;
17#[cfg(feature = "rns-hooks")]
18use rns_hooks::{create_hook_slots, EngineAccess, HookContext, HookManager, HookPoint, HookSlot};
19
20#[cfg(feature = "rns-hooks")]
21use crate::event::BackbonePeerHookEvent;
22use crate::event::{
23 BackbonePeerPoolMemberStatus, BackbonePeerPoolStatus, BackbonePeerStateEntry, BlackholeInfo,
24 DrainStatus, Event, EventReceiver, InterfaceStatsResponse, KnownDestinationEntry,
25 LifecycleState, LocalDestinationEntry, NextHopResponse, PathTableEntry, QueryRequest,
26 QueryResponse, RateTableEntry, RuntimeConfigApplyMode, RuntimeConfigEntry, RuntimeConfigError,
27 RuntimeConfigErrorCode, RuntimeConfigSource, RuntimeConfigValue, SingleInterfaceStat,
28};
29use crate::holepunch::orchestrator::{HolePunchManager, HolePunchManagerAction};
30use crate::ifac;
31#[cfg(all(feature = "iface-auto", test))]
32use crate::interface::auto::AutoRuntime;
33#[cfg(feature = "iface-auto")]
34use crate::interface::auto::AutoRuntimeConfigHandle;
35#[cfg(feature = "iface-backbone")]
36use crate::interface::backbone::{
37 start_client, BackboneClientConfig, BackboneClientRuntime, BackboneClientRuntimeConfigHandle,
38 BackbonePeerStateHandle, BackboneRuntimeConfigHandle,
39};
40#[cfg(all(feature = "iface-backbone", target_os = "linux", test))]
41use crate::interface::backbone::{BackboneAbuseConfig, BackboneServerRuntime};
42#[cfg(all(feature = "iface-i2p", test))]
43use crate::interface::i2p::I2pRuntime;
44#[cfg(feature = "iface-i2p")]
45use crate::interface::i2p::I2pRuntimeConfigHandle;
46#[cfg(all(feature = "iface-pipe", test))]
47use crate::interface::pipe::PipeRuntime;
48#[cfg(feature = "iface-pipe")]
49use crate::interface::pipe::PipeRuntimeConfigHandle;
50#[cfg(all(feature = "iface-rnode", test))]
51use crate::interface::rnode::RNodeSubConfig;
52#[cfg(feature = "iface-rnode")]
53use crate::interface::rnode::{validate_sub_config, RNodeRuntime, RNodeRuntimeConfigHandle};
54#[cfg(feature = "iface-tcp")]
55use crate::interface::tcp::TcpClientRuntimeConfigHandle;
56#[cfg(all(feature = "iface-tcp", test))]
57use crate::interface::tcp_server::TcpServerRuntime;
58#[cfg(feature = "iface-tcp")]
59use crate::interface::tcp_server::TcpServerRuntimeConfigHandle;
60#[cfg(all(feature = "iface-udp", test))]
61use crate::interface::udp::UdpRuntime;
62#[cfg(feature = "iface-udp")]
63use crate::interface::udp::UdpRuntimeConfigHandle;
64use crate::interface::{InterfaceEntry, InterfaceStats};
65use crate::link_manager::{LinkManager, LinkManagerAction};
66use crate::time;
67
68const DEFAULT_KNOWN_DESTINATIONS_TTL: f64 = 48.0 * 60.0 * 60.0;
69const DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES: usize = 8192;
70const DEFAULT_RATE_LIMITER_TTL_SECS: f64 = 48.0 * 60.0 * 60.0;
71const DEFAULT_TICK_INTERVAL_MS: u64 = 1000;
72const DEFAULT_KNOWN_DESTINATIONS_CLEANUP_INTERVAL_TICKS: u32 = 3600;
73const DEFAULT_ANNOUNCE_CACHE_CLEANUP_INTERVAL_TICKS: u32 = 3600;
74const DEFAULT_ANNOUNCE_CACHE_CLEANUP_BATCH_SIZE: usize = 10_000;
75const DEFAULT_DISCOVERY_CLEANUP_INTERVAL_TICKS: u32 = 3600;
76const DEFAULT_MANAGEMENT_ANNOUNCE_INTERVAL_SECS: f64 = 300.0;
77const SEND_RETRY_BACKOFF_MIN: Duration = Duration::from_millis(25);
78const SEND_RETRY_BACKOFF_MAX: Duration = Duration::from_millis(1000);
79
80fn inject_transport_header(raw: &[u8], next_hop: &[u8; 16]) -> Vec<u8> {
81 if raw.len() < 18 {
82 return raw.to_vec();
83 }
84
85 let new_flags = (rns_core::constants::HEADER_2 << 6)
86 | (rns_core::constants::TRANSPORT_TRANSPORT << 4)
87 | (raw[0] & 0x0F);
88
89 let mut new_raw = Vec::with_capacity(raw.len() + 16);
90 new_raw.push(new_flags);
91 new_raw.push(raw[1]);
92 new_raw.extend_from_slice(next_hop);
93 new_raw.extend_from_slice(&raw[2..]);
94 new_raw
95}
96
97fn recover_mutex_guard<'a, T>(mutex: &'a Mutex<T>, label: &str) -> std::sync::MutexGuard<'a, T> {
98 match mutex.lock() {
99 Ok(guard) => guard,
100 Err(poisoned) => {
101 log::error!("recovering from poisoned mutex: {}", label);
102 poisoned.into_inner()
103 }
104 }
105}
106
107#[derive(Debug, Clone, Copy)]
108pub(crate) struct RuntimeConfigDefaults {
109 pub(crate) tick_interval_ms: u64,
110 pub(crate) known_destinations_ttl: f64,
111 pub(crate) rate_limiter_ttl_secs: f64,
112 pub(crate) known_destinations_cleanup_interval_ticks: u32,
113 pub(crate) announce_cache_cleanup_interval_ticks: u32,
114 pub(crate) announce_cache_cleanup_batch_size: usize,
115 pub(crate) discovery_cleanup_interval_ticks: u32,
116 pub(crate) management_announce_interval_secs: f64,
117 pub(crate) direct_connect_policy: crate::event::HolePunchPolicy,
118 #[cfg(feature = "rns-hooks")]
119 pub(crate) provider_queue_max_events: usize,
120 #[cfg(feature = "rns-hooks")]
121 pub(crate) provider_queue_max_bytes: usize,
122}
123
124#[cfg(feature = "iface-backbone")]
125#[derive(Debug, Clone)]
126pub(crate) struct BackboneDiscoveryRuntime {
127 pub(crate) discoverable: bool,
128 pub(crate) config: crate::discovery::DiscoveryConfig,
129 pub(crate) transport_enabled: bool,
130 pub(crate) ifac_netname: Option<String>,
131 pub(crate) ifac_netkey: Option<String>,
132}
133
134#[cfg(feature = "iface-backbone")]
135#[derive(Debug, Clone)]
136pub(crate) struct BackboneDiscoveryRuntimeHandle {
137 pub(crate) interface_name: String,
138 pub(crate) current: BackboneDiscoveryRuntime,
139 pub(crate) startup: BackboneDiscoveryRuntime,
140}
141
142#[cfg(feature = "iface-tcp")]
143#[derive(Debug, Clone)]
144pub(crate) struct TcpServerDiscoveryRuntime {
145 pub(crate) discoverable: bool,
146 pub(crate) config: crate::discovery::DiscoveryConfig,
147 pub(crate) transport_enabled: bool,
148 pub(crate) ifac_netname: Option<String>,
149 pub(crate) ifac_netkey: Option<String>,
150}
151
152#[cfg(feature = "iface-tcp")]
153#[derive(Debug, Clone)]
154pub(crate) struct TcpServerDiscoveryRuntimeHandle {
155 pub(crate) interface_name: String,
156 pub(crate) current: TcpServerDiscoveryRuntime,
157 pub(crate) startup: TcpServerDiscoveryRuntime,
158}
159
160#[derive(Debug, Clone, PartialEq, Eq)]
161pub(crate) struct IfacRuntimeConfig {
162 pub(crate) netname: Option<String>,
163 pub(crate) netkey: Option<String>,
164 pub(crate) size: usize,
165}
166
167#[cfg(feature = "iface-backbone")]
168#[derive(Debug, Clone)]
169pub struct BackbonePeerPoolSettings {
170 pub max_connected: usize,
171 pub failure_threshold: usize,
172 pub failure_window: Duration,
173 pub cooldown: Duration,
174}
175
176#[cfg(feature = "iface-backbone")]
177pub(crate) struct BackbonePeerPoolCandidateConfig {
178 pub(crate) client: BackboneClientConfig,
179 pub(crate) mode: u8,
180 pub(crate) ingress_control: rns_core::transport::types::IngressControlConfig,
181 pub(crate) ifac_runtime: IfacRuntimeConfig,
182 pub(crate) ifac_enabled: bool,
183 pub(crate) interface_type_name: String,
184}
185
186#[cfg(feature = "iface-backbone")]
187struct BackbonePeerPool {
188 settings: BackbonePeerPoolSettings,
189 candidates: Vec<BackbonePeerPoolCandidate>,
190}
191
192#[cfg(feature = "iface-backbone")]
193struct BackbonePeerPoolCandidate {
194 config: BackbonePeerPoolCandidateConfig,
195 active_id: Option<InterfaceId>,
196 failures: Vec<f64>,
197 retry_after: Option<f64>,
198 cooldown_until: Option<f64>,
199 last_error: Option<String>,
200}
201
202#[cfg(feature = "rns-hooks")]
204struct EngineRef<'a> {
205 engine: &'a TransportEngine,
206 interfaces: &'a HashMap<InterfaceId, InterfaceEntry>,
207 link_manager: &'a LinkManager,
208 now: f64,
209}
210
211#[cfg(feature = "rns-hooks")]
212impl<'a> EngineAccess for EngineRef<'a> {
213 fn has_path(&self, dest: &[u8; 16]) -> bool {
214 self.engine.has_path(dest)
215 }
216 fn hops_to(&self, dest: &[u8; 16]) -> Option<u8> {
217 self.engine.hops_to(dest)
218 }
219 fn next_hop(&self, dest: &[u8; 16]) -> Option<[u8; 16]> {
220 self.engine.next_hop(dest)
221 }
222 fn is_blackholed(&self, identity: &[u8; 16]) -> bool {
223 self.engine.is_blackholed(identity, self.now)
224 }
225 fn interface_name(&self, id: u64) -> Option<String> {
226 self.interfaces
227 .get(&InterfaceId(id))
228 .map(|e| e.info.name.clone())
229 }
230 fn interface_mode(&self, id: u64) -> Option<u8> {
231 self.interfaces.get(&InterfaceId(id)).map(|e| e.info.mode)
232 }
233 fn identity_hash(&self) -> Option<[u8; 16]> {
234 self.engine.identity_hash().copied()
235 }
236 fn announce_rate(&self, id: u64) -> Option<i32> {
237 self.interfaces
238 .get(&InterfaceId(id))
239 .map(|e| (e.stats.outgoing_announce_freq() * 1000.0) as i32)
240 }
241 fn link_state(&self, link_hash: &[u8; 16]) -> Option<u8> {
242 use rns_core::link::types::LinkState;
243 self.link_manager.link_state(link_hash).map(|s| match s {
244 LinkState::Pending => 0,
245 LinkState::Handshake => 1,
246 LinkState::Active => 2,
247 LinkState::Stale => 3,
248 LinkState::Closed => 4,
249 })
250 }
251}
252
253#[cfg(any(test, feature = "rns-hooks"))]
258fn extract_dest_hash(raw: &[u8]) -> [u8; 16] {
259 let mut dest = [0u8; 16];
260 if raw.is_empty() {
261 return dest;
262 }
263 let is_header2 = raw[0] & 0x40 != 0;
264 let start = if is_header2 { 18 } else { 2 };
265 let end = start + 16;
266 if raw.len() >= end {
267 dest.copy_from_slice(&raw[start..end]);
268 }
269 dest
270}
271
272#[cfg(feature = "rns-hooks")]
274fn run_hook_inner(
275 programs: &mut [rns_hooks::LoadedProgram],
276 hook_manager: &Option<HookManager>,
277 engine_access: &dyn EngineAccess,
278 ctx: &HookContext,
279 now: f64,
280 provider_events_enabled: bool,
281) -> Option<rns_hooks::ExecuteResult> {
282 if programs.is_empty() {
283 return None;
284 }
285 let mgr = hook_manager.as_ref()?;
286 mgr.run_chain_with_provider_events(programs, ctx, engine_access, now, provider_events_enabled)
287}
288
289#[cfg(feature = "rns-hooks")]
290fn backbone_peer_hook_context(event: &BackbonePeerHookEvent) -> HookContext<'_> {
291 HookContext::BackbonePeer {
292 server_interface_id: event.server_interface_id.0,
293 peer_interface_id: event.peer_interface_id.map(|id| id.0),
294 peer_ip: event.peer_ip,
295 peer_port: event.peer_port,
296 connected_for: event.connected_for,
297 had_received_data: event.had_received_data,
298 penalty_level: event.penalty_level,
299 blacklist_for: event.blacklist_for,
300 }
301}
302
303#[cfg(feature = "rns-hooks")]
305fn convert_injected_actions(actions: Vec<rns_hooks::ActionWire>) -> Vec<TransportAction> {
306 actions
307 .into_iter()
308 .map(|a| {
309 use rns_hooks::ActionWire;
310 match a {
311 ActionWire::SendOnInterface { interface, raw } => {
312 TransportAction::SendOnInterface {
313 interface: InterfaceId(interface),
314 raw,
315 }
316 }
317 ActionWire::BroadcastOnAllInterfaces {
318 raw,
319 exclude,
320 has_exclude,
321 } => TransportAction::BroadcastOnAllInterfaces {
322 raw,
323 exclude: if has_exclude != 0 {
324 Some(InterfaceId(exclude))
325 } else {
326 None
327 },
328 },
329 ActionWire::DeliverLocal {
330 destination_hash,
331 raw,
332 packet_hash,
333 receiving_interface,
334 } => TransportAction::DeliverLocal {
335 destination_hash,
336 raw,
337 packet_hash,
338 receiving_interface: InterfaceId(receiving_interface),
339 },
340 ActionWire::PathUpdated {
341 destination_hash,
342 hops,
343 next_hop,
344 interface,
345 } => TransportAction::PathUpdated {
346 destination_hash,
347 hops,
348 next_hop,
349 interface: InterfaceId(interface),
350 },
351 ActionWire::CacheAnnounce { packet_hash, raw } => {
352 TransportAction::CacheAnnounce { packet_hash, raw }
353 }
354 ActionWire::TunnelEstablished {
355 tunnel_id,
356 interface,
357 } => TransportAction::TunnelEstablished {
358 tunnel_id,
359 interface: InterfaceId(interface),
360 },
361 ActionWire::TunnelSynthesize {
362 interface,
363 data,
364 dest_hash,
365 } => TransportAction::TunnelSynthesize {
366 interface: InterfaceId(interface),
367 data,
368 dest_hash,
369 },
370 ActionWire::ForwardToLocalClients {
371 raw,
372 exclude,
373 has_exclude,
374 } => TransportAction::ForwardToLocalClients {
375 raw,
376 exclude: if has_exclude != 0 {
377 Some(InterfaceId(exclude))
378 } else {
379 None
380 },
381 },
382 ActionWire::ForwardPlainBroadcast {
383 raw,
384 to_local,
385 exclude,
386 has_exclude,
387 } => TransportAction::ForwardPlainBroadcast {
388 raw,
389 to_local: to_local != 0,
390 exclude: if has_exclude != 0 {
391 Some(InterfaceId(exclude))
392 } else {
393 None
394 },
395 },
396 ActionWire::AnnounceReceived {
397 destination_hash,
398 identity_hash,
399 public_key,
400 name_hash,
401 random_hash,
402 app_data,
403 hops,
404 receiving_interface,
405 } => TransportAction::AnnounceReceived {
406 destination_hash,
407 identity_hash,
408 public_key,
409 name_hash,
410 random_hash,
411 app_data,
412 hops,
413 receiving_interface: InterfaceId(receiving_interface),
414 },
415 }
416 })
417 .collect()
418}
419
420fn infer_interface_type(name: &str) -> String {
424 if name.starts_with("TCPServerInterface") {
425 "TCPServerClientInterface".to_string()
426 } else if name.starts_with("BackboneInterface") {
427 "BackboneInterface".to_string()
428 } else if name.starts_with("LocalInterface") {
429 "LocalServerClientInterface".to_string()
430 } else {
431 "AutoInterface".to_string()
434 }
435}
436
437pub use crate::common::callbacks::Callbacks;
438
439#[derive(Clone)]
440struct SharedAnnounceRecord {
441 name_hash: [u8; 10],
442 identity_prv_key: [u8; 64],
443 app_data: Option<Vec<u8>>,
444}
445
446#[derive(Debug, Clone)]
447pub(crate) struct KnownDestinationState {
448 announced: crate::destination::AnnouncedIdentity,
449 was_used: bool,
450 last_used_at: Option<f64>,
451 retained: bool,
452}
453
454pub struct Driver {
456 pub(crate) engine: TransportEngine,
457 pub(crate) interfaces: HashMap<InterfaceId, InterfaceEntry>,
458 pub(crate) rng: OsRng,
459 pub(crate) rx: EventReceiver,
460 pub(crate) callbacks: Box<dyn Callbacks>,
461 pub(crate) started: f64,
462 pub(crate) lifecycle_state: LifecycleState,
463 pub(crate) drain_started_at: Option<Instant>,
464 pub(crate) drain_deadline: Option<Instant>,
465 pub(crate) listener_controls: Vec<crate::interface::ListenerControl>,
466 pub(crate) announce_cache: Option<crate::announce_cache::AnnounceCache>,
467 pub(crate) tunnel_synth_dest: [u8; 16],
469 pub(crate) transport_identity: Option<rns_crypto::identity::Identity>,
471 pub(crate) link_manager: LinkManager,
473 pub(crate) management_config: crate::management::ManagementConfig,
475 pub(crate) last_management_announce: f64,
477 pub(crate) initial_announce_sent: bool,
479 pub(crate) known_destinations: HashMap<[u8; 16], KnownDestinationState>,
481 pub(crate) known_destinations_ttl: f64,
483 pub(crate) known_destinations_max_entries: usize,
485 pub(crate) rate_limiter_ttl_secs: f64,
487 pub(crate) path_request_dest: [u8; 16],
489 pub(crate) proof_strategies: HashMap<
492 [u8; 16],
493 (
494 rns_core::types::ProofStrategy,
495 Option<rns_crypto::identity::Identity>,
496 ),
497 >,
498 pub(crate) sent_packets: HashMap<[u8; 32], ([u8; 16], f64)>,
500 pub(crate) completed_proofs: HashMap<[u8; 32], (f64, f64)>,
502 pub(crate) local_destinations: HashMap<[u8; 16], u8>,
504 shared_announces: HashMap<[u8; 16], SharedAnnounceRecord>,
506 shared_reconnect_pending: HashMap<InterfaceId, bool>,
508 pub(crate) holepunch_manager: HolePunchManager,
510 pub(crate) event_tx: crate::event::EventSender,
512 pub(crate) interface_writer_queue_capacity: usize,
514 pub(crate) tick_interval_ms: Arc<AtomicU64>,
516 #[cfg(feature = "iface-backbone")]
518 pub(crate) backbone_runtime: HashMap<String, BackboneRuntimeConfigHandle>,
519 #[cfg(feature = "iface-backbone")]
521 pub(crate) backbone_peer_state: HashMap<String, BackbonePeerStateHandle>,
522 #[cfg(feature = "iface-backbone")]
524 pub(crate) backbone_client_runtime: HashMap<String, BackboneClientRuntimeConfigHandle>,
525 #[cfg(feature = "iface-backbone")]
527 pub(crate) backbone_discovery_runtime: HashMap<String, BackboneDiscoveryRuntimeHandle>,
528 #[cfg(feature = "iface-backbone")]
530 backbone_peer_pool: Option<BackbonePeerPool>,
531 #[cfg(feature = "iface-tcp")]
533 pub(crate) tcp_server_runtime: HashMap<String, TcpServerRuntimeConfigHandle>,
534 #[cfg(feature = "iface-tcp")]
536 pub(crate) tcp_client_runtime: HashMap<String, TcpClientRuntimeConfigHandle>,
537 #[cfg(feature = "iface-tcp")]
539 pub(crate) tcp_server_discovery_runtime: HashMap<String, TcpServerDiscoveryRuntimeHandle>,
540 #[cfg(feature = "iface-udp")]
542 pub(crate) udp_runtime: HashMap<String, UdpRuntimeConfigHandle>,
543 #[cfg(feature = "iface-auto")]
545 pub(crate) auto_runtime: HashMap<String, AutoRuntimeConfigHandle>,
546 #[cfg(feature = "iface-i2p")]
548 pub(crate) i2p_runtime: HashMap<String, I2pRuntimeConfigHandle>,
549 #[cfg(feature = "iface-pipe")]
551 pub(crate) pipe_runtime: HashMap<String, PipeRuntimeConfigHandle>,
552 #[cfg(feature = "iface-rnode")]
554 pub(crate) rnode_runtime: HashMap<String, RNodeRuntimeConfigHandle>,
555 pub(crate) interface_runtime_defaults:
557 HashMap<String, rns_core::transport::types::InterfaceInfo>,
558 pub(crate) interface_ifac_runtime: HashMap<String, IfacRuntimeConfig>,
560 pub(crate) interface_ifac_runtime_defaults: HashMap<String, IfacRuntimeConfig>,
562 pub(crate) discovered_interfaces: crate::discovery::DiscoveredInterfaceStorage,
564 pub(crate) discovery_required_value: u8,
566 pub(crate) discovery_name_hash: [u8; 10],
568 pub(crate) probe_responder_hash: Option<[u8; 16]>,
570 pub(crate) discover_interfaces: bool,
572 pub(crate) interface_announcer: Option<crate::discovery::InterfaceAnnouncer>,
574 pub(crate) announce_verify_queue: Arc<Mutex<AnnounceVerifyQueue>>,
576 pub(crate) async_announce_verification: bool,
578 pub(crate) discovery_cleanup_counter: u32,
580 pub(crate) discovery_cleanup_interval_ticks: u32,
582 pub(crate) memory_stats_counter: u32,
584 pub(crate) cache_cleanup_counter: u32,
586 pub(crate) announce_cache_cleanup_counter: u32,
588 pub(crate) known_destinations_cleanup_interval_ticks: u32,
590 pub(crate) known_destinations_cap_evict_count: usize,
592 pub(crate) announce_cache_cleanup_interval_ticks: u32,
594 pub(crate) cache_cleanup_active_hashes: Option<Vec<[u8; 32]>>,
596 pub(crate) cache_cleanup_entries: Option<std::fs::ReadDir>,
598 pub(crate) cache_cleanup_removed: usize,
600 pub(crate) announce_cache_cleanup_batch_size: usize,
602 pub(crate) management_announce_interval_secs: f64,
604 pub(crate) runtime_config_defaults: RuntimeConfigDefaults,
606 #[cfg(feature = "rns-hooks")]
608 pub(crate) hook_slots: [HookSlot; HookPoint::COUNT],
609 #[cfg(feature = "rns-hooks")]
611 pub(crate) hook_manager: Option<HookManager>,
612 #[cfg(feature = "rns-hooks")]
613 pub(crate) provider_bridge: Option<ProviderBridge>,
614}
615
616impl Driver {
617 pub fn new(
619 config: TransportConfig,
620 rx: EventReceiver,
621 tx: crate::event::EventSender,
622 callbacks: Box<dyn Callbacks>,
623 ) -> Self {
624 let announce_queue_max_entries = config.announce_queue_max_entries;
625 let tunnel_synth_dest = rns_core::destination::destination_hash(
626 "rnstransport",
627 &["tunnel", "synthesize"],
628 None,
629 );
630 let path_request_dest =
631 rns_core::destination::destination_hash("rnstransport", &["path", "request"], None);
632 let discovery_name_hash = crate::discovery::discovery_name_hash();
633 let mut engine = TransportEngine::new(config);
634 engine.register_destination(tunnel_synth_dest, rns_core::constants::DESTINATION_PLAIN);
635 engine.register_destination(path_request_dest, rns_core::constants::DESTINATION_PLAIN);
637 let mut local_destinations = HashMap::new();
640 local_destinations.insert(tunnel_synth_dest, rns_core::constants::DESTINATION_PLAIN);
641 local_destinations.insert(path_request_dest, rns_core::constants::DESTINATION_PLAIN);
642 let runtime_config_defaults = RuntimeConfigDefaults {
643 tick_interval_ms: DEFAULT_TICK_INTERVAL_MS,
644 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
645 rate_limiter_ttl_secs: DEFAULT_RATE_LIMITER_TTL_SECS,
646 known_destinations_cleanup_interval_ticks:
647 DEFAULT_KNOWN_DESTINATIONS_CLEANUP_INTERVAL_TICKS,
648 announce_cache_cleanup_interval_ticks: DEFAULT_ANNOUNCE_CACHE_CLEANUP_INTERVAL_TICKS,
649 announce_cache_cleanup_batch_size: DEFAULT_ANNOUNCE_CACHE_CLEANUP_BATCH_SIZE,
650 discovery_cleanup_interval_ticks: DEFAULT_DISCOVERY_CLEANUP_INTERVAL_TICKS,
651 management_announce_interval_secs: DEFAULT_MANAGEMENT_ANNOUNCE_INTERVAL_SECS,
652 direct_connect_policy: crate::event::HolePunchPolicy::default(),
653 #[cfg(feature = "rns-hooks")]
654 provider_queue_max_events: crate::provider_bridge::ProviderBridgeConfig::default()
655 .queue_max_events,
656 #[cfg(feature = "rns-hooks")]
657 provider_queue_max_bytes: crate::provider_bridge::ProviderBridgeConfig::default()
658 .queue_max_bytes,
659 };
660 Driver {
661 engine,
662 interfaces: HashMap::new(),
663 rng: OsRng,
664 rx,
665 callbacks,
666 started: time::now(),
667 lifecycle_state: LifecycleState::Active,
668 drain_started_at: None,
669 drain_deadline: None,
670 listener_controls: Vec::new(),
671 announce_cache: None,
672 tunnel_synth_dest,
673 transport_identity: None,
674 link_manager: LinkManager::new(),
675 management_config: Default::default(),
676 last_management_announce: 0.0,
677 initial_announce_sent: false,
678 known_destinations: HashMap::new(),
679 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
680 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
681 rate_limiter_ttl_secs: DEFAULT_RATE_LIMITER_TTL_SECS,
682 path_request_dest,
683 proof_strategies: HashMap::new(),
684 sent_packets: HashMap::new(),
685 completed_proofs: HashMap::new(),
686 local_destinations,
687 shared_announces: HashMap::new(),
688 shared_reconnect_pending: HashMap::new(),
689 holepunch_manager: HolePunchManager::new(
690 vec![],
691 rns_core::holepunch::ProbeProtocol::Rnsp,
692 None,
693 ),
694 event_tx: tx,
695 interface_writer_queue_capacity: crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
696 tick_interval_ms: Arc::new(AtomicU64::new(DEFAULT_TICK_INTERVAL_MS)),
697 #[cfg(feature = "iface-backbone")]
698 backbone_runtime: HashMap::new(),
699 #[cfg(feature = "iface-backbone")]
700 backbone_peer_state: HashMap::new(),
701 #[cfg(feature = "iface-backbone")]
702 backbone_client_runtime: HashMap::new(),
703 #[cfg(feature = "iface-backbone")]
704 backbone_discovery_runtime: HashMap::new(),
705 #[cfg(feature = "iface-backbone")]
706 backbone_peer_pool: None,
707 #[cfg(feature = "iface-tcp")]
708 tcp_server_runtime: HashMap::new(),
709 #[cfg(feature = "iface-tcp")]
710 tcp_client_runtime: HashMap::new(),
711 #[cfg(feature = "iface-tcp")]
712 tcp_server_discovery_runtime: HashMap::new(),
713 #[cfg(feature = "iface-udp")]
714 udp_runtime: HashMap::new(),
715 #[cfg(feature = "iface-auto")]
716 auto_runtime: HashMap::new(),
717 #[cfg(feature = "iface-i2p")]
718 i2p_runtime: HashMap::new(),
719 #[cfg(feature = "iface-pipe")]
720 pipe_runtime: HashMap::new(),
721 #[cfg(feature = "iface-rnode")]
722 rnode_runtime: HashMap::new(),
723 interface_runtime_defaults: HashMap::new(),
724 interface_ifac_runtime: HashMap::new(),
725 interface_ifac_runtime_defaults: HashMap::new(),
726 discovered_interfaces: crate::discovery::DiscoveredInterfaceStorage::new(
727 std::env::temp_dir().join("rns-discovered-interfaces"),
728 ),
729 discovery_required_value: crate::discovery::DEFAULT_STAMP_VALUE,
730 discovery_name_hash,
731 probe_responder_hash: None,
732 discover_interfaces: false,
733 interface_announcer: None,
734 announce_verify_queue: Arc::new(Mutex::new(AnnounceVerifyQueue::new(
735 announce_queue_max_entries,
736 ))),
737 async_announce_verification: false,
738 discovery_cleanup_counter: 0,
739 discovery_cleanup_interval_ticks: runtime_config_defaults
740 .discovery_cleanup_interval_ticks,
741 memory_stats_counter: 0,
742 cache_cleanup_counter: 0,
743 announce_cache_cleanup_counter: 0,
744 known_destinations_cleanup_interval_ticks: runtime_config_defaults
745 .known_destinations_cleanup_interval_ticks,
746 known_destinations_cap_evict_count: 0,
747 announce_cache_cleanup_interval_ticks: runtime_config_defaults
748 .announce_cache_cleanup_interval_ticks,
749 cache_cleanup_active_hashes: None,
750 cache_cleanup_entries: None,
751 cache_cleanup_removed: 0,
752 announce_cache_cleanup_batch_size: runtime_config_defaults
753 .announce_cache_cleanup_batch_size,
754 management_announce_interval_secs: runtime_config_defaults
755 .management_announce_interval_secs,
756 runtime_config_defaults,
757 #[cfg(feature = "rns-hooks")]
758 hook_slots: create_hook_slots(),
759 #[cfg(feature = "rns-hooks")]
760 hook_manager: HookManager::new().ok(),
761 #[cfg(feature = "rns-hooks")]
762 provider_bridge: None,
763 }
764 }
765
766 pub fn set_announce_verify_queue_config(
767 &mut self,
768 max_entries: usize,
769 max_bytes: usize,
770 max_stale_secs: f64,
771 overflow_policy: OverflowPolicy,
772 ) {
773 self.announce_verify_queue = Arc::new(Mutex::new(AnnounceVerifyQueue::with_limits(
774 max_entries,
775 max_bytes,
776 max_stale_secs,
777 overflow_policy,
778 )));
779 }
780
781 fn wrap_interface_writer(
782 &self,
783 interface_id: InterfaceId,
784 interface_name: &str,
785 writer: Box<dyn crate::interface::Writer>,
786 ) -> (
787 Box<dyn crate::interface::Writer>,
788 crate::interface::AsyncWriterMetrics,
789 ) {
790 crate::interface::wrap_async_writer(
791 writer,
792 interface_id,
793 interface_name,
794 self.event_tx.clone(),
795 self.interface_writer_queue_capacity,
796 )
797 }
798
799 fn upsert_known_destination(
800 &mut self,
801 dest_hash: [u8; 16],
802 announced: crate::destination::AnnouncedIdentity,
803 ) {
804 if let Some(existing) = self.known_destinations.get_mut(&dest_hash) {
805 existing.announced = announced;
806 return;
807 }
808
809 self.enforce_known_destination_cap(true);
810 self.known_destinations.insert(
811 dest_hash,
812 KnownDestinationState {
813 announced,
814 was_used: false,
815 last_used_at: None,
816 retained: false,
817 },
818 );
819 }
820
821 fn known_destination_entry(
822 dest_hash: [u8; 16],
823 state: &KnownDestinationState,
824 ) -> KnownDestinationEntry {
825 KnownDestinationEntry {
826 dest_hash,
827 identity_hash: state.announced.identity_hash.0,
828 public_key: state.announced.public_key,
829 app_data: state.announced.app_data.clone(),
830 hops: state.announced.hops,
831 received_at: state.announced.received_at,
832 receiving_interface: state.announced.receiving_interface,
833 was_used: state.was_used,
834 last_used_at: state.last_used_at,
835 retained: state.retained,
836 }
837 }
838
839 fn known_destination_entries(&self) -> Vec<KnownDestinationEntry> {
840 let mut entries: Vec<_> = self
841 .known_destinations
842 .iter()
843 .map(|(dest_hash, state)| Self::known_destination_entry(*dest_hash, state))
844 .collect();
845 entries.sort_by(|a, b| a.dest_hash.cmp(&b.dest_hash));
846 entries
847 }
848
849 fn mark_known_destination_used(&mut self, dest_hash: &[u8; 16]) -> bool {
850 let Some(state) = self.known_destinations.get_mut(dest_hash) else {
851 return false;
852 };
853 state.was_used = true;
854 state.last_used_at = Some(time::now());
855 true
856 }
857
858 fn retain_known_destination(&mut self, dest_hash: &[u8; 16]) -> bool {
859 let Some(state) = self.known_destinations.get_mut(dest_hash) else {
860 return false;
861 };
862 state.retained = true;
863 true
864 }
865
866 fn unretain_known_destination(&mut self, dest_hash: &[u8; 16]) -> bool {
867 let Some(state) = self.known_destinations.get_mut(dest_hash) else {
868 return false;
869 };
870 state.retained = false;
871 true
872 }
873
874 fn known_destination_announced(
875 &self,
876 dest_hash: &[u8; 16],
877 ) -> Option<crate::destination::AnnouncedIdentity> {
878 self.known_destinations
879 .get(dest_hash)
880 .map(|state| state.announced.clone())
881 }
882
883 fn known_destination_relevance_time(state: &KnownDestinationState) -> f64 {
884 state.last_used_at.unwrap_or(state.announced.received_at)
885 }
886
887 fn begin_drain(&mut self, timeout: Duration) {
888 let now = Instant::now();
889 let deadline = now + timeout;
890 match self.lifecycle_state {
891 LifecycleState::Active => {
892 self.lifecycle_state = LifecycleState::Draining;
893 self.drain_started_at = Some(now);
894 self.drain_deadline = Some(deadline);
895 log::info!(
896 "driver entering drain mode with {:.3}s timeout",
897 timeout.as_secs_f64()
898 );
899 self.stop_listener_accepts();
900 }
901 LifecycleState::Draining => {
902 self.drain_deadline = Some(deadline);
903 log::info!(
904 "driver drain deadline updated to {:.3}s from now",
905 timeout.as_secs_f64()
906 );
907 self.stop_listener_accepts();
908 }
909 LifecycleState::Stopping | LifecycleState::Stopped => {
910 log::debug!(
911 "ignoring BeginDrain while lifecycle state is {:?}",
912 self.lifecycle_state
913 );
914 }
915 }
916 }
917
918 fn is_draining(&self) -> bool {
919 matches!(self.lifecycle_state, LifecycleState::Draining)
920 }
921
922 pub fn register_listener_control(&mut self, control: crate::interface::ListenerControl) {
923 self.listener_controls.push(control);
924 }
925
926 fn stop_listener_accepts(&mut self) {
927 for control in &self.listener_controls {
928 control.request_stop();
929 }
930 #[cfg(feature = "rns-hooks")]
931 if let Some(bridge) = self.provider_bridge.as_ref() {
932 bridge.stop_accepting();
933 }
934 }
935
936 fn reject_new_work(&self, op: &str) {
937 log::info!("rejecting {} while node is draining", op);
938 }
939
940 fn drain_error(&self, op: &str) -> String {
941 format!("cannot {} while node is draining", op)
942 }
943
944 fn drain_status(&self) -> DrainStatus {
945 let now = Instant::now();
946 let active_links = self.link_manager.link_count();
947 let active_resource_transfers = self.link_manager.resource_transfer_count();
948 let active_holepunch_sessions = self.holepunch_manager.session_count();
949 let interface_writer_queued_frames = self
950 .interfaces
951 .values()
952 .map(|entry| {
953 entry
954 .async_writer_metrics
955 .as_ref()
956 .map(|metrics| metrics.queued_frames())
957 .unwrap_or(0)
958 })
959 .sum();
960 #[cfg(feature = "rns-hooks")]
961 let (provider_backlog_events, provider_consumer_queued_events) = self
962 .provider_bridge
963 .as_ref()
964 .map(|bridge| {
965 let stats = bridge.stats();
966 (
967 stats.backlog_len,
968 stats
969 .consumers
970 .iter()
971 .map(|consumer| consumer.queue_len)
972 .sum(),
973 )
974 })
975 .unwrap_or((0, 0));
976 #[cfg(not(feature = "rns-hooks"))]
977 let (provider_backlog_events, provider_consumer_queued_events) = (0, 0);
978 let drain_age_seconds = self
979 .drain_started_at
980 .map(|started| started.elapsed().as_secs_f64());
981 let deadline_remaining_seconds = self.drain_deadline.map(|deadline| {
982 deadline
983 .checked_duration_since(now)
984 .map(|remaining| remaining.as_secs_f64())
985 .unwrap_or(0.0)
986 });
987 let detail = match self.lifecycle_state {
988 LifecycleState::Active => Some("node is accepting normal work".into()),
989 LifecycleState::Draining => {
990 let mut remaining = Vec::new();
991 if active_links > 0 {
992 remaining.push(format!("{active_links} link(s)"));
993 }
994 if active_resource_transfers > 0 {
995 remaining.push(format!("{active_resource_transfers} resource transfer(s)"));
996 }
997 if active_holepunch_sessions > 0 {
998 remaining.push(format!("{active_holepunch_sessions} hole-punch session(s)"));
999 }
1000 if interface_writer_queued_frames > 0 {
1001 remaining.push(format!(
1002 "{interface_writer_queued_frames} queued interface writer frame(s)"
1003 ));
1004 }
1005 if provider_backlog_events > 0 {
1006 remaining.push(format!(
1007 "{provider_backlog_events} provider backlog event(s)"
1008 ));
1009 }
1010 if provider_consumer_queued_events > 0 {
1011 remaining.push(format!(
1012 "{provider_consumer_queued_events} queued provider consumer event(s)"
1013 ));
1014 }
1015 Some(if remaining.is_empty() {
1016 "node is draining existing work; no active links, resource transfers, hole-punch sessions, or queued writer/provider work remain".into()
1017 } else {
1018 format!(
1019 "node is draining existing work; {} still active",
1020 remaining.join(", ")
1021 )
1022 })
1023 }
1024 LifecycleState::Stopping => Some("node is tearing down remaining work".into()),
1025 LifecycleState::Stopped => Some("node is stopped".into()),
1026 };
1027
1028 DrainStatus {
1029 state: self.lifecycle_state,
1030 drain_age_seconds,
1031 deadline_remaining_seconds,
1032 drain_complete: !matches!(self.lifecycle_state, LifecycleState::Draining)
1033 || (active_links == 0
1034 && active_resource_transfers == 0
1035 && active_holepunch_sessions == 0
1036 && interface_writer_queued_frames == 0
1037 && provider_backlog_events == 0
1038 && provider_consumer_queued_events == 0),
1039 interface_writer_queued_frames,
1040 provider_backlog_events,
1041 provider_consumer_queued_events,
1042 detail,
1043 }
1044 }
1045
1046 fn enforce_drain_deadline(&mut self) {
1047 if !matches!(self.lifecycle_state, LifecycleState::Draining) {
1048 return;
1049 }
1050 let Some(deadline) = self.drain_deadline else {
1051 return;
1052 };
1053 if Instant::now() < deadline {
1054 return;
1055 }
1056
1057 log::info!("driver drain deadline reached; tearing down remaining links");
1058 self.lifecycle_state = LifecycleState::Stopping;
1059 let resource_actions = self.link_manager.cancel_all_resources(&mut self.rng);
1060 self.dispatch_link_actions(resource_actions);
1061 let link_actions = self.link_manager.teardown_all_links();
1062 self.dispatch_link_actions(link_actions);
1063 let cleanup_actions = self.link_manager.tick(&mut self.rng);
1064 self.dispatch_link_actions(cleanup_actions);
1065 self.holepunch_manager.abort_all_sessions();
1066 }
1067
1068 fn enforce_known_destination_cap(&mut self, for_insert: bool) -> usize {
1069 if self.known_destinations_max_entries == usize::MAX {
1070 return 0;
1071 }
1072
1073 let mut evicted = 0usize;
1074 while if for_insert {
1075 self.known_destinations.len() >= self.known_destinations_max_entries
1076 } else {
1077 self.known_destinations.len() > self.known_destinations_max_entries
1078 } {
1079 let active_dests = self.engine.active_destination_hashes();
1080 let candidate = self
1081 .oldest_known_destination(false, &active_dests)
1082 .or_else(|| self.oldest_known_destination(true, &active_dests));
1083 let Some(dest_hash) = candidate else {
1084 break;
1085 };
1086 if self.known_destinations.remove(&dest_hash).is_some() {
1087 evicted += 1;
1088 self.known_destinations_cap_evict_count += 1;
1089 } else {
1090 break;
1091 }
1092 }
1093 evicted
1094 }
1095
1096 fn oldest_known_destination(
1097 &self,
1098 include_protected: bool,
1099 active_dests: &std::collections::BTreeSet<[u8; 16]>,
1100 ) -> Option<[u8; 16]> {
1101 self.known_destinations
1102 .iter()
1103 .filter(|(dest_hash, state)| {
1104 include_protected
1105 || (!active_dests.contains(*dest_hash)
1106 && !self.local_destinations.contains_key(*dest_hash)
1107 && !state.retained)
1108 })
1109 .min_by(|a, b| {
1110 Self::known_destination_relevance_time(a.1)
1111 .partial_cmp(&Self::known_destination_relevance_time(b.1))
1112 .unwrap_or(std::cmp::Ordering::Equal)
1113 .then_with(|| a.0.cmp(b.0))
1114 })
1115 .map(|(dest_hash, _)| *dest_hash)
1116 }
1117
1118 #[cfg(feature = "rns-hooks")]
1119 fn provider_events_enabled(&self) -> bool {
1120 self.provider_bridge.is_some()
1121 }
1122
1123 #[cfg(feature = "rns-hooks")]
1124 fn run_backbone_peer_hook(
1125 &mut self,
1126 attach_point: &str,
1127 point: HookPoint,
1128 event: &BackbonePeerHookEvent,
1129 ) {
1130 let ctx = backbone_peer_hook_context(event);
1131 let now = time::now();
1132 let engine_ref = EngineRef {
1133 engine: &self.engine,
1134 interfaces: &self.interfaces,
1135 link_manager: &self.link_manager,
1136 now,
1137 };
1138 let provider_events_enabled = self.provider_events_enabled();
1139 if let Some(ref e) = run_hook_inner(
1140 &mut self.hook_slots[point as usize].programs,
1141 &self.hook_manager,
1142 &engine_ref,
1143 &ctx,
1144 now,
1145 provider_events_enabled,
1146 ) {
1147 self.forward_hook_side_effects(attach_point, e);
1148 }
1149 }
1150
1151 #[cfg(feature = "iface-backbone")]
1152 fn make_discoverable_interface(
1153 runtime: &BackboneDiscoveryRuntimeHandle,
1154 ) -> crate::discovery::DiscoverableInterface {
1155 crate::discovery::DiscoverableInterface {
1156 interface_name: runtime.interface_name.clone(),
1157 config: runtime.current.config.clone(),
1158 transport_enabled: runtime.current.transport_enabled,
1159 ifac_netname: runtime.current.ifac_netname.clone(),
1160 ifac_netkey: runtime.current.ifac_netkey.clone(),
1161 }
1162 }
1163
1164 #[cfg(feature = "iface-backbone")]
1165 fn sync_backbone_discovery_runtime(
1166 &mut self,
1167 interface_name: &str,
1168 ) -> Result<(), RuntimeConfigError> {
1169 let handle = self
1170 .backbone_discovery_runtime
1171 .get(interface_name)
1172 .ok_or(RuntimeConfigError {
1173 code: RuntimeConfigErrorCode::NotFound,
1174 message: format!("backbone interface '{}' not found", interface_name),
1175 })?
1176 .clone();
1177
1178 if handle.current.discoverable {
1179 let iface = Self::make_discoverable_interface(&handle);
1180 if let Some(announcer) = self.interface_announcer.as_mut() {
1181 announcer.upsert_interface(iface);
1182 } else if let Some(identity) = self.transport_identity.as_ref() {
1183 self.interface_announcer = Some(crate::discovery::InterfaceAnnouncer::new(
1184 *identity.hash(),
1185 vec![iface],
1186 ));
1187 }
1188 } else if let Some(announcer) = self.interface_announcer.as_mut() {
1189 announcer.remove_interface(interface_name);
1190 if announcer.is_empty() {
1191 self.interface_announcer = None;
1192 }
1193 }
1194
1195 Ok(())
1196 }
1197
1198 #[cfg(feature = "iface-tcp")]
1199 fn make_tcp_server_discoverable_interface(
1200 runtime: &TcpServerDiscoveryRuntimeHandle,
1201 ) -> crate::discovery::DiscoverableInterface {
1202 crate::discovery::DiscoverableInterface {
1203 interface_name: runtime.interface_name.clone(),
1204 config: runtime.current.config.clone(),
1205 transport_enabled: runtime.current.transport_enabled,
1206 ifac_netname: runtime.current.ifac_netname.clone(),
1207 ifac_netkey: runtime.current.ifac_netkey.clone(),
1208 }
1209 }
1210
1211 #[cfg(feature = "iface-tcp")]
1212 fn sync_tcp_server_discovery_runtime(
1213 &mut self,
1214 interface_name: &str,
1215 ) -> Result<(), RuntimeConfigError> {
1216 let handle = self
1217 .tcp_server_discovery_runtime
1218 .get(interface_name)
1219 .ok_or(RuntimeConfigError {
1220 code: RuntimeConfigErrorCode::NotFound,
1221 message: format!("tcp server interface '{}' not found", interface_name),
1222 })?
1223 .clone();
1224
1225 if handle.current.discoverable {
1226 let iface = Self::make_tcp_server_discoverable_interface(&handle);
1227 if let Some(announcer) = self.interface_announcer.as_mut() {
1228 announcer.upsert_interface(iface);
1229 } else if let Some(identity) = self.transport_identity.as_ref() {
1230 self.interface_announcer = Some(crate::discovery::InterfaceAnnouncer::new(
1231 *identity.hash(),
1232 vec![iface],
1233 ));
1234 }
1235 } else if let Some(announcer) = self.interface_announcer.as_mut() {
1236 announcer.remove_interface(interface_name);
1237 if announcer.is_empty() {
1238 self.interface_announcer = None;
1239 }
1240 }
1241
1242 Ok(())
1243 }
1244
1245 #[cfg(feature = "rns-hooks")]
1246 fn update_hook_program<F>(
1247 &mut self,
1248 name: &str,
1249 attach_point: &str,
1250 mut update: F,
1251 ) -> Result<(), String>
1252 where
1253 F: FnMut(&mut rns_hooks::LoadedProgram),
1254 {
1255 let point_idx = crate::config::parse_hook_point(attach_point)
1256 .ok_or_else(|| format!("unknown hook point '{}'", attach_point))?;
1257 let program = self.hook_slots[point_idx]
1258 .programs
1259 .iter_mut()
1260 .find(|program| program.name == name)
1261 .ok_or_else(|| format!("hook '{}' not found at point '{}'", name, attach_point))?;
1262 update(program);
1263 Ok(())
1264 }
1265
1266 pub(crate) fn set_tick_interval_handle(&mut self, tick_interval_ms: Arc<AtomicU64>) {
1267 self.tick_interval_ms = tick_interval_ms;
1268 }
1269
1270 pub(crate) fn set_packet_hashlist_max_entries(&mut self, max_entries: usize) {
1271 self.engine.set_packet_hashlist_max_entries(max_entries);
1272 }
1273
1274 fn build_shared_announce_raw(
1275 &mut self,
1276 dest_hash: &[u8; 16],
1277 record: &SharedAnnounceRecord,
1278 path_response: bool,
1279 ) -> Option<Vec<u8>> {
1280 let identity = rns_crypto::identity::Identity::from_private_key(&record.identity_prv_key);
1281
1282 let mut random_hash = [0u8; 10];
1283 self.rng.fill_bytes(&mut random_hash[..5]);
1284 let now_secs = std::time::SystemTime::now()
1285 .duration_since(std::time::UNIX_EPOCH)
1286 .ok()?
1287 .as_secs();
1288 random_hash[5..10].copy_from_slice(&now_secs.to_be_bytes()[3..8]);
1289
1290 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
1291 &identity,
1292 dest_hash,
1293 &record.name_hash,
1294 &random_hash,
1295 None,
1296 record.app_data.as_deref(),
1297 )
1298 .ok()?;
1299
1300 let flags = rns_core::packet::PacketFlags {
1301 header_type: rns_core::constants::HEADER_1,
1302 context_flag: rns_core::constants::FLAG_UNSET,
1303 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
1304 destination_type: rns_core::constants::DESTINATION_SINGLE,
1305 packet_type: rns_core::constants::PACKET_TYPE_ANNOUNCE,
1306 };
1307 let context = if path_response {
1308 rns_core::constants::CONTEXT_PATH_RESPONSE
1309 } else {
1310 rns_core::constants::CONTEXT_NONE
1311 };
1312
1313 rns_core::packet::RawPacket::pack(flags, 0, dest_hash, None, context, &announce_data)
1314 .ok()
1315 .map(|packet| packet.raw)
1316 }
1317
1318 fn replay_shared_announces(&mut self) {
1319 let records: Vec<([u8; 16], SharedAnnounceRecord)> = self
1320 .shared_announces
1321 .iter()
1322 .map(|(dest_hash, record)| (*dest_hash, record.clone()))
1323 .collect();
1324 for (dest_hash, record) in records {
1325 if let Some(raw) = self.build_shared_announce_raw(&dest_hash, &record, true) {
1326 let event = Event::SendOutbound {
1327 raw,
1328 dest_type: rns_core::constants::DESTINATION_SINGLE,
1329 attached_interface: None,
1330 };
1331 match event {
1332 Event::SendOutbound {
1333 raw,
1334 dest_type,
1335 attached_interface,
1336 } => match RawPacket::unpack(&raw) {
1337 Ok(packet) => {
1338 let actions = self.engine.handle_outbound(
1339 &packet,
1340 dest_type,
1341 attached_interface,
1342 time::now(),
1343 );
1344 self.dispatch_all(actions);
1345 }
1346 Err(e) => {
1347 log::warn!(
1348 "Shared announce replay failed for {:02x?}: {:?}",
1349 &dest_hash[..4],
1350 e
1351 );
1352 }
1353 },
1354 _ => unreachable!(),
1355 }
1356 }
1357 }
1358 }
1359
1360 fn handle_shared_interface_down(&mut self, id: InterfaceId) {
1361 let dropped_paths = self.engine.drop_paths_for_interface(id);
1362 let dropped_reverse = self.engine.drop_reverse_for_interface(id);
1363 let dropped_links = self.engine.drop_links_for_interface(id);
1364 self.engine.drop_announce_queues();
1365 let link_actions = self.link_manager.teardown_all_links();
1366 self.dispatch_link_actions(link_actions);
1367 self.shared_reconnect_pending.insert(id, true);
1368 log::info!(
1369 "[{}] cleared shared state: {} paths, {} reverse entries, {} transport links",
1370 id.0,
1371 dropped_paths,
1372 dropped_reverse,
1373 dropped_links
1374 );
1375 }
1376
1377 #[cfg(feature = "iface-backbone")]
1378 pub(crate) fn register_backbone_runtime(&mut self, handle: BackboneRuntimeConfigHandle) {
1379 self.backbone_runtime
1380 .insert(handle.interface_name.clone(), handle);
1381 }
1382
1383 #[cfg(feature = "iface-backbone")]
1384 pub(crate) fn register_backbone_peer_state(&mut self, handle: BackbonePeerStateHandle) {
1385 self.backbone_peer_state
1386 .insert(handle.interface_name.clone(), handle);
1387 }
1388
1389 #[cfg(feature = "iface-backbone")]
1390 pub(crate) fn register_backbone_client_runtime(
1391 &mut self,
1392 handle: BackboneClientRuntimeConfigHandle,
1393 ) {
1394 self.backbone_client_runtime
1395 .insert(handle.interface_name.clone(), handle);
1396 }
1397
1398 #[cfg(feature = "iface-backbone")]
1399 pub(crate) fn register_backbone_discovery_runtime(
1400 &mut self,
1401 handle: BackboneDiscoveryRuntimeHandle,
1402 ) {
1403 self.backbone_discovery_runtime
1404 .insert(handle.interface_name.clone(), handle);
1405 }
1406
1407 #[cfg(feature = "iface-backbone")]
1408 pub(crate) fn configure_backbone_peer_pool(
1409 &mut self,
1410 settings: BackbonePeerPoolSettings,
1411 candidates: Vec<BackbonePeerPoolCandidateConfig>,
1412 ) {
1413 if settings.max_connected == 0 || candidates.is_empty() {
1414 self.backbone_peer_pool = None;
1415 return;
1416 }
1417 self.backbone_peer_pool = Some(BackbonePeerPool {
1418 settings,
1419 candidates: candidates
1420 .into_iter()
1421 .map(|config| BackbonePeerPoolCandidate {
1422 config,
1423 active_id: None,
1424 failures: Vec::new(),
1425 retry_after: None,
1426 cooldown_until: None,
1427 last_error: None,
1428 })
1429 .collect(),
1430 });
1431 self.maintain_backbone_peer_pool();
1432 }
1433
1434 #[cfg(feature = "iface-backbone")]
1435 fn maintain_backbone_peer_pool(&mut self) {
1436 let Some(pool) = self.backbone_peer_pool.as_mut() else {
1437 return;
1438 };
1439 let now = time::now();
1440 for candidate in &mut pool.candidates {
1441 if candidate.cooldown_until.is_some_and(|until| until <= now) {
1442 candidate.cooldown_until = None;
1443 candidate.retry_after = None;
1444 }
1445 }
1446
1447 loop {
1448 let Some(pool) = self.backbone_peer_pool.as_ref() else {
1449 return;
1450 };
1451 let active = pool
1452 .candidates
1453 .iter()
1454 .filter(|candidate| candidate.active_id.is_some())
1455 .count();
1456 if active >= pool.settings.max_connected {
1457 return;
1458 }
1459 let next = pool.candidates.iter().position(|candidate| {
1460 candidate.active_id.is_none()
1461 && candidate
1462 .cooldown_until
1463 .map(|until| until <= now)
1464 .unwrap_or(true)
1465 && candidate
1466 .retry_after
1467 .map(|retry_after| retry_after <= now)
1468 .unwrap_or(true)
1469 });
1470 let Some(index) = next else {
1471 return;
1472 };
1473 if let Err(err) = self.start_backbone_peer_pool_candidate(index) {
1474 self.record_backbone_peer_pool_failure(index, err.to_string());
1475 }
1476 }
1477 }
1478
1479 #[cfg(feature = "iface-backbone")]
1480 fn start_backbone_peer_pool_candidate(&mut self, index: usize) -> std::io::Result<()> {
1481 let Some(pool) = self.backbone_peer_pool.as_ref() else {
1482 return Ok(());
1483 };
1484 let Some(candidate) = pool.candidates.get(index) else {
1485 return Ok(());
1486 };
1487 let mut client = candidate.config.client.clone();
1488 client.max_reconnect_tries = Some(0);
1489 if let Ok(mut runtime) = client.runtime.lock() {
1490 runtime.max_reconnect_tries = Some(0);
1491 }
1492 let id = client.interface_id;
1493 let name = client.name.clone();
1494 let mode = candidate.config.mode;
1495 let ingress_control = candidate.config.ingress_control;
1496 let ifac_runtime = candidate.config.ifac_runtime.clone();
1497 let ifac_enabled = candidate.config.ifac_enabled;
1498 let interface_type_name = candidate.config.interface_type_name.clone();
1499 let writer = start_client(client.clone(), self.event_tx.clone())?;
1500 let info = rns_core::transport::types::InterfaceInfo {
1501 id,
1502 name: name.clone(),
1503 mode,
1504 out_capable: true,
1505 in_capable: true,
1506 bitrate: Some(1_000_000_000),
1507 announce_rate_target: None,
1508 announce_rate_grace: 0,
1509 announce_rate_penalty: 0.0,
1510 announce_cap: rns_core::constants::ANNOUNCE_CAP,
1511 is_local_client: false,
1512 wants_tunnel: false,
1513 tunnel_id: None,
1514 mtu: 65535,
1515 ingress_control,
1516 ia_freq: 0.0,
1517 started: time::now(),
1518 };
1519 let (writer, async_writer_metrics) = self.wrap_interface_writer(id, &name, writer);
1520 let ifac_state = if ifac_enabled {
1521 Some(ifac::derive_ifac(
1522 ifac_runtime.netname.as_deref(),
1523 ifac_runtime.netkey.as_deref(),
1524 ifac_runtime.size,
1525 ))
1526 } else {
1527 None
1528 };
1529 self.register_backbone_client_runtime(BackboneClientRuntimeConfigHandle {
1530 interface_name: name.clone(),
1531 runtime: Arc::clone(&client.runtime),
1532 startup: BackboneClientRuntime::from_config(&client),
1533 });
1534 self.register_interface_runtime_defaults(&info);
1535 self.register_interface_ifac_runtime(&name, ifac_runtime);
1536 self.engine.register_interface(info.clone());
1537 self.interfaces.insert(
1538 id,
1539 InterfaceEntry {
1540 id,
1541 info,
1542 writer,
1543 async_writer_metrics: Some(async_writer_metrics),
1544 enabled: true,
1545 online: false,
1546 dynamic: false,
1547 ifac: ifac_state,
1548 stats: InterfaceStats {
1549 started: time::now(),
1550 ..Default::default()
1551 },
1552 interface_type: interface_type_name,
1553 send_retry_at: None,
1554 send_retry_backoff: Duration::ZERO,
1555 },
1556 );
1557
1558 if let Some(pool) = self.backbone_peer_pool.as_mut() {
1559 if let Some(candidate) = pool.candidates.get_mut(index) {
1560 candidate.active_id = Some(id);
1561 candidate.retry_after = None;
1562 candidate.last_error = None;
1563 }
1564 }
1565 Ok(())
1566 }
1567
1568 #[cfg(feature = "iface-backbone")]
1569 fn record_backbone_peer_pool_failure(&mut self, index: usize, error: String) {
1570 let Some(pool) = self.backbone_peer_pool.as_mut() else {
1571 return;
1572 };
1573 let Some(candidate) = pool.candidates.get_mut(index) else {
1574 return;
1575 };
1576 let now = time::now();
1577 let window = pool.settings.failure_window.as_secs_f64();
1578 candidate.failures.retain(|ts| now - *ts <= window);
1579 candidate.failures.push(now);
1580 candidate.last_error = Some(error);
1581 candidate.active_id = None;
1582 if candidate.failures.len() >= pool.settings.failure_threshold {
1583 candidate.cooldown_until = Some(now + pool.settings.cooldown.as_secs_f64());
1584 candidate.retry_after = None;
1585 } else {
1586 let reconnect_wait = candidate
1587 .config
1588 .client
1589 .runtime
1590 .lock()
1591 .map(|runtime| runtime.reconnect_wait)
1592 .unwrap_or(candidate.config.client.reconnect_wait);
1593 candidate.retry_after = Some(now + reconnect_wait.as_secs_f64());
1594 }
1595 }
1596
1597 #[cfg(feature = "iface-backbone")]
1598 fn handle_backbone_peer_pool_down(&mut self, id: InterfaceId) {
1599 let Some(index) = self.backbone_peer_pool.as_ref().and_then(|pool| {
1600 pool.candidates
1601 .iter()
1602 .position(|candidate| candidate.active_id == Some(id))
1603 }) else {
1604 return;
1605 };
1606
1607 if let Some(entry) = self.interfaces.remove(&id) {
1608 let name = entry.info.name;
1609 self.interface_runtime_defaults.remove(&name);
1610 self.interface_ifac_runtime.remove(&name);
1611 self.interface_ifac_runtime_defaults.remove(&name);
1612 self.backbone_client_runtime.remove(&name);
1613 self.engine.deregister_interface(id);
1614 }
1615 self.record_backbone_peer_pool_failure(index, "interface down".into());
1616 self.maintain_backbone_peer_pool();
1617 }
1618
1619 #[cfg(feature = "iface-backbone")]
1620 fn backbone_peer_pool_status(&self) -> Option<BackbonePeerPoolStatus> {
1621 let pool = self.backbone_peer_pool.as_ref()?;
1622 let now = time::now();
1623 let mut active_count = 0usize;
1624 let mut standby_count = 0usize;
1625 let mut cooldown_count = 0usize;
1626 let members = pool
1627 .candidates
1628 .iter()
1629 .map(|candidate| {
1630 let (state, cooldown_remaining_seconds) =
1631 if let Some(until) = candidate.cooldown_until {
1632 cooldown_count += 1;
1633 ("cooldown".to_string(), Some((until - now).max(0.0)))
1634 } else if let Some(id) = candidate.active_id {
1635 active_count += 1;
1636 let online = self
1637 .interfaces
1638 .get(&id)
1639 .map(|entry| entry.online)
1640 .unwrap_or(false);
1641 (
1642 if online { "active" } else { "connecting" }.to_string(),
1643 None,
1644 )
1645 } else {
1646 standby_count += 1;
1647 ("standby".to_string(), None)
1648 };
1649 BackbonePeerPoolMemberStatus {
1650 name: candidate.config.client.name.clone(),
1651 remote: format!(
1652 "{}:{}",
1653 candidate.config.client.target_host, candidate.config.client.target_port
1654 ),
1655 state,
1656 interface_id: candidate.active_id.map(|id| id.0),
1657 failure_count: candidate.failures.len(),
1658 last_error: candidate.last_error.clone(),
1659 cooldown_remaining_seconds,
1660 }
1661 })
1662 .collect();
1663 Some(BackbonePeerPoolStatus {
1664 max_connected: pool.settings.max_connected,
1665 active_count,
1666 standby_count,
1667 cooldown_count,
1668 members,
1669 })
1670 }
1671
1672 #[cfg(feature = "iface-backbone")]
1673 fn list_backbone_peer_state(
1674 &self,
1675 interface_name: Option<&str>,
1676 ) -> Vec<BackbonePeerStateEntry> {
1677 let mut names: Vec<&String> = match interface_name {
1678 Some(name) => self
1679 .backbone_peer_state
1680 .keys()
1681 .filter(|candidate| candidate.as_str() == name)
1682 .collect(),
1683 None => self.backbone_peer_state.keys().collect(),
1684 };
1685 names.sort();
1686
1687 let mut entries = Vec::new();
1688 for name in names {
1689 if let Some(handle) = self.backbone_peer_state.get(name) {
1690 entries.extend(
1691 recover_mutex_guard(&handle.peer_state, "backbone peer state").list(name),
1692 );
1693 }
1694 }
1695 entries.sort_by(|a, b| {
1696 a.interface_name
1697 .cmp(&b.interface_name)
1698 .then_with(|| a.peer_ip.cmp(&b.peer_ip))
1699 });
1700 entries
1701 }
1702
1703 #[cfg(feature = "iface-backbone")]
1704 fn list_backbone_interfaces(&self) -> Vec<crate::event::BackboneInterfaceEntry> {
1705 let mut entries: Vec<_> = self
1706 .backbone_peer_state
1707 .values()
1708 .map(|handle| crate::event::BackboneInterfaceEntry {
1709 interface_id: handle.interface_id,
1710 interface_name: handle.interface_name.clone(),
1711 })
1712 .collect();
1713 entries.sort_by(|a, b| a.interface_name.cmp(&b.interface_name));
1714 entries
1715 }
1716
1717 #[cfg(feature = "iface-backbone")]
1718 fn clear_backbone_peer_state(
1719 &mut self,
1720 interface_name: &str,
1721 peer_ip: std::net::IpAddr,
1722 ) -> bool {
1723 self.backbone_peer_state
1724 .get(interface_name)
1725 .map(|handle| {
1726 recover_mutex_guard(&handle.peer_state, "backbone peer state").clear(peer_ip)
1727 })
1728 .unwrap_or(false)
1729 }
1730
1731 fn blacklist_backbone_peer(
1732 &mut self,
1733 interface_name: &str,
1734 peer_ip: std::net::IpAddr,
1735 duration: std::time::Duration,
1736 reason: String,
1737 penalty_level: u8,
1738 ) -> bool {
1739 let capped_duration = self
1740 .backbone_runtime
1741 .get(interface_name)
1742 .and_then(|handle| {
1743 handle
1744 .runtime
1745 .lock()
1746 .ok()
1747 .map(|runtime| runtime.abuse.max_penalty_duration)
1748 })
1749 .flatten()
1750 .map(|max| duration.min(max))
1751 .unwrap_or(duration);
1752 let Some(handle) = self.backbone_peer_state.get(interface_name) else {
1753 return false;
1754 };
1755 let ok = recover_mutex_guard(&handle.peer_state, "backbone peer state").blacklist(
1756 peer_ip,
1757 capped_duration,
1758 reason,
1759 );
1760 if ok {
1761 #[cfg(feature = "rns-hooks")]
1762 self.run_backbone_peer_hook(
1763 "BackbonePeerPenalty",
1764 HookPoint::BackbonePeerPenalty,
1765 &BackbonePeerHookEvent {
1766 server_interface_id: self
1767 .interfaces
1768 .iter()
1769 .find(|(_, entry)| entry.info.name == interface_name)
1770 .map(|(id, _)| *id)
1771 .unwrap_or(InterfaceId(0)),
1772 peer_interface_id: None,
1773 peer_ip,
1774 peer_port: 0,
1775 connected_for: Duration::ZERO,
1776 had_received_data: false,
1777 penalty_level,
1778 blacklist_for: capped_duration,
1779 },
1780 );
1781 #[cfg(not(feature = "rns-hooks"))]
1782 let _ = (peer_ip, capped_duration, penalty_level);
1783 }
1784 ok
1785 }
1786
1787 #[cfg(feature = "iface-tcp")]
1788 pub(crate) fn register_tcp_server_runtime(&mut self, handle: TcpServerRuntimeConfigHandle) {
1789 self.tcp_server_runtime
1790 .insert(handle.interface_name.clone(), handle);
1791 }
1792
1793 #[cfg(feature = "iface-tcp")]
1794 pub(crate) fn register_tcp_client_runtime(&mut self, handle: TcpClientRuntimeConfigHandle) {
1795 self.tcp_client_runtime
1796 .insert(handle.interface_name.clone(), handle);
1797 }
1798
1799 #[cfg(feature = "iface-tcp")]
1800 pub(crate) fn register_tcp_server_discovery_runtime(
1801 &mut self,
1802 handle: TcpServerDiscoveryRuntimeHandle,
1803 ) {
1804 self.tcp_server_discovery_runtime
1805 .insert(handle.interface_name.clone(), handle);
1806 }
1807
1808 #[cfg(feature = "iface-udp")]
1809 pub(crate) fn register_udp_runtime(&mut self, handle: UdpRuntimeConfigHandle) {
1810 self.udp_runtime
1811 .insert(handle.interface_name.clone(), handle);
1812 }
1813
1814 #[cfg(feature = "iface-auto")]
1815 pub(crate) fn register_auto_runtime(&mut self, handle: AutoRuntimeConfigHandle) {
1816 self.auto_runtime
1817 .insert(handle.interface_name.clone(), handle);
1818 }
1819
1820 #[cfg(feature = "iface-i2p")]
1821 pub(crate) fn register_i2p_runtime(&mut self, handle: I2pRuntimeConfigHandle) {
1822 self.i2p_runtime
1823 .insert(handle.interface_name.clone(), handle);
1824 }
1825
1826 #[cfg(feature = "iface-pipe")]
1827 pub(crate) fn register_pipe_runtime(&mut self, handle: PipeRuntimeConfigHandle) {
1828 self.pipe_runtime
1829 .insert(handle.interface_name.clone(), handle);
1830 }
1831
1832 #[cfg(feature = "iface-rnode")]
1833 pub(crate) fn register_rnode_runtime(&mut self, handle: RNodeRuntimeConfigHandle) {
1834 self.rnode_runtime
1835 .insert(handle.interface_name.clone(), handle);
1836 }
1837
1838 pub(crate) fn register_interface_runtime_defaults(
1839 &mut self,
1840 info: &rns_core::transport::types::InterfaceInfo,
1841 ) {
1842 self.interface_runtime_defaults
1843 .entry(info.name.clone())
1844 .or_insert_with(|| info.clone());
1845 }
1846
1847 pub(crate) fn register_interface_ifac_runtime(
1848 &mut self,
1849 interface_name: &str,
1850 startup: IfacRuntimeConfig,
1851 ) {
1852 self.interface_ifac_runtime_defaults
1853 .entry(interface_name.to_string())
1854 .or_insert_with(|| startup.clone());
1855 self.interface_ifac_runtime
1856 .entry(interface_name.to_string())
1857 .or_insert(startup);
1858 }
1859
1860 fn runtime_config_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
1861 let defaults = self.runtime_config_defaults;
1862 let make_entry = |key: &str,
1863 value: RuntimeConfigValue,
1864 default: RuntimeConfigValue,
1865 apply_mode: RuntimeConfigApplyMode,
1866 description: &str| RuntimeConfigEntry {
1867 key: key.to_string(),
1868 source: if value == default {
1869 RuntimeConfigSource::Startup
1870 } else {
1871 RuntimeConfigSource::RuntimeOverride
1872 },
1873 value,
1874 default,
1875 apply_mode,
1876 description: Some(description.to_string()),
1877 };
1878
1879 match key {
1880 "global.tick_interval_ms" => Some(make_entry(
1881 key,
1882 RuntimeConfigValue::Int(self.tick_interval_ms.load(Ordering::Relaxed) as i64),
1883 RuntimeConfigValue::Int(defaults.tick_interval_ms as i64),
1884 RuntimeConfigApplyMode::Immediate,
1885 "Driver tick interval in milliseconds.",
1886 )),
1887 "global.known_destinations_ttl_secs" => Some(make_entry(
1888 key,
1889 RuntimeConfigValue::Float(self.known_destinations_ttl),
1890 RuntimeConfigValue::Float(defaults.known_destinations_ttl),
1891 RuntimeConfigApplyMode::Immediate,
1892 "TTL for known destinations without an active path.",
1893 )),
1894 "global.rate_limiter_ttl_secs" => Some(make_entry(
1895 key,
1896 RuntimeConfigValue::Float(self.rate_limiter_ttl_secs),
1897 RuntimeConfigValue::Float(defaults.rate_limiter_ttl_secs),
1898 RuntimeConfigApplyMode::Immediate,
1899 "TTL for announce rate-limiter entries without an active path.",
1900 )),
1901 "global.known_destinations_cleanup_interval_ticks" => Some(make_entry(
1902 key,
1903 RuntimeConfigValue::Int(self.known_destinations_cleanup_interval_ticks as i64),
1904 RuntimeConfigValue::Int(defaults.known_destinations_cleanup_interval_ticks as i64),
1905 RuntimeConfigApplyMode::Immediate,
1906 "Tick interval between known-destinations cleanup passes.",
1907 )),
1908 "global.announce_cache_cleanup_interval_ticks" => Some(make_entry(
1909 key,
1910 RuntimeConfigValue::Int(self.announce_cache_cleanup_interval_ticks as i64),
1911 RuntimeConfigValue::Int(defaults.announce_cache_cleanup_interval_ticks as i64),
1912 RuntimeConfigApplyMode::Immediate,
1913 "Tick interval between announce-cache cleanup cycles.",
1914 )),
1915 "global.announce_cache_cleanup_batch_size" => Some(make_entry(
1916 key,
1917 RuntimeConfigValue::Int(self.announce_cache_cleanup_batch_size as i64),
1918 RuntimeConfigValue::Int(defaults.announce_cache_cleanup_batch_size as i64),
1919 RuntimeConfigApplyMode::Immediate,
1920 "Number of announce-cache entries processed per cleanup tick.",
1921 )),
1922 "global.discovery_cleanup_interval_ticks" => Some(make_entry(
1923 key,
1924 RuntimeConfigValue::Int(self.discovery_cleanup_interval_ticks as i64),
1925 RuntimeConfigValue::Int(defaults.discovery_cleanup_interval_ticks as i64),
1926 RuntimeConfigApplyMode::Immediate,
1927 "Tick interval between discovered-interface cleanup passes.",
1928 )),
1929 "global.management_announce_interval_secs" => Some(make_entry(
1930 key,
1931 RuntimeConfigValue::Float(self.management_announce_interval_secs),
1932 RuntimeConfigValue::Float(defaults.management_announce_interval_secs),
1933 RuntimeConfigApplyMode::Immediate,
1934 "Interval between management announces in seconds.",
1935 )),
1936 "global.direct_connect_policy" => Some(make_entry(
1937 key,
1938 RuntimeConfigValue::String(Self::holepunch_policy_name(
1939 self.holepunch_manager.policy(),
1940 )),
1941 RuntimeConfigValue::String(Self::holepunch_policy_name(
1942 defaults.direct_connect_policy,
1943 )),
1944 RuntimeConfigApplyMode::Immediate,
1945 "Policy for incoming direct-connect proposals.",
1946 )),
1947 #[cfg(feature = "rns-hooks")]
1948 "provider.queue_max_events" => {
1949 let value = self
1950 .provider_bridge
1951 .as_ref()
1952 .map(|b| b.queue_max_events())
1953 .unwrap_or(defaults.provider_queue_max_events);
1954 Some(make_entry(
1955 key,
1956 RuntimeConfigValue::Int(value as i64),
1957 RuntimeConfigValue::Int(defaults.provider_queue_max_events as i64),
1958 RuntimeConfigApplyMode::Immediate,
1959 "Max queued events in the provider bridge.",
1960 ))
1961 }
1962 #[cfg(feature = "rns-hooks")]
1963 "provider.queue_max_bytes" => {
1964 let value = self
1965 .provider_bridge
1966 .as_ref()
1967 .map(|b| b.queue_max_bytes())
1968 .unwrap_or(defaults.provider_queue_max_bytes);
1969 Some(make_entry(
1970 key,
1971 RuntimeConfigValue::Int(value as i64),
1972 RuntimeConfigValue::Int(defaults.provider_queue_max_bytes as i64),
1973 RuntimeConfigApplyMode::Immediate,
1974 "Max queued bytes in the provider bridge.",
1975 ))
1976 }
1977 _ => {
1978 #[cfg(feature = "iface-backbone")]
1979 if let Some(entry) = self.backbone_runtime_entry(key) {
1980 return Some(entry);
1981 }
1982 #[cfg(feature = "iface-backbone")]
1983 if let Some(entry) = self.backbone_client_runtime_entry(key) {
1984 return Some(entry);
1985 }
1986 #[cfg(feature = "iface-tcp")]
1987 if let Some(entry) = self.tcp_server_runtime_entry(key) {
1988 return Some(entry);
1989 }
1990 #[cfg(feature = "iface-tcp")]
1991 if let Some(entry) = self.tcp_client_runtime_entry(key) {
1992 return Some(entry);
1993 }
1994 #[cfg(feature = "iface-udp")]
1995 if let Some(entry) = self.udp_runtime_entry(key) {
1996 return Some(entry);
1997 }
1998 #[cfg(feature = "iface-auto")]
1999 if let Some(entry) = self.auto_runtime_entry(key) {
2000 return Some(entry);
2001 }
2002 #[cfg(feature = "iface-i2p")]
2003 if let Some(entry) = self.i2p_runtime_entry(key) {
2004 return Some(entry);
2005 }
2006 #[cfg(feature = "iface-pipe")]
2007 if let Some(entry) = self.pipe_runtime_entry(key) {
2008 return Some(entry);
2009 }
2010 #[cfg(feature = "iface-rnode")]
2011 if let Some(entry) = self.rnode_runtime_entry(key) {
2012 return Some(entry);
2013 }
2014 if let Some(entry) = self.generic_interface_runtime_entry(key) {
2015 return Some(entry);
2016 }
2017 None
2018 }
2019 }
2020 }
2021
2022 fn list_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
2023 let mut entries: Vec<RuntimeConfigEntry> = [
2024 "global.tick_interval_ms",
2025 "global.known_destinations_ttl_secs",
2026 "global.rate_limiter_ttl_secs",
2027 "global.known_destinations_cleanup_interval_ticks",
2028 "global.announce_cache_cleanup_interval_ticks",
2029 "global.announce_cache_cleanup_batch_size",
2030 "global.discovery_cleanup_interval_ticks",
2031 "global.management_announce_interval_secs",
2032 "global.direct_connect_policy",
2033 ]
2034 .into_iter()
2035 .filter_map(|key| self.runtime_config_entry(key))
2036 .collect();
2037
2038 #[cfg(feature = "rns-hooks")]
2039 {
2040 entries.extend(
2041 ["provider.queue_max_events", "provider.queue_max_bytes"]
2042 .into_iter()
2043 .filter_map(|key| self.runtime_config_entry(key)),
2044 );
2045 }
2046 #[cfg(feature = "iface-backbone")]
2047 {
2048 entries.extend(self.list_backbone_runtime_config());
2049 entries.extend(self.list_backbone_client_runtime_config());
2050 }
2051 #[cfg(feature = "iface-tcp")]
2052 {
2053 entries.extend(self.list_tcp_server_runtime_config());
2054 entries.extend(self.list_tcp_client_runtime_config());
2055 }
2056 #[cfg(feature = "iface-udp")]
2057 {
2058 entries.extend(self.list_udp_runtime_config());
2059 }
2060 #[cfg(feature = "iface-auto")]
2061 {
2062 entries.extend(self.list_auto_runtime_config());
2063 }
2064 #[cfg(feature = "iface-i2p")]
2065 {
2066 entries.extend(self.list_i2p_runtime_config());
2067 }
2068 #[cfg(feature = "iface-pipe")]
2069 {
2070 entries.extend(self.list_pipe_runtime_config());
2071 }
2072 #[cfg(feature = "iface-rnode")]
2073 {
2074 entries.extend(self.list_rnode_runtime_config());
2075 }
2076 entries.extend(self.list_generic_interface_runtime_config());
2077
2078 entries
2079 }
2080
2081 fn holepunch_policy_name(policy: crate::event::HolePunchPolicy) -> String {
2082 match policy {
2083 crate::event::HolePunchPolicy::Reject => "reject".to_string(),
2084 crate::event::HolePunchPolicy::AcceptAll => "accept_all".to_string(),
2085 crate::event::HolePunchPolicy::AskApp => "ask_app".to_string(),
2086 }
2087 }
2088
2089 fn parse_holepunch_policy(value: &RuntimeConfigValue) -> Option<crate::event::HolePunchPolicy> {
2090 match value {
2091 RuntimeConfigValue::String(s) => match s.to_ascii_lowercase().as_str() {
2092 "reject" => Some(crate::event::HolePunchPolicy::Reject),
2093 "accept_all" | "acceptall" => Some(crate::event::HolePunchPolicy::AcceptAll),
2094 "ask_app" | "askapp" => Some(crate::event::HolePunchPolicy::AskApp),
2095 _ => None,
2096 },
2097 _ => None,
2098 }
2099 }
2100
2101 fn expect_u64(value: RuntimeConfigValue, key: &str) -> Result<u64, RuntimeConfigError> {
2102 match value {
2103 RuntimeConfigValue::Int(v) if v >= 0 => Ok(v as u64),
2104 RuntimeConfigValue::Int(_) => Err(RuntimeConfigError {
2105 code: RuntimeConfigErrorCode::InvalidValue,
2106 message: format!("{} must be >= 0", key),
2107 }),
2108 _ => Err(RuntimeConfigError {
2109 code: RuntimeConfigErrorCode::InvalidType,
2110 message: format!("{} expects an integer", key),
2111 }),
2112 }
2113 }
2114
2115 fn expect_f64(value: RuntimeConfigValue, key: &str) -> Result<f64, RuntimeConfigError> {
2116 match value {
2117 RuntimeConfigValue::Float(v) if v >= 0.0 => Ok(v),
2118 RuntimeConfigValue::Int(v) if v >= 0 => Ok(v as f64),
2119 RuntimeConfigValue::Float(_) | RuntimeConfigValue::Int(_) => Err(RuntimeConfigError {
2120 code: RuntimeConfigErrorCode::InvalidValue,
2121 message: format!("{} must be >= 0", key),
2122 }),
2123 _ => Err(RuntimeConfigError {
2124 code: RuntimeConfigErrorCode::InvalidType,
2125 message: format!("{} expects a numeric value", key),
2126 }),
2127 }
2128 }
2129
2130 fn expect_i64(value: RuntimeConfigValue, key: &str) -> Result<i64, RuntimeConfigError> {
2131 match value {
2132 RuntimeConfigValue::Int(v) => Ok(v),
2133 _ => Err(RuntimeConfigError {
2134 code: RuntimeConfigErrorCode::InvalidType,
2135 message: format!("{} expects an integer", key),
2136 }),
2137 }
2138 }
2139
2140 fn expect_bool(value: RuntimeConfigValue, key: &str) -> Result<bool, RuntimeConfigError> {
2141 match value {
2142 RuntimeConfigValue::Bool(v) => Ok(v),
2143 _ => Err(RuntimeConfigError {
2144 code: RuntimeConfigErrorCode::InvalidType,
2145 message: format!("{} expects a boolean", key),
2146 }),
2147 }
2148 }
2149
2150 fn expect_string(value: RuntimeConfigValue, key: &str) -> Result<String, RuntimeConfigError> {
2151 match value {
2152 RuntimeConfigValue::String(v) => Ok(v),
2153 _ => Err(RuntimeConfigError {
2154 code: RuntimeConfigErrorCode::InvalidType,
2155 message: format!("{} expects a string", key),
2156 }),
2157 }
2158 }
2159
2160 fn expect_optional_f64(
2161 value: RuntimeConfigValue,
2162 key: &str,
2163 ) -> Result<Option<f64>, RuntimeConfigError> {
2164 match value {
2165 RuntimeConfigValue::Null => Ok(None),
2166 RuntimeConfigValue::Float(v) => Ok(Some(v)),
2167 RuntimeConfigValue::Int(v) => Ok(Some(v as f64)),
2168 _ => Err(RuntimeConfigError {
2169 code: RuntimeConfigErrorCode::InvalidType,
2170 message: format!("{} expects a numeric value or null", key),
2171 }),
2172 }
2173 }
2174
2175 fn expect_optional_string(
2176 value: RuntimeConfigValue,
2177 key: &str,
2178 ) -> Result<Option<String>, RuntimeConfigError> {
2179 match value {
2180 RuntimeConfigValue::Null => Ok(None),
2181 RuntimeConfigValue::String(v) => Ok(Some(v)),
2182 _ => Err(RuntimeConfigError {
2183 code: RuntimeConfigErrorCode::InvalidType,
2184 message: format!("{} expects a string or null", key),
2185 }),
2186 }
2187 }
2188
2189 #[cfg(feature = "iface-backbone")]
2190 fn split_backbone_runtime_key<'a>(
2191 &self,
2192 key: &'a str,
2193 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
2194 let rest = key.strip_prefix("backbone.").ok_or(RuntimeConfigError {
2195 code: RuntimeConfigErrorCode::UnknownKey,
2196 message: format!("unknown runtime-config key '{}'", key),
2197 })?;
2198 rest.split_once('.').ok_or(RuntimeConfigError {
2199 code: RuntimeConfigErrorCode::UnknownKey,
2200 message: format!("unknown runtime-config key '{}'", key),
2201 })
2202 }
2203
2204 #[cfg(feature = "iface-backbone")]
2205 fn set_optional_duration(
2206 value: RuntimeConfigValue,
2207 key: &str,
2208 ) -> Result<Option<Duration>, RuntimeConfigError> {
2209 let secs = Self::expect_f64(value, key)?;
2210 if secs == 0.0 {
2211 Ok(None)
2212 } else {
2213 Ok(Some(Duration::from_secs_f64(secs)))
2214 }
2215 }
2216
2217 #[cfg(feature = "iface-backbone")]
2218 fn set_optional_usize(
2219 value: RuntimeConfigValue,
2220 key: &str,
2221 ) -> Result<Option<usize>, RuntimeConfigError> {
2222 let raw = Self::expect_u64(value, key)?;
2223 if raw == 0 {
2224 Ok(None)
2225 } else {
2226 Ok(Some(raw as usize))
2227 }
2228 }
2229
2230 #[cfg(feature = "iface-backbone")]
2231 fn set_backbone_runtime_config(
2232 &mut self,
2233 key: &str,
2234 value: RuntimeConfigValue,
2235 ) -> Result<(), RuntimeConfigError> {
2236 let (name, setting) = self.split_backbone_runtime_key(key)?;
2237 if matches!(
2238 setting,
2239 "discoverable"
2240 | "discovery_name"
2241 | "announce_interval_secs"
2242 | "reachable_on"
2243 | "stamp_value"
2244 | "latitude"
2245 | "longitude"
2246 | "height"
2247 ) {
2248 return self.set_backbone_discovery_runtime_config(key, value);
2249 }
2250 let handle = self.backbone_runtime.get(name).ok_or(RuntimeConfigError {
2251 code: RuntimeConfigErrorCode::NotFound,
2252 message: format!("backbone interface '{}' not found", name),
2253 })?;
2254 let mut runtime = recover_mutex_guard(&handle.runtime, "backbone runtime");
2255 match setting {
2256 "idle_timeout_secs" => {
2257 runtime.idle_timeout = Self::set_optional_duration(value, key)?;
2258 Ok(())
2259 }
2260 "write_stall_timeout_secs" => {
2261 runtime.write_stall_timeout = Self::set_optional_duration(value, key)?;
2262 Ok(())
2263 }
2264 "max_penalty_duration_secs" => {
2265 runtime.abuse.max_penalty_duration = Self::set_optional_duration(value, key)?;
2266 Ok(())
2267 }
2268 "max_connections" => {
2269 runtime.max_connections = Self::set_optional_usize(value, key)?;
2270 Ok(())
2271 }
2272 _ => Err(RuntimeConfigError {
2273 code: RuntimeConfigErrorCode::UnknownKey,
2274 message: format!("unknown runtime-config key '{}'", key),
2275 }),
2276 }
2277 }
2278
2279 #[cfg(feature = "iface-backbone")]
2280 fn split_backbone_discovery_runtime_key<'a>(
2281 &self,
2282 key: &'a str,
2283 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
2284 let rest = key.strip_prefix("backbone.").ok_or(RuntimeConfigError {
2285 code: RuntimeConfigErrorCode::UnknownKey,
2286 message: format!("unknown runtime-config key '{}'", key),
2287 })?;
2288 rest.split_once('.').ok_or(RuntimeConfigError {
2289 code: RuntimeConfigErrorCode::UnknownKey,
2290 message: format!("unknown runtime-config key '{}'", key),
2291 })
2292 }
2293
2294 #[cfg(feature = "iface-backbone")]
2295 fn set_backbone_discovery_runtime_config(
2296 &mut self,
2297 key: &str,
2298 value: RuntimeConfigValue,
2299 ) -> Result<(), RuntimeConfigError> {
2300 let (name, setting) = self.split_backbone_discovery_runtime_key(key)?;
2301 let handle = self
2302 .backbone_discovery_runtime
2303 .get_mut(name)
2304 .ok_or(RuntimeConfigError {
2305 code: RuntimeConfigErrorCode::NotFound,
2306 message: format!("backbone interface '{}' not found", name),
2307 })?;
2308 match setting {
2309 "discoverable" => {
2310 handle.current.discoverable = Self::expect_bool(value, key)?;
2311 }
2312 "discovery_name" => {
2313 handle.current.config.discovery_name = Self::expect_string(value, key)?;
2314 }
2315 "announce_interval_secs" => {
2316 let secs = Self::expect_u64(value, key)?;
2317 if secs < 300 {
2318 return Err(RuntimeConfigError {
2319 code: RuntimeConfigErrorCode::InvalidValue,
2320 message: format!("{} must be >= 300", key),
2321 });
2322 }
2323 handle.current.config.announce_interval = secs;
2324 }
2325 "reachable_on" => {
2326 handle.current.config.reachable_on = Self::expect_optional_string(value, key)?;
2327 }
2328 "stamp_value" => {
2329 let raw = Self::expect_u64(value, key)?;
2330 if raw > u8::MAX as u64 {
2331 return Err(RuntimeConfigError {
2332 code: RuntimeConfigErrorCode::InvalidValue,
2333 message: format!("{} must be <= {}", key, u8::MAX),
2334 });
2335 }
2336 handle.current.config.stamp_value = raw as u8;
2337 }
2338 "latitude" => {
2339 handle.current.config.latitude = Self::expect_optional_f64(value, key)?;
2340 }
2341 "longitude" => {
2342 handle.current.config.longitude = Self::expect_optional_f64(value, key)?;
2343 }
2344 "height" => {
2345 handle.current.config.height = Self::expect_optional_f64(value, key)?;
2346 }
2347 _ => {
2348 return Err(RuntimeConfigError {
2349 code: RuntimeConfigErrorCode::UnknownKey,
2350 message: format!("unknown runtime-config key '{}'", key),
2351 });
2352 }
2353 }
2354 self.sync_backbone_discovery_runtime(name)
2355 }
2356
2357 #[cfg(feature = "iface-backbone")]
2358 fn reset_backbone_discovery_runtime_config(
2359 &mut self,
2360 key: &str,
2361 ) -> Result<(), RuntimeConfigError> {
2362 let (name, setting) = self.split_backbone_discovery_runtime_key(key)?;
2363 let handle = self
2364 .backbone_discovery_runtime
2365 .get_mut(name)
2366 .ok_or(RuntimeConfigError {
2367 code: RuntimeConfigErrorCode::NotFound,
2368 message: format!("backbone interface '{}' not found", name),
2369 })?;
2370 match setting {
2371 "discoverable" => handle.current.discoverable = handle.startup.discoverable,
2372 "discovery_name" => {
2373 handle.current.config.discovery_name = handle.startup.config.discovery_name.clone()
2374 }
2375 "announce_interval_secs" => {
2376 handle.current.config.announce_interval = handle.startup.config.announce_interval
2377 }
2378 "reachable_on" => {
2379 handle.current.config.reachable_on = handle.startup.config.reachable_on.clone()
2380 }
2381 "stamp_value" => handle.current.config.stamp_value = handle.startup.config.stamp_value,
2382 "latitude" => handle.current.config.latitude = handle.startup.config.latitude,
2383 "longitude" => handle.current.config.longitude = handle.startup.config.longitude,
2384 "height" => handle.current.config.height = handle.startup.config.height,
2385 _ => {
2386 return Err(RuntimeConfigError {
2387 code: RuntimeConfigErrorCode::UnknownKey,
2388 message: format!("unknown runtime-config key '{}'", key),
2389 });
2390 }
2391 }
2392 self.sync_backbone_discovery_runtime(name)
2393 }
2394
2395 #[cfg(feature = "iface-backbone")]
2396 fn reset_backbone_runtime_config(&mut self, key: &str) -> Result<(), RuntimeConfigError> {
2397 let (name, setting) = self.split_backbone_runtime_key(key)?;
2398 if matches!(
2399 setting,
2400 "discoverable"
2401 | "discovery_name"
2402 | "announce_interval_secs"
2403 | "reachable_on"
2404 | "stamp_value"
2405 | "latitude"
2406 | "longitude"
2407 | "height"
2408 ) {
2409 return self.reset_backbone_discovery_runtime_config(key);
2410 }
2411 let handle = self.backbone_runtime.get(name).ok_or(RuntimeConfigError {
2412 code: RuntimeConfigErrorCode::NotFound,
2413 message: format!("backbone interface '{}' not found", name),
2414 })?;
2415 let mut runtime = recover_mutex_guard(&handle.runtime, "backbone runtime");
2416 let startup = handle.startup.clone();
2417 match setting {
2418 "idle_timeout_secs" => runtime.idle_timeout = startup.idle_timeout,
2419 "write_stall_timeout_secs" => runtime.write_stall_timeout = startup.write_stall_timeout,
2420 "max_penalty_duration_secs" => {
2421 runtime.abuse.max_penalty_duration = startup.abuse.max_penalty_duration
2422 }
2423 "max_connections" => runtime.max_connections = startup.max_connections,
2424 _ => {
2425 return Err(RuntimeConfigError {
2426 code: RuntimeConfigErrorCode::UnknownKey,
2427 message: format!("unknown runtime-config key '{}'", key),
2428 })
2429 }
2430 }
2431 Ok(())
2432 }
2433
2434 #[cfg(feature = "iface-backbone")]
2435 fn list_backbone_client_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
2436 let mut entries = Vec::new();
2437 let mut names: Vec<&String> = self.backbone_client_runtime.keys().collect();
2438 names.sort();
2439 for name in names {
2440 for suffix in [
2441 "connect_timeout_secs",
2442 "reconnect_wait_secs",
2443 "max_reconnect_tries",
2444 ] {
2445 let key = format!("backbone_client.{}.{}", name, suffix);
2446 if let Some(entry) = self.backbone_client_runtime_entry(&key) {
2447 entries.push(entry);
2448 }
2449 }
2450 }
2451 entries
2452 }
2453
2454 #[cfg(feature = "iface-backbone")]
2455 fn backbone_client_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
2456 let rest = key.strip_prefix("backbone_client.")?;
2457 let (name, setting) = rest.split_once('.')?;
2458 let handle = self.backbone_client_runtime.get(name)?;
2459 let current = recover_mutex_guard(&handle.runtime, "backbone client runtime").clone();
2460 let startup = handle.startup.clone();
2461 let make_entry = |value: RuntimeConfigValue,
2462 default: RuntimeConfigValue,
2463 description: &str|
2464 -> RuntimeConfigEntry {
2465 RuntimeConfigEntry {
2466 key: key.to_string(),
2467 source: if value == default {
2468 RuntimeConfigSource::Startup
2469 } else {
2470 RuntimeConfigSource::RuntimeOverride
2471 },
2472 value,
2473 default,
2474 apply_mode: RuntimeConfigApplyMode::NextReconnect,
2475 description: Some(description.to_string()),
2476 }
2477 };
2478 match setting {
2479 "connect_timeout_secs" => Some(make_entry(
2480 RuntimeConfigValue::Float(current.connect_timeout.as_secs_f64()),
2481 RuntimeConfigValue::Float(startup.connect_timeout.as_secs_f64()),
2482 "Backbone client connect timeout in seconds; applies on the next reconnect.",
2483 )),
2484 "reconnect_wait_secs" => Some(make_entry(
2485 RuntimeConfigValue::Float(current.reconnect_wait.as_secs_f64()),
2486 RuntimeConfigValue::Float(startup.reconnect_wait.as_secs_f64()),
2487 "Delay between backbone client reconnect attempts in seconds.",
2488 )),
2489 "max_reconnect_tries" => Some(make_entry(
2490 RuntimeConfigValue::Int(current.max_reconnect_tries.unwrap_or(0) as i64),
2491 RuntimeConfigValue::Int(startup.max_reconnect_tries.unwrap_or(0) as i64),
2492 "Maximum backbone client reconnect attempts; 0 disables the cap.",
2493 )),
2494 _ => None,
2495 }
2496 }
2497
2498 #[cfg(feature = "iface-backbone")]
2499 fn split_backbone_client_runtime_key<'a>(
2500 &self,
2501 key: &'a str,
2502 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
2503 let rest = key
2504 .strip_prefix("backbone_client.")
2505 .ok_or(RuntimeConfigError {
2506 code: RuntimeConfigErrorCode::UnknownKey,
2507 message: format!("unknown runtime-config key '{}'", key),
2508 })?;
2509 rest.split_once('.').ok_or(RuntimeConfigError {
2510 code: RuntimeConfigErrorCode::UnknownKey,
2511 message: format!("unknown runtime-config key '{}'", key),
2512 })
2513 }
2514
2515 #[cfg(feature = "iface-backbone")]
2516 fn set_backbone_client_runtime_config(
2517 &mut self,
2518 key: &str,
2519 value: RuntimeConfigValue,
2520 ) -> Result<(), RuntimeConfigError> {
2521 let (name, setting) = self.split_backbone_client_runtime_key(key)?;
2522 let handle = self
2523 .backbone_client_runtime
2524 .get(name)
2525 .ok_or(RuntimeConfigError {
2526 code: RuntimeConfigErrorCode::NotFound,
2527 message: format!("backbone client interface '{}' not found", name),
2528 })?;
2529 let mut runtime = recover_mutex_guard(&handle.runtime, "backbone client runtime");
2530 match setting {
2531 "connect_timeout_secs" => {
2532 runtime.connect_timeout = Duration::from_secs_f64(Self::expect_f64(value, key)?);
2533 Ok(())
2534 }
2535 "reconnect_wait_secs" => {
2536 runtime.reconnect_wait = Duration::from_secs_f64(Self::expect_f64(value, key)?);
2537 Ok(())
2538 }
2539 "max_reconnect_tries" => {
2540 runtime.max_reconnect_tries = match Self::expect_u64(value, key)? {
2541 0 => None,
2542 raw => Some(raw as u32),
2543 };
2544 Ok(())
2545 }
2546 _ => Err(RuntimeConfigError {
2547 code: RuntimeConfigErrorCode::UnknownKey,
2548 message: format!("unknown runtime-config key '{}'", key),
2549 }),
2550 }
2551 }
2552
2553 #[cfg(feature = "iface-backbone")]
2554 fn reset_backbone_client_runtime_config(
2555 &mut self,
2556 key: &str,
2557 ) -> Result<(), RuntimeConfigError> {
2558 let (name, setting) = self.split_backbone_client_runtime_key(key)?;
2559 let handle = self
2560 .backbone_client_runtime
2561 .get(name)
2562 .ok_or(RuntimeConfigError {
2563 code: RuntimeConfigErrorCode::NotFound,
2564 message: format!("backbone client interface '{}' not found", name),
2565 })?;
2566 let mut runtime = recover_mutex_guard(&handle.runtime, "backbone client runtime");
2567 let startup = handle.startup.clone();
2568 match setting {
2569 "connect_timeout_secs" => runtime.connect_timeout = startup.connect_timeout,
2570 "reconnect_wait_secs" => runtime.reconnect_wait = startup.reconnect_wait,
2571 "max_reconnect_tries" => runtime.max_reconnect_tries = startup.max_reconnect_tries,
2572 _ => {
2573 return Err(RuntimeConfigError {
2574 code: RuntimeConfigErrorCode::UnknownKey,
2575 message: format!("unknown runtime-config key '{}'", key),
2576 })
2577 }
2578 }
2579 Ok(())
2580 }
2581
2582 #[cfg(feature = "iface-tcp")]
2583 fn list_tcp_server_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
2584 let mut entries = Vec::new();
2585 let mut names: Vec<&String> = self.tcp_server_runtime.keys().collect();
2586 names.sort();
2587 for name in names {
2588 for suffix in [
2589 "max_connections",
2590 "discoverable",
2591 "discovery_name",
2592 "announce_interval_secs",
2593 "reachable_on",
2594 "stamp_value",
2595 "latitude",
2596 "longitude",
2597 "height",
2598 ] {
2599 let key = format!("tcp_server.{}.{}", name, suffix);
2600 if let Some(entry) = self.tcp_server_runtime_entry(&key) {
2601 entries.push(entry);
2602 }
2603 }
2604 }
2605 entries
2606 }
2607
2608 #[cfg(feature = "iface-tcp")]
2609 fn list_tcp_client_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
2610 let mut entries = Vec::new();
2611 let mut names: Vec<&String> = self.tcp_client_runtime.keys().collect();
2612 names.sort();
2613 for name in names {
2614 for suffix in [
2615 "connect_timeout_secs",
2616 "reconnect_wait_secs",
2617 "max_reconnect_tries",
2618 ] {
2619 let key = format!("tcp_client.{}.{}", name, suffix);
2620 if let Some(entry) = self.tcp_client_runtime_entry(&key) {
2621 entries.push(entry);
2622 }
2623 }
2624 }
2625 entries
2626 }
2627
2628 #[cfg(feature = "iface-tcp")]
2629 fn tcp_client_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
2630 let rest = key.strip_prefix("tcp_client.")?;
2631 let (name, setting) = rest.split_once('.')?;
2632 let handle = self.tcp_client_runtime.get(name)?;
2633 let current = recover_mutex_guard(&handle.runtime, "tcp client runtime").clone();
2634 let startup = handle.startup.clone();
2635 let make_entry = |value: RuntimeConfigValue,
2636 default: RuntimeConfigValue,
2637 description: &str|
2638 -> RuntimeConfigEntry {
2639 RuntimeConfigEntry {
2640 key: key.to_string(),
2641 source: if value == default {
2642 RuntimeConfigSource::Startup
2643 } else {
2644 RuntimeConfigSource::RuntimeOverride
2645 },
2646 value,
2647 default,
2648 apply_mode: RuntimeConfigApplyMode::NextReconnect,
2649 description: Some(description.to_string()),
2650 }
2651 };
2652 match setting {
2653 "connect_timeout_secs" => Some(make_entry(
2654 RuntimeConfigValue::Float(current.connect_timeout.as_secs_f64()),
2655 RuntimeConfigValue::Float(startup.connect_timeout.as_secs_f64()),
2656 "TCP client connect timeout in seconds; applies on the next reconnect.",
2657 )),
2658 "reconnect_wait_secs" => Some(make_entry(
2659 RuntimeConfigValue::Float(current.reconnect_wait.as_secs_f64()),
2660 RuntimeConfigValue::Float(startup.reconnect_wait.as_secs_f64()),
2661 "Delay between TCP client reconnect attempts in seconds.",
2662 )),
2663 "max_reconnect_tries" => Some(make_entry(
2664 RuntimeConfigValue::Int(current.max_reconnect_tries.unwrap_or(0) as i64),
2665 RuntimeConfigValue::Int(startup.max_reconnect_tries.unwrap_or(0) as i64),
2666 "Maximum TCP client reconnect attempts; 0 disables the cap.",
2667 )),
2668 _ => None,
2669 }
2670 }
2671
2672 #[cfg(feature = "iface-tcp")]
2673 fn split_tcp_client_runtime_key<'a>(
2674 &self,
2675 key: &'a str,
2676 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
2677 let rest = key.strip_prefix("tcp_client.").ok_or(RuntimeConfigError {
2678 code: RuntimeConfigErrorCode::UnknownKey,
2679 message: format!("unknown runtime-config key '{}'", key),
2680 })?;
2681 rest.split_once('.').ok_or(RuntimeConfigError {
2682 code: RuntimeConfigErrorCode::UnknownKey,
2683 message: format!("unknown runtime-config key '{}'", key),
2684 })
2685 }
2686
2687 #[cfg(feature = "iface-tcp")]
2688 fn set_tcp_client_runtime_config(
2689 &mut self,
2690 key: &str,
2691 value: RuntimeConfigValue,
2692 ) -> Result<(), RuntimeConfigError> {
2693 let (name, setting) = self.split_tcp_client_runtime_key(key)?;
2694 let handle = self
2695 .tcp_client_runtime
2696 .get(name)
2697 .ok_or(RuntimeConfigError {
2698 code: RuntimeConfigErrorCode::NotFound,
2699 message: format!("tcp client interface '{}' not found", name),
2700 })?;
2701 let mut runtime = recover_mutex_guard(&handle.runtime, "tcp client runtime");
2702 match setting {
2703 "connect_timeout_secs" => {
2704 runtime.connect_timeout = Duration::from_secs_f64(Self::expect_f64(value, key)?);
2705 Ok(())
2706 }
2707 "reconnect_wait_secs" => {
2708 runtime.reconnect_wait = Duration::from_secs_f64(Self::expect_f64(value, key)?);
2709 Ok(())
2710 }
2711 "max_reconnect_tries" => {
2712 runtime.max_reconnect_tries = match Self::expect_u64(value, key)? {
2713 0 => None,
2714 raw => Some(raw as u32),
2715 };
2716 Ok(())
2717 }
2718 _ => Err(RuntimeConfigError {
2719 code: RuntimeConfigErrorCode::UnknownKey,
2720 message: format!("unknown runtime-config key '{}'", key),
2721 }),
2722 }
2723 }
2724
2725 #[cfg(feature = "iface-tcp")]
2726 fn reset_tcp_client_runtime_config(&mut self, key: &str) -> Result<(), RuntimeConfigError> {
2727 let (name, setting) = self.split_tcp_client_runtime_key(key)?;
2728 let handle = self
2729 .tcp_client_runtime
2730 .get(name)
2731 .ok_or(RuntimeConfigError {
2732 code: RuntimeConfigErrorCode::NotFound,
2733 message: format!("tcp client interface '{}' not found", name),
2734 })?;
2735 let mut runtime = recover_mutex_guard(&handle.runtime, "tcp client runtime");
2736 let startup = handle.startup.clone();
2737 match setting {
2738 "connect_timeout_secs" => runtime.connect_timeout = startup.connect_timeout,
2739 "reconnect_wait_secs" => runtime.reconnect_wait = startup.reconnect_wait,
2740 "max_reconnect_tries" => runtime.max_reconnect_tries = startup.max_reconnect_tries,
2741 _ => {
2742 return Err(RuntimeConfigError {
2743 code: RuntimeConfigErrorCode::UnknownKey,
2744 message: format!("unknown runtime-config key '{}'", key),
2745 })
2746 }
2747 }
2748 Ok(())
2749 }
2750
2751 #[cfg(feature = "iface-udp")]
2752 fn list_udp_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
2753 let mut entries = Vec::new();
2754 let mut names: Vec<&String> = self.udp_runtime.keys().collect();
2755 names.sort();
2756 for name in names {
2757 for suffix in ["forward_ip", "forward_port"] {
2758 let key = format!("udp.{}.{}", name, suffix);
2759 if let Some(entry) = self.udp_runtime_entry(&key) {
2760 entries.push(entry);
2761 }
2762 }
2763 }
2764 entries
2765 }
2766
2767 #[cfg(feature = "iface-udp")]
2768 fn udp_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
2769 let rest = key.strip_prefix("udp.")?;
2770 let (name, setting) = rest.split_once('.')?;
2771 let handle = self.udp_runtime.get(name)?;
2772 let current = recover_mutex_guard(&handle.runtime, "udp runtime").clone();
2773 let startup = handle.startup.clone();
2774 let make_entry = |value: RuntimeConfigValue,
2775 default: RuntimeConfigValue,
2776 description: &str|
2777 -> RuntimeConfigEntry {
2778 RuntimeConfigEntry {
2779 key: key.to_string(),
2780 source: if value == default {
2781 RuntimeConfigSource::Startup
2782 } else {
2783 RuntimeConfigSource::RuntimeOverride
2784 },
2785 value,
2786 default,
2787 apply_mode: RuntimeConfigApplyMode::Immediate,
2788 description: Some(description.to_string()),
2789 }
2790 };
2791 match setting {
2792 "forward_ip" => Some(make_entry(
2793 current
2794 .forward_ip
2795 .clone()
2796 .map(RuntimeConfigValue::String)
2797 .unwrap_or(RuntimeConfigValue::Null),
2798 startup
2799 .forward_ip
2800 .clone()
2801 .map(RuntimeConfigValue::String)
2802 .unwrap_or(RuntimeConfigValue::Null),
2803 "Outbound UDP destination IP or hostname; null clears it.",
2804 )),
2805 "forward_port" => Some(make_entry(
2806 current
2807 .forward_port
2808 .map(|value| RuntimeConfigValue::Int(value as i64))
2809 .unwrap_or(RuntimeConfigValue::Null),
2810 startup
2811 .forward_port
2812 .map(|value| RuntimeConfigValue::Int(value as i64))
2813 .unwrap_or(RuntimeConfigValue::Null),
2814 "Outbound UDP destination port; null clears it.",
2815 )),
2816 _ => None,
2817 }
2818 }
2819
2820 #[cfg(feature = "iface-udp")]
2821 fn split_udp_runtime_key<'a>(
2822 &self,
2823 key: &'a str,
2824 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
2825 let rest = key.strip_prefix("udp.").ok_or(RuntimeConfigError {
2826 code: RuntimeConfigErrorCode::UnknownKey,
2827 message: format!("unknown runtime-config key '{}'", key),
2828 })?;
2829 rest.split_once('.').ok_or(RuntimeConfigError {
2830 code: RuntimeConfigErrorCode::UnknownKey,
2831 message: format!("unknown runtime-config key '{}'", key),
2832 })
2833 }
2834
2835 #[cfg(feature = "iface-udp")]
2836 fn set_udp_runtime_config(
2837 &mut self,
2838 key: &str,
2839 value: RuntimeConfigValue,
2840 ) -> Result<(), RuntimeConfigError> {
2841 let (name, setting) = self.split_udp_runtime_key(key)?;
2842 let handle = self.udp_runtime.get(name).ok_or(RuntimeConfigError {
2843 code: RuntimeConfigErrorCode::NotFound,
2844 message: format!("udp interface '{}' not found", name),
2845 })?;
2846 let mut runtime = recover_mutex_guard(&handle.runtime, "udp runtime");
2847 match setting {
2848 "forward_ip" => {
2849 runtime.forward_ip = Self::expect_optional_string(value, key)?;
2850 Ok(())
2851 }
2852 "forward_port" => {
2853 runtime.forward_port = match value {
2854 RuntimeConfigValue::Null => None,
2855 other => Some(Self::expect_u64(other, key)? as u16),
2856 };
2857 Ok(())
2858 }
2859 _ => Err(RuntimeConfigError {
2860 code: RuntimeConfigErrorCode::UnknownKey,
2861 message: format!("unknown runtime-config key '{}'", key),
2862 }),
2863 }
2864 }
2865
2866 #[cfg(feature = "iface-udp")]
2867 fn reset_udp_runtime_config(&mut self, key: &str) -> Result<(), RuntimeConfigError> {
2868 let (name, setting) = self.split_udp_runtime_key(key)?;
2869 let handle = self.udp_runtime.get(name).ok_or(RuntimeConfigError {
2870 code: RuntimeConfigErrorCode::NotFound,
2871 message: format!("udp interface '{}' not found", name),
2872 })?;
2873 let mut runtime = recover_mutex_guard(&handle.runtime, "udp runtime");
2874 let startup = handle.startup.clone();
2875 match setting {
2876 "forward_ip" => runtime.forward_ip = startup.forward_ip,
2877 "forward_port" => runtime.forward_port = startup.forward_port,
2878 _ => {
2879 return Err(RuntimeConfigError {
2880 code: RuntimeConfigErrorCode::UnknownKey,
2881 message: format!("unknown runtime-config key '{}'", key),
2882 })
2883 }
2884 }
2885 Ok(())
2886 }
2887
2888 #[cfg(feature = "iface-auto")]
2889 fn list_auto_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
2890 let mut entries = Vec::new();
2891 let mut names: Vec<&String> = self.auto_runtime.keys().collect();
2892 names.sort();
2893 for name in names {
2894 for suffix in [
2895 "announce_interval_secs",
2896 "peer_timeout_secs",
2897 "peer_job_interval_secs",
2898 ] {
2899 let key = format!("auto.{}.{}", name, suffix);
2900 if let Some(entry) = self.auto_runtime_entry(&key) {
2901 entries.push(entry);
2902 }
2903 }
2904 }
2905 entries
2906 }
2907
2908 #[cfg(feature = "iface-auto")]
2909 fn auto_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
2910 let rest = key.strip_prefix("auto.")?;
2911 let (name, setting) = rest.split_once('.')?;
2912 let handle = self.auto_runtime.get(name)?;
2913 let current = recover_mutex_guard(&handle.runtime, "auto runtime").clone();
2914 let startup = handle.startup.clone();
2915 let make_entry = |value: RuntimeConfigValue,
2916 default: RuntimeConfigValue,
2917 description: &str|
2918 -> RuntimeConfigEntry {
2919 RuntimeConfigEntry {
2920 key: key.to_string(),
2921 source: if value == default {
2922 RuntimeConfigSource::Startup
2923 } else {
2924 RuntimeConfigSource::RuntimeOverride
2925 },
2926 value,
2927 default,
2928 apply_mode: RuntimeConfigApplyMode::Immediate,
2929 description: Some(description.to_string()),
2930 }
2931 };
2932 match setting {
2933 "announce_interval_secs" => Some(make_entry(
2934 RuntimeConfigValue::Float(current.announce_interval_secs),
2935 RuntimeConfigValue::Float(startup.announce_interval_secs),
2936 "Interval between multicast discovery announces in seconds.",
2937 )),
2938 "peer_timeout_secs" => Some(make_entry(
2939 RuntimeConfigValue::Float(current.peer_timeout_secs),
2940 RuntimeConfigValue::Float(startup.peer_timeout_secs),
2941 "How long an Auto peer may stay quiet before being culled.",
2942 )),
2943 "peer_job_interval_secs" => Some(make_entry(
2944 RuntimeConfigValue::Float(current.peer_job_interval_secs),
2945 RuntimeConfigValue::Float(startup.peer_job_interval_secs),
2946 "Interval between Auto peer maintenance passes.",
2947 )),
2948 _ => None,
2949 }
2950 }
2951
2952 #[cfg(feature = "iface-auto")]
2953 fn split_auto_runtime_key<'a>(
2954 &self,
2955 key: &'a str,
2956 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
2957 let rest = key.strip_prefix("auto.").ok_or(RuntimeConfigError {
2958 code: RuntimeConfigErrorCode::UnknownKey,
2959 message: format!("unknown runtime-config key '{}'", key),
2960 })?;
2961 rest.split_once('.').ok_or(RuntimeConfigError {
2962 code: RuntimeConfigErrorCode::UnknownKey,
2963 message: format!("unknown runtime-config key '{}'", key),
2964 })
2965 }
2966
2967 #[cfg(feature = "iface-auto")]
2968 fn set_auto_runtime_config(
2969 &mut self,
2970 key: &str,
2971 value: RuntimeConfigValue,
2972 ) -> Result<(), RuntimeConfigError> {
2973 let (name, setting) = self.split_auto_runtime_key(key)?;
2974 let handle = self.auto_runtime.get(name).ok_or(RuntimeConfigError {
2975 code: RuntimeConfigErrorCode::NotFound,
2976 message: format!("auto interface '{}' not found", name),
2977 })?;
2978 let mut runtime = recover_mutex_guard(&handle.runtime, "auto runtime");
2979 match setting {
2980 "announce_interval_secs" => {
2981 runtime.announce_interval_secs = Self::expect_f64(value, key)?.max(0.1)
2982 }
2983 "peer_timeout_secs" => {
2984 runtime.peer_timeout_secs = Self::expect_f64(value, key)?.max(0.1)
2985 }
2986 "peer_job_interval_secs" => {
2987 runtime.peer_job_interval_secs = Self::expect_f64(value, key)?.max(0.1)
2988 }
2989 _ => {
2990 return Err(RuntimeConfigError {
2991 code: RuntimeConfigErrorCode::UnknownKey,
2992 message: format!("unknown runtime-config key '{}'", key),
2993 });
2994 }
2995 }
2996 Ok(())
2997 }
2998
2999 #[cfg(feature = "iface-auto")]
3000 fn reset_auto_runtime_config(&mut self, key: &str) -> Result<(), RuntimeConfigError> {
3001 let (name, setting) = self.split_auto_runtime_key(key)?;
3002 let handle = self.auto_runtime.get(name).ok_or(RuntimeConfigError {
3003 code: RuntimeConfigErrorCode::NotFound,
3004 message: format!("auto interface '{}' not found", name),
3005 })?;
3006 let mut runtime = recover_mutex_guard(&handle.runtime, "auto runtime");
3007 let startup = handle.startup.clone();
3008 match setting {
3009 "announce_interval_secs" => {
3010 runtime.announce_interval_secs = startup.announce_interval_secs
3011 }
3012 "peer_timeout_secs" => runtime.peer_timeout_secs = startup.peer_timeout_secs,
3013 "peer_job_interval_secs" => {
3014 runtime.peer_job_interval_secs = startup.peer_job_interval_secs
3015 }
3016 _ => {
3017 return Err(RuntimeConfigError {
3018 code: RuntimeConfigErrorCode::UnknownKey,
3019 message: format!("unknown runtime-config key '{}'", key),
3020 });
3021 }
3022 }
3023 Ok(())
3024 }
3025
3026 #[cfg(feature = "iface-i2p")]
3027 fn list_i2p_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
3028 let mut entries = Vec::new();
3029 let mut names: Vec<&String> = self.i2p_runtime.keys().collect();
3030 names.sort();
3031 for name in names {
3032 let key = format!("i2p.{}.reconnect_wait_secs", name);
3033 if let Some(entry) = self.i2p_runtime_entry(&key) {
3034 entries.push(entry);
3035 }
3036 }
3037 entries
3038 }
3039
3040 #[cfg(feature = "iface-i2p")]
3041 fn i2p_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
3042 let rest = key.strip_prefix("i2p.")?;
3043 let (name, setting) = rest.split_once('.')?;
3044 let handle = self.i2p_runtime.get(name)?;
3045 let current = recover_mutex_guard(&handle.runtime, "i2p runtime").clone();
3046 let startup = handle.startup.clone();
3047 match setting {
3048 "reconnect_wait_secs" => Some(RuntimeConfigEntry {
3049 key: key.to_string(),
3050 source: if current.reconnect_wait == startup.reconnect_wait {
3051 RuntimeConfigSource::Startup
3052 } else {
3053 RuntimeConfigSource::RuntimeOverride
3054 },
3055 value: RuntimeConfigValue::Float(current.reconnect_wait.as_secs_f64()),
3056 default: RuntimeConfigValue::Float(startup.reconnect_wait.as_secs_f64()),
3057 apply_mode: RuntimeConfigApplyMode::NextReconnect,
3058 description: Some(
3059 "Delay before retrying outbound I2P peer connections.".to_string(),
3060 ),
3061 }),
3062 _ => None,
3063 }
3064 }
3065
3066 #[cfg(feature = "iface-i2p")]
3067 fn split_i2p_runtime_key<'a>(
3068 &self,
3069 key: &'a str,
3070 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
3071 let rest = key.strip_prefix("i2p.").ok_or(RuntimeConfigError {
3072 code: RuntimeConfigErrorCode::UnknownKey,
3073 message: format!("unknown runtime-config key '{}'", key),
3074 })?;
3075 rest.split_once('.').ok_or(RuntimeConfigError {
3076 code: RuntimeConfigErrorCode::UnknownKey,
3077 message: format!("unknown runtime-config key '{}'", key),
3078 })
3079 }
3080
3081 #[cfg(feature = "iface-i2p")]
3082 fn set_i2p_runtime_config(
3083 &mut self,
3084 key: &str,
3085 value: RuntimeConfigValue,
3086 ) -> Result<(), RuntimeConfigError> {
3087 let (name, setting) = self.split_i2p_runtime_key(key)?;
3088 let handle = self.i2p_runtime.get(name).ok_or(RuntimeConfigError {
3089 code: RuntimeConfigErrorCode::NotFound,
3090 message: format!("i2p interface '{}' not found", name),
3091 })?;
3092 let mut runtime = recover_mutex_guard(&handle.runtime, "i2p runtime");
3093 match setting {
3094 "reconnect_wait_secs" => {
3095 runtime.reconnect_wait =
3096 Duration::from_secs_f64(Self::expect_f64(value, key)?.max(0.1));
3097 Ok(())
3098 }
3099 _ => Err(RuntimeConfigError {
3100 code: RuntimeConfigErrorCode::UnknownKey,
3101 message: format!("unknown runtime-config key '{}'", key),
3102 }),
3103 }
3104 }
3105
3106 #[cfg(feature = "iface-i2p")]
3107 fn reset_i2p_runtime_config(&mut self, key: &str) -> Result<(), RuntimeConfigError> {
3108 let (name, setting) = self.split_i2p_runtime_key(key)?;
3109 let handle = self.i2p_runtime.get(name).ok_or(RuntimeConfigError {
3110 code: RuntimeConfigErrorCode::NotFound,
3111 message: format!("i2p interface '{}' not found", name),
3112 })?;
3113 let mut runtime = recover_mutex_guard(&handle.runtime, "i2p runtime");
3114 let startup = handle.startup.clone();
3115 match setting {
3116 "reconnect_wait_secs" => runtime.reconnect_wait = startup.reconnect_wait,
3117 _ => {
3118 return Err(RuntimeConfigError {
3119 code: RuntimeConfigErrorCode::UnknownKey,
3120 message: format!("unknown runtime-config key '{}'", key),
3121 });
3122 }
3123 }
3124 Ok(())
3125 }
3126
3127 #[cfg(feature = "iface-pipe")]
3128 fn list_pipe_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
3129 let mut entries = Vec::new();
3130 let mut names: Vec<&String> = self.pipe_runtime.keys().collect();
3131 names.sort();
3132 for name in names {
3133 let key = format!("pipe.{}.respawn_delay_secs", name);
3134 if let Some(entry) = self.pipe_runtime_entry(&key) {
3135 entries.push(entry);
3136 }
3137 }
3138 entries
3139 }
3140
3141 #[cfg(feature = "iface-pipe")]
3142 fn pipe_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
3143 let rest = key.strip_prefix("pipe.")?;
3144 let (name, setting) = rest.split_once('.')?;
3145 let handle = self.pipe_runtime.get(name)?;
3146 let current = recover_mutex_guard(&handle.runtime, "pipe runtime").clone();
3147 let startup = handle.startup.clone();
3148 match setting {
3149 "respawn_delay_secs" => Some(RuntimeConfigEntry {
3150 key: key.to_string(),
3151 source: if current.respawn_delay == startup.respawn_delay {
3152 RuntimeConfigSource::Startup
3153 } else {
3154 RuntimeConfigSource::RuntimeOverride
3155 },
3156 value: RuntimeConfigValue::Float(current.respawn_delay.as_secs_f64()),
3157 default: RuntimeConfigValue::Float(startup.respawn_delay.as_secs_f64()),
3158 apply_mode: RuntimeConfigApplyMode::NextReconnect,
3159 description: Some(
3160 "Delay before respawning the pipe subprocess after exit.".to_string(),
3161 ),
3162 }),
3163 _ => None,
3164 }
3165 }
3166
3167 #[cfg(feature = "iface-pipe")]
3168 fn split_pipe_runtime_key<'a>(
3169 &self,
3170 key: &'a str,
3171 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
3172 let rest = key.strip_prefix("pipe.").ok_or(RuntimeConfigError {
3173 code: RuntimeConfigErrorCode::UnknownKey,
3174 message: format!("unknown runtime-config key '{}'", key),
3175 })?;
3176 rest.split_once('.').ok_or(RuntimeConfigError {
3177 code: RuntimeConfigErrorCode::UnknownKey,
3178 message: format!("unknown runtime-config key '{}'", key),
3179 })
3180 }
3181
3182 #[cfg(feature = "iface-pipe")]
3183 fn set_pipe_runtime_config(
3184 &mut self,
3185 key: &str,
3186 value: RuntimeConfigValue,
3187 ) -> Result<(), RuntimeConfigError> {
3188 let (name, setting) = self.split_pipe_runtime_key(key)?;
3189 let handle = self.pipe_runtime.get(name).ok_or(RuntimeConfigError {
3190 code: RuntimeConfigErrorCode::NotFound,
3191 message: format!("pipe interface '{}' not found", name),
3192 })?;
3193 let mut runtime = recover_mutex_guard(&handle.runtime, "pipe runtime");
3194 match setting {
3195 "respawn_delay_secs" => {
3196 runtime.respawn_delay =
3197 Duration::from_secs_f64(Self::expect_f64(value, key)?.max(0.1));
3198 Ok(())
3199 }
3200 _ => Err(RuntimeConfigError {
3201 code: RuntimeConfigErrorCode::UnknownKey,
3202 message: format!("unknown runtime-config key '{}'", key),
3203 }),
3204 }
3205 }
3206
3207 #[cfg(feature = "iface-pipe")]
3208 fn reset_pipe_runtime_config(&mut self, key: &str) -> Result<(), RuntimeConfigError> {
3209 let (name, setting) = self.split_pipe_runtime_key(key)?;
3210 let handle = self.pipe_runtime.get(name).ok_or(RuntimeConfigError {
3211 code: RuntimeConfigErrorCode::NotFound,
3212 message: format!("pipe interface '{}' not found", name),
3213 })?;
3214 let mut runtime = recover_mutex_guard(&handle.runtime, "pipe runtime");
3215 let startup = handle.startup.clone();
3216 match setting {
3217 "respawn_delay_secs" => runtime.respawn_delay = startup.respawn_delay,
3218 _ => {
3219 return Err(RuntimeConfigError {
3220 code: RuntimeConfigErrorCode::UnknownKey,
3221 message: format!("unknown runtime-config key '{}'", key),
3222 });
3223 }
3224 }
3225 Ok(())
3226 }
3227
3228 #[cfg(feature = "iface-rnode")]
3229 fn list_rnode_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
3230 let mut entries = Vec::new();
3231 let mut names: Vec<&String> = self.rnode_runtime.keys().collect();
3232 names.sort();
3233 for name in names {
3234 for suffix in [
3235 "frequency_hz",
3236 "bandwidth_hz",
3237 "txpower_dbm",
3238 "spreading_factor",
3239 "coding_rate",
3240 "st_alock_pct",
3241 "lt_alock_pct",
3242 ] {
3243 let key = format!("rnode.{}.{}", name, suffix);
3244 if let Some(entry) = self.rnode_runtime_entry(&key) {
3245 entries.push(entry);
3246 }
3247 }
3248 }
3249 entries
3250 }
3251
3252 #[cfg(feature = "iface-rnode")]
3253 fn rnode_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
3254 let rest = key.strip_prefix("rnode.")?;
3255 let (name, setting) = rest.split_once('.')?;
3256 let handle = self.rnode_runtime.get(name)?;
3257 let current = recover_mutex_guard(&handle.runtime, "rnode runtime").clone();
3258 let startup = handle.startup.clone();
3259 let make_entry = |value: RuntimeConfigValue,
3260 default: RuntimeConfigValue,
3261 description: &str|
3262 -> RuntimeConfigEntry {
3263 RuntimeConfigEntry {
3264 key: key.to_string(),
3265 source: if value == default {
3266 RuntimeConfigSource::Startup
3267 } else {
3268 RuntimeConfigSource::RuntimeOverride
3269 },
3270 value,
3271 default,
3272 apply_mode: RuntimeConfigApplyMode::Immediate,
3273 description: Some(description.to_string()),
3274 }
3275 };
3276 match setting {
3277 "frequency_hz" => Some(make_entry(
3278 RuntimeConfigValue::Int(current.sub.frequency as i64),
3279 RuntimeConfigValue::Int(startup.sub.frequency as i64),
3280 "RNode radio frequency in Hz.",
3281 )),
3282 "bandwidth_hz" => Some(make_entry(
3283 RuntimeConfigValue::Int(current.sub.bandwidth as i64),
3284 RuntimeConfigValue::Int(startup.sub.bandwidth as i64),
3285 "RNode radio bandwidth in Hz.",
3286 )),
3287 "txpower_dbm" => Some(make_entry(
3288 RuntimeConfigValue::Int(current.sub.txpower as i64),
3289 RuntimeConfigValue::Int(startup.sub.txpower as i64),
3290 "RNode transmit power in dBm.",
3291 )),
3292 "spreading_factor" => Some(make_entry(
3293 RuntimeConfigValue::Int(current.sub.spreading_factor as i64),
3294 RuntimeConfigValue::Int(startup.sub.spreading_factor as i64),
3295 "RNode LoRa spreading factor.",
3296 )),
3297 "coding_rate" => Some(make_entry(
3298 RuntimeConfigValue::Int(current.sub.coding_rate as i64),
3299 RuntimeConfigValue::Int(startup.sub.coding_rate as i64),
3300 "RNode LoRa coding rate.",
3301 )),
3302 "st_alock_pct" => Some(make_entry(
3303 current
3304 .sub
3305 .st_alock
3306 .map(|value| RuntimeConfigValue::Float(value as f64))
3307 .unwrap_or(RuntimeConfigValue::Null),
3308 startup
3309 .sub
3310 .st_alock
3311 .map(|value| RuntimeConfigValue::Float(value as f64))
3312 .unwrap_or(RuntimeConfigValue::Null),
3313 "RNode short-term airtime lock percent; null clears it.",
3314 )),
3315 "lt_alock_pct" => Some(make_entry(
3316 current
3317 .sub
3318 .lt_alock
3319 .map(|value| RuntimeConfigValue::Float(value as f64))
3320 .unwrap_or(RuntimeConfigValue::Null),
3321 startup
3322 .sub
3323 .lt_alock
3324 .map(|value| RuntimeConfigValue::Float(value as f64))
3325 .unwrap_or(RuntimeConfigValue::Null),
3326 "RNode long-term airtime lock percent; null clears it.",
3327 )),
3328 _ => None,
3329 }
3330 }
3331
3332 #[cfg(feature = "iface-rnode")]
3333 fn split_rnode_runtime_key<'a>(
3334 &self,
3335 key: &'a str,
3336 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
3337 let rest = key.strip_prefix("rnode.").ok_or(RuntimeConfigError {
3338 code: RuntimeConfigErrorCode::UnknownKey,
3339 message: format!("unknown runtime-config key '{}'", key),
3340 })?;
3341 rest.split_once('.').ok_or(RuntimeConfigError {
3342 code: RuntimeConfigErrorCode::UnknownKey,
3343 message: format!("unknown runtime-config key '{}'", key),
3344 })
3345 }
3346
3347 #[cfg(feature = "iface-rnode")]
3348 fn apply_rnode_runtime(runtime: &mut RNodeRuntime) -> Result<(), RuntimeConfigError> {
3349 if let Some(err) = validate_sub_config(&runtime.sub) {
3350 return Err(RuntimeConfigError {
3351 code: RuntimeConfigErrorCode::InvalidValue,
3352 message: err,
3353 });
3354 }
3355 if let Some(writer) = runtime.writer.clone() {
3356 crate::interface::rnode::configure_subinterface(&writer, 0, &runtime.sub, false)
3357 .map_err(|e| RuntimeConfigError {
3358 code: RuntimeConfigErrorCode::ApplyFailed,
3359 message: format!("failed to apply RNode config: {}", e),
3360 })?;
3361 }
3362 Ok(())
3363 }
3364
3365 #[cfg(feature = "iface-rnode")]
3366 fn set_rnode_runtime_config(
3367 &mut self,
3368 key: &str,
3369 value: RuntimeConfigValue,
3370 ) -> Result<(), RuntimeConfigError> {
3371 let (name, setting) = self.split_rnode_runtime_key(key)?;
3372 let handle = self.rnode_runtime.get(name).ok_or(RuntimeConfigError {
3373 code: RuntimeConfigErrorCode::NotFound,
3374 message: format!("rnode interface '{}' not found", name),
3375 })?;
3376 let mut runtime = recover_mutex_guard(&handle.runtime, "rnode runtime");
3377 let old = runtime.sub.clone();
3378 match setting {
3379 "frequency_hz" => runtime.sub.frequency = Self::expect_u64(value, key)? as u32,
3380 "bandwidth_hz" => runtime.sub.bandwidth = Self::expect_u64(value, key)? as u32,
3381 "txpower_dbm" => runtime.sub.txpower = Self::expect_i64(value, key)? as i8,
3382 "spreading_factor" => {
3383 runtime.sub.spreading_factor = Self::expect_u64(value, key)? as u8
3384 }
3385 "coding_rate" => runtime.sub.coding_rate = Self::expect_u64(value, key)? as u8,
3386 "st_alock_pct" => {
3387 runtime.sub.st_alock = match value {
3388 RuntimeConfigValue::Null => None,
3389 other => Some(Self::expect_f64(other, key)? as f32),
3390 };
3391 }
3392 "lt_alock_pct" => {
3393 runtime.sub.lt_alock = match value {
3394 RuntimeConfigValue::Null => None,
3395 other => Some(Self::expect_f64(other, key)? as f32),
3396 };
3397 }
3398 _ => {
3399 return Err(RuntimeConfigError {
3400 code: RuntimeConfigErrorCode::UnknownKey,
3401 message: format!("unknown runtime-config key '{}'", key),
3402 });
3403 }
3404 }
3405 if let Err(err) = Self::apply_rnode_runtime(&mut runtime) {
3406 runtime.sub = old;
3407 return Err(err);
3408 }
3409 Ok(())
3410 }
3411
3412 #[cfg(feature = "iface-rnode")]
3413 fn reset_rnode_runtime_config(&mut self, key: &str) -> Result<(), RuntimeConfigError> {
3414 let (name, setting) = self.split_rnode_runtime_key(key)?;
3415 let handle = self.rnode_runtime.get(name).ok_or(RuntimeConfigError {
3416 code: RuntimeConfigErrorCode::NotFound,
3417 message: format!("rnode interface '{}' not found", name),
3418 })?;
3419 let mut runtime = recover_mutex_guard(&handle.runtime, "rnode runtime");
3420 let old = runtime.sub.clone();
3421 let startup = handle.startup.clone();
3422 match setting {
3423 "frequency_hz" => runtime.sub.frequency = startup.sub.frequency,
3424 "bandwidth_hz" => runtime.sub.bandwidth = startup.sub.bandwidth,
3425 "txpower_dbm" => runtime.sub.txpower = startup.sub.txpower,
3426 "spreading_factor" => runtime.sub.spreading_factor = startup.sub.spreading_factor,
3427 "coding_rate" => runtime.sub.coding_rate = startup.sub.coding_rate,
3428 "st_alock_pct" => runtime.sub.st_alock = startup.sub.st_alock,
3429 "lt_alock_pct" => runtime.sub.lt_alock = startup.sub.lt_alock,
3430 _ => {
3431 return Err(RuntimeConfigError {
3432 code: RuntimeConfigErrorCode::UnknownKey,
3433 message: format!("unknown runtime-config key '{}'", key),
3434 });
3435 }
3436 }
3437 if let Err(err) = Self::apply_rnode_runtime(&mut runtime) {
3438 runtime.sub = old;
3439 return Err(err);
3440 }
3441 Ok(())
3442 }
3443
3444 fn list_generic_interface_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
3445 let mut entries = Vec::new();
3446 let mut names: Vec<String> = self
3447 .interfaces
3448 .values()
3449 .map(|entry| entry.info.name.clone())
3450 .collect();
3451 names.sort();
3452 names.dedup();
3453 for name in names {
3454 for suffix in [
3455 "enabled",
3456 "mode",
3457 "announce_rate_target",
3458 "announce_rate_grace",
3459 "announce_rate_penalty",
3460 "announce_cap",
3461 "ingress_control",
3462 "ic_max_held_announces",
3463 "ic_burst_hold",
3464 "ic_burst_freq_new",
3465 "ic_burst_freq",
3466 "ic_new_time",
3467 "ic_burst_penalty",
3468 "ic_held_release_interval",
3469 ] {
3470 let key = format!("interface.{}.{}", name, suffix);
3471 if let Some(entry) = self.generic_interface_runtime_entry(&key) {
3472 entries.push(entry);
3473 }
3474 }
3475 if self.interface_ifac_runtime.contains_key(&name) {
3476 for suffix in ["ifac_netname", "ifac_passphrase", "ifac_size_bytes"] {
3477 let key = format!("interface.{}.{}", name, suffix);
3478 if let Some(entry) = self.generic_interface_runtime_entry(&key) {
3479 entries.push(entry);
3480 }
3481 }
3482 }
3483 }
3484 entries
3485 }
3486
3487 fn generic_interface_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
3488 let rest = key.strip_prefix("interface.")?;
3489 let (name, setting) = rest.rsplit_once('.')?;
3490 let make_entry = |value: RuntimeConfigValue,
3491 default: RuntimeConfigValue,
3492 apply_mode: RuntimeConfigApplyMode,
3493 description: &str|
3494 -> RuntimeConfigEntry {
3495 RuntimeConfigEntry {
3496 key: key.to_string(),
3497 source: if value == default {
3498 RuntimeConfigSource::Startup
3499 } else {
3500 RuntimeConfigSource::RuntimeOverride
3501 },
3502 value,
3503 default,
3504 apply_mode,
3505 description: Some(description.to_string()),
3506 }
3507 };
3508 match setting {
3509 "enabled" => {
3510 let entry = self
3511 .interfaces
3512 .values()
3513 .find(|entry| entry.info.name == name)?;
3514 Some(make_entry(
3515 RuntimeConfigValue::Bool(entry.enabled),
3516 RuntimeConfigValue::Bool(true),
3517 RuntimeConfigApplyMode::Immediate,
3518 "Administrative enable/disable state for this interface.",
3519 ))
3520 }
3521 "ifac_netname" => {
3522 let current = self.interface_ifac_runtime.get(name)?;
3523 let startup = self.interface_ifac_runtime_defaults.get(name)?;
3524 Some(make_entry(
3525 current
3526 .netname
3527 .clone()
3528 .map(RuntimeConfigValue::String)
3529 .unwrap_or(RuntimeConfigValue::Null),
3530 startup
3531 .netname
3532 .clone()
3533 .map(RuntimeConfigValue::String)
3534 .unwrap_or(RuntimeConfigValue::Null),
3535 RuntimeConfigApplyMode::Immediate,
3536 "IFAC network name for this interface; null clears it.",
3537 ))
3538 }
3539 "ifac_passphrase" => {
3540 let current = self.interface_ifac_runtime.get(name)?;
3541 let startup = self.interface_ifac_runtime_defaults.get(name)?;
3542 let current_value = current
3543 .netkey
3544 .as_ref()
3545 .map(|_| RuntimeConfigValue::String("<redacted>".to_string()))
3546 .unwrap_or(RuntimeConfigValue::Null);
3547 let default_value = startup
3548 .netkey
3549 .as_ref()
3550 .map(|_| RuntimeConfigValue::String("<redacted>".to_string()))
3551 .unwrap_or(RuntimeConfigValue::Null);
3552 Some(RuntimeConfigEntry {
3553 key: key.to_string(),
3554 source: if current.netkey == startup.netkey {
3555 RuntimeConfigSource::Startup
3556 } else {
3557 RuntimeConfigSource::RuntimeOverride
3558 },
3559 value: current_value,
3560 default: default_value,
3561 apply_mode: RuntimeConfigApplyMode::Immediate,
3562 description: Some(
3563 "IFAC passphrase for this interface; write-only, set a string to change it or null to clear it."
3564 .to_string(),
3565 ),
3566 })
3567 }
3568 "ifac_size_bytes" => {
3569 let current = self.interface_ifac_runtime.get(name)?;
3570 let startup = self.interface_ifac_runtime_defaults.get(name)?;
3571 Some(make_entry(
3572 RuntimeConfigValue::Int(current.size as i64),
3573 RuntimeConfigValue::Int(startup.size as i64),
3574 RuntimeConfigApplyMode::Immediate,
3575 "IFAC size in bytes; applies when IFAC is enabled.",
3576 ))
3577 }
3578 "mode" => {
3579 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3580 Some(make_entry(
3581 RuntimeConfigValue::String(Self::interface_mode_name(current.mode)),
3582 RuntimeConfigValue::String(Self::interface_mode_name(startup.mode)),
3583 RuntimeConfigApplyMode::Immediate,
3584 "Routing mode for this interface.",
3585 ))
3586 }
3587 "announce_rate_target" => {
3588 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3589 Some(make_entry(
3590 current
3591 .announce_rate_target
3592 .map(RuntimeConfigValue::Float)
3593 .unwrap_or(RuntimeConfigValue::Null),
3594 startup
3595 .announce_rate_target
3596 .map(RuntimeConfigValue::Float)
3597 .unwrap_or(RuntimeConfigValue::Null),
3598 RuntimeConfigApplyMode::Immediate,
3599 "Optional announce rate target in announces/sec; null disables it.",
3600 ))
3601 }
3602 "announce_rate_grace" => {
3603 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3604 Some(make_entry(
3605 RuntimeConfigValue::Int(current.announce_rate_grace as i64),
3606 RuntimeConfigValue::Int(startup.announce_rate_grace as i64),
3607 RuntimeConfigApplyMode::Immediate,
3608 "Announce rate grace period in announces.",
3609 ))
3610 }
3611 "announce_rate_penalty" => {
3612 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3613 Some(make_entry(
3614 RuntimeConfigValue::Float(current.announce_rate_penalty),
3615 RuntimeConfigValue::Float(startup.announce_rate_penalty),
3616 RuntimeConfigApplyMode::Immediate,
3617 "Announce rate penalty multiplier.",
3618 ))
3619 }
3620 "announce_cap" => {
3621 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3622 Some(make_entry(
3623 RuntimeConfigValue::Float(current.announce_cap),
3624 RuntimeConfigValue::Float(startup.announce_cap),
3625 RuntimeConfigApplyMode::Immediate,
3626 "Fraction of bitrate reserved for announces.",
3627 ))
3628 }
3629 "ingress_control" => {
3630 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3631 Some(make_entry(
3632 RuntimeConfigValue::Bool(current.ingress_control.enabled),
3633 RuntimeConfigValue::Bool(startup.ingress_control.enabled),
3634 RuntimeConfigApplyMode::Immediate,
3635 "Whether ingress control is enabled for this interface.",
3636 ))
3637 }
3638 "ic_max_held_announces" => {
3639 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3640 Some(make_entry(
3641 RuntimeConfigValue::Int(current.ingress_control.max_held_announces as i64),
3642 RuntimeConfigValue::Int(startup.ingress_control.max_held_announces as i64),
3643 RuntimeConfigApplyMode::Immediate,
3644 "Maximum held announces retained while ingress control is limiting this interface.",
3645 ))
3646 }
3647 "ic_burst_hold" => {
3648 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3649 Some(make_entry(
3650 RuntimeConfigValue::Float(current.ingress_control.burst_hold),
3651 RuntimeConfigValue::Float(startup.ingress_control.burst_hold),
3652 RuntimeConfigApplyMode::Immediate,
3653 "Seconds to keep ingress-control burst state active before releasing held announces.",
3654 ))
3655 }
3656 "ic_burst_freq_new" => {
3657 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3658 Some(make_entry(
3659 RuntimeConfigValue::Float(current.ingress_control.burst_freq_new),
3660 RuntimeConfigValue::Float(startup.ingress_control.burst_freq_new),
3661 RuntimeConfigApplyMode::Immediate,
3662 "Announce frequency threshold for new interfaces.",
3663 ))
3664 }
3665 "ic_burst_freq" => {
3666 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3667 Some(make_entry(
3668 RuntimeConfigValue::Float(current.ingress_control.burst_freq),
3669 RuntimeConfigValue::Float(startup.ingress_control.burst_freq),
3670 RuntimeConfigApplyMode::Immediate,
3671 "Announce frequency threshold for established interfaces.",
3672 ))
3673 }
3674 "ic_new_time" => {
3675 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3676 Some(make_entry(
3677 RuntimeConfigValue::Float(current.ingress_control.new_time),
3678 RuntimeConfigValue::Float(startup.ingress_control.new_time),
3679 RuntimeConfigApplyMode::Immediate,
3680 "Seconds after interface start that ingress control uses the new-interface burst threshold.",
3681 ))
3682 }
3683 "ic_burst_penalty" => {
3684 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3685 Some(make_entry(
3686 RuntimeConfigValue::Float(current.ingress_control.burst_penalty),
3687 RuntimeConfigValue::Float(startup.ingress_control.burst_penalty),
3688 RuntimeConfigApplyMode::Immediate,
3689 "Seconds to wait after a burst before releasing held announces.",
3690 ))
3691 }
3692 "ic_held_release_interval" => {
3693 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3694 Some(make_entry(
3695 RuntimeConfigValue::Float(current.ingress_control.held_release_interval),
3696 RuntimeConfigValue::Float(startup.ingress_control.held_release_interval),
3697 RuntimeConfigApplyMode::Immediate,
3698 "Seconds between held announce releases.",
3699 ))
3700 }
3701 _ => None,
3702 }
3703 }
3704
3705 fn split_generic_interface_runtime_key<'a>(
3706 &self,
3707 key: &'a str,
3708 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
3709 let rest = key.strip_prefix("interface.").ok_or(RuntimeConfigError {
3710 code: RuntimeConfigErrorCode::UnknownKey,
3711 message: format!("unknown runtime-config key '{}'", key),
3712 })?;
3713 rest.rsplit_once('.').ok_or(RuntimeConfigError {
3714 code: RuntimeConfigErrorCode::UnknownKey,
3715 message: format!("unknown runtime-config key '{}'", key),
3716 })
3717 }
3718
3719 fn interface_runtime_infos_by_name(
3720 &self,
3721 name: &str,
3722 ) -> Option<(
3723 rns_core::transport::types::InterfaceId,
3724 &rns_core::transport::types::InterfaceInfo,
3725 &rns_core::transport::types::InterfaceInfo,
3726 )> {
3727 let (id, entry) = self
3728 .interfaces
3729 .iter()
3730 .find(|(_, entry)| entry.info.name == name)?;
3731 let startup = self.interface_runtime_defaults.get(name)?;
3732 Some((*id, &entry.info, startup))
3733 }
3734
3735 fn interface_mode_name(mode: u8) -> String {
3736 match mode {
3737 rns_core::constants::MODE_FULL => "full".to_string(),
3738 rns_core::constants::MODE_ACCESS_POINT => "access_point".to_string(),
3739 rns_core::constants::MODE_POINT_TO_POINT => "point_to_point".to_string(),
3740 rns_core::constants::MODE_ROAMING => "roaming".to_string(),
3741 rns_core::constants::MODE_BOUNDARY => "boundary".to_string(),
3742 rns_core::constants::MODE_GATEWAY => "gateway".to_string(),
3743 _ => mode.to_string(),
3744 }
3745 }
3746
3747 fn parse_interface_mode(value: &RuntimeConfigValue) -> Option<u8> {
3748 match value {
3749 RuntimeConfigValue::Int(v) if *v >= 0 && *v <= u8::MAX as i64 => Some(*v as u8),
3750 RuntimeConfigValue::String(s) => match s.to_ascii_lowercase().as_str() {
3751 "full" => Some(rns_core::constants::MODE_FULL),
3752 "access_point" | "accesspoint" | "ap" => {
3753 Some(rns_core::constants::MODE_ACCESS_POINT)
3754 }
3755 "point_to_point" | "pointtopoint" | "ptp" => {
3756 Some(rns_core::constants::MODE_POINT_TO_POINT)
3757 }
3758 "roaming" => Some(rns_core::constants::MODE_ROAMING),
3759 "boundary" => Some(rns_core::constants::MODE_BOUNDARY),
3760 "gateway" | "gw" => Some(rns_core::constants::MODE_GATEWAY),
3761 _ => None,
3762 },
3763 _ => None,
3764 }
3765 }
3766
3767 fn apply_interface_ifac_runtime(entry: &mut InterfaceEntry, config: &IfacRuntimeConfig) {
3768 entry.ifac = if config.netname.is_some() || config.netkey.is_some() {
3769 Some(ifac::derive_ifac(
3770 config.netname.as_deref(),
3771 config.netkey.as_deref(),
3772 config.size,
3773 ))
3774 } else {
3775 None
3776 };
3777 }
3778
3779 fn set_generic_interface_runtime_config(
3780 &mut self,
3781 key: &str,
3782 value: RuntimeConfigValue,
3783 ) -> Result<(), RuntimeConfigError> {
3784 let (name, setting) = self.split_generic_interface_runtime_key(key)?;
3785 let (id, _) = self
3786 .interfaces
3787 .iter()
3788 .find(|(_, entry)| entry.info.name == name)
3789 .map(|(id, entry)| (*id, entry))
3790 .ok_or(RuntimeConfigError {
3791 code: RuntimeConfigErrorCode::NotFound,
3792 message: format!("interface '{}' not found", name),
3793 })?;
3794 let entry = self.interfaces.get_mut(&id).ok_or(RuntimeConfigError {
3795 code: RuntimeConfigErrorCode::NotFound,
3796 message: format!("interface '{}' not found", name),
3797 })?;
3798 match setting {
3799 "enabled" => {
3800 entry.enabled = Self::expect_bool(value, key)?;
3801 }
3802 "ifac_netname" => {
3803 let runtime =
3804 self.interface_ifac_runtime
3805 .get_mut(name)
3806 .ok_or(RuntimeConfigError {
3807 code: RuntimeConfigErrorCode::UnknownKey,
3808 message: format!("unknown runtime-config key '{}'", key),
3809 })?;
3810 runtime.netname = match value {
3811 RuntimeConfigValue::Null => None,
3812 RuntimeConfigValue::String(value) => Some(value),
3813 _ => {
3814 return Err(RuntimeConfigError {
3815 code: RuntimeConfigErrorCode::InvalidType,
3816 message: format!("{} expects a string or null", key),
3817 })
3818 }
3819 };
3820 Self::apply_interface_ifac_runtime(entry, runtime);
3821 }
3822 "ifac_passphrase" => {
3823 let runtime =
3824 self.interface_ifac_runtime
3825 .get_mut(name)
3826 .ok_or(RuntimeConfigError {
3827 code: RuntimeConfigErrorCode::UnknownKey,
3828 message: format!("unknown runtime-config key '{}'", key),
3829 })?;
3830 runtime.netkey = match value {
3831 RuntimeConfigValue::Null => None,
3832 RuntimeConfigValue::String(value) => Some(value),
3833 _ => {
3834 return Err(RuntimeConfigError {
3835 code: RuntimeConfigErrorCode::InvalidType,
3836 message: format!("{} expects a string or null", key),
3837 })
3838 }
3839 };
3840 Self::apply_interface_ifac_runtime(entry, runtime);
3841 }
3842 "ifac_size_bytes" => {
3843 let runtime =
3844 self.interface_ifac_runtime
3845 .get_mut(name)
3846 .ok_or(RuntimeConfigError {
3847 code: RuntimeConfigErrorCode::UnknownKey,
3848 message: format!("unknown runtime-config key '{}'", key),
3849 })?;
3850 runtime.size =
3851 (Self::expect_u64(value, key)? as usize).max(crate::ifac::IFAC_MIN_SIZE);
3852 Self::apply_interface_ifac_runtime(entry, runtime);
3853 }
3854 "mode" => {
3855 entry.info.mode = Self::parse_interface_mode(&value).ok_or(RuntimeConfigError {
3856 code: RuntimeConfigErrorCode::InvalidValue,
3857 message: format!("{} must be a valid interface mode", key),
3858 })?;
3859 }
3860 "announce_rate_target" => {
3861 entry.info.announce_rate_target = match value {
3862 RuntimeConfigValue::Null => None,
3863 RuntimeConfigValue::Float(v) if v >= 0.0 => Some(v),
3864 RuntimeConfigValue::Int(v) if v >= 0 => Some(v as f64),
3865 RuntimeConfigValue::Float(_) | RuntimeConfigValue::Int(_) => {
3866 return Err(RuntimeConfigError {
3867 code: RuntimeConfigErrorCode::InvalidValue,
3868 message: format!("{} must be >= 0", key),
3869 })
3870 }
3871 _ => {
3872 return Err(RuntimeConfigError {
3873 code: RuntimeConfigErrorCode::InvalidType,
3874 message: format!("{} expects a numeric value or null", key),
3875 })
3876 }
3877 };
3878 }
3879 "announce_rate_grace" => {
3880 entry.info.announce_rate_grace = Self::expect_u64(value, key)? as u32
3881 }
3882 "announce_rate_penalty" => {
3883 entry.info.announce_rate_penalty = Self::expect_f64(value, key)?
3884 }
3885 "announce_cap" => entry.info.announce_cap = Self::expect_f64(value, key)?,
3886 "ingress_control" => {
3887 entry.info.ingress_control.enabled = Self::expect_bool(value, key)?
3888 }
3889 "ic_max_held_announces" => {
3890 entry.info.ingress_control.max_held_announces =
3891 Self::expect_u64(value, key)? as usize
3892 }
3893 "ic_burst_hold" => {
3894 entry.info.ingress_control.burst_hold = Self::expect_f64(value, key)?
3895 }
3896 "ic_burst_freq_new" => {
3897 entry.info.ingress_control.burst_freq_new = Self::expect_f64(value, key)?
3898 }
3899 "ic_burst_freq" => {
3900 entry.info.ingress_control.burst_freq = Self::expect_f64(value, key)?
3901 }
3902 "ic_new_time" => entry.info.ingress_control.new_time = Self::expect_f64(value, key)?,
3903 "ic_burst_penalty" => {
3904 entry.info.ingress_control.burst_penalty = Self::expect_f64(value, key)?
3905 }
3906 "ic_held_release_interval" => {
3907 entry.info.ingress_control.held_release_interval = Self::expect_f64(value, key)?
3908 }
3909 _ => {
3910 return Err(RuntimeConfigError {
3911 code: RuntimeConfigErrorCode::UnknownKey,
3912 message: format!("unknown runtime-config key '{}'", key),
3913 })
3914 }
3915 }
3916 let info = entry.info.clone();
3917 self.engine.register_interface(info);
3918 Ok(())
3919 }
3920
3921 fn reset_generic_interface_runtime_config(
3922 &mut self,
3923 key: &str,
3924 ) -> Result<(), RuntimeConfigError> {
3925 let (name, setting) = self.split_generic_interface_runtime_key(key)?;
3926 let startup =
3927 self.interface_runtime_defaults
3928 .get(name)
3929 .cloned()
3930 .ok_or(RuntimeConfigError {
3931 code: RuntimeConfigErrorCode::NotFound,
3932 message: format!("interface '{}' not found", name),
3933 })?;
3934 let entry = self
3935 .interfaces
3936 .values_mut()
3937 .find(|entry| entry.info.name == name)
3938 .ok_or(RuntimeConfigError {
3939 code: RuntimeConfigErrorCode::NotFound,
3940 message: format!("interface '{}' not found", name),
3941 })?;
3942 match setting {
3943 "enabled" => entry.enabled = true,
3944 "ifac_netname" => {
3945 let startup_ifac =
3946 self.interface_ifac_runtime_defaults
3947 .get(name)
3948 .ok_or(RuntimeConfigError {
3949 code: RuntimeConfigErrorCode::UnknownKey,
3950 message: format!("unknown runtime-config key '{}'", key),
3951 })?;
3952 let runtime =
3953 self.interface_ifac_runtime
3954 .get_mut(name)
3955 .ok_or(RuntimeConfigError {
3956 code: RuntimeConfigErrorCode::UnknownKey,
3957 message: format!("unknown runtime-config key '{}'", key),
3958 })?;
3959 runtime.netname = startup_ifac.netname.clone();
3960 Self::apply_interface_ifac_runtime(entry, runtime);
3961 }
3962 "ifac_passphrase" => {
3963 let startup_ifac =
3964 self.interface_ifac_runtime_defaults
3965 .get(name)
3966 .ok_or(RuntimeConfigError {
3967 code: RuntimeConfigErrorCode::UnknownKey,
3968 message: format!("unknown runtime-config key '{}'", key),
3969 })?;
3970 let runtime =
3971 self.interface_ifac_runtime
3972 .get_mut(name)
3973 .ok_or(RuntimeConfigError {
3974 code: RuntimeConfigErrorCode::UnknownKey,
3975 message: format!("unknown runtime-config key '{}'", key),
3976 })?;
3977 runtime.netkey = startup_ifac.netkey.clone();
3978 Self::apply_interface_ifac_runtime(entry, runtime);
3979 }
3980 "ifac_size_bytes" => {
3981 let startup_ifac =
3982 self.interface_ifac_runtime_defaults
3983 .get(name)
3984 .ok_or(RuntimeConfigError {
3985 code: RuntimeConfigErrorCode::UnknownKey,
3986 message: format!("unknown runtime-config key '{}'", key),
3987 })?;
3988 let runtime =
3989 self.interface_ifac_runtime
3990 .get_mut(name)
3991 .ok_or(RuntimeConfigError {
3992 code: RuntimeConfigErrorCode::UnknownKey,
3993 message: format!("unknown runtime-config key '{}'", key),
3994 })?;
3995 runtime.size = startup_ifac.size;
3996 Self::apply_interface_ifac_runtime(entry, runtime);
3997 }
3998 "mode" => entry.info.mode = startup.mode,
3999 "announce_rate_target" => {
4000 entry.info.announce_rate_target = startup.announce_rate_target
4001 }
4002 "announce_rate_grace" => entry.info.announce_rate_grace = startup.announce_rate_grace,
4003 "announce_rate_penalty" => {
4004 entry.info.announce_rate_penalty = startup.announce_rate_penalty
4005 }
4006 "announce_cap" => entry.info.announce_cap = startup.announce_cap,
4007 "ingress_control" => {
4008 entry.info.ingress_control.enabled = startup.ingress_control.enabled
4009 }
4010 "ic_max_held_announces" => {
4011 entry.info.ingress_control.max_held_announces =
4012 startup.ingress_control.max_held_announces
4013 }
4014 "ic_burst_hold" => {
4015 entry.info.ingress_control.burst_hold = startup.ingress_control.burst_hold
4016 }
4017 "ic_burst_freq_new" => {
4018 entry.info.ingress_control.burst_freq_new = startup.ingress_control.burst_freq_new
4019 }
4020 "ic_burst_freq" => {
4021 entry.info.ingress_control.burst_freq = startup.ingress_control.burst_freq
4022 }
4023 "ic_new_time" => entry.info.ingress_control.new_time = startup.ingress_control.new_time,
4024 "ic_burst_penalty" => {
4025 entry.info.ingress_control.burst_penalty = startup.ingress_control.burst_penalty
4026 }
4027 "ic_held_release_interval" => {
4028 entry.info.ingress_control.held_release_interval =
4029 startup.ingress_control.held_release_interval
4030 }
4031 _ => {
4032 return Err(RuntimeConfigError {
4033 code: RuntimeConfigErrorCode::UnknownKey,
4034 message: format!("unknown runtime-config key '{}'", key),
4035 })
4036 }
4037 }
4038 let info = entry.info.clone();
4039 self.engine.register_interface(info);
4040 Ok(())
4041 }
4042
4043 #[cfg(feature = "iface-tcp")]
4044 fn tcp_server_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
4045 let rest = key.strip_prefix("tcp_server.")?;
4046 let (name, setting) = rest.split_once('.')?;
4047 if matches!(
4048 setting,
4049 "discoverable"
4050 | "discovery_name"
4051 | "announce_interval_secs"
4052 | "reachable_on"
4053 | "stamp_value"
4054 | "latitude"
4055 | "longitude"
4056 | "height"
4057 ) {
4058 let handle = self.tcp_server_discovery_runtime.get(name)?;
4059 let current = &handle.current;
4060 let startup = &handle.startup;
4061 let make_entry = |value: RuntimeConfigValue,
4062 default: RuntimeConfigValue,
4063 apply_mode: RuntimeConfigApplyMode,
4064 description: &str|
4065 -> RuntimeConfigEntry {
4066 RuntimeConfigEntry {
4067 key: key.to_string(),
4068 source: if value == default {
4069 RuntimeConfigSource::Startup
4070 } else {
4071 RuntimeConfigSource::RuntimeOverride
4072 },
4073 value,
4074 default,
4075 apply_mode,
4076 description: Some(description.to_string()),
4077 }
4078 };
4079 return match setting {
4080 "discoverable" => Some(make_entry(
4081 RuntimeConfigValue::Bool(current.discoverable),
4082 RuntimeConfigValue::Bool(startup.discoverable),
4083 RuntimeConfigApplyMode::Immediate,
4084 "Whether this TCP server interface is advertised through interface discovery.",
4085 )),
4086 "discovery_name" => Some(make_entry(
4087 RuntimeConfigValue::String(current.config.discovery_name.clone()),
4088 RuntimeConfigValue::String(startup.config.discovery_name.clone()),
4089 RuntimeConfigApplyMode::Immediate,
4090 "Human-readable discovery name advertised for this TCP server interface.",
4091 )),
4092 "announce_interval_secs" => Some(make_entry(
4093 RuntimeConfigValue::Int(current.config.announce_interval as i64),
4094 RuntimeConfigValue::Int(startup.config.announce_interval as i64),
4095 RuntimeConfigApplyMode::Immediate,
4096 "Discovery announce interval for this TCP server interface in seconds.",
4097 )),
4098 "reachable_on" => Some(make_entry(
4099 current
4100 .config
4101 .reachable_on
4102 .clone()
4103 .map(RuntimeConfigValue::String)
4104 .unwrap_or(RuntimeConfigValue::Null),
4105 startup
4106 .config
4107 .reachable_on
4108 .clone()
4109 .map(RuntimeConfigValue::String)
4110 .unwrap_or(RuntimeConfigValue::Null),
4111 RuntimeConfigApplyMode::Immediate,
4112 "Reachable hostname or IP advertised for this TCP server interface; null clears it.",
4113 )),
4114 "stamp_value" => Some(make_entry(
4115 RuntimeConfigValue::Int(current.config.stamp_value as i64),
4116 RuntimeConfigValue::Int(startup.config.stamp_value as i64),
4117 RuntimeConfigApplyMode::Immediate,
4118 "Discovery proof-of-work stamp cost for this TCP server interface.",
4119 )),
4120 "latitude" => Some(make_entry(
4121 current
4122 .config
4123 .latitude
4124 .map(RuntimeConfigValue::Float)
4125 .unwrap_or(RuntimeConfigValue::Null),
4126 startup
4127 .config
4128 .latitude
4129 .map(RuntimeConfigValue::Float)
4130 .unwrap_or(RuntimeConfigValue::Null),
4131 RuntimeConfigApplyMode::Immediate,
4132 "Latitude advertised for this TCP server interface; null clears it.",
4133 )),
4134 "longitude" => Some(make_entry(
4135 current
4136 .config
4137 .longitude
4138 .map(RuntimeConfigValue::Float)
4139 .unwrap_or(RuntimeConfigValue::Null),
4140 startup
4141 .config
4142 .longitude
4143 .map(RuntimeConfigValue::Float)
4144 .unwrap_or(RuntimeConfigValue::Null),
4145 RuntimeConfigApplyMode::Immediate,
4146 "Longitude advertised for this TCP server interface; null clears it.",
4147 )),
4148 "height" => Some(make_entry(
4149 current
4150 .config
4151 .height
4152 .map(RuntimeConfigValue::Float)
4153 .unwrap_or(RuntimeConfigValue::Null),
4154 startup
4155 .config
4156 .height
4157 .map(RuntimeConfigValue::Float)
4158 .unwrap_or(RuntimeConfigValue::Null),
4159 RuntimeConfigApplyMode::Immediate,
4160 "Height advertised for this TCP server interface; null clears it.",
4161 )),
4162 _ => None,
4163 };
4164 }
4165
4166 let handle = self.tcp_server_runtime.get(name)?;
4167 let current = recover_mutex_guard(&handle.runtime, "tcp server runtime").clone();
4168 let startup = handle.startup.clone();
4169 match setting {
4170 "max_connections" => Some(RuntimeConfigEntry {
4171 key: key.to_string(),
4172 value: RuntimeConfigValue::Int(current.max_connections.unwrap_or(0) as i64),
4173 default: RuntimeConfigValue::Int(startup.max_connections.unwrap_or(0) as i64),
4174 source: if current.max_connections == startup.max_connections {
4175 RuntimeConfigSource::Startup
4176 } else {
4177 RuntimeConfigSource::RuntimeOverride
4178 },
4179 apply_mode: RuntimeConfigApplyMode::NewConnectionsOnly,
4180 description: Some(
4181 "Maximum simultaneous inbound TCP server connections; 0 disables the cap."
4182 .to_string(),
4183 ),
4184 }),
4185 _ => None,
4186 }
4187 }
4188
4189 #[cfg(feature = "iface-tcp")]
4190 fn split_tcp_server_runtime_key<'a>(
4191 &self,
4192 key: &'a str,
4193 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
4194 let rest = key.strip_prefix("tcp_server.").ok_or(RuntimeConfigError {
4195 code: RuntimeConfigErrorCode::UnknownKey,
4196 message: format!("unknown runtime-config key '{}'", key),
4197 })?;
4198 rest.split_once('.').ok_or(RuntimeConfigError {
4199 code: RuntimeConfigErrorCode::UnknownKey,
4200 message: format!("unknown runtime-config key '{}'", key),
4201 })
4202 }
4203
4204 #[cfg(feature = "iface-tcp")]
4205 fn set_tcp_server_runtime_config(
4206 &mut self,
4207 key: &str,
4208 value: RuntimeConfigValue,
4209 ) -> Result<(), RuntimeConfigError> {
4210 let (name, setting) = self.split_tcp_server_runtime_key(key)?;
4211 if matches!(
4212 setting,
4213 "discoverable"
4214 | "discovery_name"
4215 | "announce_interval_secs"
4216 | "reachable_on"
4217 | "stamp_value"
4218 | "latitude"
4219 | "longitude"
4220 | "height"
4221 ) {
4222 return self.set_tcp_server_discovery_runtime_config(key, value);
4223 }
4224 let handle = self
4225 .tcp_server_runtime
4226 .get(name)
4227 .ok_or(RuntimeConfigError {
4228 code: RuntimeConfigErrorCode::NotFound,
4229 message: format!("tcp server interface '{}' not found", name),
4230 })?;
4231 let mut runtime = recover_mutex_guard(&handle.runtime, "tcp server runtime");
4232 match setting {
4233 "max_connections" => {
4234 runtime.max_connections = Self::set_optional_usize(value, key)?;
4235 Ok(())
4236 }
4237 _ => Err(RuntimeConfigError {
4238 code: RuntimeConfigErrorCode::UnknownKey,
4239 message: format!("unknown runtime-config key '{}'", key),
4240 }),
4241 }
4242 }
4243
4244 #[cfg(feature = "iface-tcp")]
4245 fn reset_tcp_server_runtime_config(&mut self, key: &str) -> Result<(), RuntimeConfigError> {
4246 let (name, setting) = self.split_tcp_server_runtime_key(key)?;
4247 if matches!(
4248 setting,
4249 "discoverable"
4250 | "discovery_name"
4251 | "announce_interval_secs"
4252 | "reachable_on"
4253 | "stamp_value"
4254 | "latitude"
4255 | "longitude"
4256 | "height"
4257 ) {
4258 return self.reset_tcp_server_discovery_runtime_config(key);
4259 }
4260 let handle = self
4261 .tcp_server_runtime
4262 .get(name)
4263 .ok_or(RuntimeConfigError {
4264 code: RuntimeConfigErrorCode::NotFound,
4265 message: format!("tcp server interface '{}' not found", name),
4266 })?;
4267 let mut runtime = recover_mutex_guard(&handle.runtime, "tcp server runtime");
4268 let startup = handle.startup.clone();
4269 match setting {
4270 "max_connections" => runtime.max_connections = startup.max_connections,
4271 _ => {
4272 return Err(RuntimeConfigError {
4273 code: RuntimeConfigErrorCode::UnknownKey,
4274 message: format!("unknown runtime-config key '{}'", key),
4275 })
4276 }
4277 }
4278 Ok(())
4279 }
4280
4281 #[cfg(feature = "iface-tcp")]
4282 fn set_tcp_server_discovery_runtime_config(
4283 &mut self,
4284 key: &str,
4285 value: RuntimeConfigValue,
4286 ) -> Result<(), RuntimeConfigError> {
4287 let (name, setting) = self.split_tcp_server_runtime_key(key)?;
4288 let handle = self
4289 .tcp_server_discovery_runtime
4290 .get_mut(name)
4291 .ok_or(RuntimeConfigError {
4292 code: RuntimeConfigErrorCode::NotFound,
4293 message: format!("tcp server interface '{}' not found", name),
4294 })?;
4295 match setting {
4296 "discoverable" => handle.current.discoverable = Self::expect_bool(value, key)?,
4297 "discovery_name" => {
4298 handle.current.config.discovery_name = Self::expect_string(value, key)?
4299 }
4300 "announce_interval_secs" => {
4301 let secs = Self::expect_u64(value, key)?;
4302 if secs < 300 {
4303 return Err(RuntimeConfigError {
4304 code: RuntimeConfigErrorCode::InvalidValue,
4305 message: format!("{} must be >= 300", key),
4306 });
4307 }
4308 handle.current.config.announce_interval = secs;
4309 }
4310 "reachable_on" => {
4311 handle.current.config.reachable_on = Self::expect_optional_string(value, key)?
4312 }
4313 "stamp_value" => {
4314 let raw = Self::expect_u64(value, key)?;
4315 if raw > u8::MAX as u64 {
4316 return Err(RuntimeConfigError {
4317 code: RuntimeConfigErrorCode::InvalidValue,
4318 message: format!("{} must be <= {}", key, u8::MAX),
4319 });
4320 }
4321 handle.current.config.stamp_value = raw as u8;
4322 }
4323 "latitude" => handle.current.config.latitude = Self::expect_optional_f64(value, key)?,
4324 "longitude" => handle.current.config.longitude = Self::expect_optional_f64(value, key)?,
4325 "height" => handle.current.config.height = Self::expect_optional_f64(value, key)?,
4326 _ => {
4327 return Err(RuntimeConfigError {
4328 code: RuntimeConfigErrorCode::UnknownKey,
4329 message: format!("unknown runtime-config key '{}'", key),
4330 })
4331 }
4332 }
4333 self.sync_tcp_server_discovery_runtime(name)
4334 }
4335
4336 #[cfg(feature = "iface-tcp")]
4337 fn reset_tcp_server_discovery_runtime_config(
4338 &mut self,
4339 key: &str,
4340 ) -> Result<(), RuntimeConfigError> {
4341 let (name, setting) = self.split_tcp_server_runtime_key(key)?;
4342 let handle = self
4343 .tcp_server_discovery_runtime
4344 .get_mut(name)
4345 .ok_or(RuntimeConfigError {
4346 code: RuntimeConfigErrorCode::NotFound,
4347 message: format!("tcp server interface '{}' not found", name),
4348 })?;
4349 match setting {
4350 "discoverable" => handle.current.discoverable = handle.startup.discoverable,
4351 "discovery_name" => {
4352 handle.current.config.discovery_name = handle.startup.config.discovery_name.clone()
4353 }
4354 "announce_interval_secs" => {
4355 handle.current.config.announce_interval = handle.startup.config.announce_interval
4356 }
4357 "reachable_on" => {
4358 handle.current.config.reachable_on = handle.startup.config.reachable_on.clone()
4359 }
4360 "stamp_value" => handle.current.config.stamp_value = handle.startup.config.stamp_value,
4361 "latitude" => handle.current.config.latitude = handle.startup.config.latitude,
4362 "longitude" => handle.current.config.longitude = handle.startup.config.longitude,
4363 "height" => handle.current.config.height = handle.startup.config.height,
4364 _ => {
4365 return Err(RuntimeConfigError {
4366 code: RuntimeConfigErrorCode::UnknownKey,
4367 message: format!("unknown runtime-config key '{}'", key),
4368 })
4369 }
4370 }
4371 self.sync_tcp_server_discovery_runtime(name)
4372 }
4373
4374 #[cfg(feature = "iface-backbone")]
4375 fn list_backbone_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
4376 let mut entries = Vec::new();
4377 let mut names: Vec<&String> = self.backbone_runtime.keys().collect();
4378 names.sort();
4379 for name in names {
4380 for suffix in [
4381 "idle_timeout_secs",
4382 "write_stall_timeout_secs",
4383 "max_penalty_duration_secs",
4384 "max_connections",
4385 ] {
4386 let key = format!("backbone.{}.{}", name, suffix);
4387 if let Some(entry) = self.backbone_runtime_entry(&key) {
4388 entries.push(entry);
4389 }
4390 }
4391 for suffix in [
4392 "discoverable",
4393 "discovery_name",
4394 "announce_interval_secs",
4395 "reachable_on",
4396 "stamp_value",
4397 "latitude",
4398 "longitude",
4399 "height",
4400 ] {
4401 let key = format!("backbone.{}.{}", name, suffix);
4402 if let Some(entry) = self.backbone_runtime_entry(&key) {
4403 entries.push(entry);
4404 }
4405 }
4406 }
4407 entries
4408 }
4409
4410 #[cfg(feature = "iface-backbone")]
4411 fn backbone_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
4412 let rest = key.strip_prefix("backbone.")?;
4413 let (name, setting) = rest.split_once('.')?;
4414
4415 let make_entry = |value: RuntimeConfigValue,
4416 default: RuntimeConfigValue,
4417 apply_mode: RuntimeConfigApplyMode,
4418 description: &str| RuntimeConfigEntry {
4419 key: key.to_string(),
4420 source: if value == default {
4421 RuntimeConfigSource::Startup
4422 } else {
4423 RuntimeConfigSource::RuntimeOverride
4424 },
4425 value,
4426 default,
4427 apply_mode,
4428 description: Some(description.to_string()),
4429 };
4430
4431 if matches!(
4432 setting,
4433 "discoverable"
4434 | "discovery_name"
4435 | "announce_interval_secs"
4436 | "reachable_on"
4437 | "stamp_value"
4438 | "latitude"
4439 | "longitude"
4440 | "height"
4441 ) {
4442 let handle = self.backbone_discovery_runtime.get(name)?;
4443 let current = &handle.current;
4444 let startup = &handle.startup;
4445 return match setting {
4446 "discoverable" => Some(make_entry(
4447 RuntimeConfigValue::Bool(current.discoverable),
4448 RuntimeConfigValue::Bool(startup.discoverable),
4449 RuntimeConfigApplyMode::Immediate,
4450 "Whether this backbone interface is advertised through interface discovery.",
4451 )),
4452 "discovery_name" => Some(make_entry(
4453 RuntimeConfigValue::String(current.config.discovery_name.clone()),
4454 RuntimeConfigValue::String(startup.config.discovery_name.clone()),
4455 RuntimeConfigApplyMode::Immediate,
4456 "Human-readable discovery name advertised for this backbone interface.",
4457 )),
4458 "announce_interval_secs" => Some(make_entry(
4459 RuntimeConfigValue::Int(current.config.announce_interval as i64),
4460 RuntimeConfigValue::Int(startup.config.announce_interval as i64),
4461 RuntimeConfigApplyMode::Immediate,
4462 "Discovery announce interval for this backbone interface in seconds.",
4463 )),
4464 "reachable_on" => Some(make_entry(
4465 current
4466 .config
4467 .reachable_on
4468 .clone()
4469 .map(RuntimeConfigValue::String)
4470 .unwrap_or(RuntimeConfigValue::Null),
4471 startup
4472 .config
4473 .reachable_on
4474 .clone()
4475 .map(RuntimeConfigValue::String)
4476 .unwrap_or(RuntimeConfigValue::Null),
4477 RuntimeConfigApplyMode::Immediate,
4478 "Reachable hostname or IP advertised for this backbone interface; null clears it.",
4479 )),
4480 "stamp_value" => Some(make_entry(
4481 RuntimeConfigValue::Int(current.config.stamp_value as i64),
4482 RuntimeConfigValue::Int(startup.config.stamp_value as i64),
4483 RuntimeConfigApplyMode::Immediate,
4484 "Discovery proof-of-work stamp cost for this backbone interface.",
4485 )),
4486 "latitude" => Some(make_entry(
4487 current
4488 .config
4489 .latitude
4490 .map(RuntimeConfigValue::Float)
4491 .unwrap_or(RuntimeConfigValue::Null),
4492 startup
4493 .config
4494 .latitude
4495 .map(RuntimeConfigValue::Float)
4496 .unwrap_or(RuntimeConfigValue::Null),
4497 RuntimeConfigApplyMode::Immediate,
4498 "Latitude advertised for this backbone interface; null clears it.",
4499 )),
4500 "longitude" => Some(make_entry(
4501 current
4502 .config
4503 .longitude
4504 .map(RuntimeConfigValue::Float)
4505 .unwrap_or(RuntimeConfigValue::Null),
4506 startup
4507 .config
4508 .longitude
4509 .map(RuntimeConfigValue::Float)
4510 .unwrap_or(RuntimeConfigValue::Null),
4511 RuntimeConfigApplyMode::Immediate,
4512 "Longitude advertised for this backbone interface; null clears it.",
4513 )),
4514 "height" => Some(make_entry(
4515 current
4516 .config
4517 .height
4518 .map(RuntimeConfigValue::Float)
4519 .unwrap_or(RuntimeConfigValue::Null),
4520 startup
4521 .config
4522 .height
4523 .map(RuntimeConfigValue::Float)
4524 .unwrap_or(RuntimeConfigValue::Null),
4525 RuntimeConfigApplyMode::Immediate,
4526 "Height advertised for this backbone interface; null clears it.",
4527 )),
4528 _ => None,
4529 };
4530 }
4531
4532 if let Some(handle) = self.backbone_runtime.get(name) {
4533 let current = recover_mutex_guard(&handle.runtime, "backbone runtime").clone();
4534 let startup = handle.startup.clone();
4535 return match setting {
4536 "idle_timeout_secs" => Some(make_entry(
4537 RuntimeConfigValue::Float(current.idle_timeout.map(|d| d.as_secs_f64()).unwrap_or(0.0)),
4538 RuntimeConfigValue::Float(startup.idle_timeout.map(|d| d.as_secs_f64()).unwrap_or(0.0)),
4539 RuntimeConfigApplyMode::Immediate,
4540 "Disconnect silent inbound peers after this many seconds; 0 disables the timeout.",
4541 )),
4542 "write_stall_timeout_secs" => Some(make_entry(
4543 RuntimeConfigValue::Float(current.write_stall_timeout.map(|d| d.as_secs_f64()).unwrap_or(0.0)),
4544 RuntimeConfigValue::Float(startup.write_stall_timeout.map(|d| d.as_secs_f64()).unwrap_or(0.0)),
4545 RuntimeConfigApplyMode::Immediate,
4546 "Disconnect peers whose send buffer remains unwritable for this many seconds; 0 disables the timeout.",
4547 )),
4548 "max_penalty_duration_secs" => Some(make_entry(
4549 RuntimeConfigValue::Float(current.abuse.max_penalty_duration.map(|d| d.as_secs_f64()).unwrap_or(0.0)),
4550 RuntimeConfigValue::Float(startup.abuse.max_penalty_duration.map(|d| d.as_secs_f64()).unwrap_or(0.0)),
4551 RuntimeConfigApplyMode::Immediate,
4552 "Maximum accepted backbone blacklist duration; 0 means no cap.",
4553 )),
4554 "max_connections" => Some(make_entry(
4555 RuntimeConfigValue::Int(current.max_connections.unwrap_or(0) as i64),
4556 RuntimeConfigValue::Int(startup.max_connections.unwrap_or(0) as i64),
4557 RuntimeConfigApplyMode::NewConnectionsOnly,
4558 "Maximum simultaneous inbound backbone connections; 0 disables the cap.",
4559 )),
4560 _ => None,
4561 };
4562 }
4563
4564 None
4565 }
4566
4567 #[cfg(feature = "rns-hooks")]
4568 fn forward_hook_side_effects(&mut self, attach_point: &str, exec: &rns_hooks::ExecuteResult) {
4569 if !exec.injected_actions.is_empty() {
4570 self.dispatch_all(convert_injected_actions(exec.injected_actions.clone()));
4571 }
4572 if let Some(ref bridge) = self.provider_bridge {
4573 for event in &exec.provider_events {
4574 bridge.emit_event(
4575 attach_point,
4576 event.hook_name.clone(),
4577 event.payload_type.clone(),
4578 event.payload.clone(),
4579 );
4580 }
4581 }
4582 }
4583
4584 #[cfg(feature = "rns-hooks")]
4585 fn collect_hook_side_effects(
4586 &mut self,
4587 attach_point: &str,
4588 exec: &rns_hooks::ExecuteResult,
4589 out: &mut Vec<TransportAction>,
4590 ) {
4591 if !exec.injected_actions.is_empty() {
4592 out.extend(convert_injected_actions(exec.injected_actions.clone()));
4593 }
4594 if let Some(ref bridge) = self.provider_bridge {
4595 for event in &exec.provider_events {
4596 bridge.emit_event(
4597 attach_point,
4598 event.hook_name.clone(),
4599 event.payload_type.clone(),
4600 event.payload.clone(),
4601 );
4602 }
4603 }
4604 }
4605
4606 pub fn set_probe_config(
4608 &mut self,
4609 addrs: Vec<std::net::SocketAddr>,
4610 protocol: rns_core::holepunch::ProbeProtocol,
4611 device: Option<String>,
4612 ) {
4613 self.holepunch_manager = HolePunchManager::new(addrs, protocol, device);
4614 }
4615
4616 fn handle_frame_event(&mut self, interface_id: InterfaceId, data: Vec<u8>) {
4617 if data.len() > 2 && (data[0] & 0x03) == 0x01 {
4618 log::debug!(
4619 "Announce:frame from iface {} (len={}, flags=0x{:02x})",
4620 interface_id.0,
4621 data.len(),
4622 data[0]
4623 );
4624 }
4625 if let Some(entry) = self.interfaces.get(&interface_id) {
4626 if !entry.enabled || !entry.online {
4627 return;
4628 }
4629 }
4630 if let Some(entry) = self.interfaces.get_mut(&interface_id) {
4631 entry.stats.rxb += data.len() as u64;
4632 entry.stats.rx_packets += 1;
4633 }
4634
4635 let packet = if let Some(entry) = self.interfaces.get(&interface_id) {
4636 if let Some(ref ifac_state) = entry.ifac {
4637 match ifac::unmask_inbound(&data, ifac_state) {
4638 Some(unmasked) => unmasked,
4639 None => {
4640 log::debug!("[{}] IFAC rejected packet", interface_id.0);
4641 return;
4642 }
4643 }
4644 } else {
4645 if data.len() > 2 && data[0] & 0x80 == 0x80 {
4646 log::debug!(
4647 "[{}] dropping packet with IFAC flag on non-IFAC interface",
4648 interface_id.0
4649 );
4650 return;
4651 }
4652 data
4653 }
4654 } else {
4655 data
4656 };
4657
4658 #[cfg(feature = "rns-hooks")]
4659 {
4660 let pkt_ctx = rns_hooks::PacketContext {
4661 flags: if packet.is_empty() { 0 } else { packet[0] },
4662 hops: if packet.len() > 1 { packet[1] } else { 0 },
4663 destination_hash: extract_dest_hash(&packet),
4664 context: 0,
4665 packet_hash: [0; 32],
4666 interface_id: interface_id.0,
4667 data_offset: 0,
4668 data_len: packet.len() as u32,
4669 };
4670 let ctx = HookContext::Packet {
4671 ctx: &pkt_ctx,
4672 raw: &packet,
4673 };
4674 let now = time::now();
4675 let engine_ref = EngineRef {
4676 engine: &self.engine,
4677 interfaces: &self.interfaces,
4678 link_manager: &self.link_manager,
4679 now,
4680 };
4681 let provider_events_enabled = self.provider_events_enabled();
4682 if let Some(ref e) = run_hook_inner(
4683 &mut self.hook_slots[HookPoint::PreIngress as usize].programs,
4684 &self.hook_manager,
4685 &engine_ref,
4686 &ctx,
4687 now,
4688 provider_events_enabled,
4689 ) {
4690 self.forward_hook_side_effects("PreIngress", e);
4691 if e.hook_result.as_ref().is_some_and(|r| r.is_drop()) {
4692 return;
4693 }
4694 }
4695 }
4696
4697 if packet.len() > 2 && (packet[0] & 0x03) == 0x01 {
4698 let now = time::now();
4699 if let Some(entry) = self.interfaces.get_mut(&interface_id) {
4700 entry.stats.record_incoming_announce(now);
4701 }
4702 }
4703
4704 if let Some(entry) = self.interfaces.get(&interface_id) {
4705 self.engine
4706 .update_interface_freq(interface_id, entry.stats.incoming_announce_freq());
4707 }
4708
4709 let actions = if self.async_announce_verification {
4710 let mut announce_queue = self
4711 .announce_verify_queue
4712 .lock()
4713 .unwrap_or_else(|poisoned| poisoned.into_inner());
4714 self.engine.handle_inbound_with_announce_queue(
4715 &packet,
4716 interface_id,
4717 time::now(),
4718 &mut self.rng,
4719 Some(&mut announce_queue),
4720 )
4721 } else {
4722 self.engine
4723 .handle_inbound(&packet, interface_id, time::now(), &mut self.rng)
4724 };
4725
4726 #[cfg(feature = "rns-hooks")]
4727 {
4728 let pkt_ctx = rns_hooks::PacketContext {
4729 flags: if packet.is_empty() { 0 } else { packet[0] },
4730 hops: if packet.len() > 1 { packet[1] } else { 0 },
4731 destination_hash: extract_dest_hash(&packet),
4732 context: 0,
4733 packet_hash: [0; 32],
4734 interface_id: interface_id.0,
4735 data_offset: 0,
4736 data_len: packet.len() as u32,
4737 };
4738 let ctx = HookContext::Packet {
4739 ctx: &pkt_ctx,
4740 raw: &packet,
4741 };
4742 let now = time::now();
4743 let engine_ref = EngineRef {
4744 engine: &self.engine,
4745 interfaces: &self.interfaces,
4746 link_manager: &self.link_manager,
4747 now,
4748 };
4749 let provider_events_enabled = self.provider_events_enabled();
4750 if let Some(ref e) = run_hook_inner(
4751 &mut self.hook_slots[HookPoint::PreDispatch as usize].programs,
4752 &self.hook_manager,
4753 &engine_ref,
4754 &ctx,
4755 now,
4756 provider_events_enabled,
4757 ) {
4758 self.forward_hook_side_effects("PreDispatch", e);
4759 }
4760 }
4761
4762 self.dispatch_all(actions);
4763 }
4764
4765 fn handle_announce_verified_event(
4766 &mut self,
4767 key: rns_core::transport::announce_verify_queue::AnnounceVerifyKey,
4768 validated: rns_core::announce::ValidatedAnnounce,
4769 sig_cache_key: [u8; 32],
4770 ) {
4771 let pending = {
4772 let mut announce_queue = self
4773 .announce_verify_queue
4774 .lock()
4775 .unwrap_or_else(|poisoned| poisoned.into_inner());
4776 announce_queue.complete_success(&key)
4777 };
4778 if let Some(pending) = pending {
4779 let actions = self.engine.complete_verified_announce(
4780 pending,
4781 validated,
4782 sig_cache_key,
4783 time::now(),
4784 &mut self.rng,
4785 );
4786 self.dispatch_all(actions);
4787 }
4788 }
4789
4790 fn handle_tick_event(&mut self) {
4791 #[cfg(feature = "rns-hooks")]
4792 {
4793 let ctx = HookContext::Tick;
4794 let now = time::now();
4795 let engine_ref = EngineRef {
4796 engine: &self.engine,
4797 interfaces: &self.interfaces,
4798 link_manager: &self.link_manager,
4799 now,
4800 };
4801 let provider_events_enabled = self.provider_events_enabled();
4802 if let Some(ref e) = run_hook_inner(
4803 &mut self.hook_slots[HookPoint::Tick as usize].programs,
4804 &self.hook_manager,
4805 &engine_ref,
4806 &ctx,
4807 now,
4808 provider_events_enabled,
4809 ) {
4810 self.forward_hook_side_effects("Tick", e);
4811 }
4812 }
4813
4814 let now = time::now();
4815 for (id, entry) in &self.interfaces {
4816 self.engine
4817 .update_interface_freq(*id, entry.stats.incoming_announce_freq());
4818 }
4819 let actions = self.engine.tick(now, &mut self.rng);
4820 self.dispatch_all(actions);
4821 let link_actions = self.link_manager.tick(&mut self.rng);
4822 self.dispatch_link_actions(link_actions);
4823 self.enforce_drain_deadline();
4824 {
4825 let tx = self.get_event_sender();
4826 let hp_actions = self.holepunch_manager.tick(&tx);
4827 self.dispatch_holepunch_actions(hp_actions);
4828 }
4829 self.tick_management_announces(now);
4830 self.sent_packets
4831 .retain(|_, (_, sent_time)| now - *sent_time < 60.0);
4832 self.completed_proofs
4833 .retain(|_, (_, received)| now - *received < 120.0);
4834
4835 self.tick_discovery_announcer(now);
4836 #[cfg(feature = "iface-backbone")]
4837 self.maintain_backbone_peer_pool();
4838
4839 self.memory_stats_counter += 1;
4840 if self.memory_stats_counter >= 300 {
4841 self.memory_stats_counter = 0;
4842 self.log_memory_stats();
4843 }
4844
4845 if self.discover_interfaces {
4846 self.discovery_cleanup_counter += 1;
4847 if self.discovery_cleanup_counter >= self.discovery_cleanup_interval_ticks {
4848 self.discovery_cleanup_counter = 0;
4849 if let Ok(removed) = self.discovered_interfaces.cleanup() {
4850 if removed > 0 {
4851 log::info!("Discovery cleanup: removed {} stale entries", removed);
4852 }
4853 }
4854 }
4855 }
4856
4857 self.cache_cleanup_counter += 1;
4858 if self.cache_cleanup_counter >= self.known_destinations_cleanup_interval_ticks {
4859 self.cache_cleanup_counter = 0;
4860
4861 let active_dests = self.engine.active_destination_hashes();
4862 let ttl = self.known_destinations_ttl;
4863 let kd_before = self.known_destinations.len();
4864 self.known_destinations.retain(|k, state| {
4865 active_dests.contains(k)
4866 || self.local_destinations.contains_key(k)
4867 || state.retained
4868 || now - Self::known_destination_relevance_time(state) < ttl
4869 });
4870 let kd_removed = kd_before - self.known_destinations.len();
4871 let kd_evicted = self.enforce_known_destination_cap(false);
4872 let rl_removed =
4873 self.engine
4874 .cull_rate_limiter(&active_dests, now, self.rate_limiter_ttl_secs);
4875
4876 if kd_removed > 0 || kd_evicted > 0 || rl_removed > 0 {
4877 log::info!(
4878 "Memory cleanup: removed {} known_destinations, evicted {} known_destinations, {} rate_limiter entries",
4879 kd_removed, kd_evicted, rl_removed
4880 );
4881 }
4882 }
4883
4884 self.announce_cache_cleanup_counter += 1;
4885 if self.announce_cache_cleanup_counter >= self.announce_cache_cleanup_interval_ticks {
4886 self.announce_cache_cleanup_counter = 0;
4887 if self.announce_cache.is_some() && self.cache_cleanup_active_hashes.is_none() {
4888 self.cache_cleanup_active_hashes = Some(self.engine.active_packet_hashes());
4889 self.cache_cleanup_entries = None;
4890 self.cache_cleanup_removed = 0;
4891 }
4892 }
4893
4894 if self.cache_cleanup_active_hashes.is_some() {
4895 if let Some(ref cache) = self.announce_cache {
4896 if self.cache_cleanup_entries.is_none() {
4897 match cache.entries() {
4898 Ok(entries) => self.cache_cleanup_entries = Some(entries),
4899 Err(e) => {
4900 log::warn!("Announce cache cleanup failed to open directory: {}", e);
4901 self.cache_cleanup_active_hashes = None;
4902 self.cache_cleanup_entries = None;
4903 }
4904 }
4905 }
4906 }
4907
4908 if let Some(ref cache) = self.announce_cache {
4909 let Some(active_hashes) = self.cache_cleanup_active_hashes.as_ref() else {
4910 self.cache_cleanup_entries = None;
4911 return;
4912 };
4913 let entries = match self.cache_cleanup_entries.as_mut() {
4914 Some(entries) => entries,
4915 None => return,
4916 };
4917 match cache.clean_batch(
4918 active_hashes,
4919 entries,
4920 self.announce_cache_cleanup_batch_size,
4921 ) {
4922 Ok((removed, finished)) => {
4923 self.cache_cleanup_removed += removed;
4924 if finished {
4925 if self.cache_cleanup_removed > 0 {
4926 log::info!(
4927 "Announce cache cleanup complete: removed {} stale files",
4928 self.cache_cleanup_removed
4929 );
4930 }
4931 self.cache_cleanup_active_hashes = None;
4932 self.cache_cleanup_entries = None;
4933 }
4934 }
4935 Err(e) => {
4936 log::warn!("Announce cache cleanup failed: {}", e);
4937 self.cache_cleanup_active_hashes = None;
4938 self.cache_cleanup_entries = None;
4939 }
4940 }
4941 } else {
4942 self.cache_cleanup_active_hashes = None;
4943 self.cache_cleanup_entries = None;
4944 }
4945 }
4946 }
4947
4948 fn handle_interface_up_event(
4949 &mut self,
4950 id: InterfaceId,
4951 new_writer: Option<Box<dyn crate::interface::Writer>>,
4952 info: Option<rns_core::transport::types::InterfaceInfo>,
4953 ) {
4954 let wants_tunnel;
4955 let mut replay_shared_announces = false;
4956 if let Some(mut info) = info {
4957 log::info!("[{}] dynamic interface registered", id.0);
4958 wants_tunnel = info.wants_tunnel;
4959 let iface_type = infer_interface_type(&info.name);
4960 info.started = time::now();
4961 self.register_interface_runtime_defaults(&info);
4962 self.engine.register_interface(info.clone());
4963 if let Some(writer) = new_writer {
4964 let (writer, async_writer_metrics) =
4965 self.wrap_interface_writer(id, &info.name, writer);
4966 self.interfaces.insert(
4967 id,
4968 InterfaceEntry {
4969 id,
4970 info,
4971 writer,
4972 async_writer_metrics: Some(async_writer_metrics),
4973 enabled: true,
4974 online: true,
4975 dynamic: true,
4976 ifac: None,
4977 stats: InterfaceStats {
4978 started: time::now(),
4979 ..Default::default()
4980 },
4981 interface_type: iface_type,
4982 send_retry_at: None,
4983 send_retry_backoff: Duration::ZERO,
4984 },
4985 );
4986 }
4987 self.callbacks.on_interface_up(id);
4988 #[cfg(feature = "rns-hooks")]
4989 {
4990 let ctx = HookContext::Interface { interface_id: id.0 };
4991 let now = time::now();
4992 let engine_ref = EngineRef {
4993 engine: &self.engine,
4994 interfaces: &self.interfaces,
4995 link_manager: &self.link_manager,
4996 now,
4997 };
4998 let provider_events_enabled = self.provider_events_enabled();
4999 if let Some(ref e) = run_hook_inner(
5000 &mut self.hook_slots[HookPoint::InterfaceUp as usize].programs,
5001 &self.hook_manager,
5002 &engine_ref,
5003 &ctx,
5004 now,
5005 provider_events_enabled,
5006 ) {
5007 self.forward_hook_side_effects("InterfaceUp", e);
5008 }
5009 }
5010 } else {
5011 let is_local_client = self
5012 .interfaces
5013 .get(&id)
5014 .map(|entry| entry.info.is_local_client)
5015 .unwrap_or(false);
5016 replay_shared_announces =
5017 is_local_client && self.shared_reconnect_pending.remove(&id).unwrap_or(false);
5018 let interface_name = self
5019 .interfaces
5020 .get(&id)
5021 .map(|entry| entry.info.name.clone())
5022 .unwrap_or_else(|| format!("iface-{}", id.0));
5023 let wrapped_writer =
5024 new_writer.map(|writer| self.wrap_interface_writer(id, &interface_name, writer));
5025 if let Some(entry) = self.interfaces.get_mut(&id) {
5026 log::info!("[{}] interface online", id.0);
5027 wants_tunnel = entry.info.wants_tunnel;
5028 entry.online = true;
5029 if let Some((writer, async_writer_metrics)) = wrapped_writer {
5030 log::info!("[{}] writer refreshed after reconnect", id.0);
5031 entry.writer = writer;
5032 entry.async_writer_metrics = Some(async_writer_metrics);
5033 }
5034 self.callbacks.on_interface_up(id);
5035 #[cfg(feature = "rns-hooks")]
5036 {
5037 let ctx = HookContext::Interface { interface_id: id.0 };
5038 let now = time::now();
5039 let engine_ref = EngineRef {
5040 engine: &self.engine,
5041 interfaces: &self.interfaces,
5042 link_manager: &self.link_manager,
5043 now,
5044 };
5045 let provider_events_enabled = self.provider_events_enabled();
5046 if let Some(ref e) = run_hook_inner(
5047 &mut self.hook_slots[HookPoint::InterfaceUp as usize].programs,
5048 &self.hook_manager,
5049 &engine_ref,
5050 &ctx,
5051 now,
5052 provider_events_enabled,
5053 ) {
5054 self.forward_hook_side_effects("InterfaceUp", e);
5055 }
5056 }
5057 } else {
5058 wants_tunnel = false;
5059 }
5060 }
5061
5062 if wants_tunnel {
5063 self.synthesize_tunnel_for_interface(id);
5064 }
5065 if replay_shared_announces {
5066 self.replay_shared_announces();
5067 }
5068 }
5069
5070 fn handle_interface_down_event(&mut self, id: InterfaceId) {
5071 if let Some(entry) = self.interfaces.get(&id) {
5072 if let Some(tunnel_id) = entry.info.tunnel_id {
5073 self.engine.void_tunnel_interface(&tunnel_id);
5074 }
5075 }
5076
5077 if let Some(entry) = self.interfaces.get(&id) {
5078 let is_dynamic = entry.dynamic;
5079 let is_local_client = entry.info.is_local_client;
5080 let interface_name = entry.info.name.clone();
5081 if is_dynamic {
5082 log::info!("[{}] dynamic interface removed", id.0);
5083 self.interface_runtime_defaults.remove(&interface_name);
5084 self.engine.deregister_interface(id);
5085 self.interfaces.remove(&id);
5086 } else {
5087 log::info!("[{}] interface offline", id.0);
5088 if let Some(entry) = self.interfaces.get_mut(&id) {
5089 entry.online = false;
5090 } else {
5091 log::warn!(
5092 "interface {} disappeared while handling interface-down",
5093 id.0
5094 );
5095 return;
5096 }
5097 if is_local_client {
5098 self.handle_shared_interface_down(id);
5099 }
5100 }
5101 self.callbacks.on_interface_down(id);
5102 #[cfg(feature = "rns-hooks")]
5103 {
5104 let ctx = HookContext::Interface { interface_id: id.0 };
5105 let now = time::now();
5106 let engine_ref = EngineRef {
5107 engine: &self.engine,
5108 interfaces: &self.interfaces,
5109 link_manager: &self.link_manager,
5110 now,
5111 };
5112 let provider_events_enabled = self.provider_events_enabled();
5113 if let Some(ref e) = run_hook_inner(
5114 &mut self.hook_slots[HookPoint::InterfaceDown as usize].programs,
5115 &self.hook_manager,
5116 &engine_ref,
5117 &ctx,
5118 now,
5119 provider_events_enabled,
5120 ) {
5121 self.forward_hook_side_effects("InterfaceDown", e);
5122 }
5123 }
5124 }
5125 #[cfg(feature = "iface-backbone")]
5126 self.handle_backbone_peer_pool_down(id);
5127 }
5128
5129 fn known_destination_route_hint(&self, dest_hash: &[u8; 16]) -> Option<(InterfaceId, u8)> {
5130 let announced = &self.known_destinations.get(dest_hash)?.announced;
5131 let iface = announced.receiving_interface;
5132 if iface.0 == 0 {
5133 return None;
5134 }
5135
5136 self.interfaces
5137 .get(&iface)
5138 .filter(|entry| entry.online)
5139 .map(|_| (iface, announced.hops))
5140 }
5141
5142 fn handle_send_outbound_event(
5143 &mut self,
5144 raw: Vec<u8>,
5145 dest_type: u8,
5146 attached_interface: Option<InterfaceId>,
5147 ) {
5148 if self.is_draining() {
5149 self.reject_new_work("send outbound packet");
5150 return;
5151 }
5152 match RawPacket::unpack(&raw) {
5153 Ok(packet) => {
5154 let is_announce =
5155 packet.flags.packet_type == rns_core::constants::PACKET_TYPE_ANNOUNCE;
5156 if is_announce {
5157 log::debug!(
5158 "SendOutbound: ANNOUNCE for {:02x?} (len={}, dest_type={}, attached={:?})",
5159 &packet.destination_hash[..4],
5160 raw.len(),
5161 dest_type,
5162 attached_interface
5163 );
5164 }
5165 if packet.flags.packet_type == rns_core::constants::PACKET_TYPE_DATA {
5166 self.sent_packets
5167 .insert(packet.packet_hash, (packet.destination_hash, time::now()));
5168 }
5169 let actions = self.engine.handle_outbound(
5170 &packet,
5171 dest_type,
5172 attached_interface,
5173 time::now(),
5174 );
5175 if is_announce {
5176 log::debug!(
5177 "SendOutbound: announce routed to {} actions: {:?}",
5178 actions.len(),
5179 actions
5180 .iter()
5181 .map(|a| match a {
5182 TransportAction::SendOnInterface { interface, .. } =>
5183 format!("SendOn({})", interface.0),
5184 TransportAction::BroadcastOnAllInterfaces { .. } =>
5185 "BroadcastAll".to_string(),
5186 _ => "other".to_string(),
5187 })
5188 .collect::<Vec<_>>()
5189 );
5190 }
5191 self.dispatch_all(actions);
5192 }
5193 Err(e) => {
5194 log::warn!("SendOutbound: failed to unpack packet: {:?}", e);
5195 }
5196 }
5197 }
5198
5199 pub fn run(&mut self) {
5201 loop {
5202 let event = match self.rx.recv() {
5203 Ok(e) => e,
5204 Err(_) => break, };
5206
5207 match event {
5208 Event::Frame { interface_id, data } => {
5209 self.handle_frame_event(interface_id, data);
5210 }
5211 Event::AnnounceVerified {
5212 key,
5213 validated,
5214 sig_cache_key,
5215 } => {
5216 self.handle_announce_verified_event(key, validated, sig_cache_key);
5217 }
5218 Event::AnnounceVerifyFailed { key, .. } => {
5219 let mut announce_queue = self
5220 .announce_verify_queue
5221 .lock()
5222 .unwrap_or_else(|poisoned| poisoned.into_inner());
5223 let _ = announce_queue.complete_failure(&key);
5224 }
5225 Event::Tick => self.handle_tick_event(),
5226 Event::BeginDrain { timeout } => {
5227 self.begin_drain(timeout);
5228 }
5229 Event::InterfaceUp(id, new_writer, info) => {
5230 self.handle_interface_up_event(id, new_writer, info);
5231 }
5232 Event::InterfaceDown(id) => self.handle_interface_down_event(id),
5233 Event::SendOutbound {
5234 raw,
5235 dest_type,
5236 attached_interface,
5237 } => self.handle_send_outbound_event(raw, dest_type, attached_interface),
5238 Event::RegisterDestination {
5239 dest_hash,
5240 dest_type,
5241 } => {
5242 self.engine.register_destination(dest_hash, dest_type);
5243 self.local_destinations.insert(dest_hash, dest_type);
5244 }
5245 Event::StoreSharedAnnounce {
5246 dest_hash,
5247 name_hash,
5248 identity_prv_key,
5249 app_data,
5250 } => {
5251 self.shared_announces.insert(
5252 dest_hash,
5253 SharedAnnounceRecord {
5254 name_hash,
5255 identity_prv_key,
5256 app_data,
5257 },
5258 );
5259 }
5260 Event::DeregisterDestination { dest_hash } => {
5261 self.engine.deregister_destination(&dest_hash);
5262 self.local_destinations.remove(&dest_hash);
5263 self.shared_announces.remove(&dest_hash);
5264 }
5265 Event::Query(request, response_tx) => {
5266 let response = self.handle_query_mut(request);
5267 let _ = response_tx.send(response);
5268 }
5269 Event::DeregisterLinkDestination { dest_hash } => {
5270 self.link_manager.deregister_link_destination(&dest_hash);
5271 }
5272 Event::RegisterLinkDestination {
5273 dest_hash,
5274 sig_prv_bytes,
5275 sig_pub_bytes,
5276 resource_strategy,
5277 } => {
5278 let sig_prv =
5279 rns_crypto::ed25519::Ed25519PrivateKey::from_bytes(&sig_prv_bytes);
5280 let strat = match resource_strategy {
5281 1 => crate::link_manager::ResourceStrategy::AcceptAll,
5282 2 => crate::link_manager::ResourceStrategy::AcceptApp,
5283 _ => crate::link_manager::ResourceStrategy::AcceptNone,
5284 };
5285 self.link_manager.register_link_destination(
5286 dest_hash,
5287 sig_prv,
5288 sig_pub_bytes,
5289 strat,
5290 );
5291 self.engine
5293 .register_destination(dest_hash, rns_core::constants::DESTINATION_SINGLE);
5294 self.local_destinations
5295 .insert(dest_hash, rns_core::constants::DESTINATION_SINGLE);
5296 }
5297 Event::RegisterRequestHandler {
5298 path,
5299 allowed_list,
5300 handler,
5301 } => {
5302 self.link_manager.register_request_handler(
5303 &path,
5304 allowed_list,
5305 move |link_id, p, data, remote| handler(link_id, p, data, remote),
5306 );
5307 }
5308 Event::CreateLink {
5309 dest_hash,
5310 dest_sig_pub_bytes,
5311 response_tx,
5312 } => {
5313 if self.is_draining() {
5314 self.reject_new_work("create link");
5315 let _ = (dest_hash, dest_sig_pub_bytes);
5316 let _ = response_tx.send([0u8; 16]);
5317 continue;
5318 }
5319 let next_hop_interface = self.engine.next_hop_interface(&dest_hash);
5320 let recalled_route_hint = if next_hop_interface.is_none() {
5321 self.known_destination_route_hint(&dest_hash)
5322 } else {
5323 None
5324 };
5325 if recalled_route_hint.is_some() {
5326 let _ = self.mark_known_destination_used(&dest_hash);
5327 }
5328 let attached_interface =
5329 next_hop_interface.or(recalled_route_hint.map(|(iface, _)| iface));
5330 let hops = self
5331 .engine
5332 .hops_to(&dest_hash)
5333 .or_else(|| recalled_route_hint.map(|(_, hops)| hops))
5334 .unwrap_or(0);
5335 let mtu = attached_interface
5336 .and_then(|iface_id| self.interfaces.get(&iface_id))
5337 .map(|entry| entry.info.mtu)
5338 .unwrap_or(rns_core::constants::MTU as u32);
5339 let (link_id, mut link_actions) = self.link_manager.create_link(
5340 &dest_hash,
5341 &dest_sig_pub_bytes,
5342 hops,
5343 mtu,
5344 &mut self.rng,
5345 );
5346 if next_hop_interface.is_none() {
5347 if let Some(iface) = attached_interface {
5348 for action in &mut link_actions {
5349 if let LinkManagerAction::SendPacket {
5350 dest_type,
5351 attached_interface,
5352 ..
5353 } = action
5354 {
5355 if *dest_type == rns_core::constants::DESTINATION_LINK
5356 && attached_interface.is_none()
5357 {
5358 *attached_interface = Some(iface);
5359 }
5360 }
5361 }
5362 }
5363 }
5364 let _ = response_tx.send(link_id);
5365 self.dispatch_link_actions(link_actions);
5366 }
5367 Event::SendRequest {
5368 link_id,
5369 path,
5370 data,
5371 } => {
5372 if self.is_draining() {
5373 self.reject_new_work("send link request");
5374 let _ = (link_id, path, data);
5375 continue;
5376 }
5377 let link_actions =
5378 self.link_manager
5379 .send_request(&link_id, &path, &data, &mut self.rng);
5380 self.dispatch_link_actions(link_actions);
5381 }
5382 Event::IdentifyOnLink {
5383 link_id,
5384 identity_prv_key,
5385 } => {
5386 if self.is_draining() {
5387 self.reject_new_work("identify on link");
5388 let _ = (link_id, identity_prv_key);
5389 continue;
5390 }
5391 let identity =
5392 rns_crypto::identity::Identity::from_private_key(&identity_prv_key);
5393 let link_actions =
5394 self.link_manager
5395 .identify(&link_id, &identity, &mut self.rng);
5396 self.dispatch_link_actions(link_actions);
5397 }
5398 Event::TeardownLink { link_id } => {
5399 let link_actions = self.link_manager.teardown_link(&link_id);
5400 self.dispatch_link_actions(link_actions);
5401 }
5402 Event::SendResource {
5403 link_id,
5404 data,
5405 metadata,
5406 } => {
5407 if self.is_draining() {
5408 self.reject_new_work("send resource");
5409 let _ = (link_id, data, metadata);
5410 continue;
5411 }
5412 let link_actions = self.link_manager.send_resource(
5413 &link_id,
5414 &data,
5415 metadata.as_deref(),
5416 &mut self.rng,
5417 );
5418 self.dispatch_link_actions(link_actions);
5419 }
5420 Event::SetResourceStrategy { link_id, strategy } => {
5421 use crate::link_manager::ResourceStrategy;
5422 let strat = match strategy {
5423 0 => ResourceStrategy::AcceptNone,
5424 1 => ResourceStrategy::AcceptAll,
5425 2 => ResourceStrategy::AcceptApp,
5426 _ => ResourceStrategy::AcceptNone,
5427 };
5428 self.link_manager.set_resource_strategy(&link_id, strat);
5429 }
5430 Event::AcceptResource {
5431 link_id,
5432 resource_hash,
5433 accept,
5434 } => {
5435 if self.is_draining() && accept {
5436 self.reject_new_work("accept resource");
5437 let _ = (link_id, resource_hash, accept);
5438 continue;
5439 }
5440 let link_actions = self.link_manager.accept_resource(
5441 &link_id,
5442 &resource_hash,
5443 accept,
5444 &mut self.rng,
5445 );
5446 self.dispatch_link_actions(link_actions);
5447 }
5448 Event::SendChannelMessage {
5449 link_id,
5450 msgtype,
5451 payload,
5452 response_tx,
5453 } => {
5454 if self.is_draining() {
5455 self.reject_new_work("send channel message");
5456 let _ = response_tx.send(Err(self.drain_error("send channel message")));
5457 continue;
5458 }
5459 match self.link_manager.send_channel_message(
5460 &link_id,
5461 msgtype,
5462 &payload,
5463 &mut self.rng,
5464 ) {
5465 Ok(link_actions) => {
5466 self.dispatch_link_actions(link_actions);
5467 let _ = response_tx.send(Ok(()));
5468 }
5469 Err(err) => {
5470 let _ = response_tx.send(Err(err));
5471 }
5472 }
5473 }
5474 Event::SendOnLink {
5475 link_id,
5476 data,
5477 context,
5478 } => {
5479 if self.is_draining() {
5480 self.reject_new_work("send link payload");
5481 let _ = (link_id, data, context);
5482 continue;
5483 }
5484 let link_actions =
5485 self.link_manager
5486 .send_on_link(&link_id, &data, context, &mut self.rng);
5487 self.dispatch_link_actions(link_actions);
5488 }
5489 Event::RequestPath { dest_hash } => {
5490 if self.is_draining() {
5491 self.reject_new_work("request path");
5492 let _ = dest_hash;
5493 continue;
5494 }
5495 self.handle_request_path(dest_hash);
5496 }
5497 Event::RegisterProofStrategy {
5498 dest_hash,
5499 strategy,
5500 signing_key,
5501 } => {
5502 let identity = signing_key
5503 .map(|key| rns_crypto::identity::Identity::from_private_key(&key));
5504 self.proof_strategies
5505 .insert(dest_hash, (strategy, identity));
5506 }
5507 Event::ProposeDirectConnect { link_id } => {
5508 if self.is_draining() {
5509 self.reject_new_work("propose direct connect");
5510 let _ = link_id;
5511 continue;
5512 }
5513 let derived_key = self.link_manager.get_derived_key(&link_id);
5514 if let Some(dk) = derived_key {
5515 let tx = self.get_event_sender();
5516 let hp_actions =
5517 self.holepunch_manager
5518 .propose(link_id, &dk, &mut self.rng, &tx);
5519 self.dispatch_holepunch_actions(hp_actions);
5520 } else {
5521 log::warn!(
5522 "Cannot propose direct connect: no derived key for link {:02x?}",
5523 &link_id[..4]
5524 );
5525 }
5526 }
5527 Event::SetDirectConnectPolicy { policy } => {
5528 self.holepunch_manager.set_policy(policy);
5529 }
5530 Event::HolePunchProbeResult {
5531 link_id,
5532 session_id,
5533 observed_addr,
5534 socket,
5535 probe_server,
5536 } => {
5537 let hp_actions = self.holepunch_manager.handle_probe_result(
5538 link_id,
5539 session_id,
5540 observed_addr,
5541 socket,
5542 probe_server,
5543 );
5544 self.dispatch_holepunch_actions(hp_actions);
5545 }
5546 Event::HolePunchProbeFailed {
5547 link_id,
5548 session_id,
5549 } => {
5550 let hp_actions = self
5551 .holepunch_manager
5552 .handle_probe_failed(link_id, session_id);
5553 self.dispatch_holepunch_actions(hp_actions);
5554 }
5555 Event::LoadHook {
5556 name,
5557 wasm_bytes,
5558 attach_point,
5559 priority,
5560 response_tx,
5561 } => {
5562 #[cfg(feature = "rns-hooks")]
5563 {
5564 let result = (|| -> Result<(), String> {
5565 let point_idx = crate::config::parse_hook_point(&attach_point)
5566 .ok_or_else(|| format!("unknown hook point '{}'", attach_point))?;
5567 let mgr = self
5568 .hook_manager
5569 .as_ref()
5570 .ok_or_else(|| "hook manager not available".to_string())?;
5571 let program = mgr
5572 .compile(name.clone(), &wasm_bytes, priority)
5573 .map_err(|e| format!("compile error: {}", e))?;
5574 self.hook_slots[point_idx].attach(program);
5575 log::info!(
5576 "Loaded hook '{}' at point {} (priority {})",
5577 name,
5578 attach_point,
5579 priority
5580 );
5581 Ok(())
5582 })();
5583 let _ = response_tx.send(result);
5584 }
5585 #[cfg(not(feature = "rns-hooks"))]
5586 {
5587 let _ = (name, wasm_bytes, attach_point, priority);
5588 let _ = response_tx.send(Err("hooks not enabled".to_string()));
5589 }
5590 }
5591 Event::UnloadHook {
5592 name,
5593 attach_point,
5594 response_tx,
5595 } => {
5596 #[cfg(feature = "rns-hooks")]
5597 {
5598 let result = (|| -> Result<(), String> {
5599 let point_idx = crate::config::parse_hook_point(&attach_point)
5600 .ok_or_else(|| format!("unknown hook point '{}'", attach_point))?;
5601 match self.hook_slots[point_idx].detach(&name) {
5602 Some(_) => {
5603 log::info!(
5604 "Unloaded hook '{}' from point {}",
5605 name,
5606 attach_point
5607 );
5608 Ok(())
5609 }
5610 None => Err(format!(
5611 "hook '{}' not found at point '{}'",
5612 name, attach_point
5613 )),
5614 }
5615 })();
5616 let _ = response_tx.send(result);
5617 }
5618 #[cfg(not(feature = "rns-hooks"))]
5619 {
5620 let _ = (name, attach_point);
5621 let _ = response_tx.send(Err("hooks not enabled".to_string()));
5622 }
5623 }
5624 Event::ReloadHook {
5625 name,
5626 attach_point,
5627 wasm_bytes,
5628 response_tx,
5629 } => {
5630 #[cfg(feature = "rns-hooks")]
5631 {
5632 let result = (|| -> Result<(), String> {
5633 let point_idx = crate::config::parse_hook_point(&attach_point)
5634 .ok_or_else(|| format!("unknown hook point '{}'", attach_point))?;
5635 let old =
5636 self.hook_slots[point_idx].detach(&name).ok_or_else(|| {
5637 format!("hook '{}' not found at point '{}'", name, attach_point)
5638 })?;
5639 let priority = old.priority;
5640 let mgr = match self.hook_manager.as_ref() {
5641 Some(m) => m,
5642 None => {
5643 self.hook_slots[point_idx].attach(old);
5644 return Err("hook manager not available".to_string());
5645 }
5646 };
5647 match mgr.compile(name.clone(), &wasm_bytes, priority) {
5648 Ok(program) => {
5649 self.hook_slots[point_idx].attach(program);
5650 log::info!(
5651 "Reloaded hook '{}' at point {} (priority {})",
5652 name,
5653 attach_point,
5654 priority
5655 );
5656 Ok(())
5657 }
5658 Err(e) => {
5659 self.hook_slots[point_idx].attach(old);
5660 Err(format!("compile error: {}", e))
5661 }
5662 }
5663 })();
5664 let _ = response_tx.send(result);
5665 }
5666 #[cfg(not(feature = "rns-hooks"))]
5667 {
5668 let _ = (name, attach_point, wasm_bytes);
5669 let _ = response_tx.send(Err("hooks not enabled".to_string()));
5670 }
5671 }
5672 Event::SetHookEnabled {
5673 name,
5674 attach_point,
5675 enabled,
5676 response_tx,
5677 } => {
5678 #[cfg(feature = "rns-hooks")]
5679 {
5680 let result = self.update_hook_program(&name, &attach_point, |program| {
5681 program.enabled = enabled;
5682 });
5683 if result.is_ok() {
5684 log::info!(
5685 "{} hook '{}' at point {}",
5686 if enabled { "Enabled" } else { "Disabled" },
5687 name,
5688 attach_point,
5689 );
5690 }
5691 let _ = response_tx.send(result);
5692 }
5693 #[cfg(not(feature = "rns-hooks"))]
5694 {
5695 let _ = (name, attach_point, enabled);
5696 let _ = response_tx.send(Err("hooks not enabled".to_string()));
5697 }
5698 }
5699 Event::SetHookPriority {
5700 name,
5701 attach_point,
5702 priority,
5703 response_tx,
5704 } => {
5705 #[cfg(feature = "rns-hooks")]
5706 {
5707 let result = self.update_hook_program(&name, &attach_point, |program| {
5708 program.priority = priority;
5709 });
5710 if result.is_ok() {
5711 if let Some(point_idx) = crate::config::parse_hook_point(&attach_point)
5712 {
5713 self.hook_slots[point_idx]
5714 .programs
5715 .sort_by(|a, b| b.priority.cmp(&a.priority));
5716 log::info!(
5717 "Updated hook '{}' at point {} to priority {}",
5718 name,
5719 attach_point,
5720 priority,
5721 );
5722 } else {
5723 log::error!(
5724 "hook point '{}' became invalid during priority update",
5725 attach_point
5726 );
5727 }
5728 }
5729 let _ = response_tx.send(result);
5730 }
5731 #[cfg(not(feature = "rns-hooks"))]
5732 {
5733 let _ = (name, attach_point, priority);
5734 let _ = response_tx.send(Err("hooks not enabled".to_string()));
5735 }
5736 }
5737 Event::ListHooks { response_tx } => {
5738 #[cfg(feature = "rns-hooks")]
5739 {
5740 let hook_point_names = [
5741 "PreIngress",
5742 "PreDispatch",
5743 "AnnounceReceived",
5744 "PathUpdated",
5745 "AnnounceRetransmit",
5746 "LinkRequestReceived",
5747 "LinkEstablished",
5748 "LinkClosed",
5749 "InterfaceUp",
5750 "InterfaceDown",
5751 "InterfaceConfigChanged",
5752 "BackbonePeerConnected",
5753 "BackbonePeerDisconnected",
5754 "BackbonePeerIdleTimeout",
5755 "BackbonePeerWriteStall",
5756 "BackbonePeerPenalty",
5757 "SendOnInterface",
5758 "BroadcastOnAllInterfaces",
5759 "DeliverLocal",
5760 "TunnelSynthesize",
5761 "Tick",
5762 ];
5763 let mut infos = Vec::new();
5764 for (idx, slot) in self.hook_slots.iter().enumerate() {
5765 let point_name = hook_point_names.get(idx).unwrap_or(&"Unknown");
5766 for prog in &slot.programs {
5767 infos.push(crate::event::HookInfo {
5768 name: prog.name.clone(),
5769 attach_point: point_name.to_string(),
5770 priority: prog.priority,
5771 enabled: prog.enabled,
5772 consecutive_traps: prog.consecutive_traps,
5773 });
5774 }
5775 }
5776 let _ = response_tx.send(infos);
5777 }
5778 #[cfg(not(feature = "rns-hooks"))]
5779 {
5780 let _ = response_tx.send(Vec::new());
5781 }
5782 }
5783 Event::InterfaceConfigChanged(id) => {
5784 #[cfg(feature = "rns-hooks")]
5785 {
5786 let ctx = HookContext::Interface { interface_id: id.0 };
5787 let now = time::now();
5788 let engine_ref = EngineRef {
5789 engine: &self.engine,
5790 interfaces: &self.interfaces,
5791 link_manager: &self.link_manager,
5792 now,
5793 };
5794 let provider_events_enabled = self.provider_events_enabled();
5795 if let Some(ref e) = run_hook_inner(
5796 &mut self.hook_slots[HookPoint::InterfaceConfigChanged as usize]
5797 .programs,
5798 &self.hook_manager,
5799 &engine_ref,
5800 &ctx,
5801 now,
5802 provider_events_enabled,
5803 ) {
5804 self.forward_hook_side_effects("InterfaceConfigChanged", e);
5805 }
5806 }
5807 #[cfg(not(feature = "rns-hooks"))]
5808 let _ = id;
5809 }
5810 Event::BackbonePeerConnected {
5811 server_interface_id,
5812 peer_interface_id,
5813 peer_ip,
5814 peer_port,
5815 } => {
5816 #[cfg(feature = "rns-hooks")]
5817 {
5818 self.run_backbone_peer_hook(
5819 "BackbonePeerConnected",
5820 HookPoint::BackbonePeerConnected,
5821 &BackbonePeerHookEvent {
5822 server_interface_id,
5823 peer_interface_id: Some(peer_interface_id),
5824 peer_ip,
5825 peer_port,
5826 connected_for: Duration::ZERO,
5827 had_received_data: false,
5828 penalty_level: 0,
5829 blacklist_for: Duration::ZERO,
5830 },
5831 );
5832 }
5833 #[cfg(not(feature = "rns-hooks"))]
5834 let _ = (server_interface_id, peer_interface_id, peer_ip, peer_port);
5835 }
5836 Event::BackbonePeerDisconnected {
5837 server_interface_id,
5838 peer_interface_id,
5839 peer_ip,
5840 peer_port,
5841 connected_for,
5842 had_received_data,
5843 } => {
5844 #[cfg(feature = "rns-hooks")]
5845 {
5846 self.run_backbone_peer_hook(
5847 "BackbonePeerDisconnected",
5848 HookPoint::BackbonePeerDisconnected,
5849 &BackbonePeerHookEvent {
5850 server_interface_id,
5851 peer_interface_id: Some(peer_interface_id),
5852 peer_ip,
5853 peer_port,
5854 connected_for,
5855 had_received_data,
5856 penalty_level: 0,
5857 blacklist_for: Duration::ZERO,
5858 },
5859 );
5860 }
5861 #[cfg(not(feature = "rns-hooks"))]
5862 let _ = (
5863 server_interface_id,
5864 peer_interface_id,
5865 peer_ip,
5866 peer_port,
5867 connected_for,
5868 had_received_data,
5869 );
5870 }
5871 Event::BackbonePeerIdleTimeout {
5872 server_interface_id,
5873 peer_interface_id,
5874 peer_ip,
5875 peer_port,
5876 connected_for,
5877 } => {
5878 #[cfg(feature = "rns-hooks")]
5879 {
5880 self.run_backbone_peer_hook(
5881 "BackbonePeerIdleTimeout",
5882 HookPoint::BackbonePeerIdleTimeout,
5883 &BackbonePeerHookEvent {
5884 server_interface_id,
5885 peer_interface_id: Some(peer_interface_id),
5886 peer_ip,
5887 peer_port,
5888 connected_for,
5889 had_received_data: false,
5890 penalty_level: 0,
5891 blacklist_for: Duration::ZERO,
5892 },
5893 );
5894 }
5895 #[cfg(not(feature = "rns-hooks"))]
5896 let _ = (
5897 server_interface_id,
5898 peer_interface_id,
5899 peer_ip,
5900 peer_port,
5901 connected_for,
5902 );
5903 }
5904 Event::BackbonePeerWriteStall {
5905 server_interface_id,
5906 peer_interface_id,
5907 peer_ip,
5908 peer_port,
5909 connected_for,
5910 } => {
5911 #[cfg(feature = "rns-hooks")]
5912 {
5913 self.run_backbone_peer_hook(
5914 "BackbonePeerWriteStall",
5915 HookPoint::BackbonePeerWriteStall,
5916 &BackbonePeerHookEvent {
5917 server_interface_id,
5918 peer_interface_id: Some(peer_interface_id),
5919 peer_ip,
5920 peer_port,
5921 connected_for,
5922 had_received_data: false,
5923 penalty_level: 0,
5924 blacklist_for: Duration::ZERO,
5925 },
5926 );
5927 }
5928 #[cfg(not(feature = "rns-hooks"))]
5929 let _ = (
5930 server_interface_id,
5931 peer_interface_id,
5932 peer_ip,
5933 peer_port,
5934 connected_for,
5935 );
5936 }
5937 Event::BackbonePeerPenalty {
5938 server_interface_id,
5939 peer_ip,
5940 penalty_level,
5941 blacklist_for,
5942 } => {
5943 #[cfg(feature = "rns-hooks")]
5944 {
5945 self.run_backbone_peer_hook(
5946 "BackbonePeerPenalty",
5947 HookPoint::BackbonePeerPenalty,
5948 &BackbonePeerHookEvent {
5949 server_interface_id,
5950 peer_interface_id: None,
5951 peer_ip,
5952 peer_port: 0,
5953 connected_for: Duration::ZERO,
5954 had_received_data: false,
5955 penalty_level,
5956 blacklist_for,
5957 },
5958 );
5959 }
5960 #[cfg(not(feature = "rns-hooks"))]
5961 let _ = (server_interface_id, peer_ip, penalty_level, blacklist_for);
5962 }
5963 Event::Shutdown => {
5964 self.lifecycle_state = LifecycleState::Stopped;
5965 break;
5966 }
5967 }
5968 }
5969 }
5970
5971 fn handle_interface_stats_query(&self) -> QueryResponse {
5972 let mut interfaces = Vec::new();
5973 let mut total_rxb: u64 = 0;
5974 let mut total_txb: u64 = 0;
5975 for entry in self.interfaces.values() {
5976 total_rxb += entry.stats.rxb;
5977 total_txb += entry.stats.txb;
5978 interfaces.push(SingleInterfaceStat {
5979 id: entry.info.id.0,
5980 name: entry.info.name.clone(),
5981 status: entry.online && entry.enabled,
5982 mode: entry.info.mode,
5983 rxb: entry.stats.rxb,
5984 txb: entry.stats.txb,
5985 rx_packets: entry.stats.rx_packets,
5986 tx_packets: entry.stats.tx_packets,
5987 bitrate: entry.info.bitrate,
5988 ifac_size: entry.ifac.as_ref().map(|s| s.size),
5989 started: entry.stats.started,
5990 ia_freq: entry.stats.incoming_announce_freq(),
5991 oa_freq: entry.stats.outgoing_announce_freq(),
5992 interface_type: entry.interface_type.clone(),
5993 });
5994 }
5995 interfaces.sort_by(|a, b| a.name.cmp(&b.name));
5996 QueryResponse::InterfaceStats(InterfaceStatsResponse {
5997 interfaces,
5998 transport_id: self.engine.identity_hash().copied(),
5999 transport_enabled: self.engine.transport_enabled(),
6000 transport_uptime: time::now() - self.started,
6001 total_rxb,
6002 total_txb,
6003 probe_responder: self.probe_responder_hash,
6004 #[cfg(feature = "iface-backbone")]
6005 backbone_peer_pool: self.backbone_peer_pool_status(),
6006 #[cfg(not(feature = "iface-backbone"))]
6007 backbone_peer_pool: None,
6008 })
6009 }
6010
6011 fn handle_path_table_query(&self, max_hops: Option<u8>) -> QueryResponse {
6012 let entries: Vec<PathTableEntry> = self
6013 .engine
6014 .path_table_entries()
6015 .filter(|(_, entry)| max_hops.is_none_or(|max| entry.hops <= max))
6016 .map(|(hash, entry)| {
6017 let iface_name = self
6018 .interfaces
6019 .get(&entry.receiving_interface)
6020 .map(|e| e.info.name.clone())
6021 .or_else(|| {
6022 self.engine
6023 .interface_info(&entry.receiving_interface)
6024 .map(|i| i.name.clone())
6025 })
6026 .unwrap_or_default();
6027 PathTableEntry {
6028 hash: *hash,
6029 timestamp: entry.timestamp,
6030 via: entry.next_hop,
6031 hops: entry.hops,
6032 expires: entry.expires,
6033 interface: entry.receiving_interface,
6034 interface_name: iface_name,
6035 }
6036 })
6037 .collect();
6038 QueryResponse::PathTable(entries)
6039 }
6040
6041 fn handle_runtime_config_query(&self, request: QueryRequest) -> Option<QueryResponse> {
6042 match request {
6043 QueryRequest::ListRuntimeConfig => {
6044 Some(QueryResponse::RuntimeConfigList(self.list_runtime_config()))
6045 }
6046 QueryRequest::GetRuntimeConfig { key } => Some(QueryResponse::RuntimeConfigEntry(
6047 self.runtime_config_entry(&key),
6048 )),
6049 QueryRequest::BackbonePeerState { interface_name } => {
6050 Some(QueryResponse::BackbonePeerState(
6051 self.list_backbone_peer_state(interface_name.as_deref()),
6052 ))
6053 }
6054 QueryRequest::SetRuntimeConfig { .. } => {
6055 Some(QueryResponse::RuntimeConfigSet(Err(RuntimeConfigError {
6056 code: RuntimeConfigErrorCode::Unsupported,
6057 message: "mutating runtime config is handled separately".to_string(),
6058 })))
6059 }
6060 QueryRequest::ResetRuntimeConfig { .. } => {
6061 Some(QueryResponse::RuntimeConfigReset(Err(RuntimeConfigError {
6062 code: RuntimeConfigErrorCode::Unsupported,
6063 message: "mutating runtime config is handled separately".to_string(),
6064 })))
6065 }
6066 QueryRequest::ClearBackbonePeerState { .. } => {
6067 Some(QueryResponse::ClearBackbonePeerState(false))
6068 }
6069 QueryRequest::BlacklistBackbonePeer { .. } => {
6070 Some(QueryResponse::BlacklistBackbonePeer(false))
6071 }
6072 _ => None,
6073 }
6074 }
6075
6076 fn handle_mutation_query(&mut self, request: QueryRequest) -> Option<QueryResponse> {
6077 match request {
6078 QueryRequest::BlackholeIdentity {
6079 identity_hash,
6080 duration_hours,
6081 reason,
6082 } => {
6083 let now = time::now();
6084 self.engine
6085 .blackhole_identity(identity_hash, now, duration_hours, reason);
6086 Some(QueryResponse::BlackholeResult(true))
6087 }
6088 QueryRequest::UnblackholeIdentity { identity_hash } => Some(
6089 QueryResponse::UnblackholeResult(self.engine.unblackhole_identity(&identity_hash)),
6090 ),
6091 QueryRequest::DropPath { dest_hash } => {
6092 Some(QueryResponse::DropPath(self.engine.drop_path(&dest_hash)))
6093 }
6094 QueryRequest::DropAllVia { transport_hash } => Some(QueryResponse::DropAllVia(
6095 self.engine.drop_all_via(&transport_hash),
6096 )),
6097 QueryRequest::DropAnnounceQueues => {
6098 self.engine.drop_announce_queues();
6099 Some(QueryResponse::DropAnnounceQueues)
6100 }
6101 QueryRequest::ClearBackbonePeerState {
6102 interface_name,
6103 peer_ip,
6104 } => Some(QueryResponse::ClearBackbonePeerState(
6105 self.clear_backbone_peer_state(&interface_name, peer_ip),
6106 )),
6107 QueryRequest::BlacklistBackbonePeer {
6108 interface_name,
6109 peer_ip,
6110 duration,
6111 reason,
6112 penalty_level,
6113 } => Some(QueryResponse::BlacklistBackbonePeer(
6114 self.blacklist_backbone_peer(
6115 &interface_name,
6116 peer_ip,
6117 duration,
6118 reason,
6119 penalty_level,
6120 ),
6121 )),
6122 QueryRequest::InjectPath {
6123 dest_hash,
6124 next_hop,
6125 hops,
6126 expires,
6127 interface_name,
6128 packet_hash,
6129 } => {
6130 let iface_id = self
6131 .interfaces
6132 .iter()
6133 .find(|(_, entry)| entry.info.name == interface_name)
6134 .map(|(id, _)| *id);
6135 Some(match iface_id {
6136 Some(id) => {
6137 let entry = PathEntry {
6138 timestamp: time::now(),
6139 next_hop,
6140 hops,
6141 expires,
6142 random_blobs: Vec::new(),
6143 receiving_interface: id,
6144 packet_hash,
6145 announce_raw: None,
6146 };
6147 self.engine.inject_path(dest_hash, entry);
6148 QueryResponse::InjectPath(true)
6149 }
6150 None => QueryResponse::InjectPath(false),
6151 })
6152 }
6153 QueryRequest::InjectIdentity {
6154 dest_hash,
6155 identity_hash,
6156 public_key,
6157 app_data,
6158 hops,
6159 received_at,
6160 } => {
6161 self.upsert_known_destination(
6162 dest_hash,
6163 crate::destination::AnnouncedIdentity {
6164 dest_hash: rns_core::types::DestHash(dest_hash),
6165 identity_hash: rns_core::types::IdentityHash(identity_hash),
6166 public_key,
6167 app_data,
6168 hops,
6169 received_at,
6170 receiving_interface: rns_core::transport::types::InterfaceId(0),
6171 },
6172 );
6173 Some(QueryResponse::InjectIdentity(true))
6174 }
6175 QueryRequest::RestoreKnownDestination(entry) => {
6176 self.known_destinations.insert(
6177 entry.dest_hash,
6178 KnownDestinationState {
6179 announced: crate::destination::AnnouncedIdentity {
6180 dest_hash: rns_core::types::DestHash(entry.dest_hash),
6181 identity_hash: rns_core::types::IdentityHash(entry.identity_hash),
6182 public_key: entry.public_key,
6183 app_data: entry.app_data,
6184 hops: entry.hops,
6185 received_at: entry.received_at,
6186 receiving_interface: entry.receiving_interface,
6187 },
6188 was_used: entry.was_used,
6189 last_used_at: entry.last_used_at,
6190 retained: entry.retained,
6191 },
6192 );
6193 Some(QueryResponse::RestoreKnownDestination(true))
6194 }
6195 QueryRequest::RetainKnownDestination { dest_hash } => Some(
6196 QueryResponse::RetainKnownDestination(self.retain_known_destination(&dest_hash)),
6197 ),
6198 QueryRequest::UnretainKnownDestination { dest_hash } => {
6199 Some(QueryResponse::UnretainKnownDestination(
6200 self.unretain_known_destination(&dest_hash),
6201 ))
6202 }
6203 QueryRequest::MarkKnownDestinationUsed { dest_hash } => {
6204 Some(QueryResponse::MarkKnownDestinationUsed(
6205 self.mark_known_destination_used(&dest_hash),
6206 ))
6207 }
6208 _ => None,
6209 }
6210 }
6211
6212 fn runtime_config_query_fallback(request: &QueryRequest) -> QueryResponse {
6213 match request {
6214 QueryRequest::ListRuntimeConfig => QueryResponse::RuntimeConfigList(Vec::new()),
6215 QueryRequest::GetRuntimeConfig { .. } => QueryResponse::RuntimeConfigEntry(None),
6216 QueryRequest::BackbonePeerState { .. } => QueryResponse::BackbonePeerState(Vec::new()),
6217 QueryRequest::SetRuntimeConfig { key, .. } => {
6218 QueryResponse::RuntimeConfigSet(Err(RuntimeConfigError {
6219 code: RuntimeConfigErrorCode::ApplyFailed,
6220 message: format!(
6221 "internal error: no response generated for runtime-config set '{}'",
6222 key
6223 ),
6224 }))
6225 }
6226 QueryRequest::ResetRuntimeConfig { key } => {
6227 QueryResponse::RuntimeConfigReset(Err(RuntimeConfigError {
6228 code: RuntimeConfigErrorCode::ApplyFailed,
6229 message: format!(
6230 "internal error: no response generated for runtime-config reset '{}'",
6231 key
6232 ),
6233 }))
6234 }
6235 QueryRequest::ClearBackbonePeerState { .. } => {
6236 QueryResponse::ClearBackbonePeerState(false)
6237 }
6238 QueryRequest::BlacklistBackbonePeer { .. } => {
6239 QueryResponse::BlacklistBackbonePeer(false)
6240 }
6241 _ => QueryResponse::RuntimeConfigEntry(None),
6242 }
6243 }
6244
6245 fn mutation_query_fallback(request: &QueryRequest) -> QueryResponse {
6246 match request {
6247 QueryRequest::BlackholeIdentity { .. } => QueryResponse::BlackholeResult(false),
6248 QueryRequest::UnblackholeIdentity { .. } => QueryResponse::UnblackholeResult(false),
6249 QueryRequest::DropPath { .. } => QueryResponse::DropPath(false),
6250 QueryRequest::DropAllVia { .. } => QueryResponse::DropAllVia(0),
6251 QueryRequest::DropAnnounceQueues => QueryResponse::DropAnnounceQueues,
6252 QueryRequest::ClearBackbonePeerState { .. } => {
6253 QueryResponse::ClearBackbonePeerState(false)
6254 }
6255 QueryRequest::BlacklistBackbonePeer { .. } => {
6256 QueryResponse::BlacklistBackbonePeer(false)
6257 }
6258 QueryRequest::InjectPath { .. } => QueryResponse::InjectPath(false),
6259 QueryRequest::InjectIdentity { .. } => QueryResponse::InjectIdentity(false),
6260 QueryRequest::RestoreKnownDestination(..) => {
6261 QueryResponse::RestoreKnownDestination(false)
6262 }
6263 QueryRequest::RetainKnownDestination { .. } => {
6264 QueryResponse::RetainKnownDestination(false)
6265 }
6266 QueryRequest::UnretainKnownDestination { .. } => {
6267 QueryResponse::UnretainKnownDestination(false)
6268 }
6269 QueryRequest::MarkKnownDestinationUsed { .. } => {
6270 QueryResponse::MarkKnownDestinationUsed(false)
6271 }
6272 _ => QueryResponse::InjectIdentity(false),
6273 }
6274 }
6275
6276 fn handle_query(&self, request: QueryRequest) -> QueryResponse {
6278 match request {
6279 QueryRequest::InterfaceStats => self.handle_interface_stats_query(),
6280 QueryRequest::BackboneInterfaces => {
6281 QueryResponse::BackboneInterfaces(self.list_backbone_interfaces())
6282 }
6283 QueryRequest::ProviderBridgeStats => {
6284 #[cfg(feature = "rns-hooks")]
6285 {
6286 QueryResponse::ProviderBridgeStats(
6287 self.provider_bridge.as_ref().map(|bridge| bridge.stats()),
6288 )
6289 }
6290 #[cfg(not(feature = "rns-hooks"))]
6291 {
6292 QueryResponse::ProviderBridgeStats(None::<crate::event::ProviderBridgeStats>)
6293 }
6294 }
6295 QueryRequest::DrainStatus => QueryResponse::DrainStatus(self.drain_status()),
6296 QueryRequest::PathTable { max_hops } => self.handle_path_table_query(max_hops),
6297 QueryRequest::RateTable => {
6298 let entries: Vec<RateTableEntry> = self
6299 .engine
6300 .rate_limiter()
6301 .entries()
6302 .map(|(hash, entry)| RateTableEntry {
6303 hash: *hash,
6304 last: entry.last,
6305 rate_violations: entry.rate_violations,
6306 blocked_until: entry.blocked_until,
6307 timestamps: entry.timestamps.clone(),
6308 })
6309 .collect();
6310 QueryResponse::RateTable(entries)
6311 }
6312 QueryRequest::NextHop { dest_hash } => {
6313 let resp = self
6314 .engine
6315 .next_hop(&dest_hash)
6316 .map(|next_hop| NextHopResponse {
6317 next_hop,
6318 hops: self.engine.hops_to(&dest_hash).unwrap_or(0),
6319 interface: self
6320 .engine
6321 .next_hop_interface(&dest_hash)
6322 .unwrap_or(InterfaceId(0)),
6323 });
6324 QueryResponse::NextHop(resp)
6325 }
6326 QueryRequest::NextHopIfName { dest_hash } => {
6327 let name = self
6328 .engine
6329 .next_hop_interface(&dest_hash)
6330 .and_then(|id| self.interfaces.get(&id))
6331 .map(|entry| entry.info.name.clone());
6332 QueryResponse::NextHopIfName(name)
6333 }
6334 QueryRequest::LinkCount => QueryResponse::LinkCount(
6335 self.engine.link_table_count() + self.link_manager.link_count(),
6336 ),
6337 QueryRequest::DropPath { .. } => {
6338 QueryResponse::DropPath(false)
6340 }
6341 QueryRequest::DropAllVia { .. } => QueryResponse::DropAllVia(0),
6342 QueryRequest::DropAnnounceQueues => QueryResponse::DropAnnounceQueues,
6343 QueryRequest::TransportIdentity => {
6344 QueryResponse::TransportIdentity(self.engine.identity_hash().copied())
6345 }
6346 QueryRequest::GetBlackholed => {
6347 let now = time::now();
6348 let entries: Vec<BlackholeInfo> = self
6349 .engine
6350 .blackholed_entries()
6351 .filter(|(_, e)| e.expires == 0.0 || e.expires > now)
6352 .map(|(hash, entry)| BlackholeInfo {
6353 identity_hash: *hash,
6354 created: entry.created,
6355 expires: entry.expires,
6356 reason: entry.reason.clone(),
6357 })
6358 .collect();
6359 QueryResponse::Blackholed(entries)
6360 }
6361 QueryRequest::BlackholeIdentity { .. } | QueryRequest::UnblackholeIdentity { .. } => {
6362 QueryResponse::BlackholeResult(false)
6364 }
6365 QueryRequest::InjectPath { .. } => {
6366 QueryResponse::InjectPath(false)
6368 }
6369 QueryRequest::InjectIdentity { .. } => {
6370 QueryResponse::InjectIdentity(false)
6372 }
6373 QueryRequest::RestoreKnownDestination(..) => {
6374 QueryResponse::RestoreKnownDestination(false)
6375 }
6376 QueryRequest::RetainKnownDestination { .. } => {
6377 QueryResponse::RetainKnownDestination(false)
6378 }
6379 QueryRequest::UnretainKnownDestination { .. } => {
6380 QueryResponse::UnretainKnownDestination(false)
6381 }
6382 QueryRequest::MarkKnownDestinationUsed { .. } => {
6383 QueryResponse::MarkKnownDestinationUsed(false)
6384 }
6385 QueryRequest::HasPath { dest_hash } => {
6386 QueryResponse::HasPath(self.engine.has_path(&dest_hash))
6387 }
6388 QueryRequest::HopsTo { dest_hash } => {
6389 QueryResponse::HopsTo(self.engine.hops_to(&dest_hash))
6390 }
6391 QueryRequest::RecallIdentity { .. } => QueryResponse::RecallIdentity(None),
6392 QueryRequest::KnownDestinations => {
6393 QueryResponse::KnownDestinations(self.known_destination_entries())
6394 }
6395 QueryRequest::LocalDestinations => {
6396 let entries: Vec<LocalDestinationEntry> = self
6397 .local_destinations
6398 .iter()
6399 .map(|(hash, dest_type)| LocalDestinationEntry {
6400 hash: *hash,
6401 dest_type: *dest_type,
6402 })
6403 .collect();
6404 QueryResponse::LocalDestinations(entries)
6405 }
6406 QueryRequest::Links => QueryResponse::Links(self.link_manager.link_entries()),
6407 QueryRequest::Resources => {
6408 QueryResponse::Resources(self.link_manager.resource_entries())
6409 }
6410 QueryRequest::DiscoveredInterfaces {
6411 only_available,
6412 only_transport,
6413 } => {
6414 let mut interfaces = self.discovered_interfaces.list().unwrap_or_default();
6415 crate::discovery::filter_and_sort_interfaces(
6416 &mut interfaces,
6417 only_available,
6418 only_transport,
6419 );
6420 QueryResponse::DiscoveredInterfaces(interfaces)
6421 }
6422 request @ (QueryRequest::ListRuntimeConfig
6423 | QueryRequest::GetRuntimeConfig { .. }
6424 | QueryRequest::BackbonePeerState { .. }
6425 | QueryRequest::SetRuntimeConfig { .. }
6426 | QueryRequest::ResetRuntimeConfig { .. }
6427 | QueryRequest::ClearBackbonePeerState { .. }
6428 | QueryRequest::BlacklistBackbonePeer { .. }) => {
6429 let fallback = Self::runtime_config_query_fallback(&request);
6430 self.handle_runtime_config_query(request)
6431 .unwrap_or_else(|| {
6432 log::error!(
6433 "runtime-config query branch unexpectedly returned no response"
6434 );
6435 fallback
6436 })
6437 }
6438 QueryRequest::SendProbe { .. } => QueryResponse::SendProbe(None),
6440 QueryRequest::CheckProof { .. } => QueryResponse::CheckProof(None),
6441 }
6442 }
6443
6444 fn handle_query_mut(&mut self, request: QueryRequest) -> QueryResponse {
6446 match request {
6447 request @ (QueryRequest::BlackholeIdentity { .. }
6448 | QueryRequest::UnblackholeIdentity { .. }
6449 | QueryRequest::DropPath { .. }
6450 | QueryRequest::DropAllVia { .. }
6451 | QueryRequest::DropAnnounceQueues
6452 | QueryRequest::ClearBackbonePeerState { .. }
6453 | QueryRequest::BlacklistBackbonePeer { .. }
6454 | QueryRequest::InjectPath { .. }
6455 | QueryRequest::InjectIdentity { .. }
6456 | QueryRequest::RestoreKnownDestination(..)
6457 | QueryRequest::RetainKnownDestination { .. }
6458 | QueryRequest::UnretainKnownDestination { .. }
6459 | QueryRequest::MarkKnownDestinationUsed { .. }) => {
6460 let fallback = Self::mutation_query_fallback(&request);
6461 self.handle_mutation_query(request).unwrap_or_else(|| {
6462 log::error!("mutation query branch unexpectedly returned no response");
6463 fallback
6464 })
6465 }
6466 QueryRequest::RecallIdentity { dest_hash } => {
6467 let recalled = self.known_destination_announced(&dest_hash);
6468 if recalled.is_some() {
6469 let _ = self.mark_known_destination_used(&dest_hash);
6470 }
6471 QueryResponse::RecallIdentity(recalled)
6472 }
6473 QueryRequest::DrainStatus => QueryResponse::DrainStatus(self.drain_status()),
6474 QueryRequest::SendProbe {
6475 dest_hash,
6476 payload_size,
6477 } => {
6478 let announced = self.known_destination_announced(&dest_hash);
6480 match announced {
6481 Some(recalled) => {
6482 let _ = self.mark_known_destination_used(&dest_hash);
6483 let remote_id =
6485 rns_crypto::identity::Identity::from_public_key(&recalled.public_key);
6486 let mut payload = vec![0u8; payload_size];
6487 self.rng.fill_bytes(&mut payload);
6488 match remote_id.encrypt(&payload, &mut self.rng) {
6489 Ok(ciphertext) => {
6490 let flags = rns_core::packet::PacketFlags {
6492 header_type: rns_core::constants::HEADER_1,
6493 context_flag: rns_core::constants::FLAG_UNSET,
6494 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
6495 destination_type: rns_core::constants::DESTINATION_SINGLE,
6496 packet_type: rns_core::constants::PACKET_TYPE_DATA,
6497 };
6498 match RawPacket::pack(
6499 flags,
6500 0,
6501 &dest_hash,
6502 None,
6503 rns_core::constants::CONTEXT_NONE,
6504 &ciphertext,
6505 ) {
6506 Ok(packet) => {
6507 let packet_hash = packet.packet_hash;
6508 let hops = self.engine.hops_to(&dest_hash).unwrap_or(0);
6509 self.sent_packets
6511 .insert(packet_hash, (dest_hash, time::now()));
6512 let actions = self.engine.handle_outbound(
6514 &packet,
6515 rns_core::constants::DESTINATION_SINGLE,
6516 None,
6517 time::now(),
6518 );
6519 self.dispatch_all(actions);
6520 log::debug!(
6521 "Sent probe ({} bytes) to {:02x?}",
6522 payload_size,
6523 &dest_hash[..4],
6524 );
6525 QueryResponse::SendProbe(Some((packet_hash, hops)))
6526 }
6527 Err(_) => {
6528 log::warn!("Failed to pack probe packet");
6529 QueryResponse::SendProbe(None)
6530 }
6531 }
6532 }
6533 Err(_) => {
6534 log::warn!("Failed to encrypt probe payload");
6535 QueryResponse::SendProbe(None)
6536 }
6537 }
6538 }
6539 None => {
6540 log::debug!("No known identity for probe dest {:02x?}", &dest_hash[..4]);
6541 QueryResponse::SendProbe(None)
6542 }
6543 }
6544 }
6545 QueryRequest::CheckProof { packet_hash } => {
6546 match self.completed_proofs.remove(&packet_hash) {
6547 Some((rtt, _received)) => QueryResponse::CheckProof(Some(rtt)),
6548 None => QueryResponse::CheckProof(None),
6549 }
6550 }
6551 QueryRequest::SetRuntimeConfig { key, value } => {
6552 let result = match key.as_str() {
6553 "global.tick_interval_ms" => match Self::expect_u64(value, &key) {
6554 Ok(value) => {
6555 let clamped = value.clamp(100, 10_000);
6556 self.tick_interval_ms.store(clamped, Ordering::Relaxed);
6557 Ok(())
6558 }
6559 Err(err) => Err(err),
6560 },
6561 "global.known_destinations_ttl_secs" => match Self::expect_f64(value, &key) {
6562 Ok(value) => {
6563 self.known_destinations_ttl = value;
6564 Ok(())
6565 }
6566 Err(err) => Err(err),
6567 },
6568 "global.rate_limiter_ttl_secs" => match Self::expect_f64(value, &key) {
6569 Ok(value) if value >= 0.0 => {
6570 self.rate_limiter_ttl_secs = value;
6571 Ok(())
6572 }
6573 Ok(_) => Err(RuntimeConfigError {
6574 code: RuntimeConfigErrorCode::InvalidValue,
6575 message: format!("{} must be >= 0", key),
6576 }),
6577 Err(err) => Err(err),
6578 },
6579 "global.known_destinations_cleanup_interval_ticks" => {
6580 match Self::expect_u64(value, &key) {
6581 Ok(value) if value > 0 => {
6582 self.known_destinations_cleanup_interval_ticks = value as u32;
6583 Ok(())
6584 }
6585 Ok(_) => Err(RuntimeConfigError {
6586 code: RuntimeConfigErrorCode::InvalidValue,
6587 message: format!("{} must be >= 1", key),
6588 }),
6589 Err(err) => Err(err),
6590 }
6591 }
6592 "global.announce_cache_cleanup_interval_ticks" => {
6593 match Self::expect_u64(value, &key) {
6594 Ok(value) if value > 0 => {
6595 self.announce_cache_cleanup_interval_ticks = value as u32;
6596 Ok(())
6597 }
6598 Ok(_) => Err(RuntimeConfigError {
6599 code: RuntimeConfigErrorCode::InvalidValue,
6600 message: format!("{} must be >= 1", key),
6601 }),
6602 Err(err) => Err(err),
6603 }
6604 }
6605 "global.announce_cache_cleanup_batch_size" => {
6606 match Self::expect_u64(value, &key) {
6607 Ok(value) if value > 0 => {
6608 self.announce_cache_cleanup_batch_size = value as usize;
6609 Ok(())
6610 }
6611 Ok(_) => Err(RuntimeConfigError {
6612 code: RuntimeConfigErrorCode::InvalidValue,
6613 message: format!("{} must be >= 1", key),
6614 }),
6615 Err(err) => Err(err),
6616 }
6617 }
6618 "global.discovery_cleanup_interval_ticks" => {
6619 match Self::expect_u64(value, &key) {
6620 Ok(value) if value > 0 => {
6621 self.discovery_cleanup_interval_ticks = value as u32;
6622 Ok(())
6623 }
6624 Ok(_) => Err(RuntimeConfigError {
6625 code: RuntimeConfigErrorCode::InvalidValue,
6626 message: format!("{} must be >= 1", key),
6627 }),
6628 Err(err) => Err(err),
6629 }
6630 }
6631 "global.management_announce_interval_secs" => {
6632 match Self::expect_f64(value, &key) {
6633 Ok(value) => {
6634 self.management_announce_interval_secs = value;
6635 Ok(())
6636 }
6637 Err(err) => Err(err),
6638 }
6639 }
6640 "global.direct_connect_policy" => {
6641 let policy = match Self::parse_holepunch_policy(&value) {
6642 Some(policy) => policy,
6643 None => {
6644 return QueryResponse::RuntimeConfigSet(Err(RuntimeConfigError {
6645 code: RuntimeConfigErrorCode::InvalidValue,
6646 message: format!(
6647 "{} must be one of: reject, accept_all, ask_app",
6648 key
6649 ),
6650 }))
6651 }
6652 };
6653 self.holepunch_manager.set_policy(policy);
6654 Ok(())
6655 }
6656 #[cfg(feature = "rns-hooks")]
6657 "provider.queue_max_events" => match Self::expect_u64(value, &key) {
6658 Ok(v) if v > 0 => {
6659 if let Some(ref bridge) = self.provider_bridge {
6660 bridge.set_queue_max_events(v as usize);
6661 }
6662 Ok(())
6663 }
6664 Ok(_) => Err(RuntimeConfigError {
6665 code: RuntimeConfigErrorCode::InvalidValue,
6666 message: format!("{} must be >= 1", key),
6667 }),
6668 Err(err) => Err(err),
6669 },
6670 #[cfg(feature = "rns-hooks")]
6671 "provider.queue_max_bytes" => match Self::expect_u64(value, &key) {
6672 Ok(v) if v > 0 => {
6673 if let Some(ref bridge) = self.provider_bridge {
6674 bridge.set_queue_max_bytes(v as usize);
6675 }
6676 Ok(())
6677 }
6678 Ok(_) => Err(RuntimeConfigError {
6679 code: RuntimeConfigErrorCode::InvalidValue,
6680 message: format!("{} must be >= 1", key),
6681 }),
6682 Err(err) => Err(err),
6683 },
6684 #[cfg(feature = "iface-backbone")]
6685 _ if key.starts_with("backbone.") => {
6686 self.set_backbone_runtime_config(&key, value)
6687 }
6688 #[cfg(feature = "iface-backbone")]
6689 _ if key.starts_with("backbone_client.") => {
6690 self.set_backbone_client_runtime_config(&key, value)
6691 }
6692 #[cfg(feature = "iface-tcp")]
6693 _ if key.starts_with("tcp_server.") => {
6694 self.set_tcp_server_runtime_config(&key, value)
6695 }
6696 #[cfg(feature = "iface-tcp")]
6697 _ if key.starts_with("tcp_client.") => {
6698 self.set_tcp_client_runtime_config(&key, value)
6699 }
6700 #[cfg(feature = "iface-udp")]
6701 _ if key.starts_with("udp.") => self.set_udp_runtime_config(&key, value),
6702 #[cfg(feature = "iface-auto")]
6703 _ if key.starts_with("auto.") => self.set_auto_runtime_config(&key, value),
6704 #[cfg(feature = "iface-i2p")]
6705 _ if key.starts_with("i2p.") => self.set_i2p_runtime_config(&key, value),
6706 #[cfg(feature = "iface-pipe")]
6707 _ if key.starts_with("pipe.") => self.set_pipe_runtime_config(&key, value),
6708 #[cfg(feature = "iface-rnode")]
6709 _ if key.starts_with("rnode.") => self.set_rnode_runtime_config(&key, value),
6710 _ if key.starts_with("interface.") => {
6711 self.set_generic_interface_runtime_config(&key, value)
6712 }
6713 _ => {
6714 return QueryResponse::RuntimeConfigSet(Err(RuntimeConfigError {
6715 code: RuntimeConfigErrorCode::UnknownKey,
6716 message: format!("unknown runtime-config key '{}'", key),
6717 }))
6718 }
6719 };
6720
6721 QueryResponse::RuntimeConfigSet(match result {
6722 Ok(()) => self.runtime_config_entry(&key).ok_or(RuntimeConfigError {
6723 code: RuntimeConfigErrorCode::ApplyFailed,
6724 message: format!("failed to read back runtime-config key '{}'", key),
6725 }),
6726 Err(err) => Err(err),
6727 })
6728 }
6729 QueryRequest::ResetRuntimeConfig { key } => {
6730 let defaults = self.runtime_config_defaults;
6731 let result = match key.as_str() {
6732 "global.tick_interval_ms" => {
6733 self.tick_interval_ms
6734 .store(defaults.tick_interval_ms, Ordering::Relaxed);
6735 Ok(())
6736 }
6737 "global.known_destinations_ttl_secs" => {
6738 self.known_destinations_ttl = defaults.known_destinations_ttl;
6739 Ok(())
6740 }
6741 "global.rate_limiter_ttl_secs" => {
6742 self.rate_limiter_ttl_secs = defaults.rate_limiter_ttl_secs;
6743 Ok(())
6744 }
6745 "global.known_destinations_cleanup_interval_ticks" => {
6746 self.known_destinations_cleanup_interval_ticks =
6747 defaults.known_destinations_cleanup_interval_ticks;
6748 Ok(())
6749 }
6750 "global.announce_cache_cleanup_interval_ticks" => {
6751 self.announce_cache_cleanup_interval_ticks =
6752 defaults.announce_cache_cleanup_interval_ticks;
6753 Ok(())
6754 }
6755 "global.announce_cache_cleanup_batch_size" => {
6756 self.announce_cache_cleanup_batch_size =
6757 defaults.announce_cache_cleanup_batch_size;
6758 Ok(())
6759 }
6760 "global.discovery_cleanup_interval_ticks" => {
6761 self.discovery_cleanup_interval_ticks =
6762 defaults.discovery_cleanup_interval_ticks;
6763 Ok(())
6764 }
6765 "global.management_announce_interval_secs" => {
6766 self.management_announce_interval_secs =
6767 defaults.management_announce_interval_secs;
6768 Ok(())
6769 }
6770 "global.direct_connect_policy" => {
6771 self.holepunch_manager
6772 .set_policy(defaults.direct_connect_policy);
6773 Ok(())
6774 }
6775 #[cfg(feature = "rns-hooks")]
6776 "provider.queue_max_events" => {
6777 if let Some(ref bridge) = self.provider_bridge {
6778 bridge.set_queue_max_events(defaults.provider_queue_max_events);
6779 }
6780 Ok(())
6781 }
6782 #[cfg(feature = "rns-hooks")]
6783 "provider.queue_max_bytes" => {
6784 if let Some(ref bridge) = self.provider_bridge {
6785 bridge.set_queue_max_bytes(defaults.provider_queue_max_bytes);
6786 }
6787 Ok(())
6788 }
6789 #[cfg(feature = "iface-backbone")]
6790 _ if key.starts_with("backbone.") => self.reset_backbone_runtime_config(&key),
6791 #[cfg(feature = "iface-backbone")]
6792 _ if key.starts_with("backbone_client.") => {
6793 self.reset_backbone_client_runtime_config(&key)
6794 }
6795 #[cfg(feature = "iface-tcp")]
6796 _ if key.starts_with("tcp_server.") => {
6797 self.reset_tcp_server_runtime_config(&key)
6798 }
6799 #[cfg(feature = "iface-tcp")]
6800 _ if key.starts_with("tcp_client.") => {
6801 self.reset_tcp_client_runtime_config(&key)
6802 }
6803 #[cfg(feature = "iface-udp")]
6804 _ if key.starts_with("udp.") => self.reset_udp_runtime_config(&key),
6805 #[cfg(feature = "iface-auto")]
6806 _ if key.starts_with("auto.") => self.reset_auto_runtime_config(&key),
6807 #[cfg(feature = "iface-i2p")]
6808 _ if key.starts_with("i2p.") => self.reset_i2p_runtime_config(&key),
6809 #[cfg(feature = "iface-pipe")]
6810 _ if key.starts_with("pipe.") => self.reset_pipe_runtime_config(&key),
6811 #[cfg(feature = "iface-rnode")]
6812 _ if key.starts_with("rnode.") => self.reset_rnode_runtime_config(&key),
6813 _ if key.starts_with("interface.") => {
6814 self.reset_generic_interface_runtime_config(&key)
6815 }
6816 _ => {
6817 return QueryResponse::RuntimeConfigReset(Err(RuntimeConfigError {
6818 code: RuntimeConfigErrorCode::UnknownKey,
6819 message: format!("unknown runtime-config key '{}'", key),
6820 }))
6821 }
6822 };
6823
6824 QueryResponse::RuntimeConfigReset(match result {
6825 Ok(()) => self.runtime_config_entry(&key).ok_or(RuntimeConfigError {
6826 code: RuntimeConfigErrorCode::ApplyFailed,
6827 message: format!("failed to read back runtime-config key '{}'", key),
6828 }),
6829 Err(err) => Err(err),
6830 })
6831 }
6832 other => self.handle_query(other),
6833 }
6834 }
6835
6836 fn handle_tunnel_synth_delivery(&mut self, raw: &[u8]) {
6838 let packet = match RawPacket::unpack(raw) {
6840 Ok(p) => p,
6841 Err(_) => return,
6842 };
6843
6844 match rns_core::transport::tunnel::validate_tunnel_synthesize_data(&packet.data) {
6845 Ok(validated) => {
6846 let iface_id = self
6849 .interfaces
6850 .iter()
6851 .find(|(_, entry)| entry.info.wants_tunnel && entry.online && entry.enabled)
6852 .map(|(id, _)| *id);
6853
6854 if let Some(iface) = iface_id {
6855 let now = time::now();
6856 let tunnel_actions = self.engine.handle_tunnel(validated.tunnel_id, iface, now);
6857 self.dispatch_all(tunnel_actions);
6858 }
6859 }
6860 Err(e) => {
6861 log::debug!("Tunnel synthesis validation failed: {}", e);
6862 }
6863 }
6864 }
6865
6866 fn synthesize_tunnel_for_interface(&mut self, interface: InterfaceId) {
6870 if let Some(ref identity) = self.transport_identity {
6871 let actions = self
6872 .engine
6873 .synthesize_tunnel(identity, interface, &mut self.rng);
6874 self.dispatch_all(actions);
6875 }
6876 }
6877
6878 fn handle_request_path(&mut self, dest_hash: [u8; 16]) {
6880 let mut data = Vec::with_capacity(48);
6882 data.extend_from_slice(&dest_hash);
6883
6884 if self.engine.transport_enabled() {
6885 if let Some(id_hash) = self.engine.identity_hash() {
6886 data.extend_from_slice(id_hash);
6887 }
6888 }
6889
6890 let mut tag = [0u8; 16];
6892 self.rng.fill_bytes(&mut tag);
6893 data.extend_from_slice(&tag);
6894
6895 let flags = rns_core::packet::PacketFlags {
6897 header_type: rns_core::constants::HEADER_1,
6898 context_flag: rns_core::constants::FLAG_UNSET,
6899 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
6900 destination_type: rns_core::constants::DESTINATION_PLAIN,
6901 packet_type: rns_core::constants::PACKET_TYPE_DATA,
6902 };
6903
6904 if let Ok(packet) = RawPacket::pack(
6905 flags,
6906 0,
6907 &self.path_request_dest,
6908 None,
6909 rns_core::constants::CONTEXT_NONE,
6910 &data,
6911 ) {
6912 let actions = self.engine.handle_outbound(
6913 &packet,
6914 rns_core::constants::DESTINATION_PLAIN,
6915 None,
6916 time::now(),
6917 );
6918 self.dispatch_all(actions);
6919 }
6920 }
6921
6922 fn maybe_generate_proof(&mut self, dest_hash: [u8; 16], packet_hash: &[u8; 32]) {
6925 use rns_core::types::ProofStrategy;
6926
6927 let (strategy, identity) = match self.proof_strategies.get(&dest_hash) {
6928 Some((s, id)) => (*s, id.as_ref()),
6929 None => return,
6930 };
6931
6932 let should_prove = match strategy {
6933 ProofStrategy::ProveAll => true,
6934 ProofStrategy::ProveApp => self.callbacks.on_proof_requested(
6935 rns_core::types::DestHash(dest_hash),
6936 rns_core::types::PacketHash(*packet_hash),
6937 ),
6938 ProofStrategy::ProveNone => false,
6939 };
6940
6941 if !should_prove {
6942 return;
6943 }
6944
6945 let identity = match identity {
6946 Some(id) => id,
6947 None => {
6948 log::warn!(
6949 "Cannot generate proof for {:02x?}: no signing key",
6950 &dest_hash[..4]
6951 );
6952 return;
6953 }
6954 };
6955
6956 let signature = match identity.sign(packet_hash) {
6958 Ok(sig) => sig,
6959 Err(e) => {
6960 log::warn!("Failed to sign proof for {:02x?}: {:?}", &dest_hash[..4], e);
6961 return;
6962 }
6963 };
6964
6965 let mut proof_data = Vec::with_capacity(96);
6967 proof_data.extend_from_slice(packet_hash);
6968 proof_data.extend_from_slice(&signature);
6969
6970 let mut proof_dest = [0u8; 16];
6976 proof_dest.copy_from_slice(&packet_hash[..16]);
6977
6978 let flags = rns_core::packet::PacketFlags {
6979 header_type: rns_core::constants::HEADER_1,
6980 context_flag: rns_core::constants::FLAG_UNSET,
6981 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
6982 destination_type: rns_core::constants::DESTINATION_SINGLE,
6983 packet_type: rns_core::constants::PACKET_TYPE_PROOF,
6984 };
6985
6986 if let Ok(packet) = RawPacket::pack(
6987 flags,
6988 0,
6989 &proof_dest,
6990 None,
6991 rns_core::constants::CONTEXT_NONE,
6992 &proof_data,
6993 ) {
6994 let actions = self.engine.handle_outbound(
6995 &packet,
6996 rns_core::constants::DESTINATION_SINGLE,
6997 None,
6998 time::now(),
6999 );
7000 self.dispatch_all(actions);
7001 log::debug!(
7002 "Generated proof for packet on dest {:02x?}",
7003 &dest_hash[..4]
7004 );
7005 }
7006 }
7007
7008 fn handle_inbound_proof(
7010 &mut self,
7011 dest_hash: [u8; 16],
7012 proof_data: &[u8],
7013 _raw_packet_hash: &[u8; 32],
7014 ) {
7015 let (tracked_hash, signature): ([u8; 32], &[u8]) = if proof_data.len() >= 96 {
7019 let mut tracked_hash = [0u8; 32];
7020 tracked_hash.copy_from_slice(&proof_data[..32]);
7021 (tracked_hash, &proof_data[32..96])
7022 } else if proof_data.len() == 64 {
7023 let mut candidates = self
7024 .sent_packets
7025 .iter()
7026 .filter_map(|(packet_hash, _)| {
7027 if packet_hash[..16] == dest_hash {
7028 Some(*packet_hash)
7029 } else {
7030 None
7031 }
7032 })
7033 .collect::<Vec<_>>();
7034
7035 if candidates.is_empty() {
7036 log::debug!(
7037 "Implicit proof for unknown packet prefix {:02x?} on dest {:02x?}",
7038 &dest_hash[..4],
7039 &dest_hash[..4]
7040 );
7041 return;
7042 }
7043
7044 if candidates.len() > 1 {
7047 candidates.sort_by(|a, b| {
7048 let ta = self
7049 .sent_packets
7050 .get(a)
7051 .map(|(_, t)| *t)
7052 .unwrap_or_default();
7053 let tb = self
7054 .sent_packets
7055 .get(b)
7056 .map(|(_, t)| *t)
7057 .unwrap_or_default();
7058 tb.partial_cmp(&ta).unwrap_or(core::cmp::Ordering::Equal)
7059 });
7060 log::debug!(
7061 "Implicit proof matched {} candidates for prefix {:02x?}; using newest",
7062 candidates.len(),
7063 &dest_hash[..4]
7064 );
7065 }
7066
7067 (candidates[0], &proof_data[..64])
7068 } else {
7069 log::debug!("Unsupported proof length: {} bytes", proof_data.len());
7070 return;
7071 };
7072
7073 if let Some((tracked_dest, sent_time)) = self.sent_packets.remove(&tracked_hash) {
7075 if let Some(announced) = self.known_destination_announced(&tracked_dest) {
7078 let identity =
7079 rns_crypto::identity::Identity::from_public_key(&announced.public_key);
7080 let mut sig = [0u8; 64];
7081 sig.copy_from_slice(signature);
7082 if !identity.verify(&sig, &tracked_hash) {
7083 log::debug!("Proof signature invalid for {:02x?}", &tracked_hash[..4],);
7084 return;
7085 }
7086 let _ = self.mark_known_destination_used(&tracked_dest);
7087 } else {
7088 log::debug!(
7089 "No known identity for dest {:02x?}, accepting proof without signature check",
7090 &tracked_dest[..4],
7091 );
7092 }
7093
7094 let now = time::now();
7095 let rtt = now - sent_time;
7096 log::debug!(
7097 "Proof received for {:02x?} rtt={:.3}s",
7098 &tracked_hash[..4],
7099 rtt,
7100 );
7101 self.completed_proofs.insert(tracked_hash, (rtt, now));
7102 self.callbacks.on_proof(
7103 rns_core::types::DestHash(tracked_dest),
7104 rns_core::types::PacketHash(tracked_hash),
7105 rtt,
7106 );
7107 } else {
7108 log::debug!(
7109 "Proof for unknown packet {:02x?} on dest {:02x?}",
7110 &tracked_hash[..4],
7111 &dest_hash[..4],
7112 );
7113 }
7114 }
7115
7116 fn interface_send_deferred(entry: &InterfaceEntry, now: Instant) -> bool {
7117 matches!(entry.send_retry_at, Some(retry_at) if now < retry_at)
7118 }
7119
7120 fn record_send_result(
7121 entry: &mut InterfaceEntry,
7122 result: &std::io::Result<()>,
7123 context: &str,
7124 interface_id: InterfaceId,
7125 ) {
7126 match result {
7127 Ok(()) => {
7128 entry.send_retry_at = None;
7129 entry.send_retry_backoff = Duration::ZERO;
7130 }
7131 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
7132 let next_backoff = if entry.send_retry_backoff.is_zero() {
7133 SEND_RETRY_BACKOFF_MIN
7134 } else {
7135 (entry.send_retry_backoff * 2).min(SEND_RETRY_BACKOFF_MAX)
7136 };
7137 entry.send_retry_backoff = next_backoff;
7138 entry.send_retry_at = Some(Instant::now() + next_backoff);
7139 log::debug!(
7140 "[{}] {} deferred after WouldBlock; retry in {:?}",
7141 interface_id.0,
7142 context,
7143 next_backoff
7144 );
7145 }
7146 Err(e) => {
7147 entry.send_retry_at = None;
7148 entry.send_retry_backoff = Duration::ZERO;
7149 log::warn!("[{}] {} failed: {}", interface_id.0, context, e);
7150 }
7151 }
7152 }
7153
7154 fn dispatch_send_on_interface_action(
7155 &mut self,
7156 interface: InterfaceId,
7157 raw: Vec<u8>,
7158 _hook_injected: &mut Vec<TransportAction>,
7159 ) {
7160 #[cfg(feature = "rns-hooks")]
7161 {
7162 let pkt_ctx = rns_hooks::PacketContext {
7163 flags: if raw.is_empty() { 0 } else { raw[0] },
7164 hops: if raw.len() > 1 { raw[1] } else { 0 },
7165 destination_hash: extract_dest_hash(&raw),
7166 context: 0,
7167 packet_hash: [0; 32],
7168 interface_id: interface.0,
7169 data_offset: 0,
7170 data_len: raw.len() as u32,
7171 };
7172 let ctx = HookContext::Packet {
7173 ctx: &pkt_ctx,
7174 raw: &raw,
7175 };
7176 let now = time::now();
7177 let engine_ref = EngineRef {
7178 engine: &self.engine,
7179 interfaces: &self.interfaces,
7180 link_manager: &self.link_manager,
7181 now,
7182 };
7183 let provider_events_enabled = self.provider_events_enabled();
7184 if let Some(ref e) = run_hook_inner(
7185 &mut self.hook_slots[HookPoint::SendOnInterface as usize].programs,
7186 &self.hook_manager,
7187 &engine_ref,
7188 &ctx,
7189 now,
7190 provider_events_enabled,
7191 ) {
7192 self.collect_hook_side_effects("SendOnInterface", e, _hook_injected);
7193 if e.hook_result.as_ref().is_some_and(|r| r.is_drop()) {
7194 return;
7195 }
7196 }
7197 }
7198
7199 let is_announce = raw.len() > 2 && (raw[0] & 0x03) == 0x01;
7200 if is_announce {
7201 log::debug!(
7202 "Announce:dispatching to iface {} (len={}, online={})",
7203 interface.0,
7204 raw.len(),
7205 self.interfaces
7206 .get(&interface)
7207 .map(|e| e.online && e.enabled)
7208 .unwrap_or(false)
7209 );
7210 }
7211 if let Some(entry) = self.interfaces.get_mut(&interface) {
7212 if entry.online && entry.enabled {
7213 if Self::interface_send_deferred(entry, Instant::now()) {
7214 return;
7215 }
7216 let data = if let Some(ref ifac_state) = entry.ifac {
7217 ifac::mask_outbound(&raw, ifac_state)
7218 } else {
7219 raw
7220 };
7221 entry.stats.txb += data.len() as u64;
7222 entry.stats.tx_packets += 1;
7223 if is_announce {
7224 entry.stats.record_outgoing_announce(time::now());
7225 }
7226 let send_result = entry.writer.send_frame(&data);
7227 let sent_ok = send_result.is_ok();
7228 Self::record_send_result(entry, &send_result, "send", interface);
7229 if sent_ok && is_announce {
7230 let header_type = (data[0] >> 6) & 0x03;
7231 let dest_start = if header_type == 1 { 18usize } else { 2usize };
7232 let dest_preview = if data.len() >= dest_start + 4 {
7233 format!("{:02x?}", &data[dest_start..dest_start + 4])
7234 } else {
7235 "??".into()
7236 };
7237 log::debug!(
7238 "Announce:SENT on iface {} (len={}, h={}, dest=[{}])",
7239 interface.0,
7240 data.len(),
7241 header_type,
7242 dest_preview
7243 );
7244 }
7245 }
7246 }
7247 }
7248
7249 fn dispatch_broadcast_action(
7250 &mut self,
7251 raw: Vec<u8>,
7252 exclude: Option<InterfaceId>,
7253 _hook_injected: &mut Vec<TransportAction>,
7254 ) {
7255 #[cfg(feature = "rns-hooks")]
7256 {
7257 let pkt_ctx = rns_hooks::PacketContext {
7258 flags: if raw.is_empty() { 0 } else { raw[0] },
7259 hops: if raw.len() > 1 { raw[1] } else { 0 },
7260 destination_hash: extract_dest_hash(&raw),
7261 context: 0,
7262 packet_hash: [0; 32],
7263 interface_id: 0,
7264 data_offset: 0,
7265 data_len: raw.len() as u32,
7266 };
7267 let ctx = HookContext::Packet {
7268 ctx: &pkt_ctx,
7269 raw: &raw,
7270 };
7271 let now = time::now();
7272 let engine_ref = EngineRef {
7273 engine: &self.engine,
7274 interfaces: &self.interfaces,
7275 link_manager: &self.link_manager,
7276 now,
7277 };
7278 let provider_events_enabled = self.provider_events_enabled();
7279 if let Some(ref e) = run_hook_inner(
7280 &mut self.hook_slots[HookPoint::BroadcastOnAllInterfaces as usize].programs,
7281 &self.hook_manager,
7282 &engine_ref,
7283 &ctx,
7284 now,
7285 provider_events_enabled,
7286 ) {
7287 self.collect_hook_side_effects("BroadcastOnAllInterfaces", e, _hook_injected);
7288 if e.hook_result.as_ref().is_some_and(|r| r.is_drop()) {
7289 return;
7290 }
7291 }
7292 }
7293
7294 let is_announce = raw.len() > 2 && (raw[0] & 0x03) == 0x01;
7295 for entry in self.interfaces.values_mut() {
7296 if entry.online && entry.enabled && Some(entry.id) != exclude {
7297 if Self::interface_send_deferred(entry, Instant::now()) {
7298 continue;
7299 }
7300 let data = if let Some(ref ifac_state) = entry.ifac {
7301 ifac::mask_outbound(&raw, ifac_state)
7302 } else {
7303 raw.clone()
7304 };
7305 entry.stats.txb += data.len() as u64;
7306 entry.stats.tx_packets += 1;
7307 if is_announce {
7308 entry.stats.record_outgoing_announce(time::now());
7309 }
7310 let send_result = entry.writer.send_frame(&data);
7311 Self::record_send_result(entry, &send_result, "broadcast", entry.id);
7312 }
7313 }
7314 }
7315
7316 fn dispatch_deliver_local_action(
7317 &mut self,
7318 destination_hash: [u8; 16],
7319 raw: Vec<u8>,
7320 packet_hash: [u8; 32],
7321 receiving_interface: InterfaceId,
7322 _hook_injected: &mut Vec<TransportAction>,
7323 ) {
7324 #[cfg(feature = "rns-hooks")]
7325 {
7326 let pkt_ctx = rns_hooks::PacketContext {
7327 flags: 0,
7328 hops: 0,
7329 destination_hash,
7330 context: 0,
7331 packet_hash,
7332 interface_id: receiving_interface.0,
7333 data_offset: 0,
7334 data_len: raw.len() as u32,
7335 };
7336 let ctx = HookContext::Packet {
7337 ctx: &pkt_ctx,
7338 raw: &raw,
7339 };
7340 let now = time::now();
7341 let engine_ref = EngineRef {
7342 engine: &self.engine,
7343 interfaces: &self.interfaces,
7344 link_manager: &self.link_manager,
7345 now,
7346 };
7347 let provider_events_enabled = self.provider_events_enabled();
7348 if let Some(ref e) = run_hook_inner(
7349 &mut self.hook_slots[HookPoint::DeliverLocal as usize].programs,
7350 &self.hook_manager,
7351 &engine_ref,
7352 &ctx,
7353 now,
7354 provider_events_enabled,
7355 ) {
7356 self.collect_hook_side_effects("DeliverLocal", e, _hook_injected);
7357 if e.hook_result.as_ref().is_some_and(|r| r.is_drop()) {
7358 return;
7359 }
7360 }
7361 }
7362
7363 if destination_hash == self.tunnel_synth_dest {
7364 self.handle_tunnel_synth_delivery(&raw);
7365 } else if destination_hash == self.path_request_dest {
7366 if let Ok(packet) = RawPacket::unpack(&raw) {
7367 let actions =
7368 self.engine
7369 .handle_path_request(&packet.data, receiving_interface, time::now());
7370 self.dispatch_all(actions);
7371 }
7372 } else if self.link_manager.is_link_destination(&destination_hash) {
7373 let link_actions = self.link_manager.handle_local_delivery(
7374 destination_hash,
7375 &raw,
7376 packet_hash,
7377 receiving_interface,
7378 &mut self.rng,
7379 );
7380 if link_actions.is_empty() {
7381 if let Ok(packet) = RawPacket::unpack(&raw) {
7382 if packet.flags.packet_type == rns_core::constants::PACKET_TYPE_PROOF {
7383 self.handle_inbound_proof(destination_hash, &packet.data, &packet_hash);
7384 return;
7385 }
7386 }
7387 self.maybe_generate_proof(destination_hash, &packet_hash);
7388 self.callbacks.on_local_delivery(
7389 rns_core::types::DestHash(destination_hash),
7390 raw,
7391 rns_core::types::PacketHash(packet_hash),
7392 );
7393 } else {
7394 self.dispatch_link_actions(link_actions);
7395 }
7396 } else {
7397 if let Ok(packet) = RawPacket::unpack(&raw) {
7398 if packet.flags.packet_type == rns_core::constants::PACKET_TYPE_PROOF {
7399 self.handle_inbound_proof(destination_hash, &packet.data, &packet_hash);
7400 return;
7401 }
7402 }
7403 self.maybe_generate_proof(destination_hash, &packet_hash);
7404 self.callbacks.on_local_delivery(
7405 rns_core::types::DestHash(destination_hash),
7406 raw,
7407 rns_core::types::PacketHash(packet_hash),
7408 );
7409 }
7410 }
7411
7412 fn dispatch_all(&mut self, actions: Vec<TransportAction>) {
7414 #[cfg(feature = "rns-hooks")]
7415 let mut hook_injected: Vec<TransportAction> = Vec::new();
7416 #[cfg(not(feature = "rns-hooks"))]
7417 let mut hook_injected: Vec<TransportAction> = Vec::new();
7418
7419 for action in actions {
7420 match action {
7421 TransportAction::SendOnInterface { interface, raw } => {
7422 self.dispatch_send_on_interface_action(interface, raw, &mut hook_injected);
7423 }
7424 TransportAction::BroadcastOnAllInterfaces { raw, exclude } => {
7425 self.dispatch_broadcast_action(raw, exclude, &mut hook_injected);
7426 }
7427 TransportAction::DeliverLocal {
7428 destination_hash,
7429 raw,
7430 packet_hash,
7431 receiving_interface,
7432 } => {
7433 self.dispatch_deliver_local_action(
7434 destination_hash,
7435 raw,
7436 packet_hash,
7437 receiving_interface,
7438 &mut hook_injected,
7439 );
7440 }
7441 TransportAction::AnnounceReceived {
7442 destination_hash,
7443 identity_hash,
7444 public_key,
7445 name_hash,
7446 app_data,
7447 hops,
7448 receiving_interface,
7449 ..
7450 } => {
7451 #[cfg(feature = "rns-hooks")]
7452 {
7453 let ctx = HookContext::Announce {
7454 destination_hash,
7455 hops,
7456 interface_id: receiving_interface.0,
7457 };
7458 let now = time::now();
7459 let engine_ref = EngineRef {
7460 engine: &self.engine,
7461 interfaces: &self.interfaces,
7462 link_manager: &self.link_manager,
7463 now,
7464 };
7465 let provider_events_enabled = self.provider_events_enabled();
7466 {
7467 let exec = run_hook_inner(
7468 &mut self.hook_slots[HookPoint::AnnounceReceived as usize].programs,
7469 &self.hook_manager,
7470 &engine_ref,
7471 &ctx,
7472 now,
7473 provider_events_enabled,
7474 );
7475 if let Some(ref e) = exec {
7476 self.collect_hook_side_effects(
7477 "AnnounceReceived",
7478 e,
7479 &mut hook_injected,
7480 );
7481 if e.hook_result.as_ref().map_or(false, |r| r.is_drop()) {
7482 continue;
7483 }
7484 }
7485 }
7486 }
7487
7488 if name_hash == self.discovery_name_hash {
7492 if self.discover_interfaces {
7493 if let Some(ref app_data) = app_data {
7494 if let Some(mut discovered) =
7495 crate::discovery::parse_interface_announce(
7496 app_data,
7497 &identity_hash,
7498 hops,
7499 self.discovery_required_value,
7500 )
7501 {
7502 if let Ok(Some(existing)) =
7504 self.discovered_interfaces.load(&discovered.discovery_hash)
7505 {
7506 discovered.discovered = existing.discovered;
7507 discovered.heard_count = existing.heard_count + 1;
7508 }
7509 if let Err(e) = self.discovered_interfaces.store(&discovered) {
7510 log::warn!("Failed to store discovered interface: {}", e);
7511 } else {
7512 log::debug!(
7513 "Discovered interface '{}' ({}) at {}:{} [stamp={}]",
7514 discovered.name,
7515 discovered.interface_type,
7516 discovered.reachable_on.as_deref().unwrap_or("?"),
7517 discovered
7518 .port
7519 .map(|p| p.to_string())
7520 .unwrap_or_else(|| "?".into()),
7521 discovered.stamp_value,
7522 );
7523 }
7524 }
7525 }
7526 }
7527 }
7529
7530 let announced = crate::destination::AnnouncedIdentity {
7532 dest_hash: rns_core::types::DestHash(destination_hash),
7533 identity_hash: rns_core::types::IdentityHash(identity_hash),
7534 public_key,
7535 app_data: app_data.clone(),
7536 hops,
7537 received_at: time::now(),
7538 receiving_interface,
7539 };
7540 self.upsert_known_destination(destination_hash, announced.clone());
7541 log::info!(
7542 "Announce:validated dest={:02x}{:02x}{:02x}{:02x}.. hops={}",
7543 destination_hash[0],
7544 destination_hash[1],
7545 destination_hash[2],
7546 destination_hash[3],
7547 hops,
7548 );
7549 self.callbacks.on_announce(announced);
7550 }
7551 TransportAction::PathUpdated {
7552 destination_hash,
7553 hops,
7554 interface,
7555 ..
7556 } => {
7557 #[cfg(feature = "rns-hooks")]
7558 {
7559 let ctx = HookContext::Announce {
7560 destination_hash,
7561 hops,
7562 interface_id: interface.0,
7563 };
7564 let now = time::now();
7565 let engine_ref = EngineRef {
7566 engine: &self.engine,
7567 interfaces: &self.interfaces,
7568 link_manager: &self.link_manager,
7569 now,
7570 };
7571 let provider_events_enabled = self.provider_events_enabled();
7572 if let Some(ref e) = run_hook_inner(
7573 &mut self.hook_slots[HookPoint::PathUpdated as usize].programs,
7574 &self.hook_manager,
7575 &engine_ref,
7576 &ctx,
7577 now,
7578 provider_events_enabled,
7579 ) {
7580 self.collect_hook_side_effects("PathUpdated", e, &mut hook_injected);
7581 }
7582 }
7583 #[cfg(not(feature = "rns-hooks"))]
7584 let _ = interface;
7585
7586 let _ = self.mark_known_destination_used(&destination_hash);
7587 self.callbacks
7588 .on_path_updated(rns_core::types::DestHash(destination_hash), hops);
7589 }
7590 TransportAction::ForwardToLocalClients { raw, exclude } => {
7591 for entry in self.interfaces.values_mut() {
7592 if entry.online
7593 && entry.enabled
7594 && entry.info.is_local_client
7595 && Some(entry.id) != exclude
7596 {
7597 if Self::interface_send_deferred(entry, Instant::now()) {
7598 continue;
7599 }
7600 let data = if let Some(ref ifac_state) = entry.ifac {
7601 ifac::mask_outbound(&raw, ifac_state)
7602 } else {
7603 raw.clone()
7604 };
7605 entry.stats.txb += data.len() as u64;
7606 entry.stats.tx_packets += 1;
7607 let send_result = entry.writer.send_frame(&data);
7608 Self::record_send_result(
7609 entry,
7610 &send_result,
7611 "forward to local client",
7612 entry.id,
7613 );
7614 }
7615 }
7616 }
7617 TransportAction::ForwardPlainBroadcast {
7618 raw,
7619 to_local,
7620 exclude,
7621 } => {
7622 for entry in self.interfaces.values_mut() {
7623 if entry.online
7624 && entry.enabled
7625 && entry.info.is_local_client == to_local
7626 && Some(entry.id) != exclude
7627 {
7628 if Self::interface_send_deferred(entry, Instant::now()) {
7629 continue;
7630 }
7631 let data = if let Some(ref ifac_state) = entry.ifac {
7632 ifac::mask_outbound(&raw, ifac_state)
7633 } else {
7634 raw.clone()
7635 };
7636 entry.stats.txb += data.len() as u64;
7637 entry.stats.tx_packets += 1;
7638 let send_result = entry.writer.send_frame(&data);
7639 Self::record_send_result(
7640 entry,
7641 &send_result,
7642 "forward plain broadcast",
7643 entry.id,
7644 );
7645 }
7646 }
7647 }
7648 TransportAction::CacheAnnounce { packet_hash, raw } => {
7649 if let Some(ref cache) = self.announce_cache {
7650 if let Err(e) = cache.store(&packet_hash, &raw, None) {
7651 log::warn!("Failed to cache announce: {}", e);
7652 }
7653 }
7654 }
7655 TransportAction::TunnelSynthesize {
7656 interface,
7657 data,
7658 dest_hash,
7659 } => {
7660 #[cfg(feature = "rns-hooks")]
7661 {
7662 let pkt_ctx = rns_hooks::PacketContext {
7663 flags: 0,
7664 hops: 0,
7665 destination_hash: dest_hash,
7666 context: 0,
7667 packet_hash: [0; 32],
7668 interface_id: interface.0,
7669 data_offset: 0,
7670 data_len: data.len() as u32,
7671 };
7672 let ctx = HookContext::Packet {
7673 ctx: &pkt_ctx,
7674 raw: &data,
7675 };
7676 let now = time::now();
7677 let engine_ref = EngineRef {
7678 engine: &self.engine,
7679 interfaces: &self.interfaces,
7680 link_manager: &self.link_manager,
7681 now,
7682 };
7683 let provider_events_enabled = self.provider_events_enabled();
7684 {
7685 let exec = run_hook_inner(
7686 &mut self.hook_slots[HookPoint::TunnelSynthesize as usize].programs,
7687 &self.hook_manager,
7688 &engine_ref,
7689 &ctx,
7690 now,
7691 provider_events_enabled,
7692 );
7693 if let Some(ref e) = exec {
7694 self.collect_hook_side_effects(
7695 "TunnelSynthesize",
7696 e,
7697 &mut hook_injected,
7698 );
7699 if e.hook_result.as_ref().map_or(false, |r| r.is_drop()) {
7700 continue;
7701 }
7702 }
7703 }
7704 }
7705 let flags = rns_core::packet::PacketFlags {
7707 header_type: rns_core::constants::HEADER_1,
7708 context_flag: rns_core::constants::FLAG_UNSET,
7709 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
7710 destination_type: rns_core::constants::DESTINATION_PLAIN,
7711 packet_type: rns_core::constants::PACKET_TYPE_DATA,
7712 };
7713 if let Ok(packet) = rns_core::packet::RawPacket::pack(
7714 flags,
7715 0,
7716 &dest_hash,
7717 None,
7718 rns_core::constants::CONTEXT_NONE,
7719 &data,
7720 ) {
7721 if let Some(entry) = self.interfaces.get_mut(&interface) {
7722 if entry.online && entry.enabled {
7723 let raw = if let Some(ref ifac_state) = entry.ifac {
7724 ifac::mask_outbound(&packet.raw, ifac_state)
7725 } else {
7726 packet.raw
7727 };
7728 entry.stats.txb += raw.len() as u64;
7729 entry.stats.tx_packets += 1;
7730 if let Err(e) = entry.writer.send_frame(&raw) {
7731 log::warn!(
7732 "[{}] tunnel synthesize send failed: {}",
7733 entry.info.id.0,
7734 e
7735 );
7736 }
7737 }
7738 }
7739 }
7740 }
7741 TransportAction::TunnelEstablished {
7742 tunnel_id,
7743 interface,
7744 } => {
7745 log::info!(
7746 "Tunnel established: {:02x?} on interface {}",
7747 &tunnel_id[..4],
7748 interface.0
7749 );
7750 }
7751 TransportAction::AnnounceRetransmit {
7752 destination_hash,
7753 hops,
7754 interface,
7755 } => {
7756 #[cfg(feature = "rns-hooks")]
7757 {
7758 let ctx = HookContext::Announce {
7759 destination_hash,
7760 hops,
7761 interface_id: interface.map(|i| i.0).unwrap_or(0),
7762 };
7763 let now = time::now();
7764 let engine_ref = EngineRef {
7765 engine: &self.engine,
7766 interfaces: &self.interfaces,
7767 link_manager: &self.link_manager,
7768 now,
7769 };
7770 let provider_events_enabled = self.provider_events_enabled();
7771 if let Some(ref e) = run_hook_inner(
7772 &mut self.hook_slots[HookPoint::AnnounceRetransmit as usize].programs,
7773 &self.hook_manager,
7774 &engine_ref,
7775 &ctx,
7776 now,
7777 provider_events_enabled,
7778 ) {
7779 self.collect_hook_side_effects(
7780 "AnnounceRetransmit",
7781 e,
7782 &mut hook_injected,
7783 );
7784 }
7785 }
7786 #[cfg(not(feature = "rns-hooks"))]
7787 {
7788 let _ = (destination_hash, hops, interface);
7789 }
7790 }
7791 TransportAction::LinkRequestReceived {
7792 link_id,
7793 destination_hash: _,
7794 receiving_interface,
7795 } => {
7796 #[cfg(feature = "rns-hooks")]
7797 {
7798 let ctx = HookContext::Link {
7799 link_id,
7800 interface_id: receiving_interface.0,
7801 };
7802 let now = time::now();
7803 let engine_ref = EngineRef {
7804 engine: &self.engine,
7805 interfaces: &self.interfaces,
7806 link_manager: &self.link_manager,
7807 now,
7808 };
7809 let provider_events_enabled = self.provider_events_enabled();
7810 if let Some(ref e) = run_hook_inner(
7811 &mut self.hook_slots[HookPoint::LinkRequestReceived as usize].programs,
7812 &self.hook_manager,
7813 &engine_ref,
7814 &ctx,
7815 now,
7816 provider_events_enabled,
7817 ) {
7818 self.collect_hook_side_effects(
7819 "LinkRequestReceived",
7820 e,
7821 &mut hook_injected,
7822 );
7823 }
7824 }
7825 #[cfg(not(feature = "rns-hooks"))]
7826 {
7827 let _ = (link_id, receiving_interface);
7828 }
7829 }
7830 TransportAction::LinkEstablished { link_id, interface } => {
7831 #[cfg(feature = "rns-hooks")]
7832 {
7833 let ctx = HookContext::Link {
7834 link_id,
7835 interface_id: interface.0,
7836 };
7837 let now = time::now();
7838 let engine_ref = EngineRef {
7839 engine: &self.engine,
7840 interfaces: &self.interfaces,
7841 link_manager: &self.link_manager,
7842 now,
7843 };
7844 let provider_events_enabled = self.provider_events_enabled();
7845 if let Some(ref e) = run_hook_inner(
7846 &mut self.hook_slots[HookPoint::LinkEstablished as usize].programs,
7847 &self.hook_manager,
7848 &engine_ref,
7849 &ctx,
7850 now,
7851 provider_events_enabled,
7852 ) {
7853 self.collect_hook_side_effects(
7854 "LinkEstablished",
7855 e,
7856 &mut hook_injected,
7857 );
7858 }
7859 }
7860 #[cfg(not(feature = "rns-hooks"))]
7861 {
7862 let _ = (link_id, interface);
7863 }
7864 }
7865 TransportAction::LinkClosed { link_id } => {
7866 #[cfg(feature = "rns-hooks")]
7867 {
7868 let ctx = HookContext::Link {
7869 link_id,
7870 interface_id: 0,
7871 };
7872 let now = time::now();
7873 let engine_ref = EngineRef {
7874 engine: &self.engine,
7875 interfaces: &self.interfaces,
7876 link_manager: &self.link_manager,
7877 now,
7878 };
7879 let provider_events_enabled = self.provider_events_enabled();
7880 if let Some(ref e) = run_hook_inner(
7881 &mut self.hook_slots[HookPoint::LinkClosed as usize].programs,
7882 &self.hook_manager,
7883 &engine_ref,
7884 &ctx,
7885 now,
7886 provider_events_enabled,
7887 ) {
7888 self.collect_hook_side_effects("LinkClosed", e, &mut hook_injected);
7889 }
7890 }
7891 #[cfg(not(feature = "rns-hooks"))]
7892 {
7893 let _ = link_id;
7894 }
7895 }
7896 }
7897 }
7898
7899 #[cfg(feature = "rns-hooks")]
7901 if !hook_injected.is_empty() {
7902 self.dispatch_all(hook_injected);
7903 }
7904 }
7905
7906 fn dispatch_link_actions(&mut self, actions: Vec<LinkManagerAction>) {
7908 #[cfg(feature = "rns-hooks")]
7909 let mut hook_injected: Vec<TransportAction> = Vec::new();
7910
7911 for action in actions {
7912 match action {
7913 LinkManagerAction::SendPacket {
7914 mut raw,
7915 dest_type,
7916 mut attached_interface,
7917 } => {
7918 if dest_type == rns_core::constants::DESTINATION_LINK
7919 && attached_interface.is_none()
7920 {
7921 if let Ok(packet) = RawPacket::unpack(&raw) {
7922 let link_id = packet.destination_hash;
7923 if let Some((iface, transport_id)) =
7924 self.link_manager.get_link_route_hint(&link_id)
7925 {
7926 attached_interface = Some(iface);
7927 if packet.flags.header_type == rns_core::constants::HEADER_1 {
7928 if let Some(next_hop) = transport_id {
7929 raw = inject_transport_header(&packet.raw, &next_hop);
7930 log::debug!(
7931 "Link SendPacket rewrite: link={:02x?} iface={} header=1->2 tid={:02x?}",
7932 &link_id[..4],
7933 iface.0,
7934 &next_hop[..4]
7935 );
7936 } else {
7937 log::debug!(
7938 "Link SendPacket route: link={:02x?} iface={} header=1 (no transport_id)",
7939 &link_id[..4],
7940 iface.0
7941 );
7942 }
7943 }
7944 } else {
7945 log::debug!(
7946 "Link SendPacket no route hint: link={:02x?}",
7947 &link_id[..4]
7948 );
7949 }
7950 }
7951 }
7952
7953 match RawPacket::unpack(&raw) {
7955 Ok(packet) => {
7956 if packet.flags.packet_type == rns_core::constants::PACKET_TYPE_DATA {
7957 self.sent_packets.insert(
7958 packet.packet_hash,
7959 (packet.destination_hash, time::now()),
7960 );
7961 }
7962 let transport_actions = self.engine.handle_outbound(
7963 &packet,
7964 dest_type,
7965 attached_interface,
7966 time::now(),
7967 );
7968 self.dispatch_all(transport_actions);
7969 }
7970 Err(e) => {
7971 log::warn!("LinkManager SendPacket: failed to unpack: {:?}", e);
7972 }
7973 }
7974 }
7975 LinkManagerAction::LinkEstablished {
7976 link_id,
7977 dest_hash,
7978 rtt,
7979 is_initiator,
7980 } => {
7981 #[cfg(feature = "rns-hooks")]
7982 {
7983 let ctx = HookContext::Link {
7984 link_id,
7985 interface_id: 0,
7986 };
7987 let now = time::now();
7988 let engine_ref = EngineRef {
7989 engine: &self.engine,
7990 interfaces: &self.interfaces,
7991 link_manager: &self.link_manager,
7992 now,
7993 };
7994 let provider_events_enabled = self.provider_events_enabled();
7995 if let Some(ref e) = run_hook_inner(
7996 &mut self.hook_slots[HookPoint::LinkEstablished as usize].programs,
7997 &self.hook_manager,
7998 &engine_ref,
7999 &ctx,
8000 now,
8001 provider_events_enabled,
8002 ) {
8003 self.collect_hook_side_effects(
8004 "LinkEstablished",
8005 e,
8006 &mut hook_injected,
8007 );
8008 }
8009 }
8010 log::info!(
8011 "Link established: {:02x?} rtt={:.3}s initiator={}",
8012 &link_id[..4],
8013 rtt,
8014 is_initiator,
8015 );
8016 self.callbacks.on_link_established(
8017 rns_core::types::LinkId(link_id),
8018 rns_core::types::DestHash(dest_hash),
8019 rtt,
8020 is_initiator,
8021 );
8022 }
8023 LinkManagerAction::LinkClosed { link_id, reason } => {
8024 #[cfg(feature = "rns-hooks")]
8025 {
8026 let ctx = HookContext::Link {
8027 link_id,
8028 interface_id: 0,
8029 };
8030 let now = time::now();
8031 let engine_ref = EngineRef {
8032 engine: &self.engine,
8033 interfaces: &self.interfaces,
8034 link_manager: &self.link_manager,
8035 now,
8036 };
8037 let provider_events_enabled = self.provider_events_enabled();
8038 if let Some(ref e) = run_hook_inner(
8039 &mut self.hook_slots[HookPoint::LinkClosed as usize].programs,
8040 &self.hook_manager,
8041 &engine_ref,
8042 &ctx,
8043 now,
8044 provider_events_enabled,
8045 ) {
8046 self.collect_hook_side_effects("LinkClosed", e, &mut hook_injected);
8047 }
8048 }
8049 log::info!("Link closed: {:02x?} reason={:?}", &link_id[..4], reason);
8050 self.holepunch_manager.link_closed(&link_id);
8051 self.callbacks
8052 .on_link_closed(rns_core::types::LinkId(link_id), reason);
8053 }
8054 LinkManagerAction::RemoteIdentified {
8055 link_id,
8056 identity_hash,
8057 public_key,
8058 } => {
8059 log::debug!(
8060 "Remote identified on link {:02x?}: {:02x?}",
8061 &link_id[..4],
8062 &identity_hash[..4],
8063 );
8064 self.callbacks.on_remote_identified(
8065 rns_core::types::LinkId(link_id),
8066 rns_core::types::IdentityHash(identity_hash),
8067 public_key,
8068 );
8069 }
8070 LinkManagerAction::RegisterLinkDest { link_id } => {
8071 self.engine
8073 .register_destination(link_id, rns_core::constants::DESTINATION_LINK);
8074 }
8075 LinkManagerAction::DeregisterLinkDest { link_id } => {
8076 self.engine.deregister_destination(&link_id);
8077 }
8078 LinkManagerAction::ManagementRequest {
8079 link_id,
8080 path_hash,
8081 data,
8082 request_id,
8083 remote_identity,
8084 } => {
8085 self.handle_management_request(
8086 link_id,
8087 path_hash,
8088 data,
8089 request_id,
8090 remote_identity,
8091 );
8092 }
8093 LinkManagerAction::ResourceReceived {
8094 link_id,
8095 data,
8096 metadata,
8097 } => {
8098 self.callbacks.on_resource_received(
8099 rns_core::types::LinkId(link_id),
8100 data,
8101 metadata,
8102 );
8103 }
8104 LinkManagerAction::ResourceCompleted { link_id } => {
8105 self.callbacks
8106 .on_resource_completed(rns_core::types::LinkId(link_id));
8107 }
8108 LinkManagerAction::ResourceFailed { link_id, error } => {
8109 log::debug!("Resource failed on link {:02x?}: {}", &link_id[..4], error);
8110 self.callbacks
8111 .on_resource_failed(rns_core::types::LinkId(link_id), error);
8112 }
8113 LinkManagerAction::ResourceProgress {
8114 link_id,
8115 received,
8116 total,
8117 } => {
8118 self.callbacks.on_resource_progress(
8119 rns_core::types::LinkId(link_id),
8120 received,
8121 total,
8122 );
8123 }
8124 LinkManagerAction::ResourceAcceptQuery {
8125 link_id,
8126 resource_hash,
8127 transfer_size,
8128 has_metadata,
8129 } => {
8130 let accept = self.callbacks.on_resource_accept_query(
8131 rns_core::types::LinkId(link_id),
8132 resource_hash.clone(),
8133 transfer_size,
8134 has_metadata,
8135 );
8136 let accept_actions = self.link_manager.accept_resource(
8137 &link_id,
8138 &resource_hash,
8139 accept,
8140 &mut self.rng,
8141 );
8142 self.dispatch_link_actions(accept_actions);
8144 }
8145 LinkManagerAction::ChannelMessageReceived {
8146 link_id,
8147 msgtype,
8148 payload,
8149 } => {
8150 if HolePunchManager::is_holepunch_message(msgtype) {
8152 let derived_key = self.link_manager.get_derived_key(&link_id);
8153 let tx = self.get_event_sender();
8154 let (handled, hp_actions) = self.holepunch_manager.handle_signal(
8155 link_id,
8156 msgtype,
8157 payload,
8158 derived_key.as_deref(),
8159 &tx,
8160 );
8161 if handled {
8162 self.dispatch_holepunch_actions(hp_actions);
8163 }
8164 } else {
8165 self.callbacks.on_channel_message(
8166 rns_core::types::LinkId(link_id),
8167 msgtype,
8168 payload,
8169 );
8170 }
8171 }
8172 LinkManagerAction::LinkDataReceived {
8173 link_id,
8174 context,
8175 data,
8176 } => {
8177 self.callbacks
8178 .on_link_data(rns_core::types::LinkId(link_id), context, data);
8179 }
8180 LinkManagerAction::ResponseReceived {
8181 link_id,
8182 request_id,
8183 data,
8184 } => {
8185 self.callbacks
8186 .on_response(rns_core::types::LinkId(link_id), request_id, data);
8187 }
8188 LinkManagerAction::LinkRequestReceived {
8189 link_id,
8190 receiving_interface,
8191 } => {
8192 #[cfg(feature = "rns-hooks")]
8193 {
8194 let ctx = HookContext::Link {
8195 link_id,
8196 interface_id: receiving_interface.0,
8197 };
8198 let now = time::now();
8199 let engine_ref = EngineRef {
8200 engine: &self.engine,
8201 interfaces: &self.interfaces,
8202 link_manager: &self.link_manager,
8203 now,
8204 };
8205 let provider_events_enabled = self.provider_events_enabled();
8206 if let Some(ref e) = run_hook_inner(
8207 &mut self.hook_slots[HookPoint::LinkRequestReceived as usize].programs,
8208 &self.hook_manager,
8209 &engine_ref,
8210 &ctx,
8211 now,
8212 provider_events_enabled,
8213 ) {
8214 self.collect_hook_side_effects(
8215 "LinkRequestReceived",
8216 e,
8217 &mut hook_injected,
8218 );
8219 }
8220 }
8221 #[cfg(not(feature = "rns-hooks"))]
8222 {
8223 let _ = (link_id, receiving_interface);
8224 }
8225 }
8226 }
8227 }
8228
8229 #[cfg(feature = "rns-hooks")]
8231 if !hook_injected.is_empty() {
8232 self.dispatch_all(hook_injected);
8233 }
8234 }
8235
8236 fn dispatch_holepunch_actions(&mut self, actions: Vec<HolePunchManagerAction>) {
8238 for action in actions {
8239 match action {
8240 HolePunchManagerAction::SendChannelMessage {
8241 link_id,
8242 msgtype,
8243 payload,
8244 } => {
8245 if let Ok(link_actions) = self.link_manager.send_channel_message(
8246 &link_id,
8247 msgtype,
8248 &payload,
8249 &mut self.rng,
8250 ) {
8251 self.dispatch_link_actions(link_actions);
8252 }
8253 }
8254 HolePunchManagerAction::DirectConnectEstablished {
8255 link_id,
8256 session_id,
8257 interface_id,
8258 rtt,
8259 mtu,
8260 } => {
8261 log::info!(
8262 "Direct connection established for link {:02x?} session {:02x?} iface {} rtt={:.1}ms mtu={}",
8263 &link_id[..4], &session_id[..4], interface_id.0, rtt * 1000.0, mtu
8264 );
8265 self.engine
8267 .redirect_path(&link_id, interface_id, time::now());
8268 self.link_manager.set_link_rtt(&link_id, rtt);
8270 self.link_manager.set_link_mtu(&link_id, mtu);
8271 self.link_manager.record_link_inbound(&link_id);
8274 self.link_manager.flush_channel_tx(&link_id);
8276 self.callbacks.on_direct_connect_established(
8277 rns_core::types::LinkId(link_id),
8278 interface_id,
8279 );
8280 }
8281 HolePunchManagerAction::DirectConnectFailed {
8282 link_id,
8283 session_id,
8284 reason,
8285 } => {
8286 log::debug!(
8287 "Direct connection failed for link {:02x?} session {:02x?} reason={}",
8288 &link_id[..4],
8289 &session_id[..4],
8290 reason
8291 );
8292 self.callbacks
8293 .on_direct_connect_failed(rns_core::types::LinkId(link_id), reason);
8294 }
8295 }
8296 }
8297 }
8298
8299 fn get_event_sender(&self) -> crate::event::EventSender {
8304 self.event_tx.clone()
8308 }
8309
8310 const MANAGEMENT_ANNOUNCE_DELAY: f64 = 5.0;
8312
8313 fn tick_discovery_announcer(&mut self, now: f64) {
8315 let announcer = match self.interface_announcer.as_mut() {
8316 Some(a) => a,
8317 None => return,
8318 };
8319
8320 announcer.maybe_start(now);
8321
8322 let stamp_result = match announcer.poll_ready() {
8323 Some(r) => r,
8324 None => return,
8325 };
8326
8327 if !announcer.contains_interface(&stamp_result.interface_name) {
8328 log::debug!(
8329 "Discovery: dropping completed stamp for removed interface '{}'",
8330 stamp_result.interface_name
8331 );
8332 return;
8333 }
8334
8335 let identity = match self.transport_identity.as_ref() {
8336 Some(id) => id,
8337 None => {
8338 log::warn!("Discovery: stamp ready but no transport identity");
8339 return;
8340 }
8341 };
8342
8343 let identity_hash = identity.hash();
8345 let disc_dest = rns_core::destination::destination_hash(
8346 crate::discovery::APP_NAME,
8347 &["discovery", "interface"],
8348 Some(&identity_hash),
8349 );
8350 let name_hash = self.discovery_name_hash;
8351 let mut random_hash = [0u8; 10];
8352 self.rng.fill_bytes(&mut random_hash);
8353
8354 let (announce_data, _) = match rns_core::announce::AnnounceData::pack(
8355 identity,
8356 &disc_dest,
8357 &name_hash,
8358 &random_hash,
8359 None,
8360 Some(&stamp_result.app_data),
8361 ) {
8362 Ok(v) => v,
8363 Err(e) => {
8364 log::warn!("Discovery: failed to pack announce: {}", e);
8365 return;
8366 }
8367 };
8368
8369 let flags = rns_core::packet::PacketFlags {
8370 header_type: rns_core::constants::HEADER_1,
8371 context_flag: rns_core::constants::FLAG_UNSET,
8372 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
8373 destination_type: rns_core::constants::DESTINATION_SINGLE,
8374 packet_type: rns_core::constants::PACKET_TYPE_ANNOUNCE,
8375 };
8376
8377 let packet = match RawPacket::pack(
8378 flags,
8379 0,
8380 &disc_dest,
8381 None,
8382 rns_core::constants::CONTEXT_NONE,
8383 &announce_data,
8384 ) {
8385 Ok(p) => p,
8386 Err(e) => {
8387 log::warn!("Discovery: failed to pack packet: {}", e);
8388 return;
8389 }
8390 };
8391
8392 let outbound_actions = self.engine.handle_outbound(
8393 &packet,
8394 rns_core::constants::DESTINATION_SINGLE,
8395 None,
8396 now,
8397 );
8398 log::debug!(
8399 "Discovery announce sent for interface '{}' ({} actions, dest={:02x?})",
8400 stamp_result.interface_name,
8401 outbound_actions.len(),
8402 &disc_dest[..4],
8403 );
8404 self.dispatch_all(outbound_actions);
8405 }
8406
8407 fn rss_mb() -> Option<f64> {
8409 let statm = std::fs::read_to_string("/proc/self/statm").ok()?;
8410 let rss_pages: u64 = statm.split_whitespace().nth(1)?.parse().ok()?;
8411 Some(rss_pages as f64 * 4096.0 / (1024.0 * 1024.0))
8412 }
8413
8414 fn parse_proc_kib(contents: &str, key: &str) -> Option<u64> {
8415 contents.lines().find_map(|line| {
8416 let value = line.strip_prefix(key)?;
8417 value.split_whitespace().next()?.parse().ok()
8418 })
8419 }
8420
8421 fn proc_status_mb() -> Option<(f64, f64, f64, f64)> {
8422 let status = std::fs::read_to_string("/proc/self/status").ok()?;
8423 let vm_rss = Self::parse_proc_kib(&status, "VmRSS:")? as f64 / 1024.0;
8424 let vm_hwm = Self::parse_proc_kib(&status, "VmHWM:")? as f64 / 1024.0;
8425 let vm_data = Self::parse_proc_kib(&status, "VmData:")? as f64 / 1024.0;
8426 let vm_swap = Self::parse_proc_kib(&status, "VmSwap:").unwrap_or(0) as f64 / 1024.0;
8427 Some((vm_rss, vm_hwm, vm_data, vm_swap))
8428 }
8429
8430 fn smaps_rollup_mb() -> Option<(f64, f64, f64, f64, f64, f64, f64, f64)> {
8431 let smaps = std::fs::read_to_string("/proc/self/smaps_rollup").ok()?;
8432 let rss_kib = Self::parse_proc_kib(&smaps, "Rss:")?;
8433 let anon_kib = Self::parse_proc_kib(&smaps, "Anonymous:")?;
8434 let shared_clean_kib = Self::parse_proc_kib(&smaps, "Shared_Clean:").unwrap_or(0);
8435 let shared_dirty_kib = Self::parse_proc_kib(&smaps, "Shared_Dirty:").unwrap_or(0);
8436 let private_clean_kib = Self::parse_proc_kib(&smaps, "Private_Clean:").unwrap_or(0);
8437 let private_dirty_kib = Self::parse_proc_kib(&smaps, "Private_Dirty:").unwrap_or(0);
8438 let swap_kib = Self::parse_proc_kib(&smaps, "Swap:").unwrap_or(0);
8439 let file_est_kib = rss_kib.saturating_sub(anon_kib);
8440 Some((
8441 rss_kib as f64 / 1024.0,
8442 anon_kib as f64 / 1024.0,
8443 file_est_kib as f64 / 1024.0,
8444 shared_clean_kib as f64 / 1024.0,
8445 shared_dirty_kib as f64 / 1024.0,
8446 private_clean_kib as f64 / 1024.0,
8447 private_dirty_kib as f64 / 1024.0,
8448 swap_kib as f64 / 1024.0,
8449 ))
8450 }
8451
8452 fn log_memory_stats(&self) {
8454 let rss = Self::rss_mb()
8455 .map(|v| format!("{:.1}", v))
8456 .unwrap_or_else(|| "N/A".into());
8457 let (vm_rss, vm_hwm, vm_data, vm_swap) = Self::proc_status_mb()
8458 .map(|(rss, hwm, data, swap)| {
8459 (
8460 format!("{rss:.1}"),
8461 format!("{hwm:.1}"),
8462 format!("{data:.1}"),
8463 format!("{swap:.1}"),
8464 )
8465 })
8466 .unwrap_or_else(|| ("N/A".into(), "N/A".into(), "N/A".into(), "N/A".into()));
8467 let (
8468 smaps_rss,
8469 smaps_anon,
8470 smaps_file_est,
8471 smaps_shared_clean,
8472 smaps_shared_dirty,
8473 smaps_private_clean,
8474 smaps_private_dirty,
8475 smaps_swap,
8476 ) = Self::smaps_rollup_mb()
8477 .map(
8478 |(
8479 rss,
8480 anon,
8481 file_est,
8482 shared_clean,
8483 shared_dirty,
8484 private_clean,
8485 private_dirty,
8486 swap,
8487 )| {
8488 (
8489 format!("{rss:.1}"),
8490 format!("{anon:.1}"),
8491 format!("{file_est:.1}"),
8492 format!("{shared_clean:.1}"),
8493 format!("{shared_dirty:.1}"),
8494 format!("{private_clean:.1}"),
8495 format!("{private_dirty:.1}"),
8496 format!("{swap:.1}"),
8497 )
8498 },
8499 )
8500 .unwrap_or_else(|| {
8501 (
8502 "N/A".into(),
8503 "N/A".into(),
8504 "N/A".into(),
8505 "N/A".into(),
8506 "N/A".into(),
8507 "N/A".into(),
8508 "N/A".into(),
8509 "N/A".into(),
8510 )
8511 });
8512 log::info!(
8513 "MEMSTATS rss_mb={} vmrss_mb={} vmhwm_mb={} vmdata_mb={} vmswap_mb={} smaps_rss_mb={} smaps_anon_mb={} smaps_file_est_mb={} smaps_shared_clean_mb={} smaps_shared_dirty_mb={} smaps_private_clean_mb={} smaps_private_dirty_mb={} smaps_swap_mb={} known_dest={} known_dest_cap_evict={} path={} path_cap_evict={} announce={} reverse={} link={} held_ann={} hashlist={} sig_cache={} ann_verify_q={} rate_lim={} blackhole={} tunnel={} ann_q_ifaces={} ann_q_nonempty={} ann_q_entries={} ann_q_bytes={} ann_q_iface_drop={} pr_tags={} disc_pr={} sent_pkt={} completed={} local_dest={} shared_ann={} lm_links={} hp_sessions={} proof_strat={}",
8514 rss,
8515 vm_rss,
8516 vm_hwm,
8517 vm_data,
8518 vm_swap,
8519 smaps_rss,
8520 smaps_anon,
8521 smaps_file_est,
8522 smaps_shared_clean,
8523 smaps_shared_dirty,
8524 smaps_private_clean,
8525 smaps_private_dirty,
8526 smaps_swap,
8527 self.known_destinations.len(),
8528 self.known_destinations_cap_evict_count,
8529 self.engine.path_table_count(),
8530 self.engine.path_destination_cap_evict_count(),
8531 self.engine.announce_table_count(),
8532 self.engine.reverse_table_count(),
8533 self.engine.link_table_count(),
8534 self.engine.held_announces_count(),
8535 self.engine.packet_hashlist_len(),
8536 self.engine.announce_sig_cache_len(),
8537 self.announce_verify_queue
8538 .lock()
8539 .map(|queue| queue.len())
8540 .unwrap_or(0),
8541 self.engine.rate_limiter_count(),
8542 self.engine.blackholed_count(),
8543 self.engine.tunnel_count(),
8544 self.engine.announce_queue_count(),
8545 self.engine.nonempty_announce_queue_count(),
8546 self.engine.queued_announce_count(),
8547 self.engine.queued_announce_bytes(),
8548 self.engine.announce_queue_interface_cap_drop_count(),
8549 self.engine.discovery_pr_tags_count(),
8550 self.engine.discovery_path_requests_count(),
8551 self.sent_packets.len(),
8552 self.completed_proofs.len(),
8553 self.local_destinations.len(),
8554 self.shared_announces.len(),
8555 self.link_manager.link_count(),
8556 self.holepunch_manager.session_count(),
8557 self.proof_strategies.len(),
8558 );
8559 }
8560
8561 fn tick_management_announces(&mut self, now: f64) {
8563 if self.transport_identity.is_none() {
8564 return;
8565 }
8566
8567 let uptime = now - self.started;
8568
8569 if !self.initial_announce_sent {
8571 if uptime < Self::MANAGEMENT_ANNOUNCE_DELAY {
8572 return;
8573 }
8574 self.initial_announce_sent = true;
8575 self.emit_management_announces(now);
8576 return;
8577 }
8578
8579 if now - self.last_management_announce >= self.management_announce_interval_secs {
8581 self.emit_management_announces(now);
8582 }
8583 }
8584
8585 fn emit_management_announces(&mut self, now: f64) {
8587 use crate::management;
8588
8589 self.last_management_announce = now;
8590
8591 let identity = match self.transport_identity {
8592 Some(ref id) => id,
8593 None => return,
8594 };
8595
8596 let mgmt_raw = if self.management_config.enable_remote_management {
8598 management::build_management_announce(identity, &mut self.rng)
8599 } else {
8600 None
8601 };
8602
8603 let bh_raw = if self.management_config.publish_blackhole {
8604 management::build_blackhole_announce(identity, &mut self.rng)
8605 } else {
8606 None
8607 };
8608
8609 let probe_raw = if self.probe_responder_hash.is_some() {
8610 management::build_probe_announce(identity, &mut self.rng)
8611 } else {
8612 None
8613 };
8614
8615 if let Some(raw) = mgmt_raw {
8616 if let Ok(packet) = RawPacket::unpack(&raw) {
8617 let actions = self.engine.handle_outbound(
8618 &packet,
8619 rns_core::constants::DESTINATION_SINGLE,
8620 None,
8621 now,
8622 );
8623 self.dispatch_all(actions);
8624 log::debug!("Emitted management destination announce");
8625 }
8626 }
8627
8628 if let Some(raw) = bh_raw {
8629 if let Ok(packet) = RawPacket::unpack(&raw) {
8630 let actions = self.engine.handle_outbound(
8631 &packet,
8632 rns_core::constants::DESTINATION_SINGLE,
8633 None,
8634 now,
8635 );
8636 self.dispatch_all(actions);
8637 log::debug!("Emitted blackhole info announce");
8638 }
8639 }
8640
8641 if let Some(raw) = probe_raw {
8642 if let Ok(packet) = RawPacket::unpack(&raw) {
8643 let actions = self.engine.handle_outbound(
8644 &packet,
8645 rns_core::constants::DESTINATION_SINGLE,
8646 None,
8647 now,
8648 );
8649 self.dispatch_all(actions);
8650 log::debug!("Emitted probe responder announce");
8651 }
8652 }
8653 }
8654
8655 fn handle_management_request(
8657 &mut self,
8658 link_id: [u8; 16],
8659 path_hash: [u8; 16],
8660 data: Vec<u8>,
8661 request_id: [u8; 16],
8662 remote_identity: Option<([u8; 16], [u8; 64])>,
8663 ) {
8664 use crate::management;
8665
8666 let is_restricted = path_hash == management::status_path_hash()
8668 || path_hash == management::path_path_hash();
8669
8670 if is_restricted && !self.management_config.remote_management_allowed.is_empty() {
8671 match remote_identity {
8672 Some((identity_hash, _)) => {
8673 if !self
8674 .management_config
8675 .remote_management_allowed
8676 .contains(&identity_hash)
8677 {
8678 log::debug!("Management request denied: identity not in allowed list");
8679 return;
8680 }
8681 }
8682 None => {
8683 log::debug!("Management request denied: peer not identified");
8684 return;
8685 }
8686 }
8687 }
8688
8689 let response_data = if path_hash == management::status_path_hash() {
8690 {
8691 let views: Vec<&dyn management::InterfaceStatusView> = self
8692 .interfaces
8693 .values()
8694 .map(|e| e as &dyn management::InterfaceStatusView)
8695 .collect();
8696 management::handle_status_request(
8697 &data,
8698 &self.engine,
8699 &views,
8700 self.started,
8701 self.probe_responder_hash,
8702 )
8703 }
8704 } else if path_hash == management::path_path_hash() {
8705 management::handle_path_request(&data, &self.engine)
8706 } else if path_hash == management::list_path_hash() {
8707 management::handle_blackhole_list_request(&self.engine)
8708 } else {
8709 log::warn!("Unknown management path_hash: {:02x?}", &path_hash[..4]);
8710 None
8711 };
8712
8713 if let Some(response) = response_data {
8714 let actions = self.link_manager.send_management_response(
8715 &link_id,
8716 &request_id,
8717 &response,
8718 &mut self.rng,
8719 );
8720 self.dispatch_link_actions(actions);
8721 }
8722 }
8723}
8724
8725#[cfg(test)]
8726mod tests {
8727 use super::*;
8728 use crate::event;
8729 use crate::interface::Writer;
8730 use rns_core::announce::AnnounceData;
8731 use rns_core::constants;
8732 use rns_core::packet::PacketFlags;
8733 use rns_core::transport::types::InterfaceInfo;
8734 use rns_crypto::identity::Identity;
8735 use std::io;
8736 use std::sync::mpsc;
8737 use std::sync::{Arc, Mutex};
8738 use std::thread;
8739 use std::time::{Duration, Instant};
8740
8741 struct MockWriter {
8742 sent: Arc<Mutex<Vec<Vec<u8>>>>,
8743 }
8744
8745 impl MockWriter {
8746 fn new() -> (Self, Arc<Mutex<Vec<Vec<u8>>>>) {
8747 let sent = Arc::new(Mutex::new(Vec::new()));
8748 (MockWriter { sent: sent.clone() }, sent)
8749 }
8750 }
8751
8752 impl Writer for MockWriter {
8753 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
8754 self.sent.lock().unwrap().push(data.to_vec());
8755 Ok(())
8756 }
8757 }
8758
8759 struct BlockingWriter {
8760 entered_tx: std::sync::mpsc::Sender<()>,
8761 release_rx: std::sync::mpsc::Receiver<()>,
8762 }
8763
8764 impl Writer for BlockingWriter {
8765 fn send_frame(&mut self, _data: &[u8]) -> io::Result<()> {
8766 let _ = self.entered_tx.send(());
8767 let _ = self.release_rx.recv();
8768 Ok(())
8769 }
8770 }
8771
8772 struct WouldBlockWriter {
8773 attempts: Arc<Mutex<usize>>,
8774 }
8775
8776 impl WouldBlockWriter {
8777 fn new() -> (Self, Arc<Mutex<usize>>) {
8778 let attempts = Arc::new(Mutex::new(0));
8779 (
8780 WouldBlockWriter {
8781 attempts: attempts.clone(),
8782 },
8783 attempts,
8784 )
8785 }
8786 }
8787
8788 impl Writer for WouldBlockWriter {
8789 fn send_frame(&mut self, _data: &[u8]) -> io::Result<()> {
8790 *self.attempts.lock().unwrap() += 1;
8791 Err(io::Error::new(
8792 io::ErrorKind::WouldBlock,
8793 "intentional stall",
8794 ))
8795 }
8796 }
8797
8798 fn wait_for_sent_len(sent: &Arc<Mutex<Vec<Vec<u8>>>>, expected: usize) {
8799 let deadline = Instant::now() + Duration::from_millis(200);
8800 while Instant::now() < deadline {
8801 if sent.lock().unwrap().len() == expected {
8802 return;
8803 }
8804 thread::sleep(Duration::from_millis(5));
8805 }
8806 assert_eq!(sent.lock().unwrap().len(), expected);
8807 }
8808
8809 use rns_core::types::{DestHash, IdentityHash, LinkId as TypedLinkId, PacketHash};
8810
8811 struct MockCallbacks {
8812 announces: Arc<Mutex<Vec<(DestHash, u8)>>>,
8813 paths: Arc<Mutex<Vec<(DestHash, u8)>>>,
8814 deliveries: Arc<Mutex<Vec<DestHash>>>,
8815 iface_ups: Arc<Mutex<Vec<InterfaceId>>>,
8816 iface_downs: Arc<Mutex<Vec<InterfaceId>>>,
8817 link_established: Arc<Mutex<Vec<(TypedLinkId, f64, bool)>>>,
8818 link_closed: Arc<Mutex<Vec<TypedLinkId>>>,
8819 remote_identified: Arc<Mutex<Vec<(TypedLinkId, IdentityHash)>>>,
8820 resources_received: Arc<Mutex<Vec<(TypedLinkId, Vec<u8>)>>>,
8821 resource_completed: Arc<Mutex<Vec<TypedLinkId>>>,
8822 resource_failed: Arc<Mutex<Vec<(TypedLinkId, String)>>>,
8823 channel_messages: Arc<Mutex<Vec<(TypedLinkId, u16, Vec<u8>)>>>,
8824 link_data: Arc<Mutex<Vec<(TypedLinkId, u8, Vec<u8>)>>>,
8825 responses: Arc<Mutex<Vec<(TypedLinkId, [u8; 16], Vec<u8>)>>>,
8826 proofs: Arc<Mutex<Vec<(DestHash, PacketHash, f64)>>>,
8827 proof_requested: Arc<Mutex<Vec<(DestHash, PacketHash)>>>,
8828 }
8829
8830 impl MockCallbacks {
8831 fn new() -> (
8832 Self,
8833 Arc<Mutex<Vec<(DestHash, u8)>>>,
8834 Arc<Mutex<Vec<(DestHash, u8)>>>,
8835 Arc<Mutex<Vec<DestHash>>>,
8836 Arc<Mutex<Vec<InterfaceId>>>,
8837 Arc<Mutex<Vec<InterfaceId>>>,
8838 ) {
8839 let announces = Arc::new(Mutex::new(Vec::new()));
8840 let paths = Arc::new(Mutex::new(Vec::new()));
8841 let deliveries = Arc::new(Mutex::new(Vec::new()));
8842 let iface_ups = Arc::new(Mutex::new(Vec::new()));
8843 let iface_downs = Arc::new(Mutex::new(Vec::new()));
8844 (
8845 MockCallbacks {
8846 announces: announces.clone(),
8847 paths: paths.clone(),
8848 deliveries: deliveries.clone(),
8849 iface_ups: iface_ups.clone(),
8850 iface_downs: iface_downs.clone(),
8851 link_established: Arc::new(Mutex::new(Vec::new())),
8852 link_closed: Arc::new(Mutex::new(Vec::new())),
8853 remote_identified: Arc::new(Mutex::new(Vec::new())),
8854 resources_received: Arc::new(Mutex::new(Vec::new())),
8855 resource_completed: Arc::new(Mutex::new(Vec::new())),
8856 resource_failed: Arc::new(Mutex::new(Vec::new())),
8857 channel_messages: Arc::new(Mutex::new(Vec::new())),
8858 link_data: Arc::new(Mutex::new(Vec::new())),
8859 responses: Arc::new(Mutex::new(Vec::new())),
8860 proofs: Arc::new(Mutex::new(Vec::new())),
8861 proof_requested: Arc::new(Mutex::new(Vec::new())),
8862 },
8863 announces,
8864 paths,
8865 deliveries,
8866 iface_ups,
8867 iface_downs,
8868 )
8869 }
8870
8871 fn with_link_tracking() -> (
8872 Self,
8873 Arc<Mutex<Vec<(TypedLinkId, f64, bool)>>>,
8874 Arc<Mutex<Vec<TypedLinkId>>>,
8875 Arc<Mutex<Vec<(TypedLinkId, IdentityHash)>>>,
8876 ) {
8877 let link_established = Arc::new(Mutex::new(Vec::new()));
8878 let link_closed = Arc::new(Mutex::new(Vec::new()));
8879 let remote_identified = Arc::new(Mutex::new(Vec::new()));
8880 (
8881 MockCallbacks {
8882 announces: Arc::new(Mutex::new(Vec::new())),
8883 paths: Arc::new(Mutex::new(Vec::new())),
8884 deliveries: Arc::new(Mutex::new(Vec::new())),
8885 iface_ups: Arc::new(Mutex::new(Vec::new())),
8886 iface_downs: Arc::new(Mutex::new(Vec::new())),
8887 link_established: link_established.clone(),
8888 link_closed: link_closed.clone(),
8889 remote_identified: remote_identified.clone(),
8890 resources_received: Arc::new(Mutex::new(Vec::new())),
8891 resource_completed: Arc::new(Mutex::new(Vec::new())),
8892 resource_failed: Arc::new(Mutex::new(Vec::new())),
8893 channel_messages: Arc::new(Mutex::new(Vec::new())),
8894 link_data: Arc::new(Mutex::new(Vec::new())),
8895 responses: Arc::new(Mutex::new(Vec::new())),
8896 proofs: Arc::new(Mutex::new(Vec::new())),
8897 proof_requested: Arc::new(Mutex::new(Vec::new())),
8898 },
8899 link_established,
8900 link_closed,
8901 remote_identified,
8902 )
8903 }
8904 }
8905
8906 fn new_test_driver() -> Driver {
8907 let transport_config = TransportConfig {
8908 transport_enabled: false,
8909 identity_hash: None,
8910 prefer_shorter_path: false,
8911 max_paths_per_destination: 1,
8912 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
8913 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
8914 max_path_destinations: usize::MAX,
8915 max_tunnel_destinations_total: usize::MAX,
8916 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
8917 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
8918 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
8919 announce_sig_cache_enabled: true,
8920 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
8921 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
8922 announce_queue_max_entries: 256,
8923 announce_queue_max_interfaces: 1024,
8924 };
8925 let (callbacks, _, _, _, _, _) = MockCallbacks::new();
8926 let (tx, rx) = event::channel();
8927 let mut driver = Driver::new(transport_config, rx, tx, Box::new(callbacks));
8928 driver.set_tick_interval_handle(Arc::new(AtomicU64::new(1000)));
8929 driver
8930 }
8931
8932 fn make_announced_identity(
8933 dest_hash: [u8; 16],
8934 received_at: f64,
8935 receiving_interface: InterfaceId,
8936 ) -> crate::destination::AnnouncedIdentity {
8937 crate::destination::AnnouncedIdentity {
8938 dest_hash: rns_core::types::DestHash(dest_hash),
8939 identity_hash: rns_core::types::IdentityHash([dest_hash[0]; 16]),
8940 public_key: [dest_hash[0]; 64],
8941 app_data: None,
8942 hops: 1,
8943 received_at,
8944 receiving_interface,
8945 }
8946 }
8947
8948 fn make_known_destination_state(
8949 dest_hash: [u8; 16],
8950 received_at: f64,
8951 receiving_interface: InterfaceId,
8952 ) -> KnownDestinationState {
8953 KnownDestinationState {
8954 announced: make_announced_identity(dest_hash, received_at, receiving_interface),
8955 was_used: false,
8956 last_used_at: None,
8957 retained: false,
8958 }
8959 }
8960
8961 #[cfg(feature = "iface-backbone")]
8962 fn make_pool_candidate(name: &str, port: u16, id: u64) -> BackbonePeerPoolCandidateConfig {
8963 let mut client = BackboneClientConfig {
8964 name: name.to_string(),
8965 target_host: "127.0.0.1".to_string(),
8966 target_port: port,
8967 interface_id: InterfaceId(id),
8968 reconnect_wait: Duration::from_millis(10),
8969 max_reconnect_tries: Some(0),
8970 connect_timeout: Duration::from_millis(50),
8971 transport_identity: None,
8972 ..BackboneClientConfig::default()
8973 };
8974 client.runtime = Arc::new(Mutex::new(BackboneClientRuntime::from_config(&client)));
8975 BackbonePeerPoolCandidateConfig {
8976 client,
8977 mode: constants::MODE_FULL,
8978 ingress_control: rns_core::transport::types::IngressControlConfig::disabled(),
8979 ifac_runtime: IfacRuntimeConfig {
8980 netname: None,
8981 netkey: None,
8982 size: 16,
8983 },
8984 ifac_enabled: false,
8985 interface_type_name: "BackboneInterface".to_string(),
8986 }
8987 }
8988
8989 #[cfg(feature = "iface-backbone")]
8990 #[test]
8991 fn backbone_peer_pool_respects_max_connected_order() {
8992 let listener_a = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
8993 let listener_b = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
8994 let port_a = listener_a.local_addr().unwrap().port();
8995 let port_b = listener_b.local_addr().unwrap().port();
8996 let mut driver = new_test_driver();
8997
8998 driver.configure_backbone_peer_pool(
8999 BackbonePeerPoolSettings {
9000 max_connected: 1,
9001 failure_threshold: 3,
9002 failure_window: Duration::from_secs(60),
9003 cooldown: Duration::from_secs(60),
9004 },
9005 vec![
9006 make_pool_candidate("first", port_a, 7001),
9007 make_pool_candidate("second", port_b, 7002),
9008 ],
9009 );
9010
9011 let status = driver.backbone_peer_pool_status().unwrap();
9012 assert_eq!(status.max_connected, 1);
9013 assert_eq!(status.active_count, 1);
9014 assert_eq!(status.standby_count, 1);
9015 assert_eq!(status.members[0].name, "first");
9016 assert_eq!(status.members[0].interface_id, Some(7001));
9017 assert_eq!(status.members[1].name, "second");
9018 assert_eq!(status.members[1].state, "standby");
9019 drop(listener_a);
9020 drop(listener_b);
9021 }
9022
9023 #[cfg(feature = "iface-backbone")]
9024 #[test]
9025 fn backbone_peer_pool_cools_down_failed_peer_and_tries_next() {
9026 let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
9027 let reachable_port = listener.local_addr().unwrap().port();
9028 let mut driver = new_test_driver();
9029
9030 driver.configure_backbone_peer_pool(
9031 BackbonePeerPoolSettings {
9032 max_connected: 1,
9033 failure_threshold: 1,
9034 failure_window: Duration::from_secs(60),
9035 cooldown: Duration::from_secs(60),
9036 },
9037 vec![
9038 make_pool_candidate("failed", 1, 7011),
9039 make_pool_candidate("replacement", reachable_port, 7012),
9040 ],
9041 );
9042
9043 let status = driver.backbone_peer_pool_status().unwrap();
9044 assert_eq!(status.active_count, 1);
9045 assert_eq!(status.cooldown_count, 1);
9046 assert_eq!(status.members[0].name, "failed");
9047 assert_eq!(status.members[0].state, "cooldown");
9048 assert_eq!(status.members[0].failure_count, 1);
9049 assert_eq!(status.members[1].name, "replacement");
9050 assert_eq!(status.members[1].interface_id, Some(7012));
9051 drop(listener);
9052 }
9053
9054 #[cfg(feature = "iface-backbone")]
9055 #[test]
9056 fn backbone_peer_pool_rotates_after_runtime_disconnect() {
9057 let listener_a = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
9058 let listener_b = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
9059 let port_a = listener_a.local_addr().unwrap().port();
9060 let port_b = listener_b.local_addr().unwrap().port();
9061 let mut driver = new_test_driver();
9062
9063 driver.configure_backbone_peer_pool(
9064 BackbonePeerPoolSettings {
9065 max_connected: 1,
9066 failure_threshold: 1,
9067 failure_window: Duration::from_secs(60),
9068 cooldown: Duration::from_secs(60),
9069 },
9070 vec![
9071 make_pool_candidate("first", port_a, 7021),
9072 make_pool_candidate("second", port_b, 7022),
9073 ],
9074 );
9075 driver.handle_backbone_peer_pool_down(InterfaceId(7021));
9076
9077 let status = driver.backbone_peer_pool_status().unwrap();
9078 assert_eq!(status.active_count, 1);
9079 assert_eq!(status.cooldown_count, 1);
9080 assert_eq!(status.members[0].state, "cooldown");
9081 assert_eq!(status.members[1].interface_id, Some(7022));
9082 drop(listener_a);
9083 drop(listener_b);
9084 }
9085
9086 #[cfg(feature = "iface-backbone")]
9087 fn register_test_backbone(driver: &mut Driver, name: &str) {
9088 let startup = BackboneServerRuntime {
9089 max_connections: Some(8),
9090 idle_timeout: Some(Duration::from_secs(10)),
9091 write_stall_timeout: Some(Duration::from_secs(30)),
9092 abuse: BackboneAbuseConfig {
9093 max_penalty_duration: Some(Duration::from_secs(3600)),
9094 },
9095 };
9096 let peer_state = Arc::new(std::sync::Mutex::new(
9097 crate::interface::backbone::BackbonePeerMonitor::new(),
9098 ));
9099 driver.register_backbone_runtime(BackboneRuntimeConfigHandle {
9100 interface_name: name.to_string(),
9101 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
9102 startup,
9103 });
9104 driver.register_backbone_peer_state(BackbonePeerStateHandle {
9105 interface_id: InterfaceId(1),
9106 interface_name: name.to_string(),
9107 peer_state,
9108 });
9109 }
9110
9111 #[cfg(feature = "iface-backbone")]
9112 fn register_test_backbone_client(driver: &mut Driver, name: &str) {
9113 let startup = BackboneClientRuntime {
9114 reconnect_wait: Duration::from_secs(5),
9115 max_reconnect_tries: Some(3),
9116 connect_timeout: Duration::from_secs(5),
9117 };
9118 driver.register_backbone_client_runtime(BackboneClientRuntimeConfigHandle {
9119 interface_name: name.to_string(),
9120 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
9121 startup,
9122 });
9123 }
9124
9125 #[cfg(feature = "iface-backbone")]
9126 fn register_test_backbone_discovery(driver: &mut Driver, name: &str, discoverable: bool) {
9127 let startup = BackboneDiscoveryRuntime {
9128 discoverable,
9129 config: crate::discovery::DiscoveryConfig {
9130 discovery_name: name.to_string(),
9131 announce_interval: 3600,
9132 stamp_value: crate::discovery::DEFAULT_STAMP_VALUE,
9133 reachable_on: None,
9134 interface_type: "BackboneInterface".to_string(),
9135 listen_port: Some(4242),
9136 latitude: None,
9137 longitude: None,
9138 height: None,
9139 },
9140 transport_enabled: true,
9141 ifac_netname: None,
9142 ifac_netkey: None,
9143 };
9144 driver.register_backbone_discovery_runtime(BackboneDiscoveryRuntimeHandle {
9145 interface_name: name.to_string(),
9146 current: startup.clone(),
9147 startup,
9148 });
9149 }
9150
9151 #[cfg(feature = "iface-tcp")]
9152 fn register_test_tcp_server(driver: &mut Driver, name: &str) {
9153 let startup = TcpServerRuntime {
9154 max_connections: Some(4),
9155 };
9156 driver.register_tcp_server_runtime(TcpServerRuntimeConfigHandle {
9157 interface_name: name.to_string(),
9158 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
9159 startup,
9160 });
9161 }
9162
9163 #[cfg(feature = "iface-tcp")]
9164 fn register_test_tcp_server_discovery(driver: &mut Driver, name: &str, discoverable: bool) {
9165 let startup = TcpServerDiscoveryRuntime {
9166 discoverable,
9167 config: crate::discovery::DiscoveryConfig {
9168 discovery_name: name.to_string(),
9169 announce_interval: 3600,
9170 stamp_value: crate::discovery::DEFAULT_STAMP_VALUE,
9171 reachable_on: None,
9172 interface_type: "TCPServerInterface".to_string(),
9173 listen_port: Some(4242),
9174 latitude: None,
9175 longitude: None,
9176 height: None,
9177 },
9178 transport_enabled: true,
9179 ifac_netname: None,
9180 ifac_netkey: None,
9181 };
9182 driver.register_tcp_server_discovery_runtime(TcpServerDiscoveryRuntimeHandle {
9183 interface_name: name.to_string(),
9184 current: startup.clone(),
9185 startup,
9186 });
9187 }
9188
9189 #[cfg(feature = "iface-tcp")]
9190 fn register_test_tcp_client(driver: &mut Driver, name: &str) {
9191 let startup = crate::interface::tcp::TcpClientRuntime {
9192 target_host: "127.0.0.1".into(),
9193 target_port: 4242,
9194 reconnect_wait: Duration::from_secs(5),
9195 max_reconnect_tries: Some(3),
9196 connect_timeout: Duration::from_secs(5),
9197 };
9198 driver.register_tcp_client_runtime(crate::interface::tcp::TcpClientRuntimeConfigHandle {
9199 interface_name: name.to_string(),
9200 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
9201 startup,
9202 });
9203 }
9204
9205 #[cfg(feature = "iface-udp")]
9206 fn register_test_udp(driver: &mut Driver, name: &str) {
9207 let startup = UdpRuntime {
9208 forward_ip: Some("127.0.0.1".into()),
9209 forward_port: Some(4242),
9210 };
9211 driver.register_udp_runtime(UdpRuntimeConfigHandle {
9212 interface_name: name.to_string(),
9213 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
9214 startup,
9215 });
9216 }
9217
9218 fn register_test_generic_interface(driver: &mut Driver, id: u64, name: &str) {
9219 let mut info = make_interface_info(id);
9220 info.name = name.to_string();
9221 info.mode = rns_core::constants::MODE_FULL;
9222 info.announce_rate_target = Some(1.5);
9223 info.announce_rate_grace = 2;
9224 info.announce_rate_penalty = 0.25;
9225 info.announce_cap = 0.05;
9226 info.ingress_control.enabled = true;
9227 driver.register_interface_runtime_defaults(&info);
9228 driver.register_interface_ifac_runtime(
9229 &info.name,
9230 IfacRuntimeConfig {
9231 netname: None,
9232 netkey: None,
9233 size: 16,
9234 },
9235 );
9236 driver.engine.register_interface(info.clone());
9237 let (writer, _) = MockWriter::new();
9238 driver.interfaces.insert(
9239 InterfaceId(id),
9240 InterfaceEntry {
9241 id: InterfaceId(id),
9242 info,
9243 writer: Box::new(writer),
9244 async_writer_metrics: None,
9245 enabled: true,
9246 online: true,
9247 dynamic: false,
9248 ifac: None,
9249 stats: InterfaceStats {
9250 started: time::now(),
9251 ..Default::default()
9252 },
9253 interface_type: "TestInterface".to_string(),
9254 send_retry_at: None,
9255 send_retry_backoff: Duration::ZERO,
9256 },
9257 );
9258 }
9259
9260 #[cfg(feature = "iface-auto")]
9261 fn register_test_auto(driver: &mut Driver, name: &str) {
9262 let startup = AutoRuntime {
9263 announce_interval_secs: 1.6,
9264 peer_timeout_secs: 22.0,
9265 peer_job_interval_secs: 4.0,
9266 };
9267 driver.register_auto_runtime(AutoRuntimeConfigHandle {
9268 interface_name: name.to_string(),
9269 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
9270 startup,
9271 });
9272 }
9273
9274 #[cfg(feature = "iface-i2p")]
9275 fn register_test_i2p(driver: &mut Driver, name: &str) {
9276 let startup = I2pRuntime {
9277 reconnect_wait: Duration::from_secs(15),
9278 };
9279 driver.register_i2p_runtime(I2pRuntimeConfigHandle {
9280 interface_name: name.to_string(),
9281 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
9282 startup,
9283 });
9284 }
9285
9286 #[cfg(feature = "iface-pipe")]
9287 fn register_test_pipe(driver: &mut Driver, name: &str) {
9288 let startup = PipeRuntime {
9289 respawn_delay: Duration::from_secs(5),
9290 };
9291 driver.register_pipe_runtime(PipeRuntimeConfigHandle {
9292 interface_name: name.to_string(),
9293 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
9294 startup,
9295 });
9296 }
9297
9298 #[cfg(feature = "iface-rnode")]
9299 fn register_test_rnode(driver: &mut Driver, name: &str) {
9300 let startup = RNodeRuntime {
9301 sub: RNodeSubConfig {
9302 name: name.to_string(),
9303 frequency: 868_000_000,
9304 bandwidth: 125_000,
9305 txpower: 7,
9306 spreading_factor: 8,
9307 coding_rate: 5,
9308 flow_control: false,
9309 st_alock: None,
9310 lt_alock: None,
9311 },
9312 writer: None,
9313 };
9314 driver.register_rnode_runtime(RNodeRuntimeConfigHandle {
9315 interface_name: name.to_string(),
9316 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
9317 startup,
9318 });
9319 }
9320
9321 impl Callbacks for MockCallbacks {
9322 fn on_announce(&mut self, announced: crate::destination::AnnouncedIdentity) {
9323 self.announces
9324 .lock()
9325 .unwrap()
9326 .push((announced.dest_hash, announced.hops));
9327 }
9328
9329 fn on_path_updated(&mut self, dest_hash: DestHash, hops: u8) {
9330 self.paths.lock().unwrap().push((dest_hash, hops));
9331 }
9332
9333 fn on_local_delivery(
9334 &mut self,
9335 dest_hash: DestHash,
9336 _raw: Vec<u8>,
9337 _packet_hash: PacketHash,
9338 ) {
9339 self.deliveries.lock().unwrap().push(dest_hash);
9340 }
9341
9342 fn on_interface_up(&mut self, id: InterfaceId) {
9343 self.iface_ups.lock().unwrap().push(id);
9344 }
9345
9346 fn on_interface_down(&mut self, id: InterfaceId) {
9347 self.iface_downs.lock().unwrap().push(id);
9348 }
9349
9350 fn on_link_established(
9351 &mut self,
9352 link_id: TypedLinkId,
9353 _dest_hash: DestHash,
9354 rtt: f64,
9355 is_initiator: bool,
9356 ) {
9357 self.link_established
9358 .lock()
9359 .unwrap()
9360 .push((link_id, rtt, is_initiator));
9361 }
9362
9363 fn on_link_closed(
9364 &mut self,
9365 link_id: TypedLinkId,
9366 _reason: Option<rns_core::link::TeardownReason>,
9367 ) {
9368 self.link_closed.lock().unwrap().push(link_id);
9369 }
9370
9371 fn on_remote_identified(
9372 &mut self,
9373 link_id: TypedLinkId,
9374 identity_hash: IdentityHash,
9375 _public_key: [u8; 64],
9376 ) {
9377 self.remote_identified
9378 .lock()
9379 .unwrap()
9380 .push((link_id, identity_hash));
9381 }
9382
9383 fn on_resource_received(
9384 &mut self,
9385 link_id: TypedLinkId,
9386 data: Vec<u8>,
9387 _metadata: Option<Vec<u8>>,
9388 ) {
9389 self.resources_received
9390 .lock()
9391 .unwrap()
9392 .push((link_id, data));
9393 }
9394
9395 fn on_resource_completed(&mut self, link_id: TypedLinkId) {
9396 self.resource_completed.lock().unwrap().push(link_id);
9397 }
9398
9399 fn on_resource_failed(&mut self, link_id: TypedLinkId, error: String) {
9400 self.resource_failed.lock().unwrap().push((link_id, error));
9401 }
9402
9403 fn on_channel_message(&mut self, link_id: TypedLinkId, msgtype: u16, payload: Vec<u8>) {
9404 self.channel_messages
9405 .lock()
9406 .unwrap()
9407 .push((link_id, msgtype, payload));
9408 }
9409
9410 fn on_link_data(&mut self, link_id: TypedLinkId, context: u8, data: Vec<u8>) {
9411 self.link_data
9412 .lock()
9413 .unwrap()
9414 .push((link_id, context, data));
9415 }
9416
9417 fn on_response(&mut self, link_id: TypedLinkId, request_id: [u8; 16], data: Vec<u8>) {
9418 self.responses
9419 .lock()
9420 .unwrap()
9421 .push((link_id, request_id, data));
9422 }
9423
9424 fn on_proof(&mut self, dest_hash: DestHash, packet_hash: PacketHash, rtt: f64) {
9425 self.proofs
9426 .lock()
9427 .unwrap()
9428 .push((dest_hash, packet_hash, rtt));
9429 }
9430
9431 fn on_proof_requested(&mut self, dest_hash: DestHash, packet_hash: PacketHash) -> bool {
9432 self.proof_requested
9433 .lock()
9434 .unwrap()
9435 .push((dest_hash, packet_hash));
9436 true
9437 }
9438 }
9439
9440 fn make_interface_info(id: u64) -> InterfaceInfo {
9441 InterfaceInfo {
9442 id: InterfaceId(id),
9443 name: format!("test-{}", id),
9444 mode: constants::MODE_FULL,
9445 out_capable: true,
9446 in_capable: true,
9447 bitrate: None,
9448 announce_rate_target: None,
9449 announce_rate_grace: 0,
9450 announce_rate_penalty: 0.0,
9451 announce_cap: rns_core::constants::ANNOUNCE_CAP,
9452 is_local_client: false,
9453 wants_tunnel: false,
9454 tunnel_id: None,
9455 mtu: constants::MTU as u32,
9456 ia_freq: 0.0,
9457 started: 0.0,
9458 ingress_control: rns_core::transport::types::IngressControlConfig::disabled(),
9459 }
9460 }
9461
9462 fn make_entry(id: u64, writer: Box<dyn Writer>, online: bool) -> InterfaceEntry {
9463 InterfaceEntry {
9464 id: InterfaceId(id),
9465 info: make_interface_info(id),
9466 writer,
9467 async_writer_metrics: None,
9468 enabled: true,
9469 online,
9470 dynamic: false,
9471 ifac: None,
9472 stats: InterfaceStats::default(),
9473 interface_type: String::new(),
9474 send_retry_at: None,
9475 send_retry_backoff: Duration::ZERO,
9476 }
9477 }
9478
9479 fn build_announce_packet(identity: &Identity) -> Vec<u8> {
9481 let dest_hash =
9482 rns_core::destination::destination_hash("test", &["app"], Some(identity.hash()));
9483 let name_hash = rns_core::destination::name_hash("test", &["app"]);
9484 let random_hash = [0x42u8; 10];
9485
9486 let (announce_data, _has_ratchet) =
9487 AnnounceData::pack(identity, &dest_hash, &name_hash, &random_hash, None, None).unwrap();
9488
9489 let flags = PacketFlags {
9490 header_type: constants::HEADER_1,
9491 context_flag: constants::FLAG_UNSET,
9492 transport_type: constants::TRANSPORT_BROADCAST,
9493 destination_type: constants::DESTINATION_SINGLE,
9494 packet_type: constants::PACKET_TYPE_ANNOUNCE,
9495 };
9496
9497 let packet = RawPacket::pack(
9498 flags,
9499 0,
9500 &dest_hash,
9501 None,
9502 constants::CONTEXT_NONE,
9503 &announce_data,
9504 )
9505 .unwrap();
9506 packet.raw
9507 }
9508
9509 #[test]
9510 fn process_inbound_frame() {
9511 let (tx, rx) = event::channel();
9512 let (cbs, announces, _, _, _, _) = MockCallbacks::new();
9513 let mut driver = Driver::new(
9514 TransportConfig {
9515 transport_enabled: false,
9516 identity_hash: None,
9517 prefer_shorter_path: false,
9518 max_paths_per_destination: 1,
9519 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9520 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9521 max_path_destinations: usize::MAX,
9522 max_tunnel_destinations_total: usize::MAX,
9523 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9524 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9525 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9526 announce_sig_cache_enabled: true,
9527 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9528 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9529 announce_queue_max_entries: 256,
9530 announce_queue_max_interfaces: 1024,
9531 },
9532 rx,
9533 tx.clone(),
9534 Box::new(cbs),
9535 );
9536 let info = make_interface_info(1);
9537 driver.engine.register_interface(info.clone());
9538 let (writer, _sent) = MockWriter::new();
9539 driver
9540 .interfaces
9541 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
9542
9543 let identity = Identity::new(&mut OsRng);
9544 let announce_raw = build_announce_packet(&identity);
9545
9546 tx.send(Event::Frame {
9548 interface_id: InterfaceId(1),
9549 data: announce_raw,
9550 })
9551 .unwrap();
9552 tx.send(Event::Shutdown).unwrap();
9553 driver.run();
9554
9555 assert_eq!(announces.lock().unwrap().len(), 1);
9556 }
9557
9558 #[test]
9559 fn dispatch_send() {
9560 let (tx, rx) = event::channel();
9561 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9562 let mut driver = Driver::new(
9563 TransportConfig {
9564 transport_enabled: false,
9565 identity_hash: None,
9566 prefer_shorter_path: false,
9567 max_paths_per_destination: 1,
9568 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9569 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9570 max_path_destinations: usize::MAX,
9571 max_tunnel_destinations_total: usize::MAX,
9572 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9573 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9574 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9575 announce_sig_cache_enabled: true,
9576 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9577 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9578 announce_queue_max_entries: 256,
9579 announce_queue_max_interfaces: 1024,
9580 },
9581 rx,
9582 tx.clone(),
9583 Box::new(cbs),
9584 );
9585 let (writer, sent) = MockWriter::new();
9586 driver
9587 .interfaces
9588 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
9589
9590 driver.dispatch_all(vec![TransportAction::SendOnInterface {
9591 interface: InterfaceId(1),
9592 raw: vec![0x01, 0x02, 0x03],
9593 }]);
9594
9595 assert_eq!(sent.lock().unwrap().len(), 1);
9596 assert_eq!(sent.lock().unwrap()[0], vec![0x01, 0x02, 0x03]);
9597
9598 drop(tx);
9599 }
9600
9601 #[test]
9602 fn dispatch_broadcast() {
9603 let (tx, rx) = event::channel();
9604 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9605 let mut driver = Driver::new(
9606 TransportConfig {
9607 transport_enabled: false,
9608 identity_hash: None,
9609 prefer_shorter_path: false,
9610 max_paths_per_destination: 1,
9611 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9612 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9613 max_path_destinations: usize::MAX,
9614 max_tunnel_destinations_total: usize::MAX,
9615 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9616 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9617 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9618 announce_sig_cache_enabled: true,
9619 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9620 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9621 announce_queue_max_entries: 256,
9622 announce_queue_max_interfaces: 1024,
9623 },
9624 rx,
9625 tx.clone(),
9626 Box::new(cbs),
9627 );
9628
9629 let (w1, sent1) = MockWriter::new();
9630 let (w2, sent2) = MockWriter::new();
9631 driver
9632 .interfaces
9633 .insert(InterfaceId(1), make_entry(1, Box::new(w1), true));
9634 driver
9635 .interfaces
9636 .insert(InterfaceId(2), make_entry(2, Box::new(w2), true));
9637
9638 driver.dispatch_all(vec![TransportAction::BroadcastOnAllInterfaces {
9639 raw: vec![0xAA],
9640 exclude: None,
9641 }]);
9642
9643 assert_eq!(sent1.lock().unwrap().len(), 1);
9644 assert_eq!(sent2.lock().unwrap().len(), 1);
9645
9646 drop(tx);
9647 }
9648
9649 #[test]
9650 fn dispatch_broadcast_exclude() {
9651 let (tx, rx) = event::channel();
9652 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9653 let mut driver = Driver::new(
9654 TransportConfig {
9655 transport_enabled: false,
9656 identity_hash: None,
9657 prefer_shorter_path: false,
9658 max_paths_per_destination: 1,
9659 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9660 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9661 max_path_destinations: usize::MAX,
9662 max_tunnel_destinations_total: usize::MAX,
9663 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9664 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9665 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9666 announce_sig_cache_enabled: true,
9667 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9668 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9669 announce_queue_max_entries: 256,
9670 announce_queue_max_interfaces: 1024,
9671 },
9672 rx,
9673 tx.clone(),
9674 Box::new(cbs),
9675 );
9676
9677 let (w1, sent1) = MockWriter::new();
9678 let (w2, sent2) = MockWriter::new();
9679 driver
9680 .interfaces
9681 .insert(InterfaceId(1), make_entry(1, Box::new(w1), true));
9682 driver
9683 .interfaces
9684 .insert(InterfaceId(2), make_entry(2, Box::new(w2), true));
9685
9686 driver.dispatch_all(vec![TransportAction::BroadcastOnAllInterfaces {
9687 raw: vec![0xBB],
9688 exclude: Some(InterfaceId(1)),
9689 }]);
9690
9691 assert_eq!(sent1.lock().unwrap().len(), 0); assert_eq!(sent2.lock().unwrap().len(), 1);
9693
9694 drop(tx);
9695 }
9696
9697 #[test]
9698 fn tick_event() {
9699 let (tx, rx) = event::channel();
9700 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9701 let mut driver = Driver::new(
9702 TransportConfig {
9703 transport_enabled: true,
9704 identity_hash: Some([0x42; 16]),
9705 prefer_shorter_path: false,
9706 max_paths_per_destination: 1,
9707 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9708 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9709 max_path_destinations: usize::MAX,
9710 max_tunnel_destinations_total: usize::MAX,
9711 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9712 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9713 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9714 announce_sig_cache_enabled: true,
9715 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9716 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9717 announce_queue_max_entries: 256,
9718 announce_queue_max_interfaces: 1024,
9719 },
9720 rx,
9721 tx.clone(),
9722 Box::new(cbs),
9723 );
9724 let info = make_interface_info(1);
9725 driver.engine.register_interface(info.clone());
9726 let (writer, _sent) = MockWriter::new();
9727 driver
9728 .interfaces
9729 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
9730
9731 tx.send(Event::Tick).unwrap();
9733 tx.send(Event::Shutdown).unwrap();
9734 driver.run();
9735 }
9737
9738 #[test]
9739 fn shutdown_event() {
9740 let (tx, rx) = event::channel();
9741 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9742 let mut driver = Driver::new(
9743 TransportConfig {
9744 transport_enabled: false,
9745 identity_hash: None,
9746 prefer_shorter_path: false,
9747 max_paths_per_destination: 1,
9748 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9749 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9750 max_path_destinations: usize::MAX,
9751 max_tunnel_destinations_total: usize::MAX,
9752 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9753 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9754 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9755 announce_sig_cache_enabled: true,
9756 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9757 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9758 announce_queue_max_entries: 256,
9759 announce_queue_max_interfaces: 1024,
9760 },
9761 rx,
9762 tx.clone(),
9763 Box::new(cbs),
9764 );
9765
9766 tx.send(Event::Shutdown).unwrap();
9767 driver.run(); }
9769
9770 #[test]
9771 fn begin_drain_updates_driver_status() {
9772 let (tx, rx) = event::channel();
9773 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9774 let mut driver = Driver::new(
9775 TransportConfig {
9776 transport_enabled: false,
9777 identity_hash: None,
9778 prefer_shorter_path: false,
9779 max_paths_per_destination: 1,
9780 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9781 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9782 max_path_destinations: usize::MAX,
9783 max_tunnel_destinations_total: usize::MAX,
9784 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9785 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9786 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9787 announce_sig_cache_enabled: true,
9788 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9789 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9790 announce_queue_max_entries: 256,
9791 announce_queue_max_interfaces: 1024,
9792 },
9793 rx,
9794 tx,
9795 Box::new(cbs),
9796 );
9797
9798 driver.begin_drain(Duration::from_secs(3));
9799
9800 let QueryResponse::DrainStatus(status) = driver.handle_query(QueryRequest::DrainStatus)
9801 else {
9802 panic!("expected drain status response");
9803 };
9804 assert_eq!(status.state, LifecycleState::Draining);
9805 assert!(status.drain_complete);
9806 assert!(status.drain_age_seconds.is_some());
9807 assert!(status.deadline_remaining_seconds.is_some());
9808 assert_eq!(
9809 status.detail.as_deref(),
9810 Some("node is draining existing work; no active links, resource transfers, hole-punch sessions, or queued writer/provider work remain")
9811 );
9812 }
9813
9814 #[test]
9815 fn begin_drain_with_pending_link_reports_incomplete_status() {
9816 let (tx, rx) = event::channel();
9817 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9818 let mut driver = Driver::new(
9819 TransportConfig {
9820 transport_enabled: false,
9821 identity_hash: None,
9822 prefer_shorter_path: false,
9823 max_paths_per_destination: 1,
9824 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9825 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9826 max_path_destinations: usize::MAX,
9827 max_tunnel_destinations_total: usize::MAX,
9828 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9829 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9830 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9831 announce_sig_cache_enabled: true,
9832 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9833 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9834 announce_queue_max_entries: 256,
9835 announce_queue_max_interfaces: 1024,
9836 },
9837 rx,
9838 tx,
9839 Box::new(cbs),
9840 );
9841
9842 let _ = driver.link_manager.create_link(
9843 &[0xDD; 16],
9844 &[0x11; 32],
9845 1,
9846 rns_core::constants::MTU as u32,
9847 &mut OsRng,
9848 );
9849
9850 driver.begin_drain(Duration::from_secs(3));
9851
9852 let QueryResponse::DrainStatus(status) = driver.handle_query(QueryRequest::DrainStatus)
9853 else {
9854 panic!("expected drain status response");
9855 };
9856 assert_eq!(status.state, LifecycleState::Draining);
9857 assert!(!status.drain_complete);
9858 assert!(status
9859 .detail
9860 .unwrap_or_default()
9861 .contains("1 link(s) still active"));
9862 }
9863
9864 #[test]
9865 fn begin_drain_with_queued_writer_frames_reports_incomplete_status() {
9866 let (tx, rx) = event::channel();
9867 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9868 let mut driver = Driver::new(
9869 TransportConfig {
9870 transport_enabled: false,
9871 identity_hash: None,
9872 prefer_shorter_path: false,
9873 max_paths_per_destination: 1,
9874 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9875 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9876 max_path_destinations: usize::MAX,
9877 max_tunnel_destinations_total: usize::MAX,
9878 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9879 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9880 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9881 announce_sig_cache_enabled: true,
9882 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9883 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9884 announce_queue_max_entries: 256,
9885 announce_queue_max_interfaces: 1024,
9886 },
9887 rx,
9888 tx,
9889 Box::new(cbs),
9890 );
9891
9892 let info = make_interface_info(77);
9893 let (entered_tx, entered_rx) = std::sync::mpsc::channel();
9894 let (release_tx, release_rx) = std::sync::mpsc::channel();
9895 let (writer, async_writer_metrics) = crate::interface::wrap_async_writer(
9896 Box::new(BlockingWriter {
9897 entered_tx,
9898 release_rx,
9899 }),
9900 InterfaceId(77),
9901 &info.name,
9902 driver.event_tx.clone(),
9903 1,
9904 );
9905
9906 driver.interfaces.insert(
9907 InterfaceId(77),
9908 InterfaceEntry {
9909 id: InterfaceId(77),
9910 info,
9911 writer,
9912 async_writer_metrics: Some(async_writer_metrics),
9913 enabled: true,
9914 online: true,
9915 dynamic: false,
9916 ifac: None,
9917 stats: InterfaceStats::default(),
9918 interface_type: "TestInterface".to_string(),
9919 send_retry_at: None,
9920 send_retry_backoff: Duration::ZERO,
9921 },
9922 );
9923
9924 driver.dispatch_all(vec![TransportAction::SendOnInterface {
9925 interface: InterfaceId(77),
9926 raw: vec![0x01],
9927 }]);
9928 entered_rx.recv_timeout(Duration::from_secs(1)).unwrap();
9929 driver.dispatch_all(vec![TransportAction::SendOnInterface {
9930 interface: InterfaceId(77),
9931 raw: vec![0x02],
9932 }]);
9933
9934 driver.begin_drain(Duration::from_secs(3));
9935
9936 let QueryResponse::DrainStatus(status) = driver.handle_query(QueryRequest::DrainStatus)
9937 else {
9938 panic!("expected drain status response");
9939 };
9940 assert_eq!(status.state, LifecycleState::Draining);
9941 assert!(!status.drain_complete);
9942 assert_eq!(status.interface_writer_queued_frames, 1);
9943 assert!(status
9944 .detail
9945 .unwrap_or_default()
9946 .contains("queued interface writer frame"));
9947
9948 let _ = release_tx.send(());
9949 }
9950
9951 #[test]
9952 fn enforce_drain_deadline_tears_down_remaining_links() {
9953 let (tx, rx) = event::channel();
9954 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9955 let mut driver = Driver::new(
9956 TransportConfig {
9957 transport_enabled: false,
9958 identity_hash: None,
9959 prefer_shorter_path: false,
9960 max_paths_per_destination: 1,
9961 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9962 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9963 max_path_destinations: usize::MAX,
9964 max_tunnel_destinations_total: usize::MAX,
9965 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9966 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9967 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9968 announce_sig_cache_enabled: true,
9969 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9970 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9971 announce_queue_max_entries: 256,
9972 announce_queue_max_interfaces: 1024,
9973 },
9974 rx,
9975 tx,
9976 Box::new(cbs),
9977 );
9978
9979 let _ = driver.link_manager.create_link(
9980 &[0xDD; 16],
9981 &[0x11; 32],
9982 1,
9983 rns_core::constants::MTU as u32,
9984 &mut OsRng,
9985 );
9986 driver.begin_drain(Duration::ZERO);
9987
9988 driver.enforce_drain_deadline();
9989
9990 assert_eq!(driver.lifecycle_state, LifecycleState::Stopping);
9991 assert_eq!(driver.link_manager.link_count(), 0);
9992 let QueryResponse::DrainStatus(status) = driver.handle_query(QueryRequest::DrainStatus)
9993 else {
9994 panic!("expected drain status response");
9995 };
9996 assert!(status.drain_complete);
9997 assert_eq!(status.state, LifecycleState::Stopping);
9998 }
9999
10000 #[test]
10001 fn begin_drain_with_holepunch_session_reports_incomplete_status_and_deadline_aborts_it() {
10002 let (tx, rx) = event::channel();
10003 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10004 let mut driver = Driver::new(
10005 TransportConfig {
10006 transport_enabled: false,
10007 identity_hash: None,
10008 prefer_shorter_path: false,
10009 max_paths_per_destination: 1,
10010 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10011 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10012 max_path_destinations: usize::MAX,
10013 max_tunnel_destinations_total: usize::MAX,
10014 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10015 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10016 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10017 announce_sig_cache_enabled: true,
10018 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10019 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10020 announce_queue_max_entries: 256,
10021 announce_queue_max_interfaces: 1024,
10022 },
10023 rx,
10024 tx,
10025 Box::new(cbs),
10026 );
10027 driver.holepunch_manager = crate::holepunch::orchestrator::HolePunchManager::new(
10028 vec!["127.0.0.1:4343".parse().unwrap()],
10029 rns_core::holepunch::ProbeProtocol::Rnsp,
10030 None,
10031 );
10032
10033 let _ = driver.holepunch_manager.propose(
10034 [0x44; 16],
10035 &[0xAA; 32],
10036 &mut OsRng,
10037 &driver.get_event_sender(),
10038 );
10039 assert_eq!(driver.holepunch_manager.session_count(), 1);
10040
10041 driver.begin_drain(Duration::from_secs(3));
10042
10043 let QueryResponse::DrainStatus(status) = driver.handle_query(QueryRequest::DrainStatus)
10044 else {
10045 panic!("expected drain status response");
10046 };
10047 assert_eq!(status.state, LifecycleState::Draining);
10048 assert!(!status.drain_complete);
10049 assert!(status
10050 .detail
10051 .unwrap_or_default()
10052 .contains("1 hole-punch session(s) still active"));
10053
10054 driver.begin_drain(Duration::ZERO);
10055 driver.enforce_drain_deadline();
10056
10057 assert_eq!(driver.holepunch_manager.session_count(), 0);
10058 let QueryResponse::DrainStatus(status) = driver.handle_query(QueryRequest::DrainStatus)
10059 else {
10060 panic!("expected drain status response");
10061 };
10062 assert!(status.drain_complete);
10063 assert_eq!(status.state, LifecycleState::Stopping);
10064 }
10065
10066 #[test]
10067 fn begin_drain_event_is_processed_by_run_loop() {
10068 let (tx, rx) = event::channel();
10069 let tx_query = tx.clone();
10070 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10071 let mut driver = Driver::new(
10072 TransportConfig {
10073 transport_enabled: false,
10074 identity_hash: None,
10075 prefer_shorter_path: false,
10076 max_paths_per_destination: 1,
10077 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10078 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10079 max_path_destinations: usize::MAX,
10080 max_tunnel_destinations_total: usize::MAX,
10081 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10082 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10083 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10084 announce_sig_cache_enabled: true,
10085 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10086 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10087 announce_queue_max_entries: 256,
10088 announce_queue_max_interfaces: 1024,
10089 },
10090 rx,
10091 tx.clone(),
10092 Box::new(cbs),
10093 );
10094
10095 let handle = std::thread::spawn(move || driver.run());
10096 tx.send(Event::BeginDrain {
10097 timeout: Duration::from_secs(2),
10098 })
10099 .unwrap();
10100 let (resp_tx, resp_rx) = std::sync::mpsc::channel();
10101 tx_query
10102 .send(Event::Query(QueryRequest::DrainStatus, resp_tx))
10103 .unwrap();
10104 let status = match resp_rx.recv().unwrap() {
10105 QueryResponse::DrainStatus(status) => status,
10106 other => panic!("expected drain status response, got {:?}", other),
10107 };
10108 assert_eq!(status.state, LifecycleState::Draining);
10109 tx_query.send(Event::Shutdown).unwrap();
10110 handle.join().unwrap();
10111 }
10112
10113 #[test]
10114 fn send_channel_message_returns_error_while_draining() {
10115 let (tx, rx) = event::channel();
10116 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10117 let mut driver = Driver::new(
10118 TransportConfig {
10119 transport_enabled: false,
10120 identity_hash: None,
10121 prefer_shorter_path: false,
10122 max_paths_per_destination: 1,
10123 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10124 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10125 max_path_destinations: usize::MAX,
10126 max_tunnel_destinations_total: usize::MAX,
10127 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10128 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10129 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10130 announce_sig_cache_enabled: true,
10131 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10132 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10133 announce_queue_max_entries: 256,
10134 announce_queue_max_interfaces: 1024,
10135 },
10136 rx,
10137 tx.clone(),
10138 Box::new(cbs),
10139 );
10140
10141 tx.send(Event::BeginDrain {
10142 timeout: Duration::from_secs(2),
10143 })
10144 .unwrap();
10145 let (resp_tx, resp_rx) = mpsc::channel();
10146 tx.send(Event::SendChannelMessage {
10147 link_id: [0xAA; 16],
10148 msgtype: 7,
10149 payload: b"drain".to_vec(),
10150 response_tx: resp_tx,
10151 })
10152 .unwrap();
10153 tx.send(Event::Shutdown).unwrap();
10154 driver.run();
10155
10156 let response = resp_rx.recv().unwrap();
10157 assert_eq!(
10158 response,
10159 Err("cannot send channel message while node is draining".into())
10160 );
10161 }
10162
10163 #[test]
10164 fn send_outbound_is_ignored_while_draining() {
10165 let (tx, rx) = event::channel();
10166 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10167 let mut driver = Driver::new(
10168 TransportConfig {
10169 transport_enabled: false,
10170 identity_hash: None,
10171 prefer_shorter_path: false,
10172 max_paths_per_destination: 1,
10173 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10174 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10175 max_path_destinations: usize::MAX,
10176 max_tunnel_destinations_total: usize::MAX,
10177 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10178 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10179 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10180 announce_sig_cache_enabled: true,
10181 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10182 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10183 announce_queue_max_entries: 256,
10184 announce_queue_max_interfaces: 1024,
10185 },
10186 rx,
10187 tx.clone(),
10188 Box::new(cbs),
10189 );
10190 let identity = Identity::new(&mut OsRng);
10191 let info = make_interface_info(1);
10192 driver.engine.register_interface(info);
10193 let (writer, sent) = MockWriter::new();
10194 driver
10195 .interfaces
10196 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10197
10198 tx.send(Event::BeginDrain {
10199 timeout: Duration::from_secs(2),
10200 })
10201 .unwrap();
10202 tx.send(Event::SendOutbound {
10203 raw: build_announce_packet(&identity),
10204 dest_type: constants::DESTINATION_SINGLE,
10205 attached_interface: None,
10206 })
10207 .unwrap();
10208 tx.send(Event::Shutdown).unwrap();
10209 driver.run();
10210
10211 assert!(sent.lock().unwrap().is_empty());
10212 assert!(driver.sent_packets.is_empty());
10213 }
10214
10215 #[test]
10216 fn request_path_is_ignored_while_draining() {
10217 let (tx, rx) = event::channel();
10218 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10219 let mut driver = Driver::new(
10220 TransportConfig {
10221 transport_enabled: false,
10222 identity_hash: None,
10223 prefer_shorter_path: false,
10224 max_paths_per_destination: 1,
10225 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10226 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10227 max_path_destinations: usize::MAX,
10228 max_tunnel_destinations_total: usize::MAX,
10229 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10230 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10231 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10232 announce_sig_cache_enabled: true,
10233 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10234 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10235 announce_queue_max_entries: 256,
10236 announce_queue_max_interfaces: 1024,
10237 },
10238 rx,
10239 tx.clone(),
10240 Box::new(cbs),
10241 );
10242 let info = make_interface_info(1);
10243 driver.engine.register_interface(info);
10244 let (writer, sent) = MockWriter::new();
10245 driver
10246 .interfaces
10247 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10248
10249 tx.send(Event::BeginDrain {
10250 timeout: Duration::from_secs(2),
10251 })
10252 .unwrap();
10253 tx.send(Event::RequestPath {
10254 dest_hash: [0xAA; 16],
10255 })
10256 .unwrap();
10257 tx.send(Event::Shutdown).unwrap();
10258 driver.run();
10259
10260 assert!(sent.lock().unwrap().is_empty());
10261 }
10262
10263 #[test]
10264 fn create_link_returns_zero_link_id_while_draining() {
10265 let (tx, rx) = event::channel();
10266 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10267 let mut driver = Driver::new(
10268 TransportConfig {
10269 transport_enabled: false,
10270 identity_hash: None,
10271 prefer_shorter_path: false,
10272 max_paths_per_destination: 1,
10273 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10274 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10275 max_path_destinations: usize::MAX,
10276 max_tunnel_destinations_total: usize::MAX,
10277 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10278 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10279 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10280 announce_sig_cache_enabled: true,
10281 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10282 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10283 announce_queue_max_entries: 256,
10284 announce_queue_max_interfaces: 1024,
10285 },
10286 rx,
10287 tx.clone(),
10288 Box::new(cbs),
10289 );
10290
10291 tx.send(Event::BeginDrain {
10292 timeout: Duration::from_secs(2),
10293 })
10294 .unwrap();
10295 let (resp_tx, resp_rx) = mpsc::channel();
10296 tx.send(Event::CreateLink {
10297 dest_hash: [0xAB; 16],
10298 dest_sig_pub_bytes: [0xCD; 32],
10299 response_tx: resp_tx,
10300 })
10301 .unwrap();
10302 tx.send(Event::Shutdown).unwrap();
10303 driver.run();
10304
10305 assert_eq!(resp_rx.recv().unwrap(), [0u8; 16]);
10306 }
10307
10308 #[test]
10309 fn announce_callback() {
10310 let (tx, rx) = event::channel();
10311 let (cbs, announces, paths, _, _, _) = MockCallbacks::new();
10312 let mut driver = Driver::new(
10313 TransportConfig {
10314 transport_enabled: false,
10315 identity_hash: None,
10316 prefer_shorter_path: false,
10317 max_paths_per_destination: 1,
10318 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10319 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10320 max_path_destinations: usize::MAX,
10321 max_tunnel_destinations_total: usize::MAX,
10322 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10323 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10324 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10325 announce_sig_cache_enabled: true,
10326 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10327 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10328 announce_queue_max_entries: 256,
10329 announce_queue_max_interfaces: 1024,
10330 },
10331 rx,
10332 tx.clone(),
10333 Box::new(cbs),
10334 );
10335 let info = make_interface_info(1);
10336 driver.engine.register_interface(info.clone());
10337 let (writer, _sent) = MockWriter::new();
10338 driver
10339 .interfaces
10340 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10341
10342 let identity = Identity::new(&mut OsRng);
10343 let announce_raw = build_announce_packet(&identity);
10344
10345 tx.send(Event::Frame {
10346 interface_id: InterfaceId(1),
10347 data: announce_raw,
10348 })
10349 .unwrap();
10350 tx.send(Event::Shutdown).unwrap();
10351 driver.run();
10352
10353 let ann = announces.lock().unwrap();
10354 assert_eq!(ann.len(), 1);
10355 assert_eq!(ann[0].1, 1);
10357
10358 let p = paths.lock().unwrap();
10359 assert_eq!(p.len(), 1);
10360 }
10361
10362 #[test]
10363 fn dispatch_skips_offline_interface() {
10364 let (tx, rx) = event::channel();
10365 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10366 let mut driver = Driver::new(
10367 TransportConfig {
10368 transport_enabled: false,
10369 identity_hash: None,
10370 prefer_shorter_path: false,
10371 max_paths_per_destination: 1,
10372 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10373 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10374 max_path_destinations: usize::MAX,
10375 max_tunnel_destinations_total: usize::MAX,
10376 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10377 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10378 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10379 announce_sig_cache_enabled: true,
10380 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10381 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10382 announce_queue_max_entries: 256,
10383 announce_queue_max_interfaces: 1024,
10384 },
10385 rx,
10386 tx.clone(),
10387 Box::new(cbs),
10388 );
10389
10390 let (w1, sent1) = MockWriter::new();
10391 let (w2, sent2) = MockWriter::new();
10392 driver
10393 .interfaces
10394 .insert(InterfaceId(1), make_entry(1, Box::new(w1), false)); driver
10396 .interfaces
10397 .insert(InterfaceId(2), make_entry(2, Box::new(w2), true));
10398
10399 driver.dispatch_all(vec![TransportAction::SendOnInterface {
10401 interface: InterfaceId(1),
10402 raw: vec![0x01],
10403 }]);
10404 assert_eq!(sent1.lock().unwrap().len(), 0);
10405
10406 driver.dispatch_all(vec![TransportAction::BroadcastOnAllInterfaces {
10408 raw: vec![0x02],
10409 exclude: None,
10410 }]);
10411 assert_eq!(sent1.lock().unwrap().len(), 0); assert_eq!(sent2.lock().unwrap().len(), 1);
10413
10414 drop(tx);
10415 }
10416
10417 #[test]
10418 fn interface_up_refreshes_writer() {
10419 let (tx, rx) = event::channel();
10420 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10421 let mut driver = Driver::new(
10422 TransportConfig {
10423 transport_enabled: false,
10424 identity_hash: None,
10425 prefer_shorter_path: false,
10426 max_paths_per_destination: 1,
10427 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10428 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10429 max_path_destinations: usize::MAX,
10430 max_tunnel_destinations_total: usize::MAX,
10431 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10432 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10433 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10434 announce_sig_cache_enabled: true,
10435 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10436 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10437 announce_queue_max_entries: 256,
10438 announce_queue_max_interfaces: 1024,
10439 },
10440 rx,
10441 tx.clone(),
10442 Box::new(cbs),
10443 );
10444
10445 let (w_old, sent_old) = MockWriter::new();
10446 driver
10447 .interfaces
10448 .insert(InterfaceId(1), make_entry(1, Box::new(w_old), false));
10449
10450 let (w_new, sent_new) = MockWriter::new();
10452 tx.send(Event::InterfaceUp(
10453 InterfaceId(1),
10454 Some(Box::new(w_new)),
10455 None,
10456 ))
10457 .unwrap();
10458 tx.send(Event::Shutdown).unwrap();
10459 driver.run();
10460
10461 assert!(driver.interfaces[&InterfaceId(1)].online);
10463
10464 driver.dispatch_all(vec![TransportAction::SendOnInterface {
10466 interface: InterfaceId(1),
10467 raw: vec![0xFF],
10468 }]);
10469
10470 assert_eq!(sent_old.lock().unwrap().len(), 0);
10472 wait_for_sent_len(&sent_new, 1);
10474 assert_eq!(sent_new.lock().unwrap()[0], vec![0xFF]);
10475
10476 drop(tx);
10477 }
10478
10479 #[test]
10480 fn dynamic_interface_register() {
10481 let (tx, rx) = event::channel();
10482 let (cbs, _, _, _, iface_ups, _) = MockCallbacks::new();
10483 let mut driver = Driver::new(
10484 TransportConfig {
10485 transport_enabled: false,
10486 identity_hash: None,
10487 prefer_shorter_path: false,
10488 max_paths_per_destination: 1,
10489 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10490 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10491 max_path_destinations: usize::MAX,
10492 max_tunnel_destinations_total: usize::MAX,
10493 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10494 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10495 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10496 announce_sig_cache_enabled: true,
10497 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10498 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10499 announce_queue_max_entries: 256,
10500 announce_queue_max_interfaces: 1024,
10501 },
10502 rx,
10503 tx.clone(),
10504 Box::new(cbs),
10505 );
10506
10507 let info = make_interface_info(100);
10508 let (writer, sent) = MockWriter::new();
10509
10510 tx.send(Event::InterfaceUp(
10512 InterfaceId(100),
10513 Some(Box::new(writer)),
10514 Some(info),
10515 ))
10516 .unwrap();
10517 tx.send(Event::Shutdown).unwrap();
10518 driver.run();
10519
10520 assert!(driver.interfaces.contains_key(&InterfaceId(100)));
10522 assert!(driver.interfaces[&InterfaceId(100)].online);
10523 assert!(driver.interfaces[&InterfaceId(100)].dynamic);
10524
10525 assert_eq!(iface_ups.lock().unwrap().len(), 1);
10527 assert_eq!(iface_ups.lock().unwrap()[0], InterfaceId(100));
10528
10529 driver.dispatch_all(vec![TransportAction::SendOnInterface {
10531 interface: InterfaceId(100),
10532 raw: vec![0x42],
10533 }]);
10534 wait_for_sent_len(&sent, 1);
10535
10536 drop(tx);
10537 }
10538
10539 #[test]
10540 fn dynamic_interface_deregister() {
10541 let (tx, rx) = event::channel();
10542 let (cbs, _, _, _, _, iface_downs) = MockCallbacks::new();
10543 let mut driver = Driver::new(
10544 TransportConfig {
10545 transport_enabled: false,
10546 identity_hash: None,
10547 prefer_shorter_path: false,
10548 max_paths_per_destination: 1,
10549 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10550 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10551 max_path_destinations: usize::MAX,
10552 max_tunnel_destinations_total: usize::MAX,
10553 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10554 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10555 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10556 announce_sig_cache_enabled: true,
10557 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10558 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10559 announce_queue_max_entries: 256,
10560 announce_queue_max_interfaces: 1024,
10561 },
10562 rx,
10563 tx.clone(),
10564 Box::new(cbs),
10565 );
10566
10567 let info = make_interface_info(200);
10569 driver.engine.register_interface(info.clone());
10570 let (writer, _sent) = MockWriter::new();
10571 driver.interfaces.insert(
10572 InterfaceId(200),
10573 InterfaceEntry {
10574 id: InterfaceId(200),
10575 info,
10576 writer: Box::new(writer),
10577 async_writer_metrics: None,
10578 enabled: true,
10579 online: true,
10580 dynamic: true,
10581 ifac: None,
10582 stats: InterfaceStats::default(),
10583 interface_type: String::new(),
10584 send_retry_at: None,
10585 send_retry_backoff: Duration::ZERO,
10586 },
10587 );
10588
10589 tx.send(Event::InterfaceDown(InterfaceId(200))).unwrap();
10591 tx.send(Event::Shutdown).unwrap();
10592 driver.run();
10593
10594 assert!(!driver.interfaces.contains_key(&InterfaceId(200)));
10595 assert_eq!(iface_downs.lock().unwrap().len(), 1);
10596 assert_eq!(iface_downs.lock().unwrap()[0], InterfaceId(200));
10597 }
10598
10599 #[test]
10600 fn send_wouldblock_is_backed_off_between_dispatches() {
10601 let (tx, rx) = event::channel();
10602 let (cbs, ..) = MockCallbacks::new();
10603 let mut driver = Driver::new(
10604 TransportConfig {
10605 transport_enabled: false,
10606 identity_hash: None,
10607 prefer_shorter_path: false,
10608 max_paths_per_destination: 1,
10609 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10610 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10611 max_path_destinations: usize::MAX,
10612 max_tunnel_destinations_total: usize::MAX,
10613 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10614 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10615 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10616 announce_sig_cache_enabled: true,
10617 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10618 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10619 announce_queue_max_entries: 256,
10620 announce_queue_max_interfaces: 1024,
10621 },
10622 rx,
10623 tx,
10624 Box::new(cbs),
10625 );
10626 let (writer, attempts) = WouldBlockWriter::new();
10627 driver
10628 .interfaces
10629 .insert(InterfaceId(7), make_entry(7, Box::new(writer), true));
10630
10631 let action = TransportAction::SendOnInterface {
10632 interface: InterfaceId(7),
10633 raw: vec![0x01, 0x00, 0x42],
10634 };
10635 driver.dispatch_all(vec![action.clone()]);
10636 assert_eq!(*attempts.lock().unwrap(), 1);
10637
10638 driver.dispatch_all(vec![action.clone()]);
10639 assert_eq!(
10640 *attempts.lock().unwrap(),
10641 1,
10642 "second dispatch should be deferred during backoff"
10643 );
10644
10645 let entry = driver.interfaces.get_mut(&InterfaceId(7)).unwrap();
10646 entry.send_retry_at = Some(Instant::now() - Duration::from_millis(1));
10647 driver.dispatch_all(vec![action]);
10648 assert_eq!(*attempts.lock().unwrap(), 2);
10649 }
10650
10651 #[test]
10652 fn interface_callbacks_fire() {
10653 let (tx, rx) = event::channel();
10654 let (cbs, _, _, _, iface_ups, iface_downs) = MockCallbacks::new();
10655 let mut driver = Driver::new(
10656 TransportConfig {
10657 transport_enabled: false,
10658 identity_hash: None,
10659 prefer_shorter_path: false,
10660 max_paths_per_destination: 1,
10661 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10662 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10663 max_path_destinations: usize::MAX,
10664 max_tunnel_destinations_total: usize::MAX,
10665 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10666 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10667 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10668 announce_sig_cache_enabled: true,
10669 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10670 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10671 announce_queue_max_entries: 256,
10672 announce_queue_max_interfaces: 1024,
10673 },
10674 rx,
10675 tx.clone(),
10676 Box::new(cbs),
10677 );
10678
10679 let (writer, _) = MockWriter::new();
10681 driver
10682 .interfaces
10683 .insert(InterfaceId(1), make_entry(1, Box::new(writer), false));
10684
10685 tx.send(Event::InterfaceUp(InterfaceId(1), None, None))
10686 .unwrap();
10687 tx.send(Event::InterfaceDown(InterfaceId(1))).unwrap();
10688 tx.send(Event::Shutdown).unwrap();
10689 driver.run();
10690
10691 assert_eq!(iface_ups.lock().unwrap().len(), 1);
10692 assert_eq!(iface_downs.lock().unwrap().len(), 1);
10693 assert!(driver.interfaces.contains_key(&InterfaceId(1)));
10695 assert!(!driver.interfaces[&InterfaceId(1)].online);
10696 }
10697
10698 #[test]
10703 fn frame_updates_rx_stats() {
10704 let (tx, rx) = event::channel();
10705 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10706 let mut driver = Driver::new(
10707 TransportConfig {
10708 transport_enabled: false,
10709 identity_hash: None,
10710 prefer_shorter_path: false,
10711 max_paths_per_destination: 1,
10712 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10713 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10714 max_path_destinations: usize::MAX,
10715 max_tunnel_destinations_total: usize::MAX,
10716 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10717 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10718 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10719 announce_sig_cache_enabled: true,
10720 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10721 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10722 announce_queue_max_entries: 256,
10723 announce_queue_max_interfaces: 1024,
10724 },
10725 rx,
10726 tx.clone(),
10727 Box::new(cbs),
10728 );
10729 let info = make_interface_info(1);
10730 driver.engine.register_interface(info.clone());
10731 let (writer, _sent) = MockWriter::new();
10732 driver
10733 .interfaces
10734 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10735
10736 let identity = Identity::new(&mut OsRng);
10737 let announce_raw = build_announce_packet(&identity);
10738 let announce_len = announce_raw.len() as u64;
10739
10740 tx.send(Event::Frame {
10741 interface_id: InterfaceId(1),
10742 data: announce_raw,
10743 })
10744 .unwrap();
10745 tx.send(Event::Shutdown).unwrap();
10746 driver.run();
10747
10748 let stats = &driver.interfaces[&InterfaceId(1)].stats;
10749 assert_eq!(stats.rxb, announce_len);
10750 assert_eq!(stats.rx_packets, 1);
10751 }
10752
10753 #[test]
10754 fn send_updates_tx_stats() {
10755 let (tx, rx) = event::channel();
10756 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10757 let mut driver = Driver::new(
10758 TransportConfig {
10759 transport_enabled: false,
10760 identity_hash: None,
10761 prefer_shorter_path: false,
10762 max_paths_per_destination: 1,
10763 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10764 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10765 max_path_destinations: usize::MAX,
10766 max_tunnel_destinations_total: usize::MAX,
10767 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10768 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10769 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10770 announce_sig_cache_enabled: true,
10771 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10772 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10773 announce_queue_max_entries: 256,
10774 announce_queue_max_interfaces: 1024,
10775 },
10776 rx,
10777 tx.clone(),
10778 Box::new(cbs),
10779 );
10780 let (writer, _sent) = MockWriter::new();
10781 driver
10782 .interfaces
10783 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10784
10785 driver.dispatch_all(vec![TransportAction::SendOnInterface {
10786 interface: InterfaceId(1),
10787 raw: vec![0x01, 0x02, 0x03],
10788 }]);
10789
10790 let stats = &driver.interfaces[&InterfaceId(1)].stats;
10791 assert_eq!(stats.txb, 3);
10792 assert_eq!(stats.tx_packets, 1);
10793
10794 drop(tx);
10795 }
10796
10797 #[test]
10798 fn broadcast_updates_tx_stats() {
10799 let (tx, rx) = event::channel();
10800 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10801 let mut driver = Driver::new(
10802 TransportConfig {
10803 transport_enabled: false,
10804 identity_hash: None,
10805 prefer_shorter_path: false,
10806 max_paths_per_destination: 1,
10807 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10808 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10809 max_path_destinations: usize::MAX,
10810 max_tunnel_destinations_total: usize::MAX,
10811 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10812 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10813 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10814 announce_sig_cache_enabled: true,
10815 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10816 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10817 announce_queue_max_entries: 256,
10818 announce_queue_max_interfaces: 1024,
10819 },
10820 rx,
10821 tx.clone(),
10822 Box::new(cbs),
10823 );
10824 let (w1, _s1) = MockWriter::new();
10825 let (w2, _s2) = MockWriter::new();
10826 driver
10827 .interfaces
10828 .insert(InterfaceId(1), make_entry(1, Box::new(w1), true));
10829 driver
10830 .interfaces
10831 .insert(InterfaceId(2), make_entry(2, Box::new(w2), true));
10832
10833 driver.dispatch_all(vec![TransportAction::BroadcastOnAllInterfaces {
10834 raw: vec![0xAA, 0xBB],
10835 exclude: None,
10836 }]);
10837
10838 assert_eq!(driver.interfaces[&InterfaceId(1)].stats.txb, 2);
10840 assert_eq!(driver.interfaces[&InterfaceId(1)].stats.tx_packets, 1);
10841 assert_eq!(driver.interfaces[&InterfaceId(2)].stats.txb, 2);
10842 assert_eq!(driver.interfaces[&InterfaceId(2)].stats.tx_packets, 1);
10843
10844 drop(tx);
10845 }
10846
10847 #[test]
10848 fn query_interface_stats() {
10849 let (tx, rx) = event::channel();
10850 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10851 let mut driver = Driver::new(
10852 TransportConfig {
10853 transport_enabled: true,
10854 identity_hash: Some([0x42; 16]),
10855 prefer_shorter_path: false,
10856 max_paths_per_destination: 1,
10857 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10858 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10859 max_path_destinations: usize::MAX,
10860 max_tunnel_destinations_total: usize::MAX,
10861 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10862 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10863 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10864 announce_sig_cache_enabled: true,
10865 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10866 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10867 announce_queue_max_entries: 256,
10868 announce_queue_max_interfaces: 1024,
10869 },
10870 rx,
10871 tx.clone(),
10872 Box::new(cbs),
10873 );
10874 let (writer, _sent) = MockWriter::new();
10875 driver
10876 .interfaces
10877 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10878
10879 let (resp_tx, resp_rx) = mpsc::channel();
10880 tx.send(Event::Query(QueryRequest::InterfaceStats, resp_tx))
10881 .unwrap();
10882 tx.send(Event::Shutdown).unwrap();
10883 driver.run();
10884
10885 let resp = resp_rx.recv().unwrap();
10886 match resp {
10887 QueryResponse::InterfaceStats(stats) => {
10888 assert_eq!(stats.interfaces.len(), 1);
10889 assert_eq!(stats.interfaces[0].name, "test-1");
10890 assert!(stats.interfaces[0].status);
10891 assert_eq!(stats.transport_id, Some([0x42; 16]));
10892 assert!(stats.transport_enabled);
10893 }
10894 _ => panic!("unexpected response"),
10895 }
10896 }
10897
10898 #[test]
10899 fn query_path_table() {
10900 let (tx, rx) = event::channel();
10901 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10902 let mut driver = Driver::new(
10903 TransportConfig {
10904 transport_enabled: false,
10905 identity_hash: None,
10906 prefer_shorter_path: false,
10907 max_paths_per_destination: 1,
10908 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10909 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10910 max_path_destinations: usize::MAX,
10911 max_tunnel_destinations_total: usize::MAX,
10912 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10913 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10914 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10915 announce_sig_cache_enabled: true,
10916 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10917 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10918 announce_queue_max_entries: 256,
10919 announce_queue_max_interfaces: 1024,
10920 },
10921 rx,
10922 tx.clone(),
10923 Box::new(cbs),
10924 );
10925 let info = make_interface_info(1);
10926 driver.engine.register_interface(info);
10927 let (writer, _sent) = MockWriter::new();
10928 driver
10929 .interfaces
10930 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10931
10932 let identity = Identity::new(&mut OsRng);
10934 let announce_raw = build_announce_packet(&identity);
10935 tx.send(Event::Frame {
10936 interface_id: InterfaceId(1),
10937 data: announce_raw,
10938 })
10939 .unwrap();
10940
10941 let (resp_tx, resp_rx) = mpsc::channel();
10942 tx.send(Event::Query(
10943 QueryRequest::PathTable { max_hops: None },
10944 resp_tx,
10945 ))
10946 .unwrap();
10947 tx.send(Event::Shutdown).unwrap();
10948 driver.run();
10949
10950 let resp = resp_rx.recv().unwrap();
10951 match resp {
10952 QueryResponse::PathTable(entries) => {
10953 assert_eq!(entries.len(), 1);
10954 assert_eq!(entries[0].hops, 1);
10955 }
10956 _ => panic!("unexpected response"),
10957 }
10958 }
10959
10960 #[test]
10961 fn query_drop_path() {
10962 let (tx, rx) = event::channel();
10963 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10964 let mut driver = Driver::new(
10965 TransportConfig {
10966 transport_enabled: false,
10967 identity_hash: None,
10968 prefer_shorter_path: false,
10969 max_paths_per_destination: 1,
10970 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10971 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10972 max_path_destinations: usize::MAX,
10973 max_tunnel_destinations_total: usize::MAX,
10974 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10975 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10976 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10977 announce_sig_cache_enabled: true,
10978 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10979 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10980 announce_queue_max_entries: 256,
10981 announce_queue_max_interfaces: 1024,
10982 },
10983 rx,
10984 tx.clone(),
10985 Box::new(cbs),
10986 );
10987 let info = make_interface_info(1);
10988 driver.engine.register_interface(info);
10989 let (writer, _sent) = MockWriter::new();
10990 driver
10991 .interfaces
10992 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10993
10994 let identity = Identity::new(&mut OsRng);
10996 let announce_raw = build_announce_packet(&identity);
10997 let dest_hash =
10998 rns_core::destination::destination_hash("test", &["app"], Some(identity.hash()));
10999
11000 tx.send(Event::Frame {
11001 interface_id: InterfaceId(1),
11002 data: announce_raw,
11003 })
11004 .unwrap();
11005
11006 let (resp_tx, resp_rx) = mpsc::channel();
11007 tx.send(Event::Query(QueryRequest::DropPath { dest_hash }, resp_tx))
11008 .unwrap();
11009 tx.send(Event::Shutdown).unwrap();
11010 driver.run();
11011
11012 let resp = resp_rx.recv().unwrap();
11013 match resp {
11014 QueryResponse::DropPath(dropped) => {
11015 assert!(dropped);
11016 }
11017 _ => panic!("unexpected response"),
11018 }
11019 }
11020
11021 #[test]
11022 fn send_outbound_event() {
11023 let (tx, rx) = event::channel();
11024 let (cbs, _, _, _, _, _) = MockCallbacks::new();
11025 let mut driver = Driver::new(
11026 TransportConfig {
11027 transport_enabled: false,
11028 identity_hash: None,
11029 prefer_shorter_path: false,
11030 max_paths_per_destination: 1,
11031 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11032 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11033 max_path_destinations: usize::MAX,
11034 max_tunnel_destinations_total: usize::MAX,
11035 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11036 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11037 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11038 announce_sig_cache_enabled: true,
11039 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11040 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11041 announce_queue_max_entries: 256,
11042 announce_queue_max_interfaces: 1024,
11043 },
11044 rx,
11045 tx.clone(),
11046 Box::new(cbs),
11047 );
11048 let (writer, sent) = MockWriter::new();
11049 let info = make_interface_info(1);
11050 driver.engine.register_interface(info);
11051 driver
11052 .interfaces
11053 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
11054
11055 let dest = [0xAA; 16];
11057 let flags = PacketFlags {
11058 header_type: constants::HEADER_1,
11059 context_flag: constants::FLAG_UNSET,
11060 transport_type: constants::TRANSPORT_BROADCAST,
11061 destination_type: constants::DESTINATION_PLAIN,
11062 packet_type: constants::PACKET_TYPE_DATA,
11063 };
11064 let packet =
11065 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
11066
11067 tx.send(Event::SendOutbound {
11068 raw: packet.raw,
11069 dest_type: constants::DESTINATION_PLAIN,
11070 attached_interface: None,
11071 })
11072 .unwrap();
11073 tx.send(Event::Shutdown).unwrap();
11074 driver.run();
11075
11076 assert_eq!(sent.lock().unwrap().len(), 1);
11078 }
11079
11080 #[test]
11081 fn register_destination_and_deliver() {
11082 let (tx, rx) = event::channel();
11083 let (cbs, _, _, deliveries, _, _) = MockCallbacks::new();
11084 let mut driver = Driver::new(
11085 TransportConfig {
11086 transport_enabled: false,
11087 identity_hash: None,
11088 prefer_shorter_path: false,
11089 max_paths_per_destination: 1,
11090 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11091 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11092 max_path_destinations: usize::MAX,
11093 max_tunnel_destinations_total: usize::MAX,
11094 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11095 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11096 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11097 announce_sig_cache_enabled: true,
11098 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11099 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11100 announce_queue_max_entries: 256,
11101 announce_queue_max_interfaces: 1024,
11102 },
11103 rx,
11104 tx.clone(),
11105 Box::new(cbs),
11106 );
11107 let info = make_interface_info(1);
11108 driver.engine.register_interface(info);
11109 let (writer, _sent) = MockWriter::new();
11110 driver
11111 .interfaces
11112 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
11113
11114 let dest = [0xBB; 16];
11115
11116 tx.send(Event::RegisterDestination {
11118 dest_hash: dest,
11119 dest_type: constants::DESTINATION_SINGLE,
11120 })
11121 .unwrap();
11122
11123 let flags = PacketFlags {
11124 header_type: constants::HEADER_1,
11125 context_flag: constants::FLAG_UNSET,
11126 transport_type: constants::TRANSPORT_BROADCAST,
11127 destination_type: constants::DESTINATION_SINGLE,
11128 packet_type: constants::PACKET_TYPE_DATA,
11129 };
11130 let packet =
11131 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"data").unwrap();
11132 tx.send(Event::Frame {
11133 interface_id: InterfaceId(1),
11134 data: packet.raw,
11135 })
11136 .unwrap();
11137 tx.send(Event::Shutdown).unwrap();
11138 driver.run();
11139
11140 assert_eq!(deliveries.lock().unwrap().len(), 1);
11141 assert_eq!(deliveries.lock().unwrap()[0], DestHash(dest));
11142 }
11143
11144 #[test]
11145 fn query_transport_identity() {
11146 let (tx, rx) = event::channel();
11147 let (cbs, _, _, _, _, _) = MockCallbacks::new();
11148 let mut driver = Driver::new(
11149 TransportConfig {
11150 transport_enabled: true,
11151 identity_hash: Some([0xAA; 16]),
11152 prefer_shorter_path: false,
11153 max_paths_per_destination: 1,
11154 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11155 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11156 max_path_destinations: usize::MAX,
11157 max_tunnel_destinations_total: usize::MAX,
11158 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11159 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11160 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11161 announce_sig_cache_enabled: true,
11162 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11163 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11164 announce_queue_max_entries: 256,
11165 announce_queue_max_interfaces: 1024,
11166 },
11167 rx,
11168 tx.clone(),
11169 Box::new(cbs),
11170 );
11171
11172 let (resp_tx, resp_rx) = mpsc::channel();
11173 tx.send(Event::Query(QueryRequest::TransportIdentity, resp_tx))
11174 .unwrap();
11175 tx.send(Event::Shutdown).unwrap();
11176 driver.run();
11177
11178 match resp_rx.recv().unwrap() {
11179 QueryResponse::TransportIdentity(Some(hash)) => {
11180 assert_eq!(hash, [0xAA; 16]);
11181 }
11182 _ => panic!("unexpected response"),
11183 }
11184 }
11185
11186 #[test]
11187 fn query_link_count() {
11188 let (tx, rx) = event::channel();
11189 let (cbs, _, _, _, _, _) = MockCallbacks::new();
11190 let mut driver = Driver::new(
11191 TransportConfig {
11192 transport_enabled: false,
11193 identity_hash: None,
11194 prefer_shorter_path: false,
11195 max_paths_per_destination: 1,
11196 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11197 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11198 max_path_destinations: usize::MAX,
11199 max_tunnel_destinations_total: usize::MAX,
11200 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11201 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11202 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11203 announce_sig_cache_enabled: true,
11204 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11205 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11206 announce_queue_max_entries: 256,
11207 announce_queue_max_interfaces: 1024,
11208 },
11209 rx,
11210 tx.clone(),
11211 Box::new(cbs),
11212 );
11213
11214 let (resp_tx, resp_rx) = mpsc::channel();
11215 tx.send(Event::Query(QueryRequest::LinkCount, resp_tx))
11216 .unwrap();
11217 tx.send(Event::Shutdown).unwrap();
11218 driver.run();
11219
11220 match resp_rx.recv().unwrap() {
11221 QueryResponse::LinkCount(count) => assert_eq!(count, 0),
11222 _ => panic!("unexpected response"),
11223 }
11224 }
11225
11226 #[test]
11227 fn query_rate_table() {
11228 let (tx, rx) = event::channel();
11229 let (cbs, _, _, _, _, _) = MockCallbacks::new();
11230 let mut driver = Driver::new(
11231 TransportConfig {
11232 transport_enabled: false,
11233 identity_hash: None,
11234 prefer_shorter_path: false,
11235 max_paths_per_destination: 1,
11236 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11237 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11238 max_path_destinations: usize::MAX,
11239 max_tunnel_destinations_total: usize::MAX,
11240 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11241 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11242 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11243 announce_sig_cache_enabled: true,
11244 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11245 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11246 announce_queue_max_entries: 256,
11247 announce_queue_max_interfaces: 1024,
11248 },
11249 rx,
11250 tx.clone(),
11251 Box::new(cbs),
11252 );
11253
11254 let (resp_tx, resp_rx) = mpsc::channel();
11255 tx.send(Event::Query(QueryRequest::RateTable, resp_tx))
11256 .unwrap();
11257 tx.send(Event::Shutdown).unwrap();
11258 driver.run();
11259
11260 match resp_rx.recv().unwrap() {
11261 QueryResponse::RateTable(entries) => assert!(entries.is_empty()),
11262 _ => panic!("unexpected response"),
11263 }
11264 }
11265
11266 #[test]
11267 fn query_next_hop() {
11268 let (tx, rx) = event::channel();
11269 let (cbs, _, _, _, _, _) = MockCallbacks::new();
11270 let mut driver = Driver::new(
11271 TransportConfig {
11272 transport_enabled: false,
11273 identity_hash: None,
11274 prefer_shorter_path: false,
11275 max_paths_per_destination: 1,
11276 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11277 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11278 max_path_destinations: usize::MAX,
11279 max_tunnel_destinations_total: usize::MAX,
11280 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11281 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11282 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11283 announce_sig_cache_enabled: true,
11284 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11285 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11286 announce_queue_max_entries: 256,
11287 announce_queue_max_interfaces: 1024,
11288 },
11289 rx,
11290 tx.clone(),
11291 Box::new(cbs),
11292 );
11293
11294 let dest = [0xBB; 16];
11295 let (resp_tx, resp_rx) = mpsc::channel();
11296 tx.send(Event::Query(
11297 QueryRequest::NextHop { dest_hash: dest },
11298 resp_tx,
11299 ))
11300 .unwrap();
11301 tx.send(Event::Shutdown).unwrap();
11302 driver.run();
11303
11304 match resp_rx.recv().unwrap() {
11305 QueryResponse::NextHop(None) => {}
11306 _ => panic!("unexpected response"),
11307 }
11308 }
11309
11310 #[test]
11311 fn query_next_hop_if_name() {
11312 let (tx, rx) = event::channel();
11313 let (cbs, _, _, _, _, _) = MockCallbacks::new();
11314 let mut driver = Driver::new(
11315 TransportConfig {
11316 transport_enabled: false,
11317 identity_hash: None,
11318 prefer_shorter_path: false,
11319 max_paths_per_destination: 1,
11320 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11321 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11322 max_path_destinations: usize::MAX,
11323 max_tunnel_destinations_total: usize::MAX,
11324 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11325 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11326 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11327 announce_sig_cache_enabled: true,
11328 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11329 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11330 announce_queue_max_entries: 256,
11331 announce_queue_max_interfaces: 1024,
11332 },
11333 rx,
11334 tx.clone(),
11335 Box::new(cbs),
11336 );
11337
11338 let dest = [0xCC; 16];
11339 let (resp_tx, resp_rx) = mpsc::channel();
11340 tx.send(Event::Query(
11341 QueryRequest::NextHopIfName { dest_hash: dest },
11342 resp_tx,
11343 ))
11344 .unwrap();
11345 tx.send(Event::Shutdown).unwrap();
11346 driver.run();
11347
11348 match resp_rx.recv().unwrap() {
11349 QueryResponse::NextHopIfName(None) => {}
11350 _ => panic!("unexpected response"),
11351 }
11352 }
11353
11354 #[test]
11355 fn query_drop_all_via() {
11356 let (tx, rx) = event::channel();
11357 let (cbs, _, _, _, _, _) = MockCallbacks::new();
11358 let mut driver = Driver::new(
11359 TransportConfig {
11360 transport_enabled: false,
11361 identity_hash: None,
11362 prefer_shorter_path: false,
11363 max_paths_per_destination: 1,
11364 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11365 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11366 max_path_destinations: usize::MAX,
11367 max_tunnel_destinations_total: usize::MAX,
11368 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11369 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11370 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11371 announce_sig_cache_enabled: true,
11372 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11373 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11374 announce_queue_max_entries: 256,
11375 announce_queue_max_interfaces: 1024,
11376 },
11377 rx,
11378 tx.clone(),
11379 Box::new(cbs),
11380 );
11381
11382 let transport = [0xDD; 16];
11383 let (resp_tx, resp_rx) = mpsc::channel();
11384 tx.send(Event::Query(
11385 QueryRequest::DropAllVia {
11386 transport_hash: transport,
11387 },
11388 resp_tx,
11389 ))
11390 .unwrap();
11391 tx.send(Event::Shutdown).unwrap();
11392 driver.run();
11393
11394 match resp_rx.recv().unwrap() {
11395 QueryResponse::DropAllVia(count) => assert_eq!(count, 0),
11396 _ => panic!("unexpected response"),
11397 }
11398 }
11399
11400 #[test]
11401 fn query_drop_announce_queues() {
11402 let (tx, rx) = event::channel();
11403 let (cbs, _, _, _, _, _) = MockCallbacks::new();
11404 let mut driver = Driver::new(
11405 TransportConfig {
11406 transport_enabled: false,
11407 identity_hash: None,
11408 prefer_shorter_path: false,
11409 max_paths_per_destination: 1,
11410 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11411 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11412 max_path_destinations: usize::MAX,
11413 max_tunnel_destinations_total: usize::MAX,
11414 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11415 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11416 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11417 announce_sig_cache_enabled: true,
11418 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11419 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11420 announce_queue_max_entries: 256,
11421 announce_queue_max_interfaces: 1024,
11422 },
11423 rx,
11424 tx.clone(),
11425 Box::new(cbs),
11426 );
11427
11428 let (resp_tx, resp_rx) = mpsc::channel();
11429 tx.send(Event::Query(QueryRequest::DropAnnounceQueues, resp_tx))
11430 .unwrap();
11431 tx.send(Event::Shutdown).unwrap();
11432 driver.run();
11433
11434 match resp_rx.recv().unwrap() {
11435 QueryResponse::DropAnnounceQueues => {}
11436 _ => panic!("unexpected response"),
11437 }
11438 }
11439
11440 #[test]
11445 fn register_link_dest_event() {
11446 let (tx, rx) = event::channel();
11447 let (cbs, _, _, _, _, _) = MockCallbacks::new();
11448 let mut driver = Driver::new(
11449 TransportConfig {
11450 transport_enabled: false,
11451 identity_hash: None,
11452 prefer_shorter_path: false,
11453 max_paths_per_destination: 1,
11454 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11455 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11456 max_path_destinations: usize::MAX,
11457 max_tunnel_destinations_total: usize::MAX,
11458 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11459 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11460 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11461 announce_sig_cache_enabled: true,
11462 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11463 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11464 announce_queue_max_entries: 256,
11465 announce_queue_max_interfaces: 1024,
11466 },
11467 rx,
11468 tx.clone(),
11469 Box::new(cbs),
11470 );
11471 let info = make_interface_info(1);
11472 driver.engine.register_interface(info);
11473 let (writer, _sent) = MockWriter::new();
11474 driver
11475 .interfaces
11476 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
11477
11478 let mut rng = OsRng;
11479 let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::generate(&mut rng);
11480 let sig_pub_bytes = sig_prv.public_key().public_bytes();
11481 let sig_prv_bytes = sig_prv.private_bytes();
11482 let dest_hash = [0xDD; 16];
11483
11484 tx.send(Event::RegisterLinkDestination {
11485 dest_hash,
11486 sig_prv_bytes,
11487 sig_pub_bytes,
11488 resource_strategy: 0,
11489 })
11490 .unwrap();
11491 tx.send(Event::Shutdown).unwrap();
11492 driver.run();
11493
11494 assert!(driver.link_manager.is_link_destination(&dest_hash));
11496 }
11497
11498 #[test]
11499 fn create_link_event() {
11500 let (tx, rx) = event::channel();
11501 let (cbs, _link_established, _, _) = MockCallbacks::with_link_tracking();
11502 let mut driver = Driver::new(
11503 TransportConfig {
11504 transport_enabled: false,
11505 identity_hash: None,
11506 prefer_shorter_path: false,
11507 max_paths_per_destination: 1,
11508 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11509 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11510 max_path_destinations: usize::MAX,
11511 max_tunnel_destinations_total: usize::MAX,
11512 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11513 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11514 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11515 announce_sig_cache_enabled: true,
11516 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11517 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11518 announce_queue_max_entries: 256,
11519 announce_queue_max_interfaces: 1024,
11520 },
11521 rx,
11522 tx.clone(),
11523 Box::new(cbs),
11524 );
11525 let info = make_interface_info(1);
11526 driver.engine.register_interface(info);
11527 let (writer, _sent) = MockWriter::new();
11528 driver
11529 .interfaces
11530 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
11531
11532 let dest_hash = [0xDD; 16];
11533 let dummy_sig_pub = [0xAA; 32];
11534
11535 let (resp_tx, resp_rx) = mpsc::channel();
11536 tx.send(Event::CreateLink {
11537 dest_hash,
11538 dest_sig_pub_bytes: dummy_sig_pub,
11539 response_tx: resp_tx,
11540 })
11541 .unwrap();
11542 tx.send(Event::Shutdown).unwrap();
11543 driver.run();
11544
11545 let link_id = resp_rx.recv().unwrap();
11547 assert_ne!(link_id, [0u8; 16]);
11548
11549 assert_eq!(driver.link_manager.link_count(), 1);
11551
11552 }
11557
11558 #[test]
11559 fn create_link_uses_known_destination_interface_without_path() {
11560 let (tx, rx) = event::channel();
11561 let (cbs, _link_established, _, _) = MockCallbacks::with_link_tracking();
11562 let mut driver = Driver::new(
11563 TransportConfig {
11564 transport_enabled: false,
11565 identity_hash: None,
11566 prefer_shorter_path: false,
11567 max_paths_per_destination: 1,
11568 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11569 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11570 max_path_destinations: usize::MAX,
11571 max_tunnel_destinations_total: usize::MAX,
11572 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11573 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11574 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11575 announce_sig_cache_enabled: true,
11576 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11577 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11578 announce_queue_max_entries: 256,
11579 announce_queue_max_interfaces: 1024,
11580 },
11581 rx,
11582 tx.clone(),
11583 Box::new(cbs),
11584 );
11585 for id in [1, 2] {
11586 driver.engine.register_interface(make_interface_info(id));
11587 }
11588 let (writer, sent) = MockWriter::new();
11589 let (writer2, sent2) = MockWriter::new();
11590 driver
11591 .interfaces
11592 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
11593 driver
11594 .interfaces
11595 .insert(InterfaceId(2), make_entry(2, Box::new(writer2), true));
11596
11597 let dest_hash = [0xD1; 16];
11598 driver.known_destinations.insert(
11599 dest_hash,
11600 make_known_destination_state(dest_hash, 10.0, InterfaceId(2)),
11601 );
11602
11603 let dummy_sig_pub = [0xA1; 32];
11604 let (resp_tx, resp_rx) = mpsc::channel();
11605 tx.send(Event::CreateLink {
11606 dest_hash,
11607 dest_sig_pub_bytes: dummy_sig_pub,
11608 response_tx: resp_tx,
11609 })
11610 .unwrap();
11611 tx.send(Event::Shutdown).unwrap();
11612 driver.run();
11613
11614 let link_id = resp_rx.recv().unwrap();
11615 assert_ne!(link_id, [0u8; 16]);
11616 assert_eq!(driver.link_manager.link_count(), 1);
11617
11618 let sent_packets = sent.lock().unwrap();
11619 let sent_packets2 = sent2.lock().unwrap();
11620 assert!(
11621 sent_packets.is_empty(),
11622 "LINKREQUEST should not broadcast to unrelated interfaces when a known destination interface exists"
11623 );
11624 assert_eq!(sent_packets2.len(), 1);
11625 let flags = PacketFlags::unpack(sent_packets2[0][0] & 0x7F);
11626 assert_eq!(flags.packet_type, constants::PACKET_TYPE_LINKREQUEST);
11627 assert_eq!(extract_dest_hash(&sent_packets2[0]), dest_hash);
11628 }
11629
11630 #[test]
11631 fn create_link_ignores_sentinel_known_destination_interface() {
11632 let (tx, rx) = event::channel();
11633 let (cbs, _link_established, _, _) = MockCallbacks::with_link_tracking();
11634 let mut driver = Driver::new(
11635 TransportConfig {
11636 transport_enabled: false,
11637 identity_hash: None,
11638 prefer_shorter_path: false,
11639 max_paths_per_destination: 1,
11640 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11641 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11642 max_path_destinations: usize::MAX,
11643 max_tunnel_destinations_total: usize::MAX,
11644 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11645 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11646 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11647 announce_sig_cache_enabled: true,
11648 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11649 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11650 announce_queue_max_entries: 256,
11651 announce_queue_max_interfaces: 1024,
11652 },
11653 rx,
11654 tx.clone(),
11655 Box::new(cbs),
11656 );
11657 for id in [1, 2] {
11658 driver.engine.register_interface(make_interface_info(id));
11659 }
11660 let (writer, sent) = MockWriter::new();
11661 let (writer2, sent2) = MockWriter::new();
11662 driver
11663 .interfaces
11664 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
11665 driver
11666 .interfaces
11667 .insert(InterfaceId(2), make_entry(2, Box::new(writer2), true));
11668
11669 let dest_hash = [0xD2; 16];
11670 driver.known_destinations.insert(
11671 dest_hash,
11672 make_known_destination_state(dest_hash, 10.0, InterfaceId(0)),
11673 );
11674
11675 let dummy_sig_pub = [0xA2; 32];
11676 let (resp_tx, resp_rx) = mpsc::channel();
11677 tx.send(Event::CreateLink {
11678 dest_hash,
11679 dest_sig_pub_bytes: dummy_sig_pub,
11680 response_tx: resp_tx,
11681 })
11682 .unwrap();
11683 tx.send(Event::Shutdown).unwrap();
11684 driver.run();
11685
11686 let link_id = resp_rx.recv().unwrap();
11687 assert_ne!(link_id, [0u8; 16]);
11688 assert_eq!(driver.link_manager.link_count(), 1);
11689
11690 let sent_packets = sent.lock().unwrap();
11691 let sent_packets2 = sent2.lock().unwrap();
11692 assert!(
11693 sent_packets.len() == 1 && sent_packets2.len() == 1,
11694 "sentinel InterfaceId(0) must not suppress the default broadcast behavior"
11695 );
11696 let flags = PacketFlags::unpack(sent_packets[0][0] & 0x7F);
11697 assert_eq!(flags.packet_type, constants::PACKET_TYPE_LINKREQUEST);
11698 }
11699
11700 #[test]
11701 fn deliver_local_routes_to_link_manager() {
11702 let (tx, rx) = event::channel();
11705 let (cbs, _, _, _, _, _) = MockCallbacks::new();
11706 let mut driver = Driver::new(
11707 TransportConfig {
11708 transport_enabled: false,
11709 identity_hash: None,
11710 prefer_shorter_path: false,
11711 max_paths_per_destination: 1,
11712 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11713 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11714 max_path_destinations: usize::MAX,
11715 max_tunnel_destinations_total: usize::MAX,
11716 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11717 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11718 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11719 announce_sig_cache_enabled: true,
11720 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11721 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11722 announce_queue_max_entries: 256,
11723 announce_queue_max_interfaces: 1024,
11724 },
11725 rx,
11726 tx.clone(),
11727 Box::new(cbs),
11728 );
11729 let info = make_interface_info(1);
11730 driver.engine.register_interface(info);
11731 let (writer, _sent) = MockWriter::new();
11732 driver
11733 .interfaces
11734 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
11735
11736 let mut rng = OsRng;
11738 let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::generate(&mut rng);
11739 let sig_pub_bytes = sig_prv.public_key().public_bytes();
11740 let dest_hash = [0xEE; 16];
11741 driver.link_manager.register_link_destination(
11742 dest_hash,
11743 sig_prv,
11744 sig_pub_bytes,
11745 crate::link_manager::ResourceStrategy::AcceptNone,
11746 );
11747
11748 assert!(driver.link_manager.is_link_destination(&dest_hash));
11752
11753 assert!(!driver.link_manager.is_link_destination(&[0xFF; 16]));
11755
11756 drop(tx);
11757 }
11758
11759 #[test]
11760 fn teardown_link_event() {
11761 let (tx, rx) = event::channel();
11762 let (cbs, _, link_closed, _) = MockCallbacks::with_link_tracking();
11763 let mut driver = Driver::new(
11764 TransportConfig {
11765 transport_enabled: false,
11766 identity_hash: None,
11767 prefer_shorter_path: false,
11768 max_paths_per_destination: 1,
11769 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11770 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11771 max_path_destinations: usize::MAX,
11772 max_tunnel_destinations_total: usize::MAX,
11773 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11774 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11775 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11776 announce_sig_cache_enabled: true,
11777 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11778 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11779 announce_queue_max_entries: 256,
11780 announce_queue_max_interfaces: 1024,
11781 },
11782 rx,
11783 tx.clone(),
11784 Box::new(cbs),
11785 );
11786 let info = make_interface_info(1);
11787 driver.engine.register_interface(info);
11788 let (writer, _sent) = MockWriter::new();
11789 driver
11790 .interfaces
11791 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
11792
11793 let (resp_tx, resp_rx) = mpsc::channel();
11795 tx.send(Event::CreateLink {
11796 dest_hash: [0xDD; 16],
11797 dest_sig_pub_bytes: [0xAA; 32],
11798 response_tx: resp_tx,
11799 })
11800 .unwrap();
11801 tx.send(Event::Shutdown).unwrap();
11806 driver.run();
11807
11808 let link_id = resp_rx.recv().unwrap();
11809 assert_ne!(link_id, [0u8; 16]);
11810 assert_eq!(driver.link_manager.link_count(), 1);
11811
11812 let teardown_actions = driver.link_manager.teardown_link(&link_id);
11814 driver.dispatch_link_actions(teardown_actions);
11815
11816 assert_eq!(link_closed.lock().unwrap().len(), 1);
11818 assert_eq!(link_closed.lock().unwrap()[0], TypedLinkId(link_id));
11819 }
11820
11821 #[test]
11822 fn link_count_includes_link_manager() {
11823 let (tx, rx) = event::channel();
11824 let (cbs, _, _, _, _, _) = MockCallbacks::new();
11825 let mut driver = Driver::new(
11826 TransportConfig {
11827 transport_enabled: false,
11828 identity_hash: None,
11829 prefer_shorter_path: false,
11830 max_paths_per_destination: 1,
11831 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11832 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11833 max_path_destinations: usize::MAX,
11834 max_tunnel_destinations_total: usize::MAX,
11835 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11836 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11837 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11838 announce_sig_cache_enabled: true,
11839 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11840 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11841 announce_queue_max_entries: 256,
11842 announce_queue_max_interfaces: 1024,
11843 },
11844 rx,
11845 tx.clone(),
11846 Box::new(cbs),
11847 );
11848 let info = make_interface_info(1);
11849 driver.engine.register_interface(info);
11850 let (writer, _sent) = MockWriter::new();
11851 driver
11852 .interfaces
11853 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
11854
11855 let mut rng = OsRng;
11857 let dummy_sig = [0xAA; 32];
11858 driver.link_manager.create_link(
11859 &[0xDD; 16],
11860 &dummy_sig,
11861 1,
11862 constants::MTU as u32,
11863 &mut rng,
11864 );
11865
11866 let (resp_tx, resp_rx) = mpsc::channel();
11868 tx.send(Event::Query(QueryRequest::LinkCount, resp_tx))
11869 .unwrap();
11870 tx.send(Event::Shutdown).unwrap();
11871 driver.run();
11872
11873 match resp_rx.recv().unwrap() {
11874 QueryResponse::LinkCount(count) => assert_eq!(count, 1),
11875 _ => panic!("unexpected response"),
11876 }
11877 }
11878
11879 #[test]
11880 fn register_request_handler_event() {
11881 let (tx, rx) = event::channel();
11882 let (cbs, _, _, _, _, _) = MockCallbacks::new();
11883 let mut driver = Driver::new(
11884 TransportConfig {
11885 transport_enabled: false,
11886 identity_hash: None,
11887 prefer_shorter_path: false,
11888 max_paths_per_destination: 1,
11889 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11890 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11891 max_path_destinations: usize::MAX,
11892 max_tunnel_destinations_total: usize::MAX,
11893 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11894 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11895 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11896 announce_sig_cache_enabled: true,
11897 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11898 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11899 announce_queue_max_entries: 256,
11900 announce_queue_max_interfaces: 1024,
11901 },
11902 rx,
11903 tx.clone(),
11904 Box::new(cbs),
11905 );
11906
11907 tx.send(Event::RegisterRequestHandler {
11908 path: "/status".to_string(),
11909 allowed_list: None,
11910 handler: Box::new(|_link_id, _path, _data, _remote| Some(b"OK".to_vec())),
11911 })
11912 .unwrap();
11913 tx.send(Event::Shutdown).unwrap();
11914 driver.run();
11915
11916 }
11919
11920 #[test]
11923 fn management_announces_emitted_after_delay() {
11924 let (tx, rx) = event::channel();
11925 let (cbs, _announces, _, _, _, _) = MockCallbacks::new();
11926 let identity = Identity::new(&mut OsRng);
11927 let identity_hash = *identity.hash();
11928 let mut driver = Driver::new(
11929 TransportConfig {
11930 transport_enabled: true,
11931 identity_hash: Some(identity_hash),
11932 prefer_shorter_path: false,
11933 max_paths_per_destination: 1,
11934 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11935 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11936 max_path_destinations: usize::MAX,
11937 max_tunnel_destinations_total: usize::MAX,
11938 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11939 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11940 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11941 announce_sig_cache_enabled: true,
11942 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11943 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11944 announce_queue_max_entries: 256,
11945 announce_queue_max_interfaces: 1024,
11946 },
11947 rx,
11948 tx.clone(),
11949 Box::new(cbs),
11950 );
11951
11952 let info = make_interface_info(1);
11954 driver.engine.register_interface(info.clone());
11955 let (writer, sent) = MockWriter::new();
11956 driver
11957 .interfaces
11958 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
11959
11960 driver.management_config.enable_remote_management = true;
11962 driver.transport_identity = Some(identity);
11963
11964 driver.started = time::now() - 10.0;
11966
11967 tx.send(Event::Tick).unwrap();
11969 tx.send(Event::Shutdown).unwrap();
11970 driver.run();
11971
11972 let sent_packets = sent.lock().unwrap();
11974 assert!(
11975 !sent_packets.is_empty(),
11976 "Management announce should be sent after startup delay"
11977 );
11978 }
11979
11980 #[test]
11981 fn runtime_config_list_contains_global_keys() {
11982 let driver = new_test_driver();
11983 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
11984 let QueryResponse::RuntimeConfigList(entries) = response else {
11985 panic!("expected runtime config list");
11986 };
11987 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
11988 assert!(keys.contains(&"global.tick_interval_ms".to_string()));
11989 assert!(keys.contains(&"global.known_destinations_ttl_secs".to_string()));
11990 assert!(keys.contains(&"global.rate_limiter_ttl_secs".to_string()));
11991 assert!(keys.contains(&"global.direct_connect_policy".to_string()));
11992 }
11993
11994 #[test]
11995 fn runtime_config_set_and_reset_tick_interval() {
11996 let mut driver = new_test_driver();
11997
11998 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11999 key: "global.tick_interval_ms".into(),
12000 value: RuntimeConfigValue::Int(250),
12001 });
12002 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12003 panic!("expected runtime config set success");
12004 };
12005 assert_eq!(entry.key, "global.tick_interval_ms");
12006 assert_eq!(entry.value, RuntimeConfigValue::Int(250));
12007 assert_eq!(driver.tick_interval_ms.load(Ordering::Relaxed), 250);
12008
12009 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12010 key: "global.tick_interval_ms".into(),
12011 });
12012 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12013 panic!("expected runtime config reset success");
12014 };
12015 assert_eq!(entry.value, RuntimeConfigValue::Int(1000));
12016 assert_eq!(driver.tick_interval_ms.load(Ordering::Relaxed), 1000);
12017 }
12018
12019 #[test]
12020 fn runtime_config_rejects_invalid_policy() {
12021 let mut driver = new_test_driver();
12022 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12023 key: "global.direct_connect_policy".into(),
12024 value: RuntimeConfigValue::String("bogus".into()),
12025 });
12026 let QueryResponse::RuntimeConfigSet(Err(err)) = response else {
12027 panic!("expected runtime config set failure");
12028 };
12029 assert_eq!(err.code, RuntimeConfigErrorCode::InvalidValue);
12030 }
12031
12032 #[test]
12033 fn runtime_config_set_and_reset_rate_limiter_ttl() {
12034 let mut driver = new_test_driver();
12035
12036 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12037 key: "global.rate_limiter_ttl_secs".into(),
12038 value: RuntimeConfigValue::Float(600.0),
12039 });
12040 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12041 panic!("expected runtime config set success");
12042 };
12043 assert_eq!(entry.value, RuntimeConfigValue::Float(600.0));
12044 assert_eq!(driver.rate_limiter_ttl_secs, 600.0);
12045
12046 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12047 key: "global.rate_limiter_ttl_secs".into(),
12048 });
12049 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12050 panic!("expected runtime config reset success");
12051 };
12052 assert_eq!(
12053 entry.value,
12054 RuntimeConfigValue::Float(DEFAULT_RATE_LIMITER_TTL_SECS)
12055 );
12056 assert_eq!(driver.rate_limiter_ttl_secs, DEFAULT_RATE_LIMITER_TTL_SECS);
12057 }
12058
12059 #[cfg(feature = "iface-backbone")]
12060 #[test]
12061 fn runtime_config_lists_backbone_keys() {
12062 let mut driver = new_test_driver();
12063 register_test_backbone(&mut driver, "public");
12064 register_test_backbone_client(&mut driver, "uplink");
12065 register_test_backbone_discovery(&mut driver, "public", false);
12066 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
12067 let QueryResponse::RuntimeConfigList(entries) = response else {
12068 panic!("expected runtime config list");
12069 };
12070 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
12071 assert!(keys.contains(&"backbone.public.idle_timeout_secs".to_string()));
12072 assert!(keys.contains(&"backbone.public.write_stall_timeout_secs".to_string()));
12073 assert!(keys.contains(&"backbone.public.max_connections".to_string()));
12074 assert!(keys.contains(&"backbone.public.discoverable".to_string()));
12075 assert!(keys.contains(&"backbone.public.discovery_name".to_string()));
12076 assert!(keys.contains(&"backbone.public.latitude".to_string()));
12077 assert!(keys.contains(&"backbone.public.longitude".to_string()));
12078 assert!(keys.contains(&"backbone.public.height".to_string()));
12079 assert!(keys.contains(&"backbone_client.uplink.connect_timeout_secs".to_string()));
12080 assert!(keys.contains(&"backbone_client.uplink.reconnect_wait_secs".to_string()));
12081 assert!(keys.contains(&"backbone_client.uplink.max_reconnect_tries".to_string()));
12082 }
12083
12084 #[cfg(feature = "iface-backbone")]
12085 #[test]
12086 fn runtime_config_sets_backbone_values() {
12087 let mut driver = new_test_driver();
12088 register_test_backbone(&mut driver, "public");
12089 register_test_backbone_discovery(&mut driver, "public", false);
12090 driver.transport_identity = Some(rns_crypto::identity::Identity::new(&mut OsRng));
12091
12092 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12093 key: "backbone.public.idle_timeout_secs".into(),
12094 value: RuntimeConfigValue::Float(2.5),
12095 });
12096 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12097 panic!("expected runtime config set success");
12098 };
12099 assert_eq!(entry.value, RuntimeConfigValue::Float(2.5));
12100
12101 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12102 key: "backbone.public.write_stall_timeout_secs".into(),
12103 value: RuntimeConfigValue::Float(15.0),
12104 });
12105 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12106 panic!("expected runtime config set success");
12107 };
12108 assert_eq!(entry.value, RuntimeConfigValue::Float(15.0));
12109
12110 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12111 key: "backbone.public.max_connections".into(),
12112 value: RuntimeConfigValue::Int(0),
12113 });
12114 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12115 panic!("expected runtime config set success");
12116 };
12117 assert_eq!(entry.value, RuntimeConfigValue::Int(0));
12118
12119 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12120 key: "backbone.public.max_connections".into(),
12121 });
12122 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12123 panic!("expected runtime config reset success");
12124 };
12125 assert_eq!(entry.value, RuntimeConfigValue::Int(8));
12126
12127 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12128 key: "backbone.public.write_stall_timeout_secs".into(),
12129 });
12130 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12131 panic!("expected runtime config reset success");
12132 };
12133 assert_eq!(entry.value, RuntimeConfigValue::Float(30.0));
12134
12135 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12136 key: "backbone.public.discoverable".into(),
12137 value: RuntimeConfigValue::Bool(true),
12138 });
12139 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12140 panic!("expected runtime config set success");
12141 };
12142 assert_eq!(entry.value, RuntimeConfigValue::Bool(true));
12143 assert!(driver
12144 .interface_announcer
12145 .as_ref()
12146 .map(|announcer| announcer.contains_interface("public"))
12147 .unwrap_or(false));
12148
12149 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12150 key: "backbone.public.discovery_name".into(),
12151 value: RuntimeConfigValue::String("Public Backbone".into()),
12152 });
12153 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12154 panic!("expected runtime config set success");
12155 };
12156 assert_eq!(
12157 entry.value,
12158 RuntimeConfigValue::String("Public Backbone".into())
12159 );
12160
12161 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12162 key: "backbone.public.latitude".into(),
12163 value: RuntimeConfigValue::Float(45.4642),
12164 });
12165 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12166 panic!("expected runtime config set success");
12167 };
12168 assert_eq!(entry.value, RuntimeConfigValue::Float(45.4642));
12169
12170 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12171 key: "backbone.public.longitude".into(),
12172 value: RuntimeConfigValue::Float(9.19),
12173 });
12174 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12175 panic!("expected runtime config set success");
12176 };
12177 assert_eq!(entry.value, RuntimeConfigValue::Float(9.19));
12178
12179 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12180 key: "backbone.public.height".into(),
12181 value: RuntimeConfigValue::Int(120),
12182 });
12183 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12184 panic!("expected runtime config set success");
12185 };
12186 assert_eq!(entry.value, RuntimeConfigValue::Float(120.0));
12187
12188 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12189 key: "backbone.public.discoverable".into(),
12190 });
12191 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12192 panic!("expected runtime config reset success");
12193 };
12194 assert_eq!(entry.value, RuntimeConfigValue::Bool(false));
12195 assert!(driver.interface_announcer.is_none());
12196
12197 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12198 key: "backbone.public.latitude".into(),
12199 });
12200 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12201 panic!("expected runtime config reset success");
12202 };
12203 assert_eq!(entry.value, RuntimeConfigValue::Null);
12204 }
12205
12206 #[cfg(feature = "iface-backbone")]
12207 #[test]
12208 fn runtime_config_sets_backbone_client_values() {
12209 let mut driver = new_test_driver();
12210 register_test_backbone_client(&mut driver, "uplink");
12211
12212 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12213 key: "backbone_client.uplink.connect_timeout_secs".into(),
12214 value: RuntimeConfigValue::Float(2.5),
12215 });
12216 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12217 panic!("expected runtime config set success");
12218 };
12219 assert_eq!(entry.value, RuntimeConfigValue::Float(2.5));
12220
12221 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12222 key: "backbone_client.uplink.max_reconnect_tries".into(),
12223 value: RuntimeConfigValue::Int(0),
12224 });
12225 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12226 panic!("expected runtime config set success");
12227 };
12228 assert_eq!(entry.value, RuntimeConfigValue::Int(0));
12229
12230 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12231 key: "backbone_client.uplink.connect_timeout_secs".into(),
12232 });
12233 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12234 panic!("expected runtime config reset success");
12235 };
12236 assert_eq!(entry.value, RuntimeConfigValue::Float(5.0));
12237 }
12238
12239 #[cfg(feature = "iface-backbone")]
12240 #[test]
12241 fn backbone_peer_state_query_lists_entries() {
12242 let mut driver = new_test_driver();
12243 register_test_backbone(&mut driver, "public");
12244 driver
12245 .backbone_peer_state
12246 .get("public")
12247 .unwrap()
12248 .peer_state
12249 .lock()
12250 .unwrap()
12251 .seed_entry(BackbonePeerStateEntry {
12252 interface_name: "public".into(),
12253 peer_ip: "203.0.113.10".parse().unwrap(),
12254 connected_count: 1,
12255 blacklisted_remaining_secs: Some(120.0),
12256 blacklist_reason: Some("repeated idle timeouts".into()),
12257 reject_count: 7,
12258 });
12259
12260 let response = driver.handle_query(QueryRequest::BackbonePeerState {
12261 interface_name: Some("public".into()),
12262 });
12263 let QueryResponse::BackbonePeerState(entries) = response else {
12264 panic!("expected backbone peer state list");
12265 };
12266 assert_eq!(entries.len(), 1);
12267 assert_eq!(entries[0].peer_ip.to_string(), "203.0.113.10");
12268 assert_eq!(entries[0].connected_count, 1);
12269 assert_eq!(entries[0].reject_count, 7);
12270 assert_eq!(
12271 entries[0].blacklist_reason.as_deref(),
12272 Some("repeated idle timeouts")
12273 );
12274 assert!(entries[0].blacklisted_remaining_secs.unwrap() > 0.0);
12275 }
12276
12277 #[cfg(feature = "iface-backbone")]
12278 #[test]
12279 fn backbone_peer_state_clear_removes_entry() {
12280 let mut driver = new_test_driver();
12281 register_test_backbone(&mut driver, "public");
12282 driver
12283 .backbone_peer_state
12284 .get("public")
12285 .unwrap()
12286 .peer_state
12287 .lock()
12288 .unwrap()
12289 .seed_entry(BackbonePeerStateEntry {
12290 interface_name: "public".into(),
12291 peer_ip: "203.0.113.11".parse().unwrap(),
12292 connected_count: 0,
12293 blacklisted_remaining_secs: None,
12294 blacklist_reason: None,
12295 reject_count: 0,
12296 });
12297
12298 let response = driver.handle_query_mut(QueryRequest::ClearBackbonePeerState {
12299 interface_name: "public".into(),
12300 peer_ip: "203.0.113.11".parse().unwrap(),
12301 });
12302 let QueryResponse::ClearBackbonePeerState(true) = response else {
12303 panic!("expected successful peer-state clear");
12304 };
12305
12306 let response = driver.handle_query(QueryRequest::BackbonePeerState {
12307 interface_name: Some("public".into()),
12308 });
12309 let QueryResponse::BackbonePeerState(entries) = response else {
12310 panic!("expected backbone peer state list");
12311 };
12312 assert!(entries.is_empty());
12313 }
12314
12315 #[cfg(feature = "iface-backbone")]
12316 #[test]
12317 fn backbone_peer_blacklist_sets_blacklist() {
12318 let mut driver = new_test_driver();
12319 register_test_backbone(&mut driver, "public");
12320 driver
12321 .backbone_peer_state
12322 .get("public")
12323 .unwrap()
12324 .peer_state
12325 .lock()
12326 .unwrap()
12327 .seed_entry(BackbonePeerStateEntry {
12328 interface_name: "public".into(),
12329 peer_ip: "203.0.113.50".parse().unwrap(),
12330 connected_count: 1,
12331 blacklisted_remaining_secs: None,
12332 blacklist_reason: None,
12333 reject_count: 0,
12334 });
12335
12336 let response = driver.handle_query_mut(QueryRequest::BlacklistBackbonePeer {
12337 interface_name: "public".into(),
12338 peer_ip: "203.0.113.50".parse().unwrap(),
12339 duration: Duration::from_secs(300),
12340 reason: "sentinel blacklist".into(),
12341 penalty_level: 2,
12342 });
12343 let QueryResponse::BlacklistBackbonePeer(true) = response else {
12344 panic!("expected successful blacklist");
12345 };
12346
12347 let response = driver.handle_query(QueryRequest::BackbonePeerState {
12349 interface_name: Some("public".into()),
12350 });
12351 let QueryResponse::BackbonePeerState(entries) = response else {
12352 panic!("expected backbone peer state list");
12353 };
12354 let entry = entries
12355 .iter()
12356 .find(|e| e.peer_ip == "203.0.113.50".parse::<std::net::IpAddr>().unwrap())
12357 .expect("expected entry for blacklisted peer");
12358 assert!(entry.blacklisted_remaining_secs.is_some());
12359 let remaining = entry.blacklisted_remaining_secs.unwrap();
12360 assert!(remaining > 290.0 && remaining <= 300.0);
12361 assert_eq!(
12362 entry.blacklist_reason.as_deref(),
12363 Some("sentinel blacklist")
12364 );
12365 }
12366
12367 #[cfg(feature = "iface-backbone")]
12368 #[test]
12369 fn backbone_peer_blacklist_unknown_interface_returns_false() {
12370 let mut driver = new_test_driver();
12371 let response = driver.handle_query_mut(QueryRequest::BlacklistBackbonePeer {
12372 interface_name: "nonexistent".into(),
12373 peer_ip: "203.0.113.50".parse().unwrap(),
12374 duration: Duration::from_secs(60),
12375 reason: "sentinel blacklist".into(),
12376 penalty_level: 1,
12377 });
12378 let QueryResponse::BlacklistBackbonePeer(false) = response else {
12379 panic!("expected false for unknown interface");
12380 };
12381 }
12382
12383 #[cfg(feature = "iface-backbone")]
12384 #[test]
12385 fn backbone_peer_blacklist_creates_entry_for_unknown_ip() {
12386 let mut driver = new_test_driver();
12387 register_test_backbone(&mut driver, "public");
12388
12389 let response = driver.handle_query_mut(QueryRequest::BlacklistBackbonePeer {
12391 interface_name: "public".into(),
12392 peer_ip: "198.51.100.1".parse().unwrap(),
12393 duration: Duration::from_secs(120),
12394 reason: "sentinel blacklist".into(),
12395 penalty_level: 1,
12396 });
12397 let QueryResponse::BlacklistBackbonePeer(true) = response else {
12398 panic!("expected successful blacklist for new IP");
12399 };
12400
12401 let response = driver.handle_query(QueryRequest::BackbonePeerState {
12402 interface_name: Some("public".into()),
12403 });
12404 let QueryResponse::BackbonePeerState(entries) = response else {
12405 panic!("expected backbone peer state list");
12406 };
12407 let entry = entries
12408 .iter()
12409 .find(|e| e.peer_ip == "198.51.100.1".parse::<std::net::IpAddr>().unwrap())
12410 .expect("expected entry for newly blacklisted IP");
12411 assert!(entry.blacklisted_remaining_secs.is_some());
12412 }
12413
12414 #[cfg(feature = "iface-tcp")]
12415 #[test]
12416 fn runtime_config_lists_tcp_server_keys() {
12417 let mut driver = new_test_driver();
12418 register_test_tcp_server(&mut driver, "public");
12419 register_test_tcp_server_discovery(&mut driver, "public", false);
12420 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
12421 let QueryResponse::RuntimeConfigList(entries) = response else {
12422 panic!("expected runtime config list");
12423 };
12424 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
12425 assert!(keys.contains(&"tcp_server.public.max_connections".to_string()));
12426 assert!(keys.contains(&"tcp_server.public.discoverable".to_string()));
12427 assert!(keys.contains(&"tcp_server.public.discovery_name".to_string()));
12428 }
12429
12430 #[cfg(feature = "iface-tcp")]
12431 #[test]
12432 fn runtime_config_sets_tcp_server_values() {
12433 let mut driver = new_test_driver();
12434 register_test_tcp_server(&mut driver, "public");
12435 register_test_tcp_server_discovery(&mut driver, "public", false);
12436 driver.transport_identity = Some(rns_crypto::identity::Identity::new(&mut OsRng));
12437
12438 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12439 key: "tcp_server.public.max_connections".into(),
12440 value: RuntimeConfigValue::Int(0),
12441 });
12442 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12443 panic!("expected runtime config set success");
12444 };
12445 assert_eq!(entry.value, RuntimeConfigValue::Int(0));
12446
12447 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12448 key: "tcp_server.public.max_connections".into(),
12449 });
12450 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12451 panic!("expected runtime config reset success");
12452 };
12453 assert_eq!(entry.value, RuntimeConfigValue::Int(4));
12454
12455 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12456 key: "tcp_server.public.discoverable".into(),
12457 value: RuntimeConfigValue::Bool(true),
12458 });
12459 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12460 panic!("expected runtime config set success");
12461 };
12462 assert_eq!(entry.value, RuntimeConfigValue::Bool(true));
12463
12464 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12465 key: "tcp_server.public.latitude".into(),
12466 value: RuntimeConfigValue::Float(41.9028),
12467 });
12468 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12469 panic!("expected runtime config set success");
12470 };
12471 assert_eq!(entry.value, RuntimeConfigValue::Float(41.9028));
12472
12473 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12474 key: "tcp_server.public.latitude".into(),
12475 });
12476 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12477 panic!("expected runtime config reset success");
12478 };
12479 assert_eq!(entry.value, RuntimeConfigValue::Null);
12480 }
12481
12482 #[cfg(feature = "iface-tcp")]
12483 #[test]
12484 fn runtime_config_lists_tcp_client_keys() {
12485 let mut driver = new_test_driver();
12486 register_test_tcp_client(&mut driver, "uplink");
12487 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
12488 let QueryResponse::RuntimeConfigList(entries) = response else {
12489 panic!("expected runtime config list");
12490 };
12491 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
12492 assert!(keys.contains(&"tcp_client.uplink.connect_timeout_secs".to_string()));
12493 assert!(keys.contains(&"tcp_client.uplink.reconnect_wait_secs".to_string()));
12494 assert!(keys.contains(&"tcp_client.uplink.max_reconnect_tries".to_string()));
12495 }
12496
12497 #[cfg(feature = "iface-tcp")]
12498 #[test]
12499 fn runtime_config_sets_tcp_client_values() {
12500 let mut driver = new_test_driver();
12501 register_test_tcp_client(&mut driver, "uplink");
12502
12503 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12504 key: "tcp_client.uplink.connect_timeout_secs".into(),
12505 value: RuntimeConfigValue::Float(2.5),
12506 });
12507 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12508 panic!("expected runtime config set success");
12509 };
12510 assert_eq!(entry.value, RuntimeConfigValue::Float(2.5));
12511
12512 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12513 key: "tcp_client.uplink.max_reconnect_tries".into(),
12514 value: RuntimeConfigValue::Int(0),
12515 });
12516 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12517 panic!("expected runtime config set success");
12518 };
12519 assert_eq!(entry.value, RuntimeConfigValue::Int(0));
12520
12521 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12522 key: "tcp_client.uplink.connect_timeout_secs".into(),
12523 });
12524 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12525 panic!("expected runtime config reset success");
12526 };
12527 assert_eq!(entry.value, RuntimeConfigValue::Float(5.0));
12528 }
12529
12530 #[cfg(feature = "iface-udp")]
12531 #[test]
12532 fn runtime_config_lists_udp_keys() {
12533 let mut driver = new_test_driver();
12534 register_test_udp(&mut driver, "lan");
12535 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
12536 let QueryResponse::RuntimeConfigList(entries) = response else {
12537 panic!("expected runtime config list");
12538 };
12539 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
12540 assert!(keys.contains(&"udp.lan.forward_ip".to_string()));
12541 assert!(keys.contains(&"udp.lan.forward_port".to_string()));
12542 }
12543
12544 #[cfg(feature = "iface-udp")]
12545 #[test]
12546 fn runtime_config_sets_udp_values() {
12547 let mut driver = new_test_driver();
12548 register_test_udp(&mut driver, "lan");
12549
12550 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12551 key: "udp.lan.forward_ip".into(),
12552 value: RuntimeConfigValue::String("192.168.1.10".into()),
12553 });
12554 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12555 panic!("expected set ok");
12556 };
12557 assert_eq!(
12558 entry.value,
12559 RuntimeConfigValue::String("192.168.1.10".into())
12560 );
12561
12562 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12563 key: "udp.lan.forward_port".into(),
12564 value: RuntimeConfigValue::Null,
12565 });
12566 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12567 panic!("expected set ok");
12568 };
12569 assert_eq!(entry.value, RuntimeConfigValue::Null);
12570
12571 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12572 key: "udp.lan.forward_port".into(),
12573 });
12574 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12575 panic!("expected reset ok");
12576 };
12577 assert_eq!(entry.value, RuntimeConfigValue::Int(4242));
12578 }
12579
12580 #[cfg(feature = "iface-auto")]
12581 #[test]
12582 fn runtime_config_lists_auto_keys() {
12583 let mut driver = new_test_driver();
12584 register_test_auto(&mut driver, "lan");
12585 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
12586 let QueryResponse::RuntimeConfigList(entries) = response else {
12587 panic!("expected runtime config list");
12588 };
12589 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
12590 assert!(keys.contains(&"auto.lan.announce_interval_secs".to_string()));
12591 assert!(keys.contains(&"auto.lan.peer_timeout_secs".to_string()));
12592 assert!(keys.contains(&"auto.lan.peer_job_interval_secs".to_string()));
12593 }
12594
12595 #[cfg(feature = "iface-auto")]
12596 #[test]
12597 fn runtime_config_sets_auto_values() {
12598 let mut driver = new_test_driver();
12599 register_test_auto(&mut driver, "lan");
12600
12601 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12602 key: "auto.lan.announce_interval_secs".into(),
12603 value: RuntimeConfigValue::Float(2.5),
12604 });
12605 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12606 panic!("expected set ok");
12607 };
12608 assert_eq!(entry.value, RuntimeConfigValue::Float(2.5));
12609
12610 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12611 key: "auto.lan.peer_timeout_secs".into(),
12612 value: RuntimeConfigValue::Float(30.0),
12613 });
12614 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12615 panic!("expected set ok");
12616 };
12617 assert_eq!(entry.value, RuntimeConfigValue::Float(30.0));
12618
12619 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12620 key: "auto.lan.peer_job_interval_secs".into(),
12621 });
12622 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12623 panic!("expected reset ok");
12624 };
12625 assert_eq!(entry.value, RuntimeConfigValue::Float(4.0));
12626 }
12627
12628 #[cfg(feature = "iface-i2p")]
12629 #[test]
12630 fn runtime_config_lists_i2p_keys() {
12631 let mut driver = new_test_driver();
12632 register_test_i2p(&mut driver, "anon");
12633 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
12634 let QueryResponse::RuntimeConfigList(entries) = response else {
12635 panic!("expected runtime config list");
12636 };
12637 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
12638 assert!(keys.contains(&"i2p.anon.reconnect_wait_secs".to_string()));
12639 }
12640
12641 #[cfg(feature = "iface-i2p")]
12642 #[test]
12643 fn runtime_config_sets_i2p_values() {
12644 let mut driver = new_test_driver();
12645 register_test_i2p(&mut driver, "anon");
12646
12647 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12648 key: "i2p.anon.reconnect_wait_secs".into(),
12649 value: RuntimeConfigValue::Float(3.5),
12650 });
12651 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12652 panic!("expected set ok");
12653 };
12654 assert_eq!(entry.value, RuntimeConfigValue::Float(3.5));
12655
12656 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12657 key: "i2p.anon.reconnect_wait_secs".into(),
12658 });
12659 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12660 panic!("expected reset ok");
12661 };
12662 assert_eq!(entry.value, RuntimeConfigValue::Float(15.0));
12663 }
12664
12665 #[cfg(feature = "iface-pipe")]
12666 #[test]
12667 fn runtime_config_lists_pipe_keys() {
12668 let mut driver = new_test_driver();
12669 register_test_pipe(&mut driver, "worker");
12670 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
12671 let QueryResponse::RuntimeConfigList(entries) = response else {
12672 panic!("expected runtime config list");
12673 };
12674 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
12675 assert!(keys.contains(&"pipe.worker.respawn_delay_secs".to_string()));
12676 }
12677
12678 #[cfg(feature = "iface-pipe")]
12679 #[test]
12680 fn runtime_config_sets_pipe_values() {
12681 let mut driver = new_test_driver();
12682 register_test_pipe(&mut driver, "worker");
12683
12684 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12685 key: "pipe.worker.respawn_delay_secs".into(),
12686 value: RuntimeConfigValue::Float(2.0),
12687 });
12688 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12689 panic!("expected set ok");
12690 };
12691 assert_eq!(entry.value, RuntimeConfigValue::Float(2.0));
12692
12693 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12694 key: "pipe.worker.respawn_delay_secs".into(),
12695 });
12696 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12697 panic!("expected reset ok");
12698 };
12699 assert_eq!(entry.value, RuntimeConfigValue::Float(5.0));
12700 }
12701
12702 #[cfg(feature = "iface-rnode")]
12703 #[test]
12704 fn runtime_config_lists_rnode_keys() {
12705 let mut driver = new_test_driver();
12706 register_test_rnode(&mut driver, "radio");
12707 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
12708 let QueryResponse::RuntimeConfigList(entries) = response else {
12709 panic!("expected runtime config list");
12710 };
12711 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
12712 assert!(keys.contains(&"rnode.radio.frequency_hz".to_string()));
12713 assert!(keys.contains(&"rnode.radio.bandwidth_hz".to_string()));
12714 assert!(keys.contains(&"rnode.radio.txpower_dbm".to_string()));
12715 assert!(keys.contains(&"rnode.radio.spreading_factor".to_string()));
12716 assert!(keys.contains(&"rnode.radio.coding_rate".to_string()));
12717 assert!(keys.contains(&"rnode.radio.st_alock_pct".to_string()));
12718 assert!(keys.contains(&"rnode.radio.lt_alock_pct".to_string()));
12719 }
12720
12721 #[cfg(feature = "iface-rnode")]
12722 #[test]
12723 fn runtime_config_sets_rnode_values() {
12724 let mut driver = new_test_driver();
12725 register_test_rnode(&mut driver, "radio");
12726
12727 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12728 key: "rnode.radio.frequency_hz".into(),
12729 value: RuntimeConfigValue::Int(915_000_000),
12730 });
12731 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12732 panic!("expected set ok");
12733 };
12734 assert_eq!(entry.value, RuntimeConfigValue::Int(915_000_000));
12735
12736 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12737 key: "rnode.radio.st_alock_pct".into(),
12738 value: RuntimeConfigValue::Float(12.5),
12739 });
12740 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12741 panic!("expected set ok");
12742 };
12743 assert_eq!(entry.value, RuntimeConfigValue::Float(12.5));
12744
12745 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12746 key: "rnode.radio.frequency_hz".into(),
12747 });
12748 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12749 panic!("expected reset ok");
12750 };
12751 assert_eq!(entry.value, RuntimeConfigValue::Int(868_000_000));
12752 }
12753
12754 #[test]
12755 fn runtime_config_lists_generic_interface_keys() {
12756 let mut driver = new_test_driver();
12757 register_test_generic_interface(&mut driver, 1, "public");
12758 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
12759 let QueryResponse::RuntimeConfigList(entries) = response else {
12760 panic!("expected runtime config list");
12761 };
12762 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
12763 assert!(keys.contains(&"interface.public.enabled".to_string()));
12764 assert!(keys.contains(&"interface.public.mode".to_string()));
12765 assert!(keys.contains(&"interface.public.announce_rate_target".to_string()));
12766 assert!(keys.contains(&"interface.public.announce_rate_grace".to_string()));
12767 assert!(keys.contains(&"interface.public.announce_rate_penalty".to_string()));
12768 assert!(keys.contains(&"interface.public.announce_cap".to_string()));
12769 assert!(keys.contains(&"interface.public.ingress_control".to_string()));
12770 assert!(keys.contains(&"interface.public.ic_max_held_announces".to_string()));
12771 assert!(keys.contains(&"interface.public.ic_burst_hold".to_string()));
12772 assert!(keys.contains(&"interface.public.ic_burst_freq_new".to_string()));
12773 assert!(keys.contains(&"interface.public.ic_burst_freq".to_string()));
12774 assert!(keys.contains(&"interface.public.ic_new_time".to_string()));
12775 assert!(keys.contains(&"interface.public.ic_burst_penalty".to_string()));
12776 assert!(keys.contains(&"interface.public.ic_held_release_interval".to_string()));
12777 assert!(keys.contains(&"interface.public.ifac_netname".to_string()));
12778 assert!(keys.contains(&"interface.public.ifac_passphrase".to_string()));
12779 assert!(keys.contains(&"interface.public.ifac_size_bytes".to_string()));
12780 }
12781
12782 #[test]
12783 fn runtime_config_sets_generic_interface_values() {
12784 let mut driver = new_test_driver();
12785 register_test_generic_interface(&mut driver, 1, "public");
12786
12787 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12788 key: "interface.public.enabled".into(),
12789 value: RuntimeConfigValue::Bool(false),
12790 });
12791 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12792 panic!("expected set ok");
12793 };
12794 assert_eq!(entry.value, RuntimeConfigValue::Bool(false));
12795 assert!(!driver.interfaces.get(&InterfaceId(1)).unwrap().enabled);
12796
12797 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12798 key: "interface.public.announce_cap".into(),
12799 value: RuntimeConfigValue::Float(0.15),
12800 });
12801 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12802 panic!("expected set ok");
12803 };
12804 assert_eq!(entry.value, RuntimeConfigValue::Float(0.15));
12805 assert_eq!(
12806 driver
12807 .engine
12808 .interface_info(&InterfaceId(1))
12809 .unwrap()
12810 .announce_cap,
12811 0.15
12812 );
12813
12814 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12815 key: "interface.public.mode".into(),
12816 value: RuntimeConfigValue::String("gateway".into()),
12817 });
12818 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12819 panic!("expected set ok");
12820 };
12821 assert_eq!(entry.value, RuntimeConfigValue::String("gateway".into()));
12822
12823 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12824 key: "interface.public.mode".into(),
12825 });
12826 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12827 panic!("expected reset ok");
12828 };
12829 assert_eq!(entry.value, RuntimeConfigValue::String("full".into()));
12830
12831 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12832 key: "interface.public.ic_max_held_announces".into(),
12833 value: RuntimeConfigValue::Int(17),
12834 });
12835 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12836 panic!("expected set ok");
12837 };
12838 assert_eq!(entry.value, RuntimeConfigValue::Int(17));
12839 assert_eq!(
12840 driver
12841 .engine
12842 .interface_info(&InterfaceId(1))
12843 .unwrap()
12844 .ingress_control
12845 .max_held_announces,
12846 17
12847 );
12848
12849 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12850 key: "interface.public.ic_burst_hold".into(),
12851 value: RuntimeConfigValue::Float(1.5),
12852 });
12853 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12854 panic!("expected set ok");
12855 };
12856 assert_eq!(entry.value, RuntimeConfigValue::Float(1.5));
12857
12858 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12859 key: "interface.public.ic_burst_freq_new".into(),
12860 value: RuntimeConfigValue::Float(2.5),
12861 });
12862 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12863 panic!("expected set ok");
12864 };
12865 assert_eq!(entry.value, RuntimeConfigValue::Float(2.5));
12866
12867 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12868 key: "interface.public.ic_burst_freq".into(),
12869 value: RuntimeConfigValue::Float(3.5),
12870 });
12871 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12872 panic!("expected set ok");
12873 };
12874 assert_eq!(entry.value, RuntimeConfigValue::Float(3.5));
12875
12876 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12877 key: "interface.public.ic_new_time".into(),
12878 value: RuntimeConfigValue::Float(4.5),
12879 });
12880 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12881 panic!("expected set ok");
12882 };
12883 assert_eq!(entry.value, RuntimeConfigValue::Float(4.5));
12884
12885 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12886 key: "interface.public.ic_burst_penalty".into(),
12887 value: RuntimeConfigValue::Float(5.5),
12888 });
12889 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12890 panic!("expected set ok");
12891 };
12892 assert_eq!(entry.value, RuntimeConfigValue::Float(5.5));
12893
12894 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12895 key: "interface.public.ic_held_release_interval".into(),
12896 value: RuntimeConfigValue::Float(6.5),
12897 });
12898 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12899 panic!("expected set ok");
12900 };
12901 assert_eq!(entry.value, RuntimeConfigValue::Float(6.5));
12902
12903 let ingress_control = driver
12904 .engine
12905 .interface_info(&InterfaceId(1))
12906 .unwrap()
12907 .ingress_control;
12908 assert_eq!(ingress_control.burst_hold, 1.5);
12909 assert_eq!(ingress_control.burst_freq_new, 2.5);
12910 assert_eq!(ingress_control.burst_freq, 3.5);
12911 assert_eq!(ingress_control.new_time, 4.5);
12912 assert_eq!(ingress_control.burst_penalty, 5.5);
12913 assert_eq!(ingress_control.held_release_interval, 6.5);
12914
12915 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12916 key: "interface.public.ic_max_held_announces".into(),
12917 });
12918 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12919 panic!("expected reset ok");
12920 };
12921 assert_eq!(
12922 entry.value,
12923 RuntimeConfigValue::Int(rns_core::constants::IC_MAX_HELD_ANNOUNCES as i64)
12924 );
12925
12926 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12927 key: "interface.public.enabled".into(),
12928 });
12929 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12930 panic!("expected reset ok");
12931 };
12932 assert_eq!(entry.value, RuntimeConfigValue::Bool(true));
12933 assert!(driver.interfaces.get(&InterfaceId(1)).unwrap().enabled);
12934
12935 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12936 key: "interface.public.ifac_netname".into(),
12937 value: RuntimeConfigValue::String("mesh".into()),
12938 });
12939 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12940 panic!("expected set ok");
12941 };
12942 assert_eq!(entry.value, RuntimeConfigValue::String("mesh".into()));
12943 assert_eq!(
12944 driver
12945 .interfaces
12946 .get(&InterfaceId(1))
12947 .unwrap()
12948 .ifac
12949 .as_ref()
12950 .unwrap()
12951 .size,
12952 16
12953 );
12954
12955 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12956 key: "interface.public.ifac_passphrase".into(),
12957 value: RuntimeConfigValue::String("secret".into()),
12958 });
12959 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12960 panic!("expected set ok");
12961 };
12962 assert_eq!(entry.value, RuntimeConfigValue::String("<redacted>".into()));
12963
12964 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12965 key: "interface.public.ifac_size_bytes".into(),
12966 value: RuntimeConfigValue::Int(24),
12967 });
12968 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12969 panic!("expected set ok");
12970 };
12971 assert_eq!(entry.value, RuntimeConfigValue::Int(24));
12972 let ifac = driver
12973 .interfaces
12974 .get(&InterfaceId(1))
12975 .unwrap()
12976 .ifac
12977 .as_ref()
12978 .unwrap();
12979 assert_eq!(ifac.size, 24);
12980
12981 let response = driver.handle_query(QueryRequest::GetRuntimeConfig {
12982 key: "interface.public.ifac_passphrase".into(),
12983 });
12984 let QueryResponse::RuntimeConfigEntry(Some(entry)) = response else {
12985 panic!("expected runtime config entry");
12986 };
12987 assert_eq!(entry.value, RuntimeConfigValue::String("<redacted>".into()));
12988
12989 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12990 key: "interface.public.ifac_netname".into(),
12991 });
12992 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12993 panic!("expected reset ok");
12994 };
12995 assert_eq!(entry.value, RuntimeConfigValue::Null);
12996 assert!(driver
12997 .interfaces
12998 .get(&InterfaceId(1))
12999 .unwrap()
13000 .ifac
13001 .is_some());
13002
13003 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
13004 key: "interface.public.ifac_passphrase".into(),
13005 });
13006 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
13007 panic!("expected reset ok");
13008 };
13009 assert_eq!(entry.value, RuntimeConfigValue::Null);
13010 assert!(driver
13011 .interfaces
13012 .get(&InterfaceId(1))
13013 .unwrap()
13014 .ifac
13015 .is_none());
13016 }
13017
13018 #[cfg(feature = "rns-hooks")]
13019 #[test]
13020 fn runtime_config_sets_provider_bridge_values() {
13021 let mut driver = new_test_driver();
13022
13023 let dir = tempfile::tempdir().unwrap();
13024 let socket_path = dir.path().join("provider.sock");
13025 let bridge = crate::provider_bridge::ProviderBridge::start(
13026 crate::provider_bridge::ProviderBridgeConfig {
13027 enabled: true,
13028 socket_path,
13029 queue_max_events: 1024,
13030 queue_max_bytes: 1024 * 1024,
13031 ..Default::default()
13032 },
13033 )
13034 .unwrap();
13035 driver.runtime_config_defaults.provider_queue_max_events = 1024;
13036 driver.runtime_config_defaults.provider_queue_max_bytes = 1024 * 1024;
13037 driver.provider_bridge = Some(bridge);
13038
13039 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
13040 key: "provider.queue_max_events".into(),
13041 value: RuntimeConfigValue::Int(4096),
13042 });
13043 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
13044 panic!("expected set ok");
13045 };
13046 assert_eq!(entry.value, RuntimeConfigValue::Int(4096));
13047 assert_eq!(entry.source, RuntimeConfigSource::RuntimeOverride,);
13048
13049 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
13050 key: "provider.queue_max_bytes".into(),
13051 value: RuntimeConfigValue::Int(2 * 1024 * 1024),
13052 });
13053 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
13054 panic!("expected set ok");
13055 };
13056 assert_eq!(entry.value, RuntimeConfigValue::Int(2 * 1024 * 1024));
13057
13058 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
13060 key: "provider.queue_max_events".into(),
13061 value: RuntimeConfigValue::Int(0),
13062 });
13063 assert!(matches!(response, QueryResponse::RuntimeConfigSet(Err(_))));
13064
13065 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
13067 key: "provider.queue_max_events".into(),
13068 });
13069 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
13070 panic!("expected reset ok");
13071 };
13072 assert_eq!(entry.value, RuntimeConfigValue::Int(1024));
13073 assert_eq!(entry.source, RuntimeConfigSource::Startup);
13074
13075 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
13076 key: "provider.queue_max_bytes".into(),
13077 });
13078 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
13079 panic!("expected reset ok");
13080 };
13081 assert_eq!(entry.value, RuntimeConfigValue::Int(1024 * 1024));
13082 }
13083
13084 #[test]
13085 fn disabled_interface_drops_ingress_and_egress() {
13086 let (tx, rx) = event::channel();
13087 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13088 let mut driver = Driver::new(
13089 TransportConfig {
13090 transport_enabled: false,
13091 identity_hash: None,
13092 prefer_shorter_path: false,
13093 max_paths_per_destination: 1,
13094 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13095 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13096 max_path_destinations: usize::MAX,
13097 max_tunnel_destinations_total: usize::MAX,
13098 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13099 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13100 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13101 announce_sig_cache_enabled: true,
13102 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13103 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13104 announce_queue_max_entries: 256,
13105 announce_queue_max_interfaces: 1024,
13106 },
13107 rx,
13108 tx.clone(),
13109 Box::new(cbs),
13110 );
13111 let info = make_interface_info(1);
13112 driver.register_interface_runtime_defaults(&info);
13113 driver.engine.register_interface(info.clone());
13114 let (writer, sent) = MockWriter::new();
13115 driver.interfaces.insert(
13116 InterfaceId(1),
13117 InterfaceEntry {
13118 id: InterfaceId(1),
13119 info,
13120 writer: Box::new(writer),
13121 async_writer_metrics: None,
13122 enabled: false,
13123 online: true,
13124 dynamic: false,
13125 ifac: None,
13126 stats: InterfaceStats::default(),
13127 interface_type: String::new(),
13128 send_retry_at: None,
13129 send_retry_backoff: Duration::ZERO,
13130 },
13131 );
13132
13133 driver.dispatch_all(vec![TransportAction::SendOnInterface {
13134 interface: InterfaceId(1),
13135 raw: vec![0x00, 0x01, 0x42],
13136 }]);
13137 assert!(sent.lock().unwrap().is_empty());
13138
13139 tx.send(Event::Frame {
13140 interface_id: InterfaceId(1),
13141 data: vec![0x00, 0x01, 0x42],
13142 })
13143 .unwrap();
13144 tx.send(Event::Shutdown).unwrap();
13145 driver.run();
13146
13147 let entry = driver.interfaces.get(&InterfaceId(1)).unwrap();
13148 assert_eq!(entry.stats.rxb, 0);
13149 assert_eq!(entry.stats.rx_packets, 0);
13150 }
13151
13152 #[test]
13153 fn management_announces_not_emitted_when_disabled() {
13154 let (tx, rx) = event::channel();
13155 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13156 let identity = Identity::new(&mut OsRng);
13157 let identity_hash = *identity.hash();
13158 let mut driver = Driver::new(
13159 TransportConfig {
13160 transport_enabled: true,
13161 identity_hash: Some(identity_hash),
13162 prefer_shorter_path: false,
13163 max_paths_per_destination: 1,
13164 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13165 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13166 max_path_destinations: usize::MAX,
13167 max_tunnel_destinations_total: usize::MAX,
13168 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13169 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13170 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13171 announce_sig_cache_enabled: true,
13172 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13173 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13174 announce_queue_max_entries: 256,
13175 announce_queue_max_interfaces: 1024,
13176 },
13177 rx,
13178 tx.clone(),
13179 Box::new(cbs),
13180 );
13181
13182 let info = make_interface_info(1);
13183 driver.engine.register_interface(info.clone());
13184 let (writer, sent) = MockWriter::new();
13185 driver
13186 .interfaces
13187 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13188
13189 driver.transport_identity = Some(identity);
13191 driver.started = time::now() - 10.0;
13192
13193 tx.send(Event::Tick).unwrap();
13194 tx.send(Event::Shutdown).unwrap();
13195 driver.run();
13196
13197 let sent_packets = sent.lock().unwrap();
13199 assert!(
13200 sent_packets.is_empty(),
13201 "No announces should be sent when management is disabled"
13202 );
13203 }
13204
13205 #[test]
13206 fn management_announces_not_emitted_before_delay() {
13207 let (tx, rx) = event::channel();
13208 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13209 let identity = Identity::new(&mut OsRng);
13210 let identity_hash = *identity.hash();
13211 let mut driver = Driver::new(
13212 TransportConfig {
13213 transport_enabled: true,
13214 identity_hash: Some(identity_hash),
13215 prefer_shorter_path: false,
13216 max_paths_per_destination: 1,
13217 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13218 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13219 max_path_destinations: usize::MAX,
13220 max_tunnel_destinations_total: usize::MAX,
13221 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13222 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13223 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13224 announce_sig_cache_enabled: true,
13225 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13226 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13227 announce_queue_max_entries: 256,
13228 announce_queue_max_interfaces: 1024,
13229 },
13230 rx,
13231 tx.clone(),
13232 Box::new(cbs),
13233 );
13234
13235 let info = make_interface_info(1);
13236 driver.engine.register_interface(info.clone());
13237 let (writer, sent) = MockWriter::new();
13238 driver
13239 .interfaces
13240 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13241
13242 driver.management_config.enable_remote_management = true;
13243 driver.transport_identity = Some(identity);
13244 driver.started = time::now();
13246
13247 tx.send(Event::Tick).unwrap();
13248 tx.send(Event::Shutdown).unwrap();
13249 driver.run();
13250
13251 let sent_packets = sent.lock().unwrap();
13252 assert!(sent_packets.is_empty(), "No announces before startup delay");
13253 }
13254
13255 #[test]
13260 fn announce_received_populates_known_destinations() {
13261 let (tx, rx) = event::channel();
13262 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13263 let mut driver = Driver::new(
13264 TransportConfig {
13265 transport_enabled: false,
13266 identity_hash: None,
13267 prefer_shorter_path: false,
13268 max_paths_per_destination: 1,
13269 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13270 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13271 max_path_destinations: usize::MAX,
13272 max_tunnel_destinations_total: usize::MAX,
13273 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13274 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13275 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13276 announce_sig_cache_enabled: true,
13277 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13278 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13279 announce_queue_max_entries: 256,
13280 announce_queue_max_interfaces: 1024,
13281 },
13282 rx,
13283 tx.clone(),
13284 Box::new(cbs),
13285 );
13286 let info = make_interface_info(1);
13287 driver.engine.register_interface(info);
13288 let (writer, _sent) = MockWriter::new();
13289 driver
13290 .interfaces
13291 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13292
13293 let identity = Identity::new(&mut OsRng);
13294 let announce_raw = build_announce_packet(&identity);
13295
13296 let dest_hash =
13297 rns_core::destination::destination_hash("test", &["app"], Some(identity.hash()));
13298
13299 tx.send(Event::Frame {
13300 interface_id: InterfaceId(1),
13301 data: announce_raw,
13302 })
13303 .unwrap();
13304 tx.send(Event::Shutdown).unwrap();
13305 driver.run();
13306
13307 assert!(driver.known_destinations.contains_key(&dest_hash));
13309 let recalled = &driver.known_destinations[&dest_hash];
13310 assert_eq!(recalled.announced.dest_hash.0, dest_hash);
13311 assert_eq!(recalled.announced.identity_hash.0, *identity.hash());
13312 assert_eq!(
13313 &recalled.announced.public_key,
13314 &identity.get_public_key().unwrap()
13315 );
13316 assert_eq!(recalled.announced.hops, 1);
13317 }
13318
13319 #[test]
13320 fn known_destinations_cleanup_respects_ttl() {
13321 let (tx, rx) = event::channel();
13322 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13323 let mut driver = Driver::new(
13324 TransportConfig {
13325 transport_enabled: false,
13326 identity_hash: None,
13327 prefer_shorter_path: false,
13328 max_paths_per_destination: 1,
13329 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13330 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13331 max_path_destinations: usize::MAX,
13332 max_tunnel_destinations_total: usize::MAX,
13333 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13334 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13335 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13336 announce_sig_cache_enabled: true,
13337 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13338 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13339 announce_queue_max_entries: 256,
13340 announce_queue_max_interfaces: 1024,
13341 },
13342 rx,
13343 tx.clone(),
13344 Box::new(cbs),
13345 );
13346
13347 driver.known_destinations_ttl = 10.0;
13348 driver.cache_cleanup_counter = 3599;
13349
13350 let stale_dest = [0x11; 16];
13351 let fresh_dest = [0x22; 16];
13352 driver.known_destinations.insert(
13353 stale_dest,
13354 KnownDestinationState {
13355 announced: crate::destination::AnnouncedIdentity {
13356 dest_hash: rns_core::types::DestHash(stale_dest),
13357 identity_hash: rns_core::types::IdentityHash([0x33; 16]),
13358 public_key: [0x44; 64],
13359 app_data: None,
13360 hops: 1,
13361 received_at: time::now() - 20.0,
13362 receiving_interface: InterfaceId(1),
13363 },
13364 was_used: false,
13365 last_used_at: None,
13366 retained: false,
13367 },
13368 );
13369 driver.known_destinations.insert(
13370 fresh_dest,
13371 KnownDestinationState {
13372 announced: crate::destination::AnnouncedIdentity {
13373 dest_hash: rns_core::types::DestHash(fresh_dest),
13374 identity_hash: rns_core::types::IdentityHash([0x55; 16]),
13375 public_key: [0x66; 64],
13376 app_data: None,
13377 hops: 1,
13378 received_at: time::now() - 5.0,
13379 receiving_interface: InterfaceId(1),
13380 },
13381 was_used: false,
13382 last_used_at: None,
13383 retained: false,
13384 },
13385 );
13386
13387 tx.send(Event::Tick).unwrap();
13388 tx.send(Event::Shutdown).unwrap();
13389 driver.run();
13390
13391 assert!(!driver.known_destinations.contains_key(&stale_dest));
13392 assert!(driver.known_destinations.contains_key(&fresh_dest));
13393 }
13394
13395 #[test]
13396 fn known_destinations_cap_prefers_evicting_oldest_non_active_non_local() {
13397 let mut driver = new_test_driver();
13398 driver.known_destinations_max_entries = 2;
13399 driver.engine.register_interface(make_interface_info(1));
13400
13401 let active_dest = [0x11; 16];
13402 let evictable_dest = [0x22; 16];
13403 let new_dest = [0x33; 16];
13404
13405 driver.engine.inject_path(
13406 active_dest,
13407 PathEntry {
13408 timestamp: 100.0,
13409 next_hop: [0x44; 16],
13410 hops: 1,
13411 expires: 1000.0,
13412 random_blobs: Vec::new(),
13413 receiving_interface: InterfaceId(1),
13414 packet_hash: [0x55; 32],
13415 announce_raw: None,
13416 },
13417 );
13418
13419 driver.upsert_known_destination(
13420 active_dest,
13421 make_announced_identity(active_dest, 10.0, InterfaceId(1)),
13422 );
13423 driver.upsert_known_destination(
13424 evictable_dest,
13425 make_announced_identity(evictable_dest, 20.0, InterfaceId(1)),
13426 );
13427 driver.upsert_known_destination(
13428 new_dest,
13429 make_announced_identity(new_dest, 30.0, InterfaceId(1)),
13430 );
13431
13432 assert!(driver.known_destinations.contains_key(&active_dest));
13433 assert!(!driver.known_destinations.contains_key(&evictable_dest));
13434 assert!(driver.known_destinations.contains_key(&new_dest));
13435 assert_eq!(driver.known_destinations_cap_evict_count, 1);
13436 }
13437
13438 #[test]
13439 fn known_destinations_cap_falls_back_to_oldest_overall_when_all_protected() {
13440 let mut driver = new_test_driver();
13441 driver.known_destinations_max_entries = 2;
13442
13443 let local_oldest = [0x41; 16];
13444 let local_newer = [0x42; 16];
13445 let new_dest = [0x43; 16];
13446 driver
13447 .local_destinations
13448 .insert(local_oldest, rns_core::constants::DESTINATION_SINGLE);
13449 driver
13450 .local_destinations
13451 .insert(local_newer, rns_core::constants::DESTINATION_SINGLE);
13452
13453 driver.upsert_known_destination(
13454 local_oldest,
13455 make_announced_identity(local_oldest, 10.0, InterfaceId(1)),
13456 );
13457 driver.upsert_known_destination(
13458 local_newer,
13459 make_announced_identity(local_newer, 20.0, InterfaceId(1)),
13460 );
13461 driver.upsert_known_destination(
13462 new_dest,
13463 make_announced_identity(new_dest, 30.0, InterfaceId(1)),
13464 );
13465
13466 assert!(!driver.known_destinations.contains_key(&local_oldest));
13467 assert!(driver.known_destinations.contains_key(&local_newer));
13468 assert!(driver.known_destinations.contains_key(&new_dest));
13469 assert_eq!(driver.known_destinations_cap_evict_count, 1);
13470 }
13471
13472 #[test]
13473 fn known_destinations_cap_update_existing_entry_does_not_evict() {
13474 let mut driver = new_test_driver();
13475 driver.known_destinations_max_entries = 1;
13476
13477 let dest = [0x61; 16];
13478 driver.upsert_known_destination(dest, make_announced_identity(dest, 10.0, InterfaceId(1)));
13479 driver.upsert_known_destination(dest, make_announced_identity(dest, 20.0, InterfaceId(2)));
13480
13481 assert_eq!(driver.known_destinations.len(), 1);
13482 assert_eq!(
13483 driver.known_destinations[&dest]
13484 .announced
13485 .receiving_interface,
13486 InterfaceId(2)
13487 );
13488 assert_eq!(driver.known_destinations_cap_evict_count, 0);
13489 }
13490
13491 #[test]
13492 fn known_destinations_cleanup_enforces_cap() {
13493 let (tx, rx) = event::channel();
13494 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13495 let mut driver = Driver::new(
13496 TransportConfig {
13497 transport_enabled: false,
13498 identity_hash: None,
13499 prefer_shorter_path: false,
13500 max_paths_per_destination: 1,
13501 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13502 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13503 max_path_destinations: usize::MAX,
13504 max_tunnel_destinations_total: usize::MAX,
13505 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13506 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13507 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13508 announce_sig_cache_enabled: true,
13509 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13510 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13511 announce_queue_max_entries: 256,
13512 announce_queue_max_interfaces: 1024,
13513 },
13514 rx,
13515 tx.clone(),
13516 Box::new(cbs),
13517 );
13518
13519 driver.known_destinations_ttl = 1000.0;
13520 driver.known_destinations_max_entries = 2;
13521 driver.cache_cleanup_counter = 3599;
13522 let now = time::now();
13523 driver.known_destinations.insert(
13524 [0x71; 16],
13525 make_known_destination_state([0x71; 16], now - 30.0, InterfaceId(1)),
13526 );
13527 driver.known_destinations.insert(
13528 [0x72; 16],
13529 make_known_destination_state([0x72; 16], now - 20.0, InterfaceId(1)),
13530 );
13531 driver.known_destinations.insert(
13532 [0x73; 16],
13533 make_known_destination_state([0x73; 16], now - 10.0, InterfaceId(1)),
13534 );
13535
13536 tx.send(Event::Tick).unwrap();
13537 tx.send(Event::Shutdown).unwrap();
13538 driver.run();
13539
13540 assert_eq!(driver.known_destinations.len(), 2);
13541 assert!(!driver.known_destinations.contains_key(&[0x71; 16]));
13542 assert_eq!(driver.known_destinations_cap_evict_count, 1);
13543 }
13544
13545 #[test]
13546 fn recall_identity_marks_known_destination_used() {
13547 let mut driver = new_test_driver();
13548 let dest = [0x81; 16];
13549 driver.upsert_known_destination(dest, make_announced_identity(dest, 10.0, InterfaceId(1)));
13550
13551 let response = driver.handle_query_mut(QueryRequest::RecallIdentity { dest_hash: dest });
13552 assert!(matches!(response, QueryResponse::RecallIdentity(Some(_))));
13553
13554 let entry = driver.known_destinations.get(&dest).unwrap();
13555 assert!(entry.was_used);
13556 assert!(entry.last_used_at.is_some());
13557 }
13558
13559 #[test]
13560 fn retained_known_destination_survives_cleanup() {
13561 let (tx, rx) = event::channel();
13562 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13563 let mut driver = Driver::new(
13564 TransportConfig {
13565 transport_enabled: false,
13566 identity_hash: None,
13567 prefer_shorter_path: false,
13568 max_paths_per_destination: 1,
13569 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13570 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13571 max_path_destinations: usize::MAX,
13572 max_tunnel_destinations_total: usize::MAX,
13573 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13574 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13575 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13576 announce_sig_cache_enabled: true,
13577 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13578 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13579 announce_queue_max_entries: 256,
13580 announce_queue_max_interfaces: 1024,
13581 },
13582 rx,
13583 tx.clone(),
13584 Box::new(cbs),
13585 );
13586 driver.known_destinations_ttl = 10.0;
13587 driver.cache_cleanup_counter = 3599;
13588
13589 let dest = [0x82; 16];
13590 driver.upsert_known_destination(
13591 dest,
13592 make_announced_identity(dest, time::now() - 30.0, InterfaceId(1)),
13593 );
13594 assert!(driver.retain_known_destination(&dest));
13595
13596 tx.send(Event::Tick).unwrap();
13597 tx.send(Event::Shutdown).unwrap();
13598 driver.run();
13599
13600 assert!(driver.known_destinations.contains_key(&dest));
13601 }
13602
13603 #[test]
13604 fn used_known_destination_cleanup_uses_last_used_time() {
13605 let (tx, rx) = event::channel();
13606 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13607 let mut driver = Driver::new(
13608 TransportConfig {
13609 transport_enabled: false,
13610 identity_hash: None,
13611 prefer_shorter_path: false,
13612 max_paths_per_destination: 1,
13613 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13614 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13615 max_path_destinations: usize::MAX,
13616 max_tunnel_destinations_total: usize::MAX,
13617 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13618 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13619 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13620 announce_sig_cache_enabled: true,
13621 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13622 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13623 announce_queue_max_entries: 256,
13624 announce_queue_max_interfaces: 1024,
13625 },
13626 rx,
13627 tx.clone(),
13628 Box::new(cbs),
13629 );
13630 driver.known_destinations_ttl = 10.0;
13631 driver.cache_cleanup_counter = 3599;
13632
13633 let dest = [0x83; 16];
13634 driver.known_destinations.insert(
13635 dest,
13636 KnownDestinationState {
13637 announced: make_announced_identity(dest, time::now() - 50.0, InterfaceId(1)),
13638 was_used: true,
13639 last_used_at: Some(time::now() - 5.0),
13640 retained: false,
13641 },
13642 );
13643
13644 tx.send(Event::Tick).unwrap();
13645 tx.send(Event::Shutdown).unwrap();
13646 driver.run();
13647
13648 assert!(driver.known_destinations.contains_key(&dest));
13649 }
13650
13651 #[test]
13652 fn query_has_path() {
13653 let (tx, rx) = event::channel();
13654 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13655 let mut driver = Driver::new(
13656 TransportConfig {
13657 transport_enabled: false,
13658 identity_hash: None,
13659 prefer_shorter_path: false,
13660 max_paths_per_destination: 1,
13661 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13662 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13663 max_path_destinations: usize::MAX,
13664 max_tunnel_destinations_total: usize::MAX,
13665 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13666 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13667 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13668 announce_sig_cache_enabled: true,
13669 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13670 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13671 announce_queue_max_entries: 256,
13672 announce_queue_max_interfaces: 1024,
13673 },
13674 rx,
13675 tx.clone(),
13676 Box::new(cbs),
13677 );
13678 let info = make_interface_info(1);
13679 driver.engine.register_interface(info);
13680 let (writer, _sent) = MockWriter::new();
13681 driver
13682 .interfaces
13683 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13684
13685 let (resp_tx, resp_rx) = mpsc::channel();
13687 tx.send(Event::Query(
13688 QueryRequest::HasPath {
13689 dest_hash: [0xAA; 16],
13690 },
13691 resp_tx,
13692 ))
13693 .unwrap();
13694
13695 let identity = Identity::new(&mut OsRng);
13697 let announce_raw = build_announce_packet(&identity);
13698 let dest_hash =
13699 rns_core::destination::destination_hash("test", &["app"], Some(identity.hash()));
13700 tx.send(Event::Frame {
13701 interface_id: InterfaceId(1),
13702 data: announce_raw,
13703 })
13704 .unwrap();
13705
13706 let (resp_tx2, resp_rx2) = mpsc::channel();
13707 tx.send(Event::Query(QueryRequest::HasPath { dest_hash }, resp_tx2))
13708 .unwrap();
13709
13710 tx.send(Event::Shutdown).unwrap();
13711 driver.run();
13712
13713 match resp_rx.recv().unwrap() {
13715 QueryResponse::HasPath(false) => {}
13716 other => panic!("expected HasPath(false), got {:?}", other),
13717 }
13718
13719 match resp_rx2.recv().unwrap() {
13721 QueryResponse::HasPath(true) => {}
13722 other => panic!("expected HasPath(true), got {:?}", other),
13723 }
13724 }
13725
13726 #[test]
13727 fn query_hops_to() {
13728 let (tx, rx) = event::channel();
13729 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13730 let mut driver = Driver::new(
13731 TransportConfig {
13732 transport_enabled: false,
13733 identity_hash: None,
13734 prefer_shorter_path: false,
13735 max_paths_per_destination: 1,
13736 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13737 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13738 max_path_destinations: usize::MAX,
13739 max_tunnel_destinations_total: usize::MAX,
13740 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13741 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13742 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13743 announce_sig_cache_enabled: true,
13744 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13745 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13746 announce_queue_max_entries: 256,
13747 announce_queue_max_interfaces: 1024,
13748 },
13749 rx,
13750 tx.clone(),
13751 Box::new(cbs),
13752 );
13753 let info = make_interface_info(1);
13754 driver.engine.register_interface(info);
13755 let (writer, _sent) = MockWriter::new();
13756 driver
13757 .interfaces
13758 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13759
13760 let identity = Identity::new(&mut OsRng);
13762 let announce_raw = build_announce_packet(&identity);
13763 let dest_hash =
13764 rns_core::destination::destination_hash("test", &["app"], Some(identity.hash()));
13765
13766 tx.send(Event::Frame {
13767 interface_id: InterfaceId(1),
13768 data: announce_raw,
13769 })
13770 .unwrap();
13771
13772 let (resp_tx, resp_rx) = mpsc::channel();
13773 tx.send(Event::Query(QueryRequest::HopsTo { dest_hash }, resp_tx))
13774 .unwrap();
13775 tx.send(Event::Shutdown).unwrap();
13776 driver.run();
13777
13778 match resp_rx.recv().unwrap() {
13779 QueryResponse::HopsTo(Some(1)) => {}
13780 other => panic!("expected HopsTo(Some(1)), got {:?}", other),
13781 }
13782 }
13783
13784 #[test]
13785 fn query_recall_identity() {
13786 let (tx, rx) = event::channel();
13787 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13788 let mut driver = Driver::new(
13789 TransportConfig {
13790 transport_enabled: false,
13791 identity_hash: None,
13792 prefer_shorter_path: false,
13793 max_paths_per_destination: 1,
13794 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13795 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13796 max_path_destinations: usize::MAX,
13797 max_tunnel_destinations_total: usize::MAX,
13798 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13799 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13800 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13801 announce_sig_cache_enabled: true,
13802 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13803 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13804 announce_queue_max_entries: 256,
13805 announce_queue_max_interfaces: 1024,
13806 },
13807 rx,
13808 tx.clone(),
13809 Box::new(cbs),
13810 );
13811 let info = make_interface_info(1);
13812 driver.engine.register_interface(info);
13813 let (writer, _sent) = MockWriter::new();
13814 driver
13815 .interfaces
13816 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13817
13818 let identity = Identity::new(&mut OsRng);
13819 let announce_raw = build_announce_packet(&identity);
13820 let dest_hash =
13821 rns_core::destination::destination_hash("test", &["app"], Some(identity.hash()));
13822
13823 tx.send(Event::Frame {
13824 interface_id: InterfaceId(1),
13825 data: announce_raw,
13826 })
13827 .unwrap();
13828
13829 let (resp_tx, resp_rx) = mpsc::channel();
13831 tx.send(Event::Query(
13832 QueryRequest::RecallIdentity { dest_hash },
13833 resp_tx,
13834 ))
13835 .unwrap();
13836
13837 let (resp_tx2, resp_rx2) = mpsc::channel();
13839 tx.send(Event::Query(
13840 QueryRequest::RecallIdentity {
13841 dest_hash: [0xFF; 16],
13842 },
13843 resp_tx2,
13844 ))
13845 .unwrap();
13846
13847 tx.send(Event::Shutdown).unwrap();
13848 driver.run();
13849
13850 match resp_rx.recv().unwrap() {
13851 QueryResponse::RecallIdentity(Some(recalled)) => {
13852 assert_eq!(recalled.dest_hash.0, dest_hash);
13853 assert_eq!(recalled.identity_hash.0, *identity.hash());
13854 assert_eq!(recalled.public_key, identity.get_public_key().unwrap());
13855 assert_eq!(recalled.hops, 1);
13856 }
13857 other => panic!("expected RecallIdentity(Some(..)), got {:?}", other),
13858 }
13859
13860 match resp_rx2.recv().unwrap() {
13861 QueryResponse::RecallIdentity(None) => {}
13862 other => panic!("expected RecallIdentity(None), got {:?}", other),
13863 }
13864 }
13865
13866 #[test]
13867 fn request_path_sends_packet() {
13868 let (tx, rx) = event::channel();
13869 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13870 let mut driver = Driver::new(
13871 TransportConfig {
13872 transport_enabled: false,
13873 identity_hash: None,
13874 prefer_shorter_path: false,
13875 max_paths_per_destination: 1,
13876 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13877 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13878 max_path_destinations: usize::MAX,
13879 max_tunnel_destinations_total: usize::MAX,
13880 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13881 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13882 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13883 announce_sig_cache_enabled: true,
13884 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13885 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13886 announce_queue_max_entries: 256,
13887 announce_queue_max_interfaces: 1024,
13888 },
13889 rx,
13890 tx.clone(),
13891 Box::new(cbs),
13892 );
13893 let info = make_interface_info(1);
13894 driver.engine.register_interface(info);
13895 let (writer, sent) = MockWriter::new();
13896 driver
13897 .interfaces
13898 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13899
13900 tx.send(Event::RequestPath {
13902 dest_hash: [0xAA; 16],
13903 })
13904 .unwrap();
13905 tx.send(Event::Shutdown).unwrap();
13906 driver.run();
13907
13908 let sent_packets = sent.lock().unwrap();
13910 assert!(
13911 !sent_packets.is_empty(),
13912 "Path request should be sent on wire"
13913 );
13914
13915 let raw = &sent_packets[0];
13917 let flags = rns_core::packet::PacketFlags::unpack(raw[0] & 0x7F);
13918 assert_eq!(flags.packet_type, constants::PACKET_TYPE_DATA);
13919 assert_eq!(flags.destination_type, constants::DESTINATION_PLAIN);
13920 assert_eq!(flags.transport_type, constants::TRANSPORT_BROADCAST);
13921 }
13922
13923 #[test]
13924 fn request_path_includes_transport_id() {
13925 let (tx, rx) = event::channel();
13926 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13927 let mut driver = Driver::new(
13928 TransportConfig {
13929 transport_enabled: true,
13930 identity_hash: Some([0xBB; 16]),
13931 prefer_shorter_path: false,
13932 max_paths_per_destination: 1,
13933 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13934 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13935 max_path_destinations: usize::MAX,
13936 max_tunnel_destinations_total: usize::MAX,
13937 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13938 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13939 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13940 announce_sig_cache_enabled: true,
13941 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13942 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13943 announce_queue_max_entries: 256,
13944 announce_queue_max_interfaces: 1024,
13945 },
13946 rx,
13947 tx.clone(),
13948 Box::new(cbs),
13949 );
13950 let info = make_interface_info(1);
13951 driver.engine.register_interface(info);
13952 let (writer, sent) = MockWriter::new();
13953 driver
13954 .interfaces
13955 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13956
13957 tx.send(Event::RequestPath {
13958 dest_hash: [0xAA; 16],
13959 })
13960 .unwrap();
13961 tx.send(Event::Shutdown).unwrap();
13962 driver.run();
13963
13964 let sent_packets = sent.lock().unwrap();
13965 assert!(!sent_packets.is_empty());
13966
13967 let raw = &sent_packets[0];
13969 if let Ok(packet) = RawPacket::unpack(raw) {
13970 assert_eq!(
13972 packet.data.len(),
13973 48,
13974 "Path request data should be 48 bytes with transport_id"
13975 );
13976 assert_eq!(
13977 &packet.data[..16],
13978 &[0xAA; 16],
13979 "First 16 bytes should be dest_hash"
13980 );
13981 assert_eq!(
13982 &packet.data[16..32],
13983 &[0xBB; 16],
13984 "Next 16 bytes should be transport_id"
13985 );
13986 } else {
13987 panic!("Could not unpack sent packet");
13988 }
13989 }
13990
13991 #[test]
13992 fn path_request_dest_registered() {
13993 let (tx, rx) = event::channel();
13994 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13995 let driver = Driver::new(
13996 TransportConfig {
13997 transport_enabled: false,
13998 identity_hash: None,
13999 prefer_shorter_path: false,
14000 max_paths_per_destination: 1,
14001 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14002 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14003 max_path_destinations: usize::MAX,
14004 max_tunnel_destinations_total: usize::MAX,
14005 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14006 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14007 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14008 announce_sig_cache_enabled: true,
14009 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14010 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14011 announce_queue_max_entries: 256,
14012 announce_queue_max_interfaces: 1024,
14013 },
14014 rx,
14015 tx.clone(),
14016 Box::new(cbs),
14017 );
14018
14019 let expected_dest =
14021 rns_core::destination::destination_hash("rnstransport", &["path", "request"], None);
14022 assert_eq!(driver.path_request_dest, expected_dest);
14023
14024 drop(tx);
14025 }
14026
14027 #[test]
14032 fn register_proof_strategy_event() {
14033 let (tx, rx) = event::channel();
14034 let (cbs, _, _, _, _, _) = MockCallbacks::new();
14035 let mut driver = Driver::new(
14036 TransportConfig {
14037 transport_enabled: false,
14038 identity_hash: None,
14039 prefer_shorter_path: false,
14040 max_paths_per_destination: 1,
14041 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14042 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14043 max_path_destinations: usize::MAX,
14044 max_tunnel_destinations_total: usize::MAX,
14045 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14046 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14047 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14048 announce_sig_cache_enabled: true,
14049 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14050 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14051 announce_queue_max_entries: 256,
14052 announce_queue_max_interfaces: 1024,
14053 },
14054 rx,
14055 tx.clone(),
14056 Box::new(cbs),
14057 );
14058
14059 let dest = [0xAA; 16];
14060 let identity = Identity::new(&mut OsRng);
14061 let prv_key = identity.get_private_key().unwrap();
14062
14063 tx.send(Event::RegisterProofStrategy {
14064 dest_hash: dest,
14065 strategy: rns_core::types::ProofStrategy::ProveAll,
14066 signing_key: Some(prv_key),
14067 })
14068 .unwrap();
14069 tx.send(Event::Shutdown).unwrap();
14070 driver.run();
14071
14072 assert!(driver.proof_strategies.contains_key(&dest));
14073 let (strategy, ref id_opt) = driver.proof_strategies[&dest];
14074 assert_eq!(strategy, rns_core::types::ProofStrategy::ProveAll);
14075 assert!(id_opt.is_some());
14076 }
14077
14078 #[test]
14079 fn register_proof_strategy_prove_none_no_identity() {
14080 let (tx, rx) = event::channel();
14081 let (cbs, _, _, _, _, _) = MockCallbacks::new();
14082 let mut driver = Driver::new(
14083 TransportConfig {
14084 transport_enabled: false,
14085 identity_hash: None,
14086 prefer_shorter_path: false,
14087 max_paths_per_destination: 1,
14088 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14089 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14090 max_path_destinations: usize::MAX,
14091 max_tunnel_destinations_total: usize::MAX,
14092 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14093 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14094 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14095 announce_sig_cache_enabled: true,
14096 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14097 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14098 announce_queue_max_entries: 256,
14099 announce_queue_max_interfaces: 1024,
14100 },
14101 rx,
14102 tx.clone(),
14103 Box::new(cbs),
14104 );
14105
14106 let dest = [0xBB; 16];
14107 tx.send(Event::RegisterProofStrategy {
14108 dest_hash: dest,
14109 strategy: rns_core::types::ProofStrategy::ProveNone,
14110 signing_key: None,
14111 })
14112 .unwrap();
14113 tx.send(Event::Shutdown).unwrap();
14114 driver.run();
14115
14116 assert!(driver.proof_strategies.contains_key(&dest));
14117 let (strategy, ref id_opt) = driver.proof_strategies[&dest];
14118 assert_eq!(strategy, rns_core::types::ProofStrategy::ProveNone);
14119 assert!(id_opt.is_none());
14120 }
14121
14122 #[test]
14123 fn send_outbound_tracks_sent_packets() {
14124 let (tx, rx) = event::channel();
14125 let (cbs, _, _, _, _, _) = MockCallbacks::new();
14126 let mut driver = Driver::new(
14127 TransportConfig {
14128 transport_enabled: false,
14129 identity_hash: None,
14130 prefer_shorter_path: false,
14131 max_paths_per_destination: 1,
14132 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14133 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14134 max_path_destinations: usize::MAX,
14135 max_tunnel_destinations_total: usize::MAX,
14136 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14137 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14138 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14139 announce_sig_cache_enabled: true,
14140 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14141 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14142 announce_queue_max_entries: 256,
14143 announce_queue_max_interfaces: 1024,
14144 },
14145 rx,
14146 tx.clone(),
14147 Box::new(cbs),
14148 );
14149 let info = make_interface_info(1);
14150 driver.engine.register_interface(info);
14151 let (writer, _sent) = MockWriter::new();
14152 driver
14153 .interfaces
14154 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14155
14156 let dest = [0xCC; 16];
14158 let flags = PacketFlags {
14159 header_type: constants::HEADER_1,
14160 context_flag: constants::FLAG_UNSET,
14161 transport_type: constants::TRANSPORT_BROADCAST,
14162 destination_type: constants::DESTINATION_PLAIN,
14163 packet_type: constants::PACKET_TYPE_DATA,
14164 };
14165 let packet =
14166 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"test data").unwrap();
14167 let expected_hash = packet.packet_hash;
14168
14169 tx.send(Event::SendOutbound {
14170 raw: packet.raw,
14171 dest_type: constants::DESTINATION_PLAIN,
14172 attached_interface: None,
14173 })
14174 .unwrap();
14175 tx.send(Event::Shutdown).unwrap();
14176 driver.run();
14177
14178 assert!(driver.sent_packets.contains_key(&expected_hash));
14180 let (tracked_dest, _sent_time) = &driver.sent_packets[&expected_hash];
14181 assert_eq!(tracked_dest, &dest);
14182 }
14183
14184 #[test]
14185 fn prove_all_generates_proof_on_delivery() {
14186 let (tx, rx) = event::channel();
14187 let (cbs, _, _, deliveries, _, _) = MockCallbacks::new();
14188 let mut driver = Driver::new(
14189 TransportConfig {
14190 transport_enabled: false,
14191 identity_hash: None,
14192 prefer_shorter_path: false,
14193 max_paths_per_destination: 1,
14194 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14195 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14196 max_path_destinations: usize::MAX,
14197 max_tunnel_destinations_total: usize::MAX,
14198 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14199 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14200 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14201 announce_sig_cache_enabled: true,
14202 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14203 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14204 announce_queue_max_entries: 256,
14205 announce_queue_max_interfaces: 1024,
14206 },
14207 rx,
14208 tx.clone(),
14209 Box::new(cbs),
14210 );
14211 let info = make_interface_info(1);
14212 driver.engine.register_interface(info);
14213 let (writer, sent) = MockWriter::new();
14214 driver
14215 .interfaces
14216 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14217
14218 let dest = [0xDD; 16];
14220 let identity = Identity::new(&mut OsRng);
14221 let prv_key = identity.get_private_key().unwrap();
14222 driver
14223 .engine
14224 .register_destination(dest, constants::DESTINATION_SINGLE);
14225 driver.proof_strategies.insert(
14226 dest,
14227 (
14228 rns_core::types::ProofStrategy::ProveAll,
14229 Some(Identity::from_private_key(&prv_key)),
14230 ),
14231 );
14232
14233 let flags = PacketFlags {
14235 header_type: constants::HEADER_1,
14236 context_flag: constants::FLAG_UNSET,
14237 transport_type: constants::TRANSPORT_BROADCAST,
14238 destination_type: constants::DESTINATION_SINGLE,
14239 packet_type: constants::PACKET_TYPE_DATA,
14240 };
14241 let packet =
14242 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
14243
14244 tx.send(Event::Frame {
14245 interface_id: InterfaceId(1),
14246 data: packet.raw,
14247 })
14248 .unwrap();
14249 tx.send(Event::Shutdown).unwrap();
14250 driver.run();
14251
14252 assert_eq!(deliveries.lock().unwrap().len(), 1);
14254
14255 let sent_packets = sent.lock().unwrap();
14257 let has_proof = sent_packets.iter().any(|raw| {
14259 let flags = PacketFlags::unpack(raw[0] & 0x7F);
14260 flags.packet_type == constants::PACKET_TYPE_PROOF
14261 });
14262 assert!(
14263 has_proof,
14264 "ProveAll should generate a proof packet: sent {} packets",
14265 sent_packets.len()
14266 );
14267 }
14268
14269 #[test]
14270 fn prove_none_does_not_generate_proof() {
14271 let (tx, rx) = event::channel();
14272 let (cbs, _, _, deliveries, _, _) = MockCallbacks::new();
14273 let mut driver = Driver::new(
14274 TransportConfig {
14275 transport_enabled: false,
14276 identity_hash: None,
14277 prefer_shorter_path: false,
14278 max_paths_per_destination: 1,
14279 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14280 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14281 max_path_destinations: usize::MAX,
14282 max_tunnel_destinations_total: usize::MAX,
14283 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14284 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14285 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14286 announce_sig_cache_enabled: true,
14287 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14288 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14289 announce_queue_max_entries: 256,
14290 announce_queue_max_interfaces: 1024,
14291 },
14292 rx,
14293 tx.clone(),
14294 Box::new(cbs),
14295 );
14296 let info = make_interface_info(1);
14297 driver.engine.register_interface(info);
14298 let (writer, sent) = MockWriter::new();
14299 driver
14300 .interfaces
14301 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14302
14303 let dest = [0xDD; 16];
14305 driver
14306 .engine
14307 .register_destination(dest, constants::DESTINATION_SINGLE);
14308 driver
14309 .proof_strategies
14310 .insert(dest, (rns_core::types::ProofStrategy::ProveNone, None));
14311
14312 let flags = PacketFlags {
14314 header_type: constants::HEADER_1,
14315 context_flag: constants::FLAG_UNSET,
14316 transport_type: constants::TRANSPORT_BROADCAST,
14317 destination_type: constants::DESTINATION_SINGLE,
14318 packet_type: constants::PACKET_TYPE_DATA,
14319 };
14320 let packet =
14321 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
14322
14323 tx.send(Event::Frame {
14324 interface_id: InterfaceId(1),
14325 data: packet.raw,
14326 })
14327 .unwrap();
14328 tx.send(Event::Shutdown).unwrap();
14329 driver.run();
14330
14331 assert_eq!(deliveries.lock().unwrap().len(), 1);
14333
14334 let sent_packets = sent.lock().unwrap();
14336 let has_proof = sent_packets.iter().any(|raw| {
14337 let flags = PacketFlags::unpack(raw[0] & 0x7F);
14338 flags.packet_type == constants::PACKET_TYPE_PROOF
14339 });
14340 assert!(!has_proof, "ProveNone should not generate a proof packet");
14341 }
14342
14343 #[test]
14344 fn no_proof_strategy_does_not_generate_proof() {
14345 let (tx, rx) = event::channel();
14346 let (cbs, _, _, deliveries, _, _) = MockCallbacks::new();
14347 let mut driver = Driver::new(
14348 TransportConfig {
14349 transport_enabled: false,
14350 identity_hash: None,
14351 prefer_shorter_path: false,
14352 max_paths_per_destination: 1,
14353 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14354 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14355 max_path_destinations: usize::MAX,
14356 max_tunnel_destinations_total: usize::MAX,
14357 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14358 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14359 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14360 announce_sig_cache_enabled: true,
14361 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14362 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14363 announce_queue_max_entries: 256,
14364 announce_queue_max_interfaces: 1024,
14365 },
14366 rx,
14367 tx.clone(),
14368 Box::new(cbs),
14369 );
14370 let info = make_interface_info(1);
14371 driver.engine.register_interface(info);
14372 let (writer, sent) = MockWriter::new();
14373 driver
14374 .interfaces
14375 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14376
14377 let dest = [0xDD; 16];
14379 driver
14380 .engine
14381 .register_destination(dest, constants::DESTINATION_SINGLE);
14382
14383 let flags = PacketFlags {
14384 header_type: constants::HEADER_1,
14385 context_flag: constants::FLAG_UNSET,
14386 transport_type: constants::TRANSPORT_BROADCAST,
14387 destination_type: constants::DESTINATION_SINGLE,
14388 packet_type: constants::PACKET_TYPE_DATA,
14389 };
14390 let packet =
14391 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
14392
14393 tx.send(Event::Frame {
14394 interface_id: InterfaceId(1),
14395 data: packet.raw,
14396 })
14397 .unwrap();
14398 tx.send(Event::Shutdown).unwrap();
14399 driver.run();
14400
14401 assert_eq!(deliveries.lock().unwrap().len(), 1);
14402
14403 let sent_packets = sent.lock().unwrap();
14404 let has_proof = sent_packets.iter().any(|raw| {
14405 let flags = PacketFlags::unpack(raw[0] & 0x7F);
14406 flags.packet_type == constants::PACKET_TYPE_PROOF
14407 });
14408 assert!(!has_proof, "No proof strategy means no proof generated");
14409 }
14410
14411 #[test]
14412 fn prove_app_calls_callback() {
14413 let (tx, rx) = event::channel();
14414 let proof_requested = Arc::new(Mutex::new(Vec::new()));
14415 let deliveries = Arc::new(Mutex::new(Vec::new()));
14416 let cbs = MockCallbacks {
14417 announces: Arc::new(Mutex::new(Vec::new())),
14418 paths: Arc::new(Mutex::new(Vec::new())),
14419 deliveries: deliveries.clone(),
14420 iface_ups: Arc::new(Mutex::new(Vec::new())),
14421 iface_downs: Arc::new(Mutex::new(Vec::new())),
14422 link_established: Arc::new(Mutex::new(Vec::new())),
14423 link_closed: Arc::new(Mutex::new(Vec::new())),
14424 remote_identified: Arc::new(Mutex::new(Vec::new())),
14425 resources_received: Arc::new(Mutex::new(Vec::new())),
14426 resource_completed: Arc::new(Mutex::new(Vec::new())),
14427 resource_failed: Arc::new(Mutex::new(Vec::new())),
14428 channel_messages: Arc::new(Mutex::new(Vec::new())),
14429 link_data: Arc::new(Mutex::new(Vec::new())),
14430 responses: Arc::new(Mutex::new(Vec::new())),
14431 proofs: Arc::new(Mutex::new(Vec::new())),
14432 proof_requested: proof_requested.clone(),
14433 };
14434
14435 let mut driver = Driver::new(
14436 TransportConfig {
14437 transport_enabled: false,
14438 identity_hash: None,
14439 prefer_shorter_path: false,
14440 max_paths_per_destination: 1,
14441 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14442 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14443 max_path_destinations: usize::MAX,
14444 max_tunnel_destinations_total: usize::MAX,
14445 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14446 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14447 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14448 announce_sig_cache_enabled: true,
14449 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14450 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14451 announce_queue_max_entries: 256,
14452 announce_queue_max_interfaces: 1024,
14453 },
14454 rx,
14455 tx.clone(),
14456 Box::new(cbs),
14457 );
14458 let info = make_interface_info(1);
14459 driver.engine.register_interface(info);
14460 let (writer, sent) = MockWriter::new();
14461 driver
14462 .interfaces
14463 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14464
14465 let dest = [0xDD; 16];
14467 let identity = Identity::new(&mut OsRng);
14468 let prv_key = identity.get_private_key().unwrap();
14469 driver
14470 .engine
14471 .register_destination(dest, constants::DESTINATION_SINGLE);
14472 driver.proof_strategies.insert(
14473 dest,
14474 (
14475 rns_core::types::ProofStrategy::ProveApp,
14476 Some(Identity::from_private_key(&prv_key)),
14477 ),
14478 );
14479
14480 let flags = PacketFlags {
14481 header_type: constants::HEADER_1,
14482 context_flag: constants::FLAG_UNSET,
14483 transport_type: constants::TRANSPORT_BROADCAST,
14484 destination_type: constants::DESTINATION_SINGLE,
14485 packet_type: constants::PACKET_TYPE_DATA,
14486 };
14487 let packet =
14488 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"app test").unwrap();
14489
14490 tx.send(Event::Frame {
14491 interface_id: InterfaceId(1),
14492 data: packet.raw,
14493 })
14494 .unwrap();
14495 tx.send(Event::Shutdown).unwrap();
14496 driver.run();
14497
14498 let prs = proof_requested.lock().unwrap();
14500 assert_eq!(prs.len(), 1);
14501 assert_eq!(prs[0].0, DestHash(dest));
14502
14503 let sent_packets = sent.lock().unwrap();
14505 let has_proof = sent_packets.iter().any(|raw| {
14506 let flags = PacketFlags::unpack(raw[0] & 0x7F);
14507 flags.packet_type == constants::PACKET_TYPE_PROOF
14508 });
14509 assert!(
14510 has_proof,
14511 "ProveApp (callback returns true) should generate a proof"
14512 );
14513 }
14514
14515 #[test]
14516 fn inbound_proof_fires_callback() {
14517 let (tx, rx) = event::channel();
14518 let proofs = Arc::new(Mutex::new(Vec::new()));
14519 let cbs = MockCallbacks {
14520 announces: Arc::new(Mutex::new(Vec::new())),
14521 paths: Arc::new(Mutex::new(Vec::new())),
14522 deliveries: Arc::new(Mutex::new(Vec::new())),
14523 iface_ups: Arc::new(Mutex::new(Vec::new())),
14524 iface_downs: Arc::new(Mutex::new(Vec::new())),
14525 link_established: Arc::new(Mutex::new(Vec::new())),
14526 link_closed: Arc::new(Mutex::new(Vec::new())),
14527 remote_identified: Arc::new(Mutex::new(Vec::new())),
14528 resources_received: Arc::new(Mutex::new(Vec::new())),
14529 resource_completed: Arc::new(Mutex::new(Vec::new())),
14530 resource_failed: Arc::new(Mutex::new(Vec::new())),
14531 channel_messages: Arc::new(Mutex::new(Vec::new())),
14532 link_data: Arc::new(Mutex::new(Vec::new())),
14533 responses: Arc::new(Mutex::new(Vec::new())),
14534 proofs: proofs.clone(),
14535 proof_requested: Arc::new(Mutex::new(Vec::new())),
14536 };
14537
14538 let mut driver = Driver::new(
14539 TransportConfig {
14540 transport_enabled: false,
14541 identity_hash: None,
14542 prefer_shorter_path: false,
14543 max_paths_per_destination: 1,
14544 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14545 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14546 max_path_destinations: usize::MAX,
14547 max_tunnel_destinations_total: usize::MAX,
14548 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14549 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14550 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14551 announce_sig_cache_enabled: true,
14552 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14553 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14554 announce_queue_max_entries: 256,
14555 announce_queue_max_interfaces: 1024,
14556 },
14557 rx,
14558 tx.clone(),
14559 Box::new(cbs),
14560 );
14561 let info = make_interface_info(1);
14562 driver.engine.register_interface(info);
14563 let (writer, _sent) = MockWriter::new();
14564 driver
14565 .interfaces
14566 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14567
14568 let dest = [0xEE; 16];
14570 driver
14571 .engine
14572 .register_destination(dest, constants::DESTINATION_SINGLE);
14573
14574 let tracked_hash = [0x42u8; 32];
14576 let sent_time = time::now() - 0.5; driver.sent_packets.insert(tracked_hash, (dest, sent_time));
14578
14579 let mut proof_data = Vec::new();
14581 proof_data.extend_from_slice(&tracked_hash);
14582 proof_data.extend_from_slice(&[0xAA; 64]); let flags = PacketFlags {
14585 header_type: constants::HEADER_1,
14586 context_flag: constants::FLAG_UNSET,
14587 transport_type: constants::TRANSPORT_BROADCAST,
14588 destination_type: constants::DESTINATION_SINGLE,
14589 packet_type: constants::PACKET_TYPE_PROOF,
14590 };
14591 let packet =
14592 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, &proof_data).unwrap();
14593
14594 tx.send(Event::Frame {
14595 interface_id: InterfaceId(1),
14596 data: packet.raw,
14597 })
14598 .unwrap();
14599 tx.send(Event::Shutdown).unwrap();
14600 driver.run();
14601
14602 let proof_list = proofs.lock().unwrap();
14604 assert_eq!(proof_list.len(), 1);
14605 assert_eq!(proof_list[0].0, DestHash(dest));
14606 assert_eq!(proof_list[0].1, PacketHash(tracked_hash));
14607 assert!(
14608 proof_list[0].2 >= 0.4,
14609 "RTT should be approximately 0.5s, got {}",
14610 proof_list[0].2
14611 );
14612
14613 assert!(!driver.sent_packets.contains_key(&tracked_hash));
14615 }
14616
14617 #[test]
14618 fn inbound_proof_for_unknown_packet_is_ignored() {
14619 let (tx, rx) = event::channel();
14620 let proofs = Arc::new(Mutex::new(Vec::new()));
14621 let cbs = MockCallbacks {
14622 announces: Arc::new(Mutex::new(Vec::new())),
14623 paths: Arc::new(Mutex::new(Vec::new())),
14624 deliveries: Arc::new(Mutex::new(Vec::new())),
14625 iface_ups: Arc::new(Mutex::new(Vec::new())),
14626 iface_downs: Arc::new(Mutex::new(Vec::new())),
14627 link_established: Arc::new(Mutex::new(Vec::new())),
14628 link_closed: Arc::new(Mutex::new(Vec::new())),
14629 remote_identified: Arc::new(Mutex::new(Vec::new())),
14630 resources_received: Arc::new(Mutex::new(Vec::new())),
14631 resource_completed: Arc::new(Mutex::new(Vec::new())),
14632 resource_failed: Arc::new(Mutex::new(Vec::new())),
14633 channel_messages: Arc::new(Mutex::new(Vec::new())),
14634 link_data: Arc::new(Mutex::new(Vec::new())),
14635 responses: Arc::new(Mutex::new(Vec::new())),
14636 proofs: proofs.clone(),
14637 proof_requested: Arc::new(Mutex::new(Vec::new())),
14638 };
14639
14640 let mut driver = Driver::new(
14641 TransportConfig {
14642 transport_enabled: false,
14643 identity_hash: None,
14644 prefer_shorter_path: false,
14645 max_paths_per_destination: 1,
14646 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14647 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14648 max_path_destinations: usize::MAX,
14649 max_tunnel_destinations_total: usize::MAX,
14650 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14651 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14652 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14653 announce_sig_cache_enabled: true,
14654 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14655 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14656 announce_queue_max_entries: 256,
14657 announce_queue_max_interfaces: 1024,
14658 },
14659 rx,
14660 tx.clone(),
14661 Box::new(cbs),
14662 );
14663 let info = make_interface_info(1);
14664 driver.engine.register_interface(info);
14665 let (writer, _sent) = MockWriter::new();
14666 driver
14667 .interfaces
14668 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14669
14670 let dest = [0xEE; 16];
14671 driver
14672 .engine
14673 .register_destination(dest, constants::DESTINATION_SINGLE);
14674
14675 let unknown_hash = [0xFF; 32];
14677 let mut proof_data = Vec::new();
14678 proof_data.extend_from_slice(&unknown_hash);
14679 proof_data.extend_from_slice(&[0xAA; 64]);
14680
14681 let flags = PacketFlags {
14682 header_type: constants::HEADER_1,
14683 context_flag: constants::FLAG_UNSET,
14684 transport_type: constants::TRANSPORT_BROADCAST,
14685 destination_type: constants::DESTINATION_SINGLE,
14686 packet_type: constants::PACKET_TYPE_PROOF,
14687 };
14688 let packet =
14689 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, &proof_data).unwrap();
14690
14691 tx.send(Event::Frame {
14692 interface_id: InterfaceId(1),
14693 data: packet.raw,
14694 })
14695 .unwrap();
14696 tx.send(Event::Shutdown).unwrap();
14697 driver.run();
14698
14699 assert!(proofs.lock().unwrap().is_empty());
14701 }
14702
14703 #[test]
14704 fn inbound_implicit_proof_matches_truncated_destination() {
14705 let (tx, rx) = event::channel();
14706 let proofs = Arc::new(Mutex::new(Vec::new()));
14707 let cbs = MockCallbacks {
14708 announces: Arc::new(Mutex::new(Vec::new())),
14709 paths: Arc::new(Mutex::new(Vec::new())),
14710 deliveries: Arc::new(Mutex::new(Vec::new())),
14711 iface_ups: Arc::new(Mutex::new(Vec::new())),
14712 iface_downs: Arc::new(Mutex::new(Vec::new())),
14713 link_established: Arc::new(Mutex::new(Vec::new())),
14714 link_closed: Arc::new(Mutex::new(Vec::new())),
14715 remote_identified: Arc::new(Mutex::new(Vec::new())),
14716 resources_received: Arc::new(Mutex::new(Vec::new())),
14717 resource_completed: Arc::new(Mutex::new(Vec::new())),
14718 resource_failed: Arc::new(Mutex::new(Vec::new())),
14719 channel_messages: Arc::new(Mutex::new(Vec::new())),
14720 link_data: Arc::new(Mutex::new(Vec::new())),
14721 responses: Arc::new(Mutex::new(Vec::new())),
14722 proofs: proofs.clone(),
14723 proof_requested: Arc::new(Mutex::new(Vec::new())),
14724 };
14725
14726 let mut driver = Driver::new(
14727 TransportConfig {
14728 transport_enabled: false,
14729 identity_hash: None,
14730 prefer_shorter_path: false,
14731 max_paths_per_destination: 1,
14732 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14733 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14734 max_path_destinations: usize::MAX,
14735 max_tunnel_destinations_total: usize::MAX,
14736 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14737 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14738 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14739 announce_sig_cache_enabled: true,
14740 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14741 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14742 announce_queue_max_entries: 256,
14743 announce_queue_max_interfaces: 1024,
14744 },
14745 rx,
14746 tx.clone(),
14747 Box::new(cbs),
14748 );
14749 let info = make_interface_info(1);
14750 driver.engine.register_interface(info);
14751 let (writer, _sent) = MockWriter::new();
14752 driver
14753 .interfaces
14754 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14755
14756 let tracked_hash = [0x3Cu8; 32];
14757 let sent_time = time::now() - 0.25;
14758 driver
14759 .sent_packets
14760 .insert(tracked_hash, ([0xEE; 16], sent_time));
14761
14762 let mut proof_dest = [0u8; 16];
14763 proof_dest.copy_from_slice(&tracked_hash[..16]);
14764 driver
14765 .engine
14766 .register_destination(proof_dest, constants::DESTINATION_SINGLE);
14767
14768 let proof_data = vec![0xAA; 64];
14770 let flags = PacketFlags {
14771 header_type: constants::HEADER_1,
14772 context_flag: constants::FLAG_UNSET,
14773 transport_type: constants::TRANSPORT_BROADCAST,
14774 destination_type: constants::DESTINATION_SINGLE,
14775 packet_type: constants::PACKET_TYPE_PROOF,
14776 };
14777 let packet = RawPacket::pack(
14778 flags,
14779 0,
14780 &proof_dest,
14781 None,
14782 constants::CONTEXT_NONE,
14783 &proof_data,
14784 )
14785 .unwrap();
14786
14787 tx.send(Event::Frame {
14788 interface_id: InterfaceId(1),
14789 data: packet.raw,
14790 })
14791 .unwrap();
14792 tx.send(Event::Shutdown).unwrap();
14793 driver.run();
14794
14795 let proof_list = proofs.lock().unwrap();
14796 assert_eq!(proof_list.len(), 1);
14797 assert_eq!(proof_list[0].0, DestHash([0xEE; 16]));
14798 assert_eq!(proof_list[0].1, PacketHash(tracked_hash));
14799 assert!(!driver.sent_packets.contains_key(&tracked_hash));
14800 }
14801
14802 #[test]
14803 fn link_manager_data_send_is_tracked_for_proofs() {
14804 let mut driver = new_test_driver();
14805 let (writer, _sent) = MockWriter::new();
14806 driver
14807 .interfaces
14808 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14809
14810 let flags = PacketFlags {
14811 header_type: constants::HEADER_1,
14812 context_flag: constants::FLAG_UNSET,
14813 transport_type: constants::TRANSPORT_BROADCAST,
14814 destination_type: constants::DESTINATION_LINK,
14815 packet_type: constants::PACKET_TYPE_DATA,
14816 };
14817 let packet = RawPacket::pack(
14818 flags,
14819 0,
14820 &[0x77; 16],
14821 None,
14822 constants::CONTEXT_NONE,
14823 b"track me",
14824 )
14825 .unwrap();
14826 let packet_hash = packet.packet_hash;
14827 let destination_hash = packet.destination_hash;
14828
14829 driver.dispatch_link_actions(vec![LinkManagerAction::SendPacket {
14830 raw: packet.raw,
14831 dest_type: constants::DESTINATION_LINK,
14832 attached_interface: Some(InterfaceId(1)),
14833 }]);
14834
14835 assert_eq!(
14836 driver.sent_packets.get(&packet_hash).map(|(dest, _)| *dest),
14837 Some(destination_hash)
14838 );
14839 }
14840
14841 #[test]
14842 fn inbound_proof_with_valid_signature_fires_callback() {
14843 let (tx, rx) = event::channel();
14845 let proofs = Arc::new(Mutex::new(Vec::new()));
14846 let cbs = MockCallbacks {
14847 announces: Arc::new(Mutex::new(Vec::new())),
14848 paths: Arc::new(Mutex::new(Vec::new())),
14849 deliveries: Arc::new(Mutex::new(Vec::new())),
14850 iface_ups: Arc::new(Mutex::new(Vec::new())),
14851 iface_downs: Arc::new(Mutex::new(Vec::new())),
14852 link_established: Arc::new(Mutex::new(Vec::new())),
14853 link_closed: Arc::new(Mutex::new(Vec::new())),
14854 remote_identified: Arc::new(Mutex::new(Vec::new())),
14855 resources_received: Arc::new(Mutex::new(Vec::new())),
14856 resource_completed: Arc::new(Mutex::new(Vec::new())),
14857 resource_failed: Arc::new(Mutex::new(Vec::new())),
14858 channel_messages: Arc::new(Mutex::new(Vec::new())),
14859 link_data: Arc::new(Mutex::new(Vec::new())),
14860 responses: Arc::new(Mutex::new(Vec::new())),
14861 proofs: proofs.clone(),
14862 proof_requested: Arc::new(Mutex::new(Vec::new())),
14863 };
14864
14865 let mut driver = Driver::new(
14866 TransportConfig {
14867 transport_enabled: false,
14868 identity_hash: None,
14869 prefer_shorter_path: false,
14870 max_paths_per_destination: 1,
14871 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14872 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14873 max_path_destinations: usize::MAX,
14874 max_tunnel_destinations_total: usize::MAX,
14875 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14876 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14877 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14878 announce_sig_cache_enabled: true,
14879 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14880 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14881 announce_queue_max_entries: 256,
14882 announce_queue_max_interfaces: 1024,
14883 },
14884 rx,
14885 tx.clone(),
14886 Box::new(cbs),
14887 );
14888 let info = make_interface_info(1);
14889 driver.engine.register_interface(info);
14890 let (writer, _sent) = MockWriter::new();
14891 driver
14892 .interfaces
14893 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14894
14895 let dest = [0xEE; 16];
14896 driver
14897 .engine
14898 .register_destination(dest, constants::DESTINATION_SINGLE);
14899
14900 let identity = Identity::new(&mut OsRng);
14902 let pub_key = identity.get_public_key();
14903 driver.known_destinations.insert(
14904 dest,
14905 KnownDestinationState {
14906 announced: crate::destination::AnnouncedIdentity {
14907 dest_hash: DestHash(dest),
14908 identity_hash: IdentityHash(*identity.hash()),
14909 public_key: pub_key.unwrap(),
14910 app_data: None,
14911 hops: 0,
14912 received_at: time::now(),
14913 receiving_interface: InterfaceId(0),
14914 },
14915 was_used: false,
14916 last_used_at: None,
14917 retained: false,
14918 },
14919 );
14920
14921 let tracked_hash = [0x42u8; 32];
14923 let sent_time = time::now() - 0.5;
14924 driver.sent_packets.insert(tracked_hash, (dest, sent_time));
14925
14926 let signature = identity.sign(&tracked_hash).unwrap();
14927 let mut proof_data = Vec::new();
14928 proof_data.extend_from_slice(&tracked_hash);
14929 proof_data.extend_from_slice(&signature);
14930
14931 let flags = PacketFlags {
14932 header_type: constants::HEADER_1,
14933 context_flag: constants::FLAG_UNSET,
14934 transport_type: constants::TRANSPORT_BROADCAST,
14935 destination_type: constants::DESTINATION_SINGLE,
14936 packet_type: constants::PACKET_TYPE_PROOF,
14937 };
14938 let packet =
14939 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, &proof_data).unwrap();
14940
14941 tx.send(Event::Frame {
14942 interface_id: InterfaceId(1),
14943 data: packet.raw,
14944 })
14945 .unwrap();
14946 tx.send(Event::Shutdown).unwrap();
14947 driver.run();
14948
14949 let proof_list = proofs.lock().unwrap();
14951 assert_eq!(proof_list.len(), 1);
14952 assert_eq!(proof_list[0].0, DestHash(dest));
14953 assert_eq!(proof_list[0].1, PacketHash(tracked_hash));
14954 }
14955
14956 #[test]
14957 fn inbound_proof_with_invalid_signature_rejected() {
14958 let (tx, rx) = event::channel();
14960 let proofs = Arc::new(Mutex::new(Vec::new()));
14961 let cbs = MockCallbacks {
14962 announces: Arc::new(Mutex::new(Vec::new())),
14963 paths: Arc::new(Mutex::new(Vec::new())),
14964 deliveries: Arc::new(Mutex::new(Vec::new())),
14965 iface_ups: Arc::new(Mutex::new(Vec::new())),
14966 iface_downs: Arc::new(Mutex::new(Vec::new())),
14967 link_established: Arc::new(Mutex::new(Vec::new())),
14968 link_closed: Arc::new(Mutex::new(Vec::new())),
14969 remote_identified: Arc::new(Mutex::new(Vec::new())),
14970 resources_received: Arc::new(Mutex::new(Vec::new())),
14971 resource_completed: Arc::new(Mutex::new(Vec::new())),
14972 resource_failed: Arc::new(Mutex::new(Vec::new())),
14973 channel_messages: Arc::new(Mutex::new(Vec::new())),
14974 link_data: Arc::new(Mutex::new(Vec::new())),
14975 responses: Arc::new(Mutex::new(Vec::new())),
14976 proofs: proofs.clone(),
14977 proof_requested: Arc::new(Mutex::new(Vec::new())),
14978 };
14979
14980 let mut driver = Driver::new(
14981 TransportConfig {
14982 transport_enabled: false,
14983 identity_hash: None,
14984 prefer_shorter_path: false,
14985 max_paths_per_destination: 1,
14986 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14987 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14988 max_path_destinations: usize::MAX,
14989 max_tunnel_destinations_total: usize::MAX,
14990 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14991 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14992 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14993 announce_sig_cache_enabled: true,
14994 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14995 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14996 announce_queue_max_entries: 256,
14997 announce_queue_max_interfaces: 1024,
14998 },
14999 rx,
15000 tx.clone(),
15001 Box::new(cbs),
15002 );
15003 let info = make_interface_info(1);
15004 driver.engine.register_interface(info);
15005 let (writer, _sent) = MockWriter::new();
15006 driver
15007 .interfaces
15008 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
15009
15010 let dest = [0xEE; 16];
15011 driver
15012 .engine
15013 .register_destination(dest, constants::DESTINATION_SINGLE);
15014
15015 let identity = Identity::new(&mut OsRng);
15017 let pub_key = identity.get_public_key();
15018 driver.known_destinations.insert(
15019 dest,
15020 KnownDestinationState {
15021 announced: crate::destination::AnnouncedIdentity {
15022 dest_hash: DestHash(dest),
15023 identity_hash: IdentityHash(*identity.hash()),
15024 public_key: pub_key.unwrap(),
15025 app_data: None,
15026 hops: 0,
15027 received_at: time::now(),
15028 receiving_interface: InterfaceId(0),
15029 },
15030 was_used: false,
15031 last_used_at: None,
15032 retained: false,
15033 },
15034 );
15035
15036 let tracked_hash = [0x42u8; 32];
15038 let sent_time = time::now() - 0.5;
15039 driver.sent_packets.insert(tracked_hash, (dest, sent_time));
15040
15041 let mut proof_data = Vec::new();
15043 proof_data.extend_from_slice(&tracked_hash);
15044 proof_data.extend_from_slice(&[0xAA; 64]);
15045
15046 let flags = PacketFlags {
15047 header_type: constants::HEADER_1,
15048 context_flag: constants::FLAG_UNSET,
15049 transport_type: constants::TRANSPORT_BROADCAST,
15050 destination_type: constants::DESTINATION_SINGLE,
15051 packet_type: constants::PACKET_TYPE_PROOF,
15052 };
15053 let packet =
15054 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, &proof_data).unwrap();
15055
15056 tx.send(Event::Frame {
15057 interface_id: InterfaceId(1),
15058 data: packet.raw,
15059 })
15060 .unwrap();
15061 tx.send(Event::Shutdown).unwrap();
15062 driver.run();
15063
15064 assert!(proofs.lock().unwrap().is_empty());
15066 }
15067
15068 #[test]
15069 fn proof_data_is_valid_explicit_proof() {
15070 let (tx, rx) = event::channel();
15072 let (cbs, _, _, _, _, _) = MockCallbacks::new();
15073 let mut driver = Driver::new(
15074 TransportConfig {
15075 transport_enabled: false,
15076 identity_hash: None,
15077 prefer_shorter_path: false,
15078 max_paths_per_destination: 1,
15079 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15080 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15081 max_path_destinations: usize::MAX,
15082 max_tunnel_destinations_total: usize::MAX,
15083 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15084 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15085 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15086 announce_sig_cache_enabled: true,
15087 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15088 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15089 announce_queue_max_entries: 256,
15090 announce_queue_max_interfaces: 1024,
15091 },
15092 rx,
15093 tx.clone(),
15094 Box::new(cbs),
15095 );
15096 let info = make_interface_info(1);
15097 driver.engine.register_interface(info);
15098 let (writer, sent) = MockWriter::new();
15099 driver
15100 .interfaces
15101 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
15102
15103 let dest = [0xDD; 16];
15104 let identity = Identity::new(&mut OsRng);
15105 let prv_key = identity.get_private_key().unwrap();
15106 driver
15107 .engine
15108 .register_destination(dest, constants::DESTINATION_SINGLE);
15109 driver.proof_strategies.insert(
15110 dest,
15111 (
15112 rns_core::types::ProofStrategy::ProveAll,
15113 Some(Identity::from_private_key(&prv_key)),
15114 ),
15115 );
15116
15117 let flags = PacketFlags {
15118 header_type: constants::HEADER_1,
15119 context_flag: constants::FLAG_UNSET,
15120 transport_type: constants::TRANSPORT_BROADCAST,
15121 destination_type: constants::DESTINATION_SINGLE,
15122 packet_type: constants::PACKET_TYPE_DATA,
15123 };
15124 let data_packet =
15125 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"verify me").unwrap();
15126 let data_packet_hash = data_packet.packet_hash;
15127
15128 tx.send(Event::Frame {
15129 interface_id: InterfaceId(1),
15130 data: data_packet.raw,
15131 })
15132 .unwrap();
15133 tx.send(Event::Shutdown).unwrap();
15134 driver.run();
15135
15136 let sent_packets = sent.lock().unwrap();
15138 let proof_raw = sent_packets.iter().find(|raw| {
15139 let f = PacketFlags::unpack(raw[0] & 0x7F);
15140 f.packet_type == constants::PACKET_TYPE_PROOF
15141 });
15142 assert!(proof_raw.is_some(), "Should have sent a proof");
15143
15144 let proof_packet = RawPacket::unpack(proof_raw.unwrap()).unwrap();
15145 assert_eq!(
15147 proof_packet.data.len(),
15148 96,
15149 "Explicit proof should be 96 bytes"
15150 );
15151
15152 let result = rns_core::receipt::validate_proof(
15154 &proof_packet.data,
15155 &data_packet_hash,
15156 &Identity::from_private_key(&prv_key), );
15158 assert_eq!(result, rns_core::receipt::ProofResult::Valid);
15159 }
15160
15161 #[test]
15162 fn query_local_destinations_empty() {
15163 let (tx, rx) = event::channel();
15164 let (cbs, _, _, _, _, _) = MockCallbacks::new();
15165 let driver_config = TransportConfig {
15166 transport_enabled: false,
15167 identity_hash: None,
15168 prefer_shorter_path: false,
15169 max_paths_per_destination: 1,
15170 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15171 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15172 max_path_destinations: usize::MAX,
15173 max_tunnel_destinations_total: usize::MAX,
15174 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15175 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15176 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15177 announce_sig_cache_enabled: true,
15178 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15179 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15180 announce_queue_max_entries: 256,
15181 announce_queue_max_interfaces: 1024,
15182 };
15183 let mut driver = Driver::new(driver_config, rx, tx.clone(), Box::new(cbs));
15184
15185 let (resp_tx, resp_rx) = mpsc::channel();
15186 tx.send(Event::Query(QueryRequest::LocalDestinations, resp_tx))
15187 .unwrap();
15188 tx.send(Event::Shutdown).unwrap();
15189 driver.run();
15190
15191 match resp_rx.recv().unwrap() {
15192 QueryResponse::LocalDestinations(entries) => {
15193 assert_eq!(entries.len(), 2);
15195 for entry in &entries {
15196 assert_eq!(entry.dest_type, rns_core::constants::DESTINATION_PLAIN);
15197 }
15198 }
15199 other => panic!("expected LocalDestinations, got {:?}", other),
15200 }
15201 }
15202
15203 #[test]
15204 fn query_local_destinations_with_registered() {
15205 let (tx, rx) = event::channel();
15206 let (cbs, _, _, _, _, _) = MockCallbacks::new();
15207 let driver_config = TransportConfig {
15208 transport_enabled: false,
15209 identity_hash: None,
15210 prefer_shorter_path: false,
15211 max_paths_per_destination: 1,
15212 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15213 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15214 max_path_destinations: usize::MAX,
15215 max_tunnel_destinations_total: usize::MAX,
15216 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15217 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15218 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15219 announce_sig_cache_enabled: true,
15220 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15221 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15222 announce_queue_max_entries: 256,
15223 announce_queue_max_interfaces: 1024,
15224 };
15225 let mut driver = Driver::new(driver_config, rx, tx.clone(), Box::new(cbs));
15226
15227 let dest_hash = [0xAA; 16];
15228 tx.send(Event::RegisterDestination {
15229 dest_hash,
15230 dest_type: rns_core::constants::DESTINATION_SINGLE,
15231 })
15232 .unwrap();
15233
15234 let (resp_tx, resp_rx) = mpsc::channel();
15235 tx.send(Event::Query(QueryRequest::LocalDestinations, resp_tx))
15236 .unwrap();
15237 tx.send(Event::Shutdown).unwrap();
15238 driver.run();
15239
15240 match resp_rx.recv().unwrap() {
15241 QueryResponse::LocalDestinations(entries) => {
15242 assert_eq!(entries.len(), 3);
15244 assert!(entries.iter().any(|e| e.hash == dest_hash
15245 && e.dest_type == rns_core::constants::DESTINATION_SINGLE));
15246 }
15247 other => panic!("expected LocalDestinations, got {:?}", other),
15248 }
15249 }
15250
15251 #[test]
15252 fn query_local_destinations_tracks_link_dest() {
15253 let (tx, rx) = event::channel();
15254 let (cbs, _, _, _, _, _) = MockCallbacks::new();
15255 let driver_config = TransportConfig {
15256 transport_enabled: false,
15257 identity_hash: None,
15258 prefer_shorter_path: false,
15259 max_paths_per_destination: 1,
15260 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15261 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15262 max_path_destinations: usize::MAX,
15263 max_tunnel_destinations_total: usize::MAX,
15264 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15265 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15266 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15267 announce_sig_cache_enabled: true,
15268 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15269 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15270 announce_queue_max_entries: 256,
15271 announce_queue_max_interfaces: 1024,
15272 };
15273 let mut driver = Driver::new(driver_config, rx, tx.clone(), Box::new(cbs));
15274
15275 let dest_hash = [0xBB; 16];
15276 tx.send(Event::RegisterLinkDestination {
15277 dest_hash,
15278 sig_prv_bytes: [0x11; 32],
15279 sig_pub_bytes: [0x22; 32],
15280 resource_strategy: 0,
15281 })
15282 .unwrap();
15283
15284 let (resp_tx, resp_rx) = mpsc::channel();
15285 tx.send(Event::Query(QueryRequest::LocalDestinations, resp_tx))
15286 .unwrap();
15287 tx.send(Event::Shutdown).unwrap();
15288 driver.run();
15289
15290 match resp_rx.recv().unwrap() {
15291 QueryResponse::LocalDestinations(entries) => {
15292 assert_eq!(entries.len(), 3);
15294 assert!(entries.iter().any(|e| e.hash == dest_hash
15295 && e.dest_type == rns_core::constants::DESTINATION_SINGLE));
15296 }
15297 other => panic!("expected LocalDestinations, got {:?}", other),
15298 }
15299 }
15300
15301 #[test]
15302 fn query_links_empty() {
15303 let (tx, rx) = event::channel();
15304 let (cbs, _, _, _, _, _) = MockCallbacks::new();
15305 let driver_config = TransportConfig {
15306 transport_enabled: false,
15307 identity_hash: None,
15308 prefer_shorter_path: false,
15309 max_paths_per_destination: 1,
15310 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15311 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15312 max_path_destinations: usize::MAX,
15313 max_tunnel_destinations_total: usize::MAX,
15314 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15315 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15316 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15317 announce_sig_cache_enabled: true,
15318 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15319 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15320 announce_queue_max_entries: 256,
15321 announce_queue_max_interfaces: 1024,
15322 };
15323 let mut driver = Driver::new(driver_config, rx, tx.clone(), Box::new(cbs));
15324
15325 let (resp_tx, resp_rx) = mpsc::channel();
15326 tx.send(Event::Query(QueryRequest::Links, resp_tx)).unwrap();
15327 tx.send(Event::Shutdown).unwrap();
15328 driver.run();
15329
15330 match resp_rx.recv().unwrap() {
15331 QueryResponse::Links(entries) => {
15332 assert!(entries.is_empty());
15333 }
15334 other => panic!("expected Links, got {:?}", other),
15335 }
15336 }
15337
15338 #[test]
15339 fn query_resources_empty() {
15340 let (tx, rx) = event::channel();
15341 let (cbs, _, _, _, _, _) = MockCallbacks::new();
15342 let driver_config = TransportConfig {
15343 transport_enabled: false,
15344 identity_hash: None,
15345 prefer_shorter_path: false,
15346 max_paths_per_destination: 1,
15347 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15348 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15349 max_path_destinations: usize::MAX,
15350 max_tunnel_destinations_total: usize::MAX,
15351 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15352 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15353 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15354 announce_sig_cache_enabled: true,
15355 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15356 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15357 announce_queue_max_entries: 256,
15358 announce_queue_max_interfaces: 1024,
15359 };
15360 let mut driver = Driver::new(driver_config, rx, tx.clone(), Box::new(cbs));
15361
15362 let (resp_tx, resp_rx) = mpsc::channel();
15363 tx.send(Event::Query(QueryRequest::Resources, resp_tx))
15364 .unwrap();
15365 tx.send(Event::Shutdown).unwrap();
15366 driver.run();
15367
15368 match resp_rx.recv().unwrap() {
15369 QueryResponse::Resources(entries) => {
15370 assert!(entries.is_empty());
15371 }
15372 other => panic!("expected Resources, got {:?}", other),
15373 }
15374 }
15375
15376 #[test]
15377 fn infer_interface_type_from_name() {
15378 assert_eq!(
15379 super::infer_interface_type("TCPServerInterface/Client-1234"),
15380 "TCPServerClientInterface"
15381 );
15382 assert_eq!(
15383 super::infer_interface_type("BackboneInterface/5"),
15384 "BackboneInterface"
15385 );
15386 assert_eq!(
15387 super::infer_interface_type("LocalInterface"),
15388 "LocalServerClientInterface"
15389 );
15390 assert_eq!(
15391 super::infer_interface_type("MyAutoGroup:fe80::1"),
15392 "AutoInterface"
15393 );
15394 }
15395
15396 #[test]
15399 fn test_extract_dest_hash_empty() {
15400 assert_eq!(super::extract_dest_hash(&[]), [0u8; 16]);
15401 }
15402
15403 #[test]
15408 fn send_probe_unknown_dest_returns_none() {
15409 let (tx, rx) = event::channel();
15410 let (cbs, _, _, _, _, _) = MockCallbacks::new();
15411 let mut driver = Driver::new(
15412 TransportConfig {
15413 transport_enabled: false,
15414 identity_hash: None,
15415 prefer_shorter_path: false,
15416 max_paths_per_destination: 1,
15417 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15418 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15419 max_path_destinations: usize::MAX,
15420 max_tunnel_destinations_total: usize::MAX,
15421 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15422 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15423 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15424 announce_sig_cache_enabled: true,
15425 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15426 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15427 announce_queue_max_entries: 256,
15428 announce_queue_max_interfaces: 1024,
15429 },
15430 rx,
15431 tx.clone(),
15432 Box::new(cbs),
15433 );
15434 let info = make_interface_info(1);
15435 driver.engine.register_interface(info);
15436 let (writer, _sent) = MockWriter::new();
15437 driver
15438 .interfaces
15439 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
15440
15441 let (resp_tx, resp_rx) = mpsc::channel();
15443 tx.send(Event::Query(
15444 QueryRequest::SendProbe {
15445 dest_hash: [0xAA; 16],
15446 payload_size: 16,
15447 },
15448 resp_tx,
15449 ))
15450 .unwrap();
15451 tx.send(Event::Shutdown).unwrap();
15452 driver.run();
15453
15454 match resp_rx.recv().unwrap() {
15455 QueryResponse::SendProbe(None) => {}
15456 other => panic!("expected SendProbe(None), got {:?}", other),
15457 }
15458 }
15459
15460 #[test]
15461 fn send_probe_known_dest_returns_packet_hash() {
15462 let (tx, rx) = event::channel();
15463 let (cbs, _, _, _, _, _) = MockCallbacks::new();
15464 let mut driver = Driver::new(
15465 TransportConfig {
15466 transport_enabled: false,
15467 identity_hash: None,
15468 prefer_shorter_path: false,
15469 max_paths_per_destination: 1,
15470 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15471 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15472 max_path_destinations: usize::MAX,
15473 max_tunnel_destinations_total: usize::MAX,
15474 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15475 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15476 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15477 announce_sig_cache_enabled: true,
15478 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15479 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15480 announce_queue_max_entries: 256,
15481 announce_queue_max_interfaces: 1024,
15482 },
15483 rx,
15484 tx.clone(),
15485 Box::new(cbs),
15486 );
15487 let info = make_interface_info(1);
15488 driver.engine.register_interface(info);
15489 let (writer, sent) = MockWriter::new();
15490 driver
15491 .interfaces
15492 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
15493
15494 let remote_identity = Identity::new(&mut OsRng);
15496 let dest_hash = rns_core::destination::destination_hash(
15497 "rnstransport",
15498 &["probe"],
15499 Some(remote_identity.hash()),
15500 );
15501
15502 let (inject_tx, inject_rx) = mpsc::channel();
15504 tx.send(Event::Query(
15505 QueryRequest::InjectIdentity {
15506 dest_hash,
15507 identity_hash: *remote_identity.hash(),
15508 public_key: remote_identity.get_public_key().unwrap(),
15509 app_data: None,
15510 hops: 1,
15511 received_at: 0.0,
15512 },
15513 inject_tx,
15514 ))
15515 .unwrap();
15516
15517 let (resp_tx, resp_rx) = mpsc::channel();
15519 tx.send(Event::Query(
15520 QueryRequest::SendProbe {
15521 dest_hash,
15522 payload_size: 16,
15523 },
15524 resp_tx,
15525 ))
15526 .unwrap();
15527 tx.send(Event::Shutdown).unwrap();
15528 driver.run();
15529
15530 match inject_rx.recv().unwrap() {
15532 QueryResponse::InjectIdentity(true) => {}
15533 other => panic!("expected InjectIdentity(true), got {:?}", other),
15534 }
15535
15536 match resp_rx.recv().unwrap() {
15538 QueryResponse::SendProbe(Some((packet_hash, _hops))) => {
15539 assert_ne!(packet_hash, [0u8; 32]);
15541 assert!(driver.sent_packets.contains_key(&packet_hash));
15543 let sent_data = sent.lock().unwrap();
15545 assert!(!sent_data.is_empty(), "Probe packet should be sent on wire");
15546 let raw = &sent_data[0];
15548 let flags = PacketFlags::unpack(raw[0] & 0x7F);
15549 assert_eq!(flags.packet_type, constants::PACKET_TYPE_DATA);
15550 assert_eq!(flags.destination_type, constants::DESTINATION_SINGLE);
15551 }
15552 other => panic!("expected SendProbe(Some(..)), got {:?}", other),
15553 }
15554 }
15555
15556 #[test]
15557 fn check_proof_not_found_returns_none() {
15558 let (tx, rx) = event::channel();
15559 let (cbs, _, _, _, _, _) = MockCallbacks::new();
15560 let mut driver = Driver::new(
15561 TransportConfig {
15562 transport_enabled: false,
15563 identity_hash: None,
15564 prefer_shorter_path: false,
15565 max_paths_per_destination: 1,
15566 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15567 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15568 max_path_destinations: usize::MAX,
15569 max_tunnel_destinations_total: usize::MAX,
15570 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15571 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15572 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15573 announce_sig_cache_enabled: true,
15574 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15575 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15576 announce_queue_max_entries: 256,
15577 announce_queue_max_interfaces: 1024,
15578 },
15579 rx,
15580 tx.clone(),
15581 Box::new(cbs),
15582 );
15583
15584 let (resp_tx, resp_rx) = mpsc::channel();
15585 tx.send(Event::Query(
15586 QueryRequest::CheckProof {
15587 packet_hash: [0xBB; 32],
15588 },
15589 resp_tx,
15590 ))
15591 .unwrap();
15592 tx.send(Event::Shutdown).unwrap();
15593 driver.run();
15594
15595 match resp_rx.recv().unwrap() {
15596 QueryResponse::CheckProof(None) => {}
15597 other => panic!("expected CheckProof(None), got {:?}", other),
15598 }
15599 }
15600
15601 #[test]
15602 fn check_proof_found_returns_rtt() {
15603 let (tx, rx) = event::channel();
15604 let (cbs, _, _, _, _, _) = MockCallbacks::new();
15605 let mut driver = Driver::new(
15606 TransportConfig {
15607 transport_enabled: false,
15608 identity_hash: None,
15609 prefer_shorter_path: false,
15610 max_paths_per_destination: 1,
15611 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15612 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15613 max_path_destinations: usize::MAX,
15614 max_tunnel_destinations_total: usize::MAX,
15615 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15616 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15617 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15618 announce_sig_cache_enabled: true,
15619 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15620 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15621 announce_queue_max_entries: 256,
15622 announce_queue_max_interfaces: 1024,
15623 },
15624 rx,
15625 tx.clone(),
15626 Box::new(cbs),
15627 );
15628
15629 let packet_hash = [0xCC; 32];
15631 driver
15632 .completed_proofs
15633 .insert(packet_hash, (0.123, time::now()));
15634
15635 let (resp_tx, resp_rx) = mpsc::channel();
15636 tx.send(Event::Query(
15637 QueryRequest::CheckProof { packet_hash },
15638 resp_tx,
15639 ))
15640 .unwrap();
15641 tx.send(Event::Shutdown).unwrap();
15642 driver.run();
15643
15644 match resp_rx.recv().unwrap() {
15645 QueryResponse::CheckProof(Some(rtt)) => {
15646 assert!(
15647 (rtt - 0.123).abs() < 0.001,
15648 "RTT should be ~0.123, got {}",
15649 rtt
15650 );
15651 }
15652 other => panic!("expected CheckProof(Some(..)), got {:?}", other),
15653 }
15654 assert!(!driver.completed_proofs.contains_key(&packet_hash));
15656 }
15657
15658 #[test]
15659 fn inbound_proof_populates_completed_proofs() {
15660 let (tx, rx) = event::channel();
15661 let proofs = Arc::new(Mutex::new(Vec::new()));
15662 let cbs = MockCallbacks {
15663 announces: Arc::new(Mutex::new(Vec::new())),
15664 paths: Arc::new(Mutex::new(Vec::new())),
15665 deliveries: Arc::new(Mutex::new(Vec::new())),
15666 iface_ups: Arc::new(Mutex::new(Vec::new())),
15667 iface_downs: Arc::new(Mutex::new(Vec::new())),
15668 link_established: Arc::new(Mutex::new(Vec::new())),
15669 link_closed: Arc::new(Mutex::new(Vec::new())),
15670 remote_identified: Arc::new(Mutex::new(Vec::new())),
15671 resources_received: Arc::new(Mutex::new(Vec::new())),
15672 resource_completed: Arc::new(Mutex::new(Vec::new())),
15673 resource_failed: Arc::new(Mutex::new(Vec::new())),
15674 channel_messages: Arc::new(Mutex::new(Vec::new())),
15675 link_data: Arc::new(Mutex::new(Vec::new())),
15676 responses: Arc::new(Mutex::new(Vec::new())),
15677 proofs: proofs.clone(),
15678 proof_requested: Arc::new(Mutex::new(Vec::new())),
15679 };
15680
15681 let mut driver = Driver::new(
15682 TransportConfig {
15683 transport_enabled: false,
15684 identity_hash: None,
15685 prefer_shorter_path: false,
15686 max_paths_per_destination: 1,
15687 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15688 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15689 max_path_destinations: usize::MAX,
15690 max_tunnel_destinations_total: usize::MAX,
15691 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15692 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15693 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15694 announce_sig_cache_enabled: true,
15695 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15696 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15697 announce_queue_max_entries: 256,
15698 announce_queue_max_interfaces: 1024,
15699 },
15700 rx,
15701 tx.clone(),
15702 Box::new(cbs),
15703 );
15704 let info = make_interface_info(1);
15705 driver.engine.register_interface(info);
15706 let (writer, sent) = MockWriter::new();
15707 driver
15708 .interfaces
15709 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
15710
15711 let dest = [0xDD; 16];
15713 let identity = Identity::new(&mut OsRng);
15714 let prv_key = identity.get_private_key().unwrap();
15715 driver
15716 .engine
15717 .register_destination(dest, constants::DESTINATION_SINGLE);
15718 driver.proof_strategies.insert(
15719 dest,
15720 (
15721 rns_core::types::ProofStrategy::ProveAll,
15722 Some(Identity::from_private_key(&prv_key)),
15723 ),
15724 );
15725
15726 let flags = PacketFlags {
15728 header_type: constants::HEADER_1,
15729 context_flag: constants::FLAG_UNSET,
15730 transport_type: constants::TRANSPORT_BROADCAST,
15731 destination_type: constants::DESTINATION_SINGLE,
15732 packet_type: constants::PACKET_TYPE_DATA,
15733 };
15734 let data_packet = RawPacket::pack(
15735 flags,
15736 0,
15737 &dest,
15738 None,
15739 constants::CONTEXT_NONE,
15740 b"probe data",
15741 )
15742 .unwrap();
15743 let data_packet_hash = data_packet.packet_hash;
15744
15745 driver
15747 .sent_packets
15748 .insert(data_packet_hash, (dest, time::now()));
15749
15750 tx.send(Event::Frame {
15752 interface_id: InterfaceId(1),
15753 data: data_packet.raw,
15754 })
15755 .unwrap();
15756 tx.send(Event::Shutdown).unwrap();
15757 driver.run();
15758
15759 let sent_packets = sent.lock().unwrap();
15761 let proof_packets: Vec<_> = sent_packets
15762 .iter()
15763 .filter(|raw| {
15764 let flags = PacketFlags::unpack(raw[0] & 0x7F);
15765 flags.packet_type == constants::PACKET_TYPE_PROOF
15766 })
15767 .collect();
15768 assert!(!proof_packets.is_empty(), "Should have sent a proof packet");
15769
15770 let proof_raw = proof_packets[0].clone();
15781 drop(sent_packets); let (tx2, rx2) = event::channel();
15785 let proofs2 = Arc::new(Mutex::new(Vec::new()));
15786 let cbs2 = MockCallbacks {
15787 announces: Arc::new(Mutex::new(Vec::new())),
15788 paths: Arc::new(Mutex::new(Vec::new())),
15789 deliveries: Arc::new(Mutex::new(Vec::new())),
15790 iface_ups: Arc::new(Mutex::new(Vec::new())),
15791 iface_downs: Arc::new(Mutex::new(Vec::new())),
15792 link_established: Arc::new(Mutex::new(Vec::new())),
15793 link_closed: Arc::new(Mutex::new(Vec::new())),
15794 remote_identified: Arc::new(Mutex::new(Vec::new())),
15795 resources_received: Arc::new(Mutex::new(Vec::new())),
15796 resource_completed: Arc::new(Mutex::new(Vec::new())),
15797 resource_failed: Arc::new(Mutex::new(Vec::new())),
15798 channel_messages: Arc::new(Mutex::new(Vec::new())),
15799 link_data: Arc::new(Mutex::new(Vec::new())),
15800 responses: Arc::new(Mutex::new(Vec::new())),
15801 proofs: proofs2.clone(),
15802 proof_requested: Arc::new(Mutex::new(Vec::new())),
15803 };
15804 let mut driver2 = Driver::new(
15805 TransportConfig {
15806 transport_enabled: false,
15807 identity_hash: None,
15808 prefer_shorter_path: false,
15809 max_paths_per_destination: 1,
15810 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15811 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15812 max_path_destinations: usize::MAX,
15813 max_tunnel_destinations_total: usize::MAX,
15814 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15815 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15816 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15817 announce_sig_cache_enabled: true,
15818 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15819 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15820 announce_queue_max_entries: 256,
15821 announce_queue_max_interfaces: 1024,
15822 },
15823 rx2,
15824 tx2.clone(),
15825 Box::new(cbs2),
15826 );
15827 let info2 = make_interface_info(1);
15828 driver2.engine.register_interface(info2);
15829 let (writer2, _sent2) = MockWriter::new();
15830 driver2
15831 .interfaces
15832 .insert(InterfaceId(1), make_entry(1, Box::new(writer2), true));
15833
15834 driver2
15836 .sent_packets
15837 .insert(data_packet_hash, (dest, time::now()));
15838
15839 tx2.send(Event::Frame {
15841 interface_id: InterfaceId(1),
15842 data: proof_raw,
15843 })
15844 .unwrap();
15845 tx2.send(Event::Shutdown).unwrap();
15846 driver2.run();
15847
15848 let proof_events = proofs2.lock().unwrap();
15850 assert_eq!(proof_events.len(), 1, "on_proof callback should fire once");
15851 assert_eq!(
15852 proof_events[0].1 .0, data_packet_hash,
15853 "proof should match original packet hash"
15854 );
15855 assert!(proof_events[0].2 >= 0.0, "RTT should be non-negative");
15856
15857 assert!(
15859 driver2.completed_proofs.contains_key(&data_packet_hash),
15860 "completed_proofs should contain the packet hash"
15861 );
15862 let (rtt, _received) = driver2.completed_proofs[&data_packet_hash];
15863 assert!(rtt >= 0.0, "RTT should be non-negative");
15864 }
15865
15866 #[test]
15867 fn interface_stats_includes_probe_responder() {
15868 let (tx, rx) = event::channel();
15869 let (cbs, _, _, _, _, _) = MockCallbacks::new();
15870 let mut driver = Driver::new(
15871 TransportConfig {
15872 transport_enabled: true,
15873 identity_hash: Some([0x42; 16]),
15874 prefer_shorter_path: false,
15875 max_paths_per_destination: 1,
15876 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15877 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15878 max_path_destinations: usize::MAX,
15879 max_tunnel_destinations_total: usize::MAX,
15880 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15881 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15882 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15883 announce_sig_cache_enabled: true,
15884 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15885 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15886 announce_queue_max_entries: 256,
15887 announce_queue_max_interfaces: 1024,
15888 },
15889 rx,
15890 tx.clone(),
15891 Box::new(cbs),
15892 );
15893 let (writer, _sent) = MockWriter::new();
15894 driver
15895 .interfaces
15896 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
15897
15898 driver.probe_responder_hash = Some([0xEE; 16]);
15900
15901 let (resp_tx, resp_rx) = mpsc::channel();
15902 tx.send(Event::Query(QueryRequest::InterfaceStats, resp_tx))
15903 .unwrap();
15904 tx.send(Event::Shutdown).unwrap();
15905 driver.run();
15906
15907 match resp_rx.recv().unwrap() {
15908 QueryResponse::InterfaceStats(stats) => {
15909 assert_eq!(stats.probe_responder, Some([0xEE; 16]));
15910 }
15911 other => panic!("expected InterfaceStats, got {:?}", other),
15912 }
15913 }
15914
15915 #[test]
15916 fn interface_stats_probe_responder_none_when_disabled() {
15917 let (tx, rx) = event::channel();
15918 let (cbs, _, _, _, _, _) = MockCallbacks::new();
15919 let mut driver = Driver::new(
15920 TransportConfig {
15921 transport_enabled: false,
15922 identity_hash: None,
15923 prefer_shorter_path: false,
15924 max_paths_per_destination: 1,
15925 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15926 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15927 max_path_destinations: usize::MAX,
15928 max_tunnel_destinations_total: usize::MAX,
15929 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15930 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15931 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15932 announce_sig_cache_enabled: true,
15933 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15934 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15935 announce_queue_max_entries: 256,
15936 announce_queue_max_interfaces: 1024,
15937 },
15938 rx,
15939 tx.clone(),
15940 Box::new(cbs),
15941 );
15942 let (writer, _sent) = MockWriter::new();
15943 driver
15944 .interfaces
15945 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
15946
15947 let (resp_tx, resp_rx) = mpsc::channel();
15948 tx.send(Event::Query(QueryRequest::InterfaceStats, resp_tx))
15949 .unwrap();
15950 tx.send(Event::Shutdown).unwrap();
15951 driver.run();
15952
15953 match resp_rx.recv().unwrap() {
15954 QueryResponse::InterfaceStats(stats) => {
15955 assert_eq!(stats.probe_responder, None);
15956 }
15957 other => panic!("expected InterfaceStats, got {:?}", other),
15958 }
15959 }
15960
15961 #[test]
15962 fn test_extract_dest_hash_too_short() {
15963 assert_eq!(super::extract_dest_hash(&[0x00, 0x00, 0xAA]), [0u8; 16]);
15965 }
15966
15967 #[test]
15968 fn test_extract_dest_hash_header1() {
15969 let mut raw = vec![0x00, 0x00]; let dest = [0x11; 16];
15972 raw.extend_from_slice(&dest);
15973 raw.extend_from_slice(&[0xFF; 10]); assert_eq!(super::extract_dest_hash(&raw), dest);
15975 }
15976
15977 #[test]
15978 fn test_extract_dest_hash_header2() {
15979 let mut raw = vec![0x40, 0x00]; raw.extend_from_slice(&[0xAA; 16]); let dest = [0x22; 16];
15983 raw.extend_from_slice(&dest); raw.extend_from_slice(&[0xFF; 10]); assert_eq!(super::extract_dest_hash(&raw), dest);
15986 }
15987
15988 #[test]
15989 fn test_extract_dest_hash_header2_too_short() {
15990 let mut raw = vec![0x40, 0x00];
15992 raw.extend_from_slice(&[0xAA; 16]); assert_eq!(super::extract_dest_hash(&raw), [0u8; 16]);
15994 }
15995
15996 #[test]
15997 fn announce_stores_receiving_interface_in_known_destinations() {
15998 let (tx, rx) = event::channel();
16001 let (cbs, _, _, _, _, _) = MockCallbacks::new();
16002 let mut driver = Driver::new(
16003 TransportConfig {
16004 transport_enabled: false,
16005 identity_hash: None,
16006 prefer_shorter_path: false,
16007 max_paths_per_destination: 1,
16008 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
16009 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
16010 max_path_destinations: usize::MAX,
16011 max_tunnel_destinations_total: usize::MAX,
16012 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
16013 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
16014 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
16015 announce_sig_cache_enabled: true,
16016 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
16017 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
16018 announce_queue_max_entries: 256,
16019 announce_queue_max_interfaces: 1024,
16020 },
16021 rx,
16022 tx.clone(),
16023 Box::new(cbs),
16024 );
16025 let info = make_interface_info(1);
16026 driver.engine.register_interface(info);
16027 let (writer, _sent) = MockWriter::new();
16028 driver
16029 .interfaces
16030 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
16031
16032 let identity = Identity::new(&mut OsRng);
16033 let announce_raw = build_announce_packet(&identity);
16034
16035 tx.send(Event::Frame {
16036 interface_id: InterfaceId(1),
16037 data: announce_raw,
16038 })
16039 .unwrap();
16040 tx.send(Event::Shutdown).unwrap();
16041 driver.run();
16042
16043 assert_eq!(driver.known_destinations.len(), 1);
16045 let (_, announced) = driver.known_destinations.iter().next().unwrap();
16046 assert_eq!(
16047 announced.announced.receiving_interface,
16048 InterfaceId(1),
16049 "receiving_interface should match the interface the announce arrived on"
16050 );
16051 }
16052
16053 #[test]
16054 fn announce_on_different_interfaces_stores_correct_id() {
16055 let (tx, rx) = event::channel();
16057 let (cbs, _, _, _, _, _) = MockCallbacks::new();
16058 let mut driver = Driver::new(
16059 TransportConfig {
16060 transport_enabled: false,
16061 identity_hash: None,
16062 prefer_shorter_path: false,
16063 max_paths_per_destination: 1,
16064 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
16065 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
16066 max_path_destinations: usize::MAX,
16067 max_tunnel_destinations_total: usize::MAX,
16068 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
16069 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
16070 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
16071 announce_sig_cache_enabled: true,
16072 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
16073 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
16074 announce_queue_max_entries: 256,
16075 announce_queue_max_interfaces: 1024,
16076 },
16077 rx,
16078 tx.clone(),
16079 Box::new(cbs),
16080 );
16081 for id in [1, 2] {
16083 driver.engine.register_interface(make_interface_info(id));
16084 let (writer, _) = MockWriter::new();
16085 driver
16086 .interfaces
16087 .insert(InterfaceId(id), make_entry(id, Box::new(writer), true));
16088 }
16089
16090 let identity = Identity::new(&mut OsRng);
16091 let announce_raw = build_announce_packet(&identity);
16092
16093 tx.send(Event::Frame {
16095 interface_id: InterfaceId(2),
16096 data: announce_raw,
16097 })
16098 .unwrap();
16099 tx.send(Event::Shutdown).unwrap();
16100 driver.run();
16101
16102 assert_eq!(driver.known_destinations.len(), 1);
16103 let (_, announced) = driver.known_destinations.iter().next().unwrap();
16104 assert_eq!(announced.announced.receiving_interface, InterfaceId(2));
16105 }
16106
16107 #[test]
16108 fn inject_identity_stores_sentinel_interface() {
16109 let (tx, rx) = event::channel();
16112 let (cbs, _, _, _, _, _) = MockCallbacks::new();
16113 let mut driver = Driver::new(
16114 TransportConfig {
16115 transport_enabled: false,
16116 identity_hash: None,
16117 prefer_shorter_path: false,
16118 max_paths_per_destination: 1,
16119 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
16120 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
16121 max_path_destinations: usize::MAX,
16122 max_tunnel_destinations_total: usize::MAX,
16123 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
16124 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
16125 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
16126 announce_sig_cache_enabled: true,
16127 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
16128 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
16129 announce_queue_max_entries: 256,
16130 announce_queue_max_interfaces: 1024,
16131 },
16132 rx,
16133 tx.clone(),
16134 Box::new(cbs),
16135 );
16136
16137 let identity = Identity::new(&mut OsRng);
16138 let dest_hash =
16139 rns_core::destination::destination_hash("test", &["app"], Some(identity.hash()));
16140
16141 let (resp_tx, resp_rx) = mpsc::channel();
16142 tx.send(Event::Query(
16143 QueryRequest::InjectIdentity {
16144 dest_hash,
16145 identity_hash: *identity.hash(),
16146 public_key: identity.get_public_key().unwrap(),
16147 app_data: Some(b"restored".to_vec()),
16148 hops: 2,
16149 received_at: 99.0,
16150 },
16151 resp_tx,
16152 ))
16153 .unwrap();
16154 tx.send(Event::Shutdown).unwrap();
16155 driver.run();
16156
16157 match resp_rx.recv().unwrap() {
16158 QueryResponse::InjectIdentity(true) => {}
16159 other => panic!("expected InjectIdentity(true), got {:?}", other),
16160 }
16161
16162 let announced = driver
16163 .known_destinations
16164 .get(&dest_hash)
16165 .expect("identity should be cached");
16166 assert_eq!(
16167 announced.announced.receiving_interface,
16168 InterfaceId(0),
16169 "injected identity should have sentinel InterfaceId(0)"
16170 );
16171 assert_eq!(announced.announced.dest_hash.0, dest_hash);
16172 assert_eq!(announced.announced.identity_hash.0, *identity.hash());
16173 assert_eq!(
16174 announced.announced.public_key,
16175 identity.get_public_key().unwrap()
16176 );
16177 assert_eq!(announced.announced.app_data, Some(b"restored".to_vec()));
16178 assert_eq!(announced.announced.hops, 2);
16179 assert_eq!(announced.announced.received_at, 99.0);
16180 }
16181
16182 #[test]
16183 fn inject_identity_overwrites_previous_entry() {
16184 let (tx, rx) = event::channel();
16186 let (cbs, _, _, _, _, _) = MockCallbacks::new();
16187 let mut driver = Driver::new(
16188 TransportConfig {
16189 transport_enabled: false,
16190 identity_hash: None,
16191 prefer_shorter_path: false,
16192 max_paths_per_destination: 1,
16193 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
16194 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
16195 max_path_destinations: usize::MAX,
16196 max_tunnel_destinations_total: usize::MAX,
16197 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
16198 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
16199 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
16200 announce_sig_cache_enabled: true,
16201 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
16202 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
16203 announce_queue_max_entries: 256,
16204 announce_queue_max_interfaces: 1024,
16205 },
16206 rx,
16207 tx.clone(),
16208 Box::new(cbs),
16209 );
16210
16211 let identity = Identity::new(&mut OsRng);
16212 let dest_hash =
16213 rns_core::destination::destination_hash("test", &["app"], Some(identity.hash()));
16214
16215 let (resp_tx1, resp_rx1) = mpsc::channel();
16217 tx.send(Event::Query(
16218 QueryRequest::InjectIdentity {
16219 dest_hash,
16220 identity_hash: *identity.hash(),
16221 public_key: identity.get_public_key().unwrap(),
16222 app_data: Some(b"first".to_vec()),
16223 hops: 1,
16224 received_at: 10.0,
16225 },
16226 resp_tx1,
16227 ))
16228 .unwrap();
16229
16230 let (resp_tx2, resp_rx2) = mpsc::channel();
16232 tx.send(Event::Query(
16233 QueryRequest::InjectIdentity {
16234 dest_hash,
16235 identity_hash: *identity.hash(),
16236 public_key: identity.get_public_key().unwrap(),
16237 app_data: Some(b"second".to_vec()),
16238 hops: 3,
16239 received_at: 20.0,
16240 },
16241 resp_tx2,
16242 ))
16243 .unwrap();
16244
16245 tx.send(Event::Shutdown).unwrap();
16246 driver.run();
16247
16248 assert!(matches!(
16249 resp_rx1.recv().unwrap(),
16250 QueryResponse::InjectIdentity(true)
16251 ));
16252 assert!(matches!(
16253 resp_rx2.recv().unwrap(),
16254 QueryResponse::InjectIdentity(true)
16255 ));
16256
16257 let announced = driver.known_destinations.get(&dest_hash).unwrap();
16259 assert_eq!(announced.announced.app_data, Some(b"second".to_vec()));
16260 assert_eq!(announced.announced.hops, 3);
16261 assert_eq!(announced.announced.received_at, 20.0);
16262 }
16263
16264 #[test]
16265 fn re_announce_updates_receiving_interface() {
16266 let (tx, rx) = event::channel();
16269 let (cbs, _, _, _, _, _) = MockCallbacks::new();
16270 let mut driver = Driver::new(
16271 TransportConfig {
16272 transport_enabled: false,
16273 identity_hash: None,
16274 prefer_shorter_path: false,
16275 max_paths_per_destination: 1,
16276 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
16277 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
16278 max_path_destinations: usize::MAX,
16279 max_tunnel_destinations_total: usize::MAX,
16280 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
16281 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
16282 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
16283 announce_sig_cache_enabled: true,
16284 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
16285 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
16286 announce_queue_max_entries: 256,
16287 announce_queue_max_interfaces: 1024,
16288 },
16289 rx,
16290 tx.clone(),
16291 Box::new(cbs),
16292 );
16293 for id in [1, 2] {
16294 driver.engine.register_interface(make_interface_info(id));
16295 let (writer, _) = MockWriter::new();
16296 driver
16297 .interfaces
16298 .insert(InterfaceId(id), make_entry(id, Box::new(writer), true));
16299 }
16300
16301 let identity = Identity::new(&mut OsRng);
16302 let announce_raw = build_announce_packet(&identity);
16303
16304 tx.send(Event::Frame {
16306 interface_id: InterfaceId(1),
16307 data: announce_raw.clone(),
16308 })
16309 .unwrap();
16310 let identity2 = Identity::new(&mut OsRng);
16314 let announce_raw2 = build_announce_packet(&identity2);
16315 tx.send(Event::Frame {
16316 interface_id: InterfaceId(2),
16317 data: announce_raw2,
16318 })
16319 .unwrap();
16320 tx.send(Event::Shutdown).unwrap();
16321 driver.run();
16322
16323 assert_eq!(driver.known_destinations.len(), 2);
16325 for (_, announced) in &driver.known_destinations {
16326 assert!(
16328 announced.announced.receiving_interface == InterfaceId(1)
16329 || announced.announced.receiving_interface == InterfaceId(2)
16330 );
16331 }
16332 let ifaces: Vec<_> = driver
16334 .known_destinations
16335 .values()
16336 .map(|a| a.announced.receiving_interface)
16337 .collect();
16338 assert!(ifaces.contains(&InterfaceId(1)));
16339 assert!(ifaces.contains(&InterfaceId(2)));
16340 }
16341
16342 #[test]
16343 fn test_extract_dest_hash_other_flags_preserved() {
16344 let mut raw = vec![0x3F, 0x00];
16347 let dest = [0x33; 16];
16348 raw.extend_from_slice(&dest);
16349 raw.extend_from_slice(&[0xFF; 10]);
16350 assert_eq!(super::extract_dest_hash(&raw), dest);
16351
16352 let mut raw2 = vec![0xFF, 0x00];
16354 raw2.extend_from_slice(&[0xBB; 16]); let dest2 = [0x44; 16];
16356 raw2.extend_from_slice(&dest2);
16357 raw2.extend_from_slice(&[0xFF; 10]);
16358 assert_eq!(super::extract_dest_hash(&raw2), dest2);
16359 }
16360}