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 BackbonePeerStateEntry, BlackholeInfo, DrainStatus, Event, EventReceiver,
24 InterfaceStatsResponse, LifecycleState, LocalDestinationEntry, NextHopResponse, PathTableEntry,
25 QueryRequest, QueryResponse, RateTableEntry, RuntimeConfigApplyMode, RuntimeConfigEntry,
26 RuntimeConfigError, RuntimeConfigErrorCode, RuntimeConfigSource, RuntimeConfigValue,
27 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(all(feature = "iface-backbone", target_os = "linux", test))]
36use crate::interface::backbone::{
37 BackboneAbuseConfig, BackboneClientRuntime, BackboneServerRuntime,
38};
39#[cfg(feature = "iface-backbone")]
40use crate::interface::backbone::{
41 BackboneClientRuntimeConfigHandle, BackbonePeerStateHandle, BackboneRuntimeConfigHandle,
42};
43#[cfg(all(feature = "iface-i2p", test))]
44use crate::interface::i2p::I2pRuntime;
45#[cfg(feature = "iface-i2p")]
46use crate::interface::i2p::I2pRuntimeConfigHandle;
47#[cfg(all(feature = "iface-pipe", test))]
48use crate::interface::pipe::PipeRuntime;
49#[cfg(feature = "iface-pipe")]
50use crate::interface::pipe::PipeRuntimeConfigHandle;
51#[cfg(all(feature = "iface-rnode", test))]
52use crate::interface::rnode::RNodeSubConfig;
53#[cfg(feature = "iface-rnode")]
54use crate::interface::rnode::{validate_sub_config, RNodeRuntime, RNodeRuntimeConfigHandle};
55#[cfg(feature = "iface-tcp")]
56use crate::interface::tcp::TcpClientRuntimeConfigHandle;
57#[cfg(all(feature = "iface-tcp", test))]
58use crate::interface::tcp_server::TcpServerRuntime;
59#[cfg(feature = "iface-tcp")]
60use crate::interface::tcp_server::TcpServerRuntimeConfigHandle;
61#[cfg(all(feature = "iface-udp", test))]
62use crate::interface::udp::UdpRuntime;
63#[cfg(feature = "iface-udp")]
64use crate::interface::udp::UdpRuntimeConfigHandle;
65use crate::interface::{InterfaceEntry, InterfaceStats};
66use crate::link_manager::{LinkManager, LinkManagerAction};
67use crate::time;
68
69const DEFAULT_KNOWN_DESTINATIONS_TTL: f64 = 48.0 * 60.0 * 60.0;
70const DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES: usize = 8192;
71const DEFAULT_RATE_LIMITER_TTL_SECS: f64 = 48.0 * 60.0 * 60.0;
72const DEFAULT_TICK_INTERVAL_MS: u64 = 1000;
73const DEFAULT_KNOWN_DESTINATIONS_CLEANUP_INTERVAL_TICKS: u32 = 3600;
74const DEFAULT_ANNOUNCE_CACHE_CLEANUP_INTERVAL_TICKS: u32 = 3600;
75const DEFAULT_ANNOUNCE_CACHE_CLEANUP_BATCH_SIZE: usize = 10_000;
76const DEFAULT_DISCOVERY_CLEANUP_INTERVAL_TICKS: u32 = 3600;
77const DEFAULT_MANAGEMENT_ANNOUNCE_INTERVAL_SECS: f64 = 300.0;
78const SEND_RETRY_BACKOFF_MIN: Duration = Duration::from_millis(25);
79const SEND_RETRY_BACKOFF_MAX: Duration = Duration::from_millis(1000);
80
81fn inject_transport_header(raw: &[u8], next_hop: &[u8; 16]) -> Vec<u8> {
82 if raw.len() < 18 {
83 return raw.to_vec();
84 }
85
86 let new_flags = (rns_core::constants::HEADER_2 << 6)
87 | (rns_core::constants::TRANSPORT_TRANSPORT << 4)
88 | (raw[0] & 0x0F);
89
90 let mut new_raw = Vec::with_capacity(raw.len() + 16);
91 new_raw.push(new_flags);
92 new_raw.push(raw[1]);
93 new_raw.extend_from_slice(next_hop);
94 new_raw.extend_from_slice(&raw[2..]);
95 new_raw
96}
97
98#[derive(Debug, Clone, Copy)]
99pub(crate) struct RuntimeConfigDefaults {
100 pub(crate) tick_interval_ms: u64,
101 pub(crate) known_destinations_ttl: f64,
102 pub(crate) rate_limiter_ttl_secs: f64,
103 pub(crate) known_destinations_cleanup_interval_ticks: u32,
104 pub(crate) announce_cache_cleanup_interval_ticks: u32,
105 pub(crate) announce_cache_cleanup_batch_size: usize,
106 pub(crate) discovery_cleanup_interval_ticks: u32,
107 pub(crate) management_announce_interval_secs: f64,
108 pub(crate) direct_connect_policy: crate::event::HolePunchPolicy,
109 #[cfg(feature = "rns-hooks")]
110 pub(crate) provider_queue_max_events: usize,
111 #[cfg(feature = "rns-hooks")]
112 pub(crate) provider_queue_max_bytes: usize,
113}
114
115#[cfg(feature = "iface-backbone")]
116#[derive(Debug, Clone)]
117pub(crate) struct BackboneDiscoveryRuntime {
118 pub(crate) discoverable: bool,
119 pub(crate) config: crate::discovery::DiscoveryConfig,
120 pub(crate) transport_enabled: bool,
121 pub(crate) ifac_netname: Option<String>,
122 pub(crate) ifac_netkey: Option<String>,
123}
124
125#[cfg(feature = "iface-backbone")]
126#[derive(Debug, Clone)]
127pub(crate) struct BackboneDiscoveryRuntimeHandle {
128 pub(crate) interface_name: String,
129 pub(crate) current: BackboneDiscoveryRuntime,
130 pub(crate) startup: BackboneDiscoveryRuntime,
131}
132
133#[cfg(feature = "iface-tcp")]
134#[derive(Debug, Clone)]
135pub(crate) struct TcpServerDiscoveryRuntime {
136 pub(crate) discoverable: bool,
137 pub(crate) config: crate::discovery::DiscoveryConfig,
138 pub(crate) transport_enabled: bool,
139 pub(crate) ifac_netname: Option<String>,
140 pub(crate) ifac_netkey: Option<String>,
141}
142
143#[cfg(feature = "iface-tcp")]
144#[derive(Debug, Clone)]
145pub(crate) struct TcpServerDiscoveryRuntimeHandle {
146 pub(crate) interface_name: String,
147 pub(crate) current: TcpServerDiscoveryRuntime,
148 pub(crate) startup: TcpServerDiscoveryRuntime,
149}
150
151#[derive(Debug, Clone, PartialEq, Eq)]
152pub(crate) struct IfacRuntimeConfig {
153 pub(crate) netname: Option<String>,
154 pub(crate) netkey: Option<String>,
155 pub(crate) size: usize,
156}
157
158#[cfg(feature = "rns-hooks")]
160struct EngineRef<'a> {
161 engine: &'a TransportEngine,
162 interfaces: &'a HashMap<InterfaceId, InterfaceEntry>,
163 link_manager: &'a LinkManager,
164 now: f64,
165}
166
167#[cfg(feature = "rns-hooks")]
168impl<'a> EngineAccess for EngineRef<'a> {
169 fn has_path(&self, dest: &[u8; 16]) -> bool {
170 self.engine.has_path(dest)
171 }
172 fn hops_to(&self, dest: &[u8; 16]) -> Option<u8> {
173 self.engine.hops_to(dest)
174 }
175 fn next_hop(&self, dest: &[u8; 16]) -> Option<[u8; 16]> {
176 self.engine.next_hop(dest)
177 }
178 fn is_blackholed(&self, identity: &[u8; 16]) -> bool {
179 self.engine.is_blackholed(identity, self.now)
180 }
181 fn interface_name(&self, id: u64) -> Option<String> {
182 self.interfaces
183 .get(&InterfaceId(id))
184 .map(|e| e.info.name.clone())
185 }
186 fn interface_mode(&self, id: u64) -> Option<u8> {
187 self.interfaces.get(&InterfaceId(id)).map(|e| e.info.mode)
188 }
189 fn identity_hash(&self) -> Option<[u8; 16]> {
190 self.engine.identity_hash().copied()
191 }
192 fn announce_rate(&self, id: u64) -> Option<i32> {
193 self.interfaces
194 .get(&InterfaceId(id))
195 .map(|e| (e.stats.outgoing_announce_freq() * 1000.0) as i32)
196 }
197 fn link_state(&self, link_hash: &[u8; 16]) -> Option<u8> {
198 use rns_core::link::types::LinkState;
199 self.link_manager.link_state(link_hash).map(|s| match s {
200 LinkState::Pending => 0,
201 LinkState::Handshake => 1,
202 LinkState::Active => 2,
203 LinkState::Stale => 3,
204 LinkState::Closed => 4,
205 })
206 }
207}
208
209#[cfg(any(test, feature = "rns-hooks"))]
214fn extract_dest_hash(raw: &[u8]) -> [u8; 16] {
215 let mut dest = [0u8; 16];
216 if raw.is_empty() {
217 return dest;
218 }
219 let is_header2 = raw[0] & 0x40 != 0;
220 let start = if is_header2 { 18 } else { 2 };
221 let end = start + 16;
222 if raw.len() >= end {
223 dest.copy_from_slice(&raw[start..end]);
224 }
225 dest
226}
227
228#[cfg(feature = "rns-hooks")]
230fn run_hook_inner(
231 programs: &mut [rns_hooks::LoadedProgram],
232 hook_manager: &Option<HookManager>,
233 engine_access: &dyn EngineAccess,
234 ctx: &HookContext,
235 now: f64,
236 provider_events_enabled: bool,
237) -> Option<rns_hooks::ExecuteResult> {
238 if programs.is_empty() {
239 return None;
240 }
241 let mgr = hook_manager.as_ref()?;
242 mgr.run_chain_with_provider_events(programs, ctx, engine_access, now, provider_events_enabled)
243}
244
245#[cfg(feature = "rns-hooks")]
246fn backbone_peer_hook_context(event: &BackbonePeerHookEvent) -> HookContext<'_> {
247 HookContext::BackbonePeer {
248 server_interface_id: event.server_interface_id.0,
249 peer_interface_id: event.peer_interface_id.map(|id| id.0),
250 peer_ip: event.peer_ip,
251 peer_port: event.peer_port,
252 connected_for: event.connected_for,
253 had_received_data: event.had_received_data,
254 penalty_level: event.penalty_level,
255 blacklist_for: event.blacklist_for,
256 }
257}
258
259#[cfg(feature = "rns-hooks")]
261fn convert_injected_actions(actions: Vec<rns_hooks::ActionWire>) -> Vec<TransportAction> {
262 actions
263 .into_iter()
264 .map(|a| {
265 use rns_hooks::ActionWire;
266 match a {
267 ActionWire::SendOnInterface { interface, raw } => {
268 TransportAction::SendOnInterface {
269 interface: InterfaceId(interface),
270 raw,
271 }
272 }
273 ActionWire::BroadcastOnAllInterfaces {
274 raw,
275 exclude,
276 has_exclude,
277 } => TransportAction::BroadcastOnAllInterfaces {
278 raw,
279 exclude: if has_exclude != 0 {
280 Some(InterfaceId(exclude))
281 } else {
282 None
283 },
284 },
285 ActionWire::DeliverLocal {
286 destination_hash,
287 raw,
288 packet_hash,
289 receiving_interface,
290 } => TransportAction::DeliverLocal {
291 destination_hash,
292 raw,
293 packet_hash,
294 receiving_interface: InterfaceId(receiving_interface),
295 },
296 ActionWire::PathUpdated {
297 destination_hash,
298 hops,
299 next_hop,
300 interface,
301 } => TransportAction::PathUpdated {
302 destination_hash,
303 hops,
304 next_hop,
305 interface: InterfaceId(interface),
306 },
307 ActionWire::CacheAnnounce { packet_hash, raw } => {
308 TransportAction::CacheAnnounce { packet_hash, raw }
309 }
310 ActionWire::TunnelEstablished {
311 tunnel_id,
312 interface,
313 } => TransportAction::TunnelEstablished {
314 tunnel_id,
315 interface: InterfaceId(interface),
316 },
317 ActionWire::TunnelSynthesize {
318 interface,
319 data,
320 dest_hash,
321 } => TransportAction::TunnelSynthesize {
322 interface: InterfaceId(interface),
323 data,
324 dest_hash,
325 },
326 ActionWire::ForwardToLocalClients {
327 raw,
328 exclude,
329 has_exclude,
330 } => TransportAction::ForwardToLocalClients {
331 raw,
332 exclude: if has_exclude != 0 {
333 Some(InterfaceId(exclude))
334 } else {
335 None
336 },
337 },
338 ActionWire::ForwardPlainBroadcast {
339 raw,
340 to_local,
341 exclude,
342 has_exclude,
343 } => TransportAction::ForwardPlainBroadcast {
344 raw,
345 to_local: to_local != 0,
346 exclude: if has_exclude != 0 {
347 Some(InterfaceId(exclude))
348 } else {
349 None
350 },
351 },
352 ActionWire::AnnounceReceived {
353 destination_hash,
354 identity_hash,
355 public_key,
356 name_hash,
357 random_hash,
358 app_data,
359 hops,
360 receiving_interface,
361 } => TransportAction::AnnounceReceived {
362 destination_hash,
363 identity_hash,
364 public_key,
365 name_hash,
366 random_hash,
367 app_data,
368 hops,
369 receiving_interface: InterfaceId(receiving_interface),
370 },
371 }
372 })
373 .collect()
374}
375
376fn infer_interface_type(name: &str) -> String {
380 if name.starts_with("TCPServerInterface") {
381 "TCPServerClientInterface".to_string()
382 } else if name.starts_with("BackboneInterface") {
383 "BackboneInterface".to_string()
384 } else if name.starts_with("LocalInterface") {
385 "LocalServerClientInterface".to_string()
386 } else {
387 "AutoInterface".to_string()
390 }
391}
392
393pub use crate::common::callbacks::Callbacks;
394
395#[derive(Clone)]
396struct SharedAnnounceRecord {
397 name_hash: [u8; 10],
398 identity_prv_key: [u8; 64],
399 app_data: Option<Vec<u8>>,
400}
401
402pub struct Driver {
404 pub(crate) engine: TransportEngine,
405 pub(crate) interfaces: HashMap<InterfaceId, InterfaceEntry>,
406 pub(crate) rng: OsRng,
407 pub(crate) rx: EventReceiver,
408 pub(crate) callbacks: Box<dyn Callbacks>,
409 pub(crate) started: f64,
410 pub(crate) lifecycle_state: LifecycleState,
411 pub(crate) drain_started_at: Option<Instant>,
412 pub(crate) drain_deadline: Option<Instant>,
413 pub(crate) listener_controls: Vec<crate::interface::ListenerControl>,
414 pub(crate) announce_cache: Option<crate::announce_cache::AnnounceCache>,
415 pub(crate) tunnel_synth_dest: [u8; 16],
417 pub(crate) transport_identity: Option<rns_crypto::identity::Identity>,
419 pub(crate) link_manager: LinkManager,
421 pub(crate) management_config: crate::management::ManagementConfig,
423 pub(crate) last_management_announce: f64,
425 pub(crate) initial_announce_sent: bool,
427 pub(crate) known_destinations: HashMap<[u8; 16], crate::destination::AnnouncedIdentity>,
429 pub(crate) known_destinations_ttl: f64,
431 pub(crate) known_destinations_max_entries: usize,
433 pub(crate) rate_limiter_ttl_secs: f64,
435 pub(crate) path_request_dest: [u8; 16],
437 pub(crate) proof_strategies: HashMap<
440 [u8; 16],
441 (
442 rns_core::types::ProofStrategy,
443 Option<rns_crypto::identity::Identity>,
444 ),
445 >,
446 pub(crate) sent_packets: HashMap<[u8; 32], ([u8; 16], f64)>,
448 pub(crate) completed_proofs: HashMap<[u8; 32], (f64, f64)>,
450 pub(crate) local_destinations: HashMap<[u8; 16], u8>,
452 shared_announces: HashMap<[u8; 16], SharedAnnounceRecord>,
454 shared_reconnect_pending: HashMap<InterfaceId, bool>,
456 pub(crate) holepunch_manager: HolePunchManager,
458 pub(crate) event_tx: crate::event::EventSender,
460 pub(crate) interface_writer_queue_capacity: usize,
462 pub(crate) tick_interval_ms: Arc<AtomicU64>,
464 #[cfg(feature = "iface-backbone")]
466 pub(crate) backbone_runtime: HashMap<String, BackboneRuntimeConfigHandle>,
467 #[cfg(feature = "iface-backbone")]
469 pub(crate) backbone_peer_state: HashMap<String, BackbonePeerStateHandle>,
470 #[cfg(feature = "iface-backbone")]
472 pub(crate) backbone_client_runtime: HashMap<String, BackboneClientRuntimeConfigHandle>,
473 #[cfg(feature = "iface-backbone")]
475 pub(crate) backbone_discovery_runtime: HashMap<String, BackboneDiscoveryRuntimeHandle>,
476 #[cfg(feature = "iface-tcp")]
478 pub(crate) tcp_server_runtime: HashMap<String, TcpServerRuntimeConfigHandle>,
479 #[cfg(feature = "iface-tcp")]
481 pub(crate) tcp_client_runtime: HashMap<String, TcpClientRuntimeConfigHandle>,
482 #[cfg(feature = "iface-tcp")]
484 pub(crate) tcp_server_discovery_runtime: HashMap<String, TcpServerDiscoveryRuntimeHandle>,
485 #[cfg(feature = "iface-udp")]
487 pub(crate) udp_runtime: HashMap<String, UdpRuntimeConfigHandle>,
488 #[cfg(feature = "iface-auto")]
490 pub(crate) auto_runtime: HashMap<String, AutoRuntimeConfigHandle>,
491 #[cfg(feature = "iface-i2p")]
493 pub(crate) i2p_runtime: HashMap<String, I2pRuntimeConfigHandle>,
494 #[cfg(feature = "iface-pipe")]
496 pub(crate) pipe_runtime: HashMap<String, PipeRuntimeConfigHandle>,
497 #[cfg(feature = "iface-rnode")]
499 pub(crate) rnode_runtime: HashMap<String, RNodeRuntimeConfigHandle>,
500 pub(crate) interface_runtime_defaults:
502 HashMap<String, rns_core::transport::types::InterfaceInfo>,
503 pub(crate) interface_ifac_runtime: HashMap<String, IfacRuntimeConfig>,
505 pub(crate) interface_ifac_runtime_defaults: HashMap<String, IfacRuntimeConfig>,
507 pub(crate) discovered_interfaces: crate::discovery::DiscoveredInterfaceStorage,
509 pub(crate) discovery_required_value: u8,
511 pub(crate) discovery_name_hash: [u8; 10],
513 pub(crate) probe_responder_hash: Option<[u8; 16]>,
515 pub(crate) discover_interfaces: bool,
517 pub(crate) interface_announcer: Option<crate::discovery::InterfaceAnnouncer>,
519 pub(crate) announce_verify_queue: Arc<Mutex<AnnounceVerifyQueue>>,
521 pub(crate) async_announce_verification: bool,
523 pub(crate) discovery_cleanup_counter: u32,
525 pub(crate) discovery_cleanup_interval_ticks: u32,
527 pub(crate) memory_stats_counter: u32,
529 pub(crate) cache_cleanup_counter: u32,
531 pub(crate) announce_cache_cleanup_counter: u32,
533 pub(crate) known_destinations_cleanup_interval_ticks: u32,
535 pub(crate) known_destinations_cap_evict_count: usize,
537 pub(crate) announce_cache_cleanup_interval_ticks: u32,
539 pub(crate) cache_cleanup_active_hashes: Option<Vec<[u8; 32]>>,
541 pub(crate) cache_cleanup_entries: Option<std::fs::ReadDir>,
543 pub(crate) cache_cleanup_removed: usize,
545 pub(crate) announce_cache_cleanup_batch_size: usize,
547 pub(crate) management_announce_interval_secs: f64,
549 pub(crate) runtime_config_defaults: RuntimeConfigDefaults,
551 #[cfg(feature = "rns-hooks")]
553 pub(crate) hook_slots: [HookSlot; HookPoint::COUNT],
554 #[cfg(feature = "rns-hooks")]
556 pub(crate) hook_manager: Option<HookManager>,
557 #[cfg(feature = "rns-hooks")]
558 pub(crate) provider_bridge: Option<ProviderBridge>,
559}
560
561impl Driver {
562 pub fn new(
564 config: TransportConfig,
565 rx: EventReceiver,
566 tx: crate::event::EventSender,
567 callbacks: Box<dyn Callbacks>,
568 ) -> Self {
569 let announce_queue_max_entries = config.announce_queue_max_entries;
570 let tunnel_synth_dest = rns_core::destination::destination_hash(
571 "rnstransport",
572 &["tunnel", "synthesize"],
573 None,
574 );
575 let path_request_dest =
576 rns_core::destination::destination_hash("rnstransport", &["path", "request"], None);
577 let discovery_name_hash = crate::discovery::discovery_name_hash();
578 let mut engine = TransportEngine::new(config);
579 engine.register_destination(tunnel_synth_dest, rns_core::constants::DESTINATION_PLAIN);
580 engine.register_destination(path_request_dest, rns_core::constants::DESTINATION_PLAIN);
582 let mut local_destinations = HashMap::new();
585 local_destinations.insert(tunnel_synth_dest, rns_core::constants::DESTINATION_PLAIN);
586 local_destinations.insert(path_request_dest, rns_core::constants::DESTINATION_PLAIN);
587 let runtime_config_defaults = RuntimeConfigDefaults {
588 tick_interval_ms: DEFAULT_TICK_INTERVAL_MS,
589 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
590 rate_limiter_ttl_secs: DEFAULT_RATE_LIMITER_TTL_SECS,
591 known_destinations_cleanup_interval_ticks:
592 DEFAULT_KNOWN_DESTINATIONS_CLEANUP_INTERVAL_TICKS,
593 announce_cache_cleanup_interval_ticks: DEFAULT_ANNOUNCE_CACHE_CLEANUP_INTERVAL_TICKS,
594 announce_cache_cleanup_batch_size: DEFAULT_ANNOUNCE_CACHE_CLEANUP_BATCH_SIZE,
595 discovery_cleanup_interval_ticks: DEFAULT_DISCOVERY_CLEANUP_INTERVAL_TICKS,
596 management_announce_interval_secs: DEFAULT_MANAGEMENT_ANNOUNCE_INTERVAL_SECS,
597 direct_connect_policy: crate::event::HolePunchPolicy::default(),
598 #[cfg(feature = "rns-hooks")]
599 provider_queue_max_events: crate::provider_bridge::ProviderBridgeConfig::default()
600 .queue_max_events,
601 #[cfg(feature = "rns-hooks")]
602 provider_queue_max_bytes: crate::provider_bridge::ProviderBridgeConfig::default()
603 .queue_max_bytes,
604 };
605 Driver {
606 engine,
607 interfaces: HashMap::new(),
608 rng: OsRng,
609 rx,
610 callbacks,
611 started: time::now(),
612 lifecycle_state: LifecycleState::Active,
613 drain_started_at: None,
614 drain_deadline: None,
615 listener_controls: Vec::new(),
616 announce_cache: None,
617 tunnel_synth_dest,
618 transport_identity: None,
619 link_manager: LinkManager::new(),
620 management_config: Default::default(),
621 last_management_announce: 0.0,
622 initial_announce_sent: false,
623 known_destinations: HashMap::new(),
624 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
625 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
626 rate_limiter_ttl_secs: DEFAULT_RATE_LIMITER_TTL_SECS,
627 path_request_dest,
628 proof_strategies: HashMap::new(),
629 sent_packets: HashMap::new(),
630 completed_proofs: HashMap::new(),
631 local_destinations,
632 shared_announces: HashMap::new(),
633 shared_reconnect_pending: HashMap::new(),
634 holepunch_manager: HolePunchManager::new(
635 vec![],
636 rns_core::holepunch::ProbeProtocol::Rnsp,
637 None,
638 ),
639 event_tx: tx,
640 interface_writer_queue_capacity: crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
641 tick_interval_ms: Arc::new(AtomicU64::new(DEFAULT_TICK_INTERVAL_MS)),
642 #[cfg(feature = "iface-backbone")]
643 backbone_runtime: HashMap::new(),
644 #[cfg(feature = "iface-backbone")]
645 backbone_peer_state: HashMap::new(),
646 #[cfg(feature = "iface-backbone")]
647 backbone_client_runtime: HashMap::new(),
648 #[cfg(feature = "iface-backbone")]
649 backbone_discovery_runtime: HashMap::new(),
650 #[cfg(feature = "iface-tcp")]
651 tcp_server_runtime: HashMap::new(),
652 #[cfg(feature = "iface-tcp")]
653 tcp_client_runtime: HashMap::new(),
654 #[cfg(feature = "iface-tcp")]
655 tcp_server_discovery_runtime: HashMap::new(),
656 #[cfg(feature = "iface-udp")]
657 udp_runtime: HashMap::new(),
658 #[cfg(feature = "iface-auto")]
659 auto_runtime: HashMap::new(),
660 #[cfg(feature = "iface-i2p")]
661 i2p_runtime: HashMap::new(),
662 #[cfg(feature = "iface-pipe")]
663 pipe_runtime: HashMap::new(),
664 #[cfg(feature = "iface-rnode")]
665 rnode_runtime: HashMap::new(),
666 interface_runtime_defaults: HashMap::new(),
667 interface_ifac_runtime: HashMap::new(),
668 interface_ifac_runtime_defaults: HashMap::new(),
669 discovered_interfaces: crate::discovery::DiscoveredInterfaceStorage::new(
670 std::env::temp_dir().join("rns-discovered-interfaces"),
671 ),
672 discovery_required_value: crate::discovery::DEFAULT_STAMP_VALUE,
673 discovery_name_hash,
674 probe_responder_hash: None,
675 discover_interfaces: false,
676 interface_announcer: None,
677 announce_verify_queue: Arc::new(Mutex::new(AnnounceVerifyQueue::new(
678 announce_queue_max_entries,
679 ))),
680 async_announce_verification: false,
681 discovery_cleanup_counter: 0,
682 discovery_cleanup_interval_ticks: runtime_config_defaults
683 .discovery_cleanup_interval_ticks,
684 memory_stats_counter: 0,
685 cache_cleanup_counter: 0,
686 announce_cache_cleanup_counter: 0,
687 known_destinations_cleanup_interval_ticks: runtime_config_defaults
688 .known_destinations_cleanup_interval_ticks,
689 known_destinations_cap_evict_count: 0,
690 announce_cache_cleanup_interval_ticks: runtime_config_defaults
691 .announce_cache_cleanup_interval_ticks,
692 cache_cleanup_active_hashes: None,
693 cache_cleanup_entries: None,
694 cache_cleanup_removed: 0,
695 announce_cache_cleanup_batch_size: runtime_config_defaults
696 .announce_cache_cleanup_batch_size,
697 management_announce_interval_secs: runtime_config_defaults
698 .management_announce_interval_secs,
699 runtime_config_defaults,
700 #[cfg(feature = "rns-hooks")]
701 hook_slots: create_hook_slots(),
702 #[cfg(feature = "rns-hooks")]
703 hook_manager: HookManager::new().ok(),
704 #[cfg(feature = "rns-hooks")]
705 provider_bridge: None,
706 }
707 }
708
709 pub fn set_announce_verify_queue_config(
710 &mut self,
711 max_entries: usize,
712 max_bytes: usize,
713 max_stale_secs: f64,
714 overflow_policy: OverflowPolicy,
715 ) {
716 self.announce_verify_queue = Arc::new(Mutex::new(AnnounceVerifyQueue::with_limits(
717 max_entries,
718 max_bytes,
719 max_stale_secs,
720 overflow_policy,
721 )));
722 }
723
724 fn wrap_interface_writer(
725 &self,
726 interface_id: InterfaceId,
727 interface_name: &str,
728 writer: Box<dyn crate::interface::Writer>,
729 ) -> (
730 Box<dyn crate::interface::Writer>,
731 crate::interface::AsyncWriterMetrics,
732 ) {
733 crate::interface::wrap_async_writer(
734 writer,
735 interface_id,
736 interface_name,
737 self.event_tx.clone(),
738 self.interface_writer_queue_capacity,
739 )
740 }
741
742 fn upsert_known_destination(
743 &mut self,
744 dest_hash: [u8; 16],
745 announced: crate::destination::AnnouncedIdentity,
746 ) {
747 if let Some(existing) = self.known_destinations.get_mut(&dest_hash) {
748 *existing = announced;
749 return;
750 }
751
752 self.enforce_known_destination_cap(true);
753 self.known_destinations.insert(dest_hash, announced);
754 }
755
756 fn begin_drain(&mut self, timeout: Duration) {
757 let now = Instant::now();
758 let deadline = now + timeout;
759 match self.lifecycle_state {
760 LifecycleState::Active => {
761 self.lifecycle_state = LifecycleState::Draining;
762 self.drain_started_at = Some(now);
763 self.drain_deadline = Some(deadline);
764 log::info!(
765 "driver entering drain mode with {:.3}s timeout",
766 timeout.as_secs_f64()
767 );
768 self.stop_listener_accepts();
769 }
770 LifecycleState::Draining => {
771 self.drain_deadline = Some(deadline);
772 log::info!(
773 "driver drain deadline updated to {:.3}s from now",
774 timeout.as_secs_f64()
775 );
776 self.stop_listener_accepts();
777 }
778 LifecycleState::Stopping | LifecycleState::Stopped => {
779 log::debug!(
780 "ignoring BeginDrain while lifecycle state is {:?}",
781 self.lifecycle_state
782 );
783 }
784 }
785 }
786
787 fn is_draining(&self) -> bool {
788 matches!(self.lifecycle_state, LifecycleState::Draining)
789 }
790
791 pub fn register_listener_control(&mut self, control: crate::interface::ListenerControl) {
792 self.listener_controls.push(control);
793 }
794
795 fn stop_listener_accepts(&mut self) {
796 for control in &self.listener_controls {
797 control.request_stop();
798 }
799 #[cfg(feature = "rns-hooks")]
800 if let Some(bridge) = self.provider_bridge.as_ref() {
801 bridge.stop_accepting();
802 }
803 }
804
805 fn reject_new_work(&self, op: &str) {
806 log::info!("rejecting {} while node is draining", op);
807 }
808
809 fn drain_error(&self, op: &str) -> String {
810 format!("cannot {} while node is draining", op)
811 }
812
813 fn drain_status(&self) -> DrainStatus {
814 let now = Instant::now();
815 let active_links = self.link_manager.link_count();
816 let active_resource_transfers = self.link_manager.resource_transfer_count();
817 let active_holepunch_sessions = self.holepunch_manager.session_count();
818 let interface_writer_queued_frames = self
819 .interfaces
820 .values()
821 .map(|entry| {
822 entry
823 .async_writer_metrics
824 .as_ref()
825 .map(|metrics| metrics.queued_frames())
826 .unwrap_or(0)
827 })
828 .sum();
829 #[cfg(feature = "rns-hooks")]
830 let (provider_backlog_events, provider_consumer_queued_events) = self
831 .provider_bridge
832 .as_ref()
833 .map(|bridge| {
834 let stats = bridge.stats();
835 (
836 stats.backlog_len,
837 stats
838 .consumers
839 .iter()
840 .map(|consumer| consumer.queue_len)
841 .sum(),
842 )
843 })
844 .unwrap_or((0, 0));
845 #[cfg(not(feature = "rns-hooks"))]
846 let (provider_backlog_events, provider_consumer_queued_events) = (0, 0);
847 let drain_age_seconds = self
848 .drain_started_at
849 .map(|started| started.elapsed().as_secs_f64());
850 let deadline_remaining_seconds = self.drain_deadline.map(|deadline| {
851 deadline
852 .checked_duration_since(now)
853 .map(|remaining| remaining.as_secs_f64())
854 .unwrap_or(0.0)
855 });
856 let detail = match self.lifecycle_state {
857 LifecycleState::Active => Some("node is accepting normal work".into()),
858 LifecycleState::Draining => {
859 let mut remaining = Vec::new();
860 if active_links > 0 {
861 remaining.push(format!("{active_links} link(s)"));
862 }
863 if active_resource_transfers > 0 {
864 remaining.push(format!("{active_resource_transfers} resource transfer(s)"));
865 }
866 if active_holepunch_sessions > 0 {
867 remaining.push(format!("{active_holepunch_sessions} hole-punch session(s)"));
868 }
869 if interface_writer_queued_frames > 0 {
870 remaining.push(format!(
871 "{interface_writer_queued_frames} queued interface writer frame(s)"
872 ));
873 }
874 if provider_backlog_events > 0 {
875 remaining.push(format!(
876 "{provider_backlog_events} provider backlog event(s)"
877 ));
878 }
879 if provider_consumer_queued_events > 0 {
880 remaining.push(format!(
881 "{provider_consumer_queued_events} queued provider consumer event(s)"
882 ));
883 }
884 Some(if remaining.is_empty() {
885 "node is draining existing work; no active links, resource transfers, hole-punch sessions, or queued writer/provider work remain".into()
886 } else {
887 format!(
888 "node is draining existing work; {} still active",
889 remaining.join(", ")
890 )
891 })
892 }
893 LifecycleState::Stopping => Some("node is tearing down remaining work".into()),
894 LifecycleState::Stopped => Some("node is stopped".into()),
895 };
896
897 DrainStatus {
898 state: self.lifecycle_state,
899 drain_age_seconds,
900 deadline_remaining_seconds,
901 drain_complete: !matches!(self.lifecycle_state, LifecycleState::Draining)
902 || (active_links == 0
903 && active_resource_transfers == 0
904 && active_holepunch_sessions == 0
905 && interface_writer_queued_frames == 0
906 && provider_backlog_events == 0
907 && provider_consumer_queued_events == 0),
908 interface_writer_queued_frames,
909 provider_backlog_events,
910 provider_consumer_queued_events,
911 detail,
912 }
913 }
914
915 fn enforce_drain_deadline(&mut self) {
916 if !matches!(self.lifecycle_state, LifecycleState::Draining) {
917 return;
918 }
919 let Some(deadline) = self.drain_deadline else {
920 return;
921 };
922 if Instant::now() < deadline {
923 return;
924 }
925
926 log::info!("driver drain deadline reached; tearing down remaining links");
927 self.lifecycle_state = LifecycleState::Stopping;
928 let resource_actions = self.link_manager.cancel_all_resources(&mut self.rng);
929 self.dispatch_link_actions(resource_actions);
930 let link_actions = self.link_manager.teardown_all_links();
931 self.dispatch_link_actions(link_actions);
932 let cleanup_actions = self.link_manager.tick(&mut self.rng);
933 self.dispatch_link_actions(cleanup_actions);
934 self.holepunch_manager.abort_all_sessions();
935 }
936
937 fn enforce_known_destination_cap(&mut self, for_insert: bool) -> usize {
938 if self.known_destinations_max_entries == usize::MAX {
939 return 0;
940 }
941
942 let mut evicted = 0usize;
943 while if for_insert {
944 self.known_destinations.len() >= self.known_destinations_max_entries
945 } else {
946 self.known_destinations.len() > self.known_destinations_max_entries
947 } {
948 let active_dests = self.engine.active_destination_hashes();
949 let candidate = self
950 .oldest_known_destination(false, &active_dests)
951 .or_else(|| self.oldest_known_destination(true, &active_dests));
952 let Some(dest_hash) = candidate else {
953 break;
954 };
955 if self.known_destinations.remove(&dest_hash).is_some() {
956 evicted += 1;
957 self.known_destinations_cap_evict_count += 1;
958 } else {
959 break;
960 }
961 }
962 evicted
963 }
964
965 fn oldest_known_destination(
966 &self,
967 include_protected: bool,
968 active_dests: &std::collections::BTreeSet<[u8; 16]>,
969 ) -> Option<[u8; 16]> {
970 self.known_destinations
971 .iter()
972 .filter(|(dest_hash, _)| {
973 include_protected
974 || (!active_dests.contains(*dest_hash)
975 && !self.local_destinations.contains_key(*dest_hash))
976 })
977 .min_by(|a, b| {
978 a.1.received_at
979 .partial_cmp(&b.1.received_at)
980 .unwrap_or(std::cmp::Ordering::Equal)
981 .then_with(|| a.0.cmp(b.0))
982 })
983 .map(|(dest_hash, _)| *dest_hash)
984 }
985
986 #[cfg(feature = "rns-hooks")]
987 fn provider_events_enabled(&self) -> bool {
988 self.provider_bridge.is_some()
989 }
990
991 #[cfg(feature = "rns-hooks")]
992 fn run_backbone_peer_hook(
993 &mut self,
994 attach_point: &str,
995 point: HookPoint,
996 event: &BackbonePeerHookEvent,
997 ) {
998 let ctx = backbone_peer_hook_context(event);
999 let now = time::now();
1000 let engine_ref = EngineRef {
1001 engine: &self.engine,
1002 interfaces: &self.interfaces,
1003 link_manager: &self.link_manager,
1004 now,
1005 };
1006 let provider_events_enabled = self.provider_events_enabled();
1007 if let Some(ref e) = run_hook_inner(
1008 &mut self.hook_slots[point as usize].programs,
1009 &self.hook_manager,
1010 &engine_ref,
1011 &ctx,
1012 now,
1013 provider_events_enabled,
1014 ) {
1015 self.forward_hook_side_effects(attach_point, e);
1016 }
1017 }
1018
1019 #[cfg(feature = "iface-backbone")]
1020 fn make_discoverable_interface(
1021 runtime: &BackboneDiscoveryRuntimeHandle,
1022 ) -> crate::discovery::DiscoverableInterface {
1023 crate::discovery::DiscoverableInterface {
1024 interface_name: runtime.interface_name.clone(),
1025 config: runtime.current.config.clone(),
1026 transport_enabled: runtime.current.transport_enabled,
1027 ifac_netname: runtime.current.ifac_netname.clone(),
1028 ifac_netkey: runtime.current.ifac_netkey.clone(),
1029 }
1030 }
1031
1032 #[cfg(feature = "iface-backbone")]
1033 fn sync_backbone_discovery_runtime(
1034 &mut self,
1035 interface_name: &str,
1036 ) -> Result<(), RuntimeConfigError> {
1037 let handle = self
1038 .backbone_discovery_runtime
1039 .get(interface_name)
1040 .ok_or(RuntimeConfigError {
1041 code: RuntimeConfigErrorCode::NotFound,
1042 message: format!("backbone interface '{}' not found", interface_name),
1043 })?
1044 .clone();
1045
1046 if handle.current.discoverable {
1047 let iface = Self::make_discoverable_interface(&handle);
1048 if let Some(announcer) = self.interface_announcer.as_mut() {
1049 announcer.upsert_interface(iface);
1050 } else if let Some(identity) = self.transport_identity.as_ref() {
1051 self.interface_announcer = Some(crate::discovery::InterfaceAnnouncer::new(
1052 *identity.hash(),
1053 vec![iface],
1054 ));
1055 }
1056 } else if let Some(announcer) = self.interface_announcer.as_mut() {
1057 announcer.remove_interface(interface_name);
1058 if announcer.is_empty() {
1059 self.interface_announcer = None;
1060 }
1061 }
1062
1063 Ok(())
1064 }
1065
1066 #[cfg(feature = "iface-tcp")]
1067 fn make_tcp_server_discoverable_interface(
1068 runtime: &TcpServerDiscoveryRuntimeHandle,
1069 ) -> crate::discovery::DiscoverableInterface {
1070 crate::discovery::DiscoverableInterface {
1071 interface_name: runtime.interface_name.clone(),
1072 config: runtime.current.config.clone(),
1073 transport_enabled: runtime.current.transport_enabled,
1074 ifac_netname: runtime.current.ifac_netname.clone(),
1075 ifac_netkey: runtime.current.ifac_netkey.clone(),
1076 }
1077 }
1078
1079 #[cfg(feature = "iface-tcp")]
1080 fn sync_tcp_server_discovery_runtime(
1081 &mut self,
1082 interface_name: &str,
1083 ) -> Result<(), RuntimeConfigError> {
1084 let handle = self
1085 .tcp_server_discovery_runtime
1086 .get(interface_name)
1087 .ok_or(RuntimeConfigError {
1088 code: RuntimeConfigErrorCode::NotFound,
1089 message: format!("tcp server interface '{}' not found", interface_name),
1090 })?
1091 .clone();
1092
1093 if handle.current.discoverable {
1094 let iface = Self::make_tcp_server_discoverable_interface(&handle);
1095 if let Some(announcer) = self.interface_announcer.as_mut() {
1096 announcer.upsert_interface(iface);
1097 } else if let Some(identity) = self.transport_identity.as_ref() {
1098 self.interface_announcer = Some(crate::discovery::InterfaceAnnouncer::new(
1099 *identity.hash(),
1100 vec![iface],
1101 ));
1102 }
1103 } else if let Some(announcer) = self.interface_announcer.as_mut() {
1104 announcer.remove_interface(interface_name);
1105 if announcer.is_empty() {
1106 self.interface_announcer = None;
1107 }
1108 }
1109
1110 Ok(())
1111 }
1112
1113 #[cfg(feature = "rns-hooks")]
1114 fn update_hook_program<F>(
1115 &mut self,
1116 name: &str,
1117 attach_point: &str,
1118 mut update: F,
1119 ) -> Result<(), String>
1120 where
1121 F: FnMut(&mut rns_hooks::LoadedProgram),
1122 {
1123 let point_idx = crate::config::parse_hook_point(attach_point)
1124 .ok_or_else(|| format!("unknown hook point '{}'", attach_point))?;
1125 let program = self.hook_slots[point_idx]
1126 .programs
1127 .iter_mut()
1128 .find(|program| program.name == name)
1129 .ok_or_else(|| format!("hook '{}' not found at point '{}'", name, attach_point))?;
1130 update(program);
1131 Ok(())
1132 }
1133
1134 pub(crate) fn set_tick_interval_handle(&mut self, tick_interval_ms: Arc<AtomicU64>) {
1135 self.tick_interval_ms = tick_interval_ms;
1136 }
1137
1138 pub(crate) fn set_packet_hashlist_max_entries(&mut self, max_entries: usize) {
1139 self.engine.set_packet_hashlist_max_entries(max_entries);
1140 }
1141
1142 fn build_shared_announce_raw(
1143 &mut self,
1144 dest_hash: &[u8; 16],
1145 record: &SharedAnnounceRecord,
1146 path_response: bool,
1147 ) -> Option<Vec<u8>> {
1148 let identity = rns_crypto::identity::Identity::from_private_key(&record.identity_prv_key);
1149
1150 let mut random_hash = [0u8; 10];
1151 self.rng.fill_bytes(&mut random_hash[..5]);
1152 let now_secs = std::time::SystemTime::now()
1153 .duration_since(std::time::UNIX_EPOCH)
1154 .ok()?
1155 .as_secs();
1156 random_hash[5..10].copy_from_slice(&now_secs.to_be_bytes()[3..8]);
1157
1158 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
1159 &identity,
1160 dest_hash,
1161 &record.name_hash,
1162 &random_hash,
1163 None,
1164 record.app_data.as_deref(),
1165 )
1166 .ok()?;
1167
1168 let flags = rns_core::packet::PacketFlags {
1169 header_type: rns_core::constants::HEADER_1,
1170 context_flag: rns_core::constants::FLAG_UNSET,
1171 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
1172 destination_type: rns_core::constants::DESTINATION_SINGLE,
1173 packet_type: rns_core::constants::PACKET_TYPE_ANNOUNCE,
1174 };
1175 let context = if path_response {
1176 rns_core::constants::CONTEXT_PATH_RESPONSE
1177 } else {
1178 rns_core::constants::CONTEXT_NONE
1179 };
1180
1181 rns_core::packet::RawPacket::pack(flags, 0, dest_hash, None, context, &announce_data)
1182 .ok()
1183 .map(|packet| packet.raw)
1184 }
1185
1186 fn replay_shared_announces(&mut self) {
1187 let records: Vec<([u8; 16], SharedAnnounceRecord)> = self
1188 .shared_announces
1189 .iter()
1190 .map(|(dest_hash, record)| (*dest_hash, record.clone()))
1191 .collect();
1192 for (dest_hash, record) in records {
1193 if let Some(raw) = self.build_shared_announce_raw(&dest_hash, &record, true) {
1194 let event = Event::SendOutbound {
1195 raw,
1196 dest_type: rns_core::constants::DESTINATION_SINGLE,
1197 attached_interface: None,
1198 };
1199 match event {
1200 Event::SendOutbound {
1201 raw,
1202 dest_type,
1203 attached_interface,
1204 } => match RawPacket::unpack(&raw) {
1205 Ok(packet) => {
1206 let actions = self.engine.handle_outbound(
1207 &packet,
1208 dest_type,
1209 attached_interface,
1210 time::now(),
1211 );
1212 self.dispatch_all(actions);
1213 }
1214 Err(e) => {
1215 log::warn!(
1216 "Shared announce replay failed for {:02x?}: {:?}",
1217 &dest_hash[..4],
1218 e
1219 );
1220 }
1221 },
1222 _ => unreachable!(),
1223 }
1224 }
1225 }
1226 }
1227
1228 fn handle_shared_interface_down(&mut self, id: InterfaceId) {
1229 let dropped_paths = self.engine.drop_paths_for_interface(id);
1230 let dropped_reverse = self.engine.drop_reverse_for_interface(id);
1231 let dropped_links = self.engine.drop_links_for_interface(id);
1232 self.engine.drop_announce_queues();
1233 let link_actions = self.link_manager.teardown_all_links();
1234 self.dispatch_link_actions(link_actions);
1235 self.shared_reconnect_pending.insert(id, true);
1236 log::info!(
1237 "[{}] cleared shared state: {} paths, {} reverse entries, {} transport links",
1238 id.0,
1239 dropped_paths,
1240 dropped_reverse,
1241 dropped_links
1242 );
1243 }
1244
1245 #[cfg(feature = "iface-backbone")]
1246 pub(crate) fn register_backbone_runtime(&mut self, handle: BackboneRuntimeConfigHandle) {
1247 self.backbone_runtime
1248 .insert(handle.interface_name.clone(), handle);
1249 }
1250
1251 #[cfg(feature = "iface-backbone")]
1252 pub(crate) fn register_backbone_peer_state(&mut self, handle: BackbonePeerStateHandle) {
1253 self.backbone_peer_state
1254 .insert(handle.interface_name.clone(), handle);
1255 }
1256
1257 #[cfg(feature = "iface-backbone")]
1258 pub(crate) fn register_backbone_client_runtime(
1259 &mut self,
1260 handle: BackboneClientRuntimeConfigHandle,
1261 ) {
1262 self.backbone_client_runtime
1263 .insert(handle.interface_name.clone(), handle);
1264 }
1265
1266 #[cfg(feature = "iface-backbone")]
1267 pub(crate) fn register_backbone_discovery_runtime(
1268 &mut self,
1269 handle: BackboneDiscoveryRuntimeHandle,
1270 ) {
1271 self.backbone_discovery_runtime
1272 .insert(handle.interface_name.clone(), handle);
1273 }
1274
1275 #[cfg(feature = "iface-backbone")]
1276 fn list_backbone_peer_state(
1277 &self,
1278 interface_name: Option<&str>,
1279 ) -> Vec<BackbonePeerStateEntry> {
1280 let mut names: Vec<&String> = match interface_name {
1281 Some(name) => self
1282 .backbone_peer_state
1283 .keys()
1284 .filter(|candidate| candidate.as_str() == name)
1285 .collect(),
1286 None => self.backbone_peer_state.keys().collect(),
1287 };
1288 names.sort();
1289
1290 let mut entries = Vec::new();
1291 for name in names {
1292 if let Some(handle) = self.backbone_peer_state.get(name) {
1293 entries.extend(handle.peer_state.lock().unwrap().list(name));
1294 }
1295 }
1296 entries.sort_by(|a, b| {
1297 a.interface_name
1298 .cmp(&b.interface_name)
1299 .then_with(|| a.peer_ip.cmp(&b.peer_ip))
1300 });
1301 entries
1302 }
1303
1304 #[cfg(feature = "iface-backbone")]
1305 fn list_backbone_interfaces(&self) -> Vec<crate::event::BackboneInterfaceEntry> {
1306 let mut entries: Vec<_> = self
1307 .backbone_peer_state
1308 .values()
1309 .map(|handle| crate::event::BackboneInterfaceEntry {
1310 interface_id: handle.interface_id,
1311 interface_name: handle.interface_name.clone(),
1312 })
1313 .collect();
1314 entries.sort_by(|a, b| a.interface_name.cmp(&b.interface_name));
1315 entries
1316 }
1317
1318 #[cfg(feature = "iface-backbone")]
1319 fn clear_backbone_peer_state(
1320 &mut self,
1321 interface_name: &str,
1322 peer_ip: std::net::IpAddr,
1323 ) -> bool {
1324 self.backbone_peer_state
1325 .get(interface_name)
1326 .map(|handle| handle.peer_state.lock().unwrap().clear(peer_ip))
1327 .unwrap_or(false)
1328 }
1329
1330 fn blacklist_backbone_peer(
1331 &mut self,
1332 interface_name: &str,
1333 peer_ip: std::net::IpAddr,
1334 duration: std::time::Duration,
1335 reason: String,
1336 penalty_level: u8,
1337 ) -> bool {
1338 let capped_duration = self
1339 .backbone_runtime
1340 .get(interface_name)
1341 .and_then(|handle| {
1342 handle
1343 .runtime
1344 .lock()
1345 .ok()
1346 .map(|runtime| runtime.abuse.max_penalty_duration)
1347 })
1348 .flatten()
1349 .map(|max| duration.min(max))
1350 .unwrap_or(duration);
1351 let Some(handle) = self.backbone_peer_state.get(interface_name) else {
1352 return false;
1353 };
1354 let ok = handle
1355 .peer_state
1356 .lock()
1357 .unwrap()
1358 .blacklist(peer_ip, capped_duration, reason);
1359 if ok {
1360 #[cfg(feature = "rns-hooks")]
1361 self.run_backbone_peer_hook(
1362 "BackbonePeerPenalty",
1363 HookPoint::BackbonePeerPenalty,
1364 &BackbonePeerHookEvent {
1365 server_interface_id: self
1366 .interfaces
1367 .iter()
1368 .find(|(_, entry)| entry.info.name == interface_name)
1369 .map(|(id, _)| *id)
1370 .unwrap_or(InterfaceId(0)),
1371 peer_interface_id: None,
1372 peer_ip,
1373 peer_port: 0,
1374 connected_for: Duration::ZERO,
1375 had_received_data: false,
1376 penalty_level,
1377 blacklist_for: capped_duration,
1378 },
1379 );
1380 #[cfg(not(feature = "rns-hooks"))]
1381 let _ = (peer_ip, capped_duration, penalty_level);
1382 }
1383 ok
1384 }
1385
1386 #[cfg(feature = "iface-tcp")]
1387 pub(crate) fn register_tcp_server_runtime(&mut self, handle: TcpServerRuntimeConfigHandle) {
1388 self.tcp_server_runtime
1389 .insert(handle.interface_name.clone(), handle);
1390 }
1391
1392 #[cfg(feature = "iface-tcp")]
1393 pub(crate) fn register_tcp_client_runtime(&mut self, handle: TcpClientRuntimeConfigHandle) {
1394 self.tcp_client_runtime
1395 .insert(handle.interface_name.clone(), handle);
1396 }
1397
1398 #[cfg(feature = "iface-tcp")]
1399 pub(crate) fn register_tcp_server_discovery_runtime(
1400 &mut self,
1401 handle: TcpServerDiscoveryRuntimeHandle,
1402 ) {
1403 self.tcp_server_discovery_runtime
1404 .insert(handle.interface_name.clone(), handle);
1405 }
1406
1407 #[cfg(feature = "iface-udp")]
1408 pub(crate) fn register_udp_runtime(&mut self, handle: UdpRuntimeConfigHandle) {
1409 self.udp_runtime
1410 .insert(handle.interface_name.clone(), handle);
1411 }
1412
1413 #[cfg(feature = "iface-auto")]
1414 pub(crate) fn register_auto_runtime(&mut self, handle: AutoRuntimeConfigHandle) {
1415 self.auto_runtime
1416 .insert(handle.interface_name.clone(), handle);
1417 }
1418
1419 #[cfg(feature = "iface-i2p")]
1420 pub(crate) fn register_i2p_runtime(&mut self, handle: I2pRuntimeConfigHandle) {
1421 self.i2p_runtime
1422 .insert(handle.interface_name.clone(), handle);
1423 }
1424
1425 #[cfg(feature = "iface-pipe")]
1426 pub(crate) fn register_pipe_runtime(&mut self, handle: PipeRuntimeConfigHandle) {
1427 self.pipe_runtime
1428 .insert(handle.interface_name.clone(), handle);
1429 }
1430
1431 #[cfg(feature = "iface-rnode")]
1432 pub(crate) fn register_rnode_runtime(&mut self, handle: RNodeRuntimeConfigHandle) {
1433 self.rnode_runtime
1434 .insert(handle.interface_name.clone(), handle);
1435 }
1436
1437 pub(crate) fn register_interface_runtime_defaults(
1438 &mut self,
1439 info: &rns_core::transport::types::InterfaceInfo,
1440 ) {
1441 self.interface_runtime_defaults
1442 .entry(info.name.clone())
1443 .or_insert_with(|| info.clone());
1444 }
1445
1446 pub(crate) fn register_interface_ifac_runtime(
1447 &mut self,
1448 interface_name: &str,
1449 startup: IfacRuntimeConfig,
1450 ) {
1451 self.interface_ifac_runtime_defaults
1452 .entry(interface_name.to_string())
1453 .or_insert_with(|| startup.clone());
1454 self.interface_ifac_runtime
1455 .entry(interface_name.to_string())
1456 .or_insert(startup);
1457 }
1458
1459 fn runtime_config_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
1460 let defaults = self.runtime_config_defaults;
1461 let make_entry = |key: &str,
1462 value: RuntimeConfigValue,
1463 default: RuntimeConfigValue,
1464 apply_mode: RuntimeConfigApplyMode,
1465 description: &str| RuntimeConfigEntry {
1466 key: key.to_string(),
1467 source: if value == default {
1468 RuntimeConfigSource::Startup
1469 } else {
1470 RuntimeConfigSource::RuntimeOverride
1471 },
1472 value,
1473 default,
1474 apply_mode,
1475 description: Some(description.to_string()),
1476 };
1477
1478 match key {
1479 "global.tick_interval_ms" => Some(make_entry(
1480 key,
1481 RuntimeConfigValue::Int(self.tick_interval_ms.load(Ordering::Relaxed) as i64),
1482 RuntimeConfigValue::Int(defaults.tick_interval_ms as i64),
1483 RuntimeConfigApplyMode::Immediate,
1484 "Driver tick interval in milliseconds.",
1485 )),
1486 "global.known_destinations_ttl_secs" => Some(make_entry(
1487 key,
1488 RuntimeConfigValue::Float(self.known_destinations_ttl),
1489 RuntimeConfigValue::Float(defaults.known_destinations_ttl),
1490 RuntimeConfigApplyMode::Immediate,
1491 "TTL for known destinations without an active path.",
1492 )),
1493 "global.rate_limiter_ttl_secs" => Some(make_entry(
1494 key,
1495 RuntimeConfigValue::Float(self.rate_limiter_ttl_secs),
1496 RuntimeConfigValue::Float(defaults.rate_limiter_ttl_secs),
1497 RuntimeConfigApplyMode::Immediate,
1498 "TTL for announce rate-limiter entries without an active path.",
1499 )),
1500 "global.known_destinations_cleanup_interval_ticks" => Some(make_entry(
1501 key,
1502 RuntimeConfigValue::Int(self.known_destinations_cleanup_interval_ticks as i64),
1503 RuntimeConfigValue::Int(defaults.known_destinations_cleanup_interval_ticks as i64),
1504 RuntimeConfigApplyMode::Immediate,
1505 "Tick interval between known-destinations cleanup passes.",
1506 )),
1507 "global.announce_cache_cleanup_interval_ticks" => Some(make_entry(
1508 key,
1509 RuntimeConfigValue::Int(self.announce_cache_cleanup_interval_ticks as i64),
1510 RuntimeConfigValue::Int(defaults.announce_cache_cleanup_interval_ticks as i64),
1511 RuntimeConfigApplyMode::Immediate,
1512 "Tick interval between announce-cache cleanup cycles.",
1513 )),
1514 "global.announce_cache_cleanup_batch_size" => Some(make_entry(
1515 key,
1516 RuntimeConfigValue::Int(self.announce_cache_cleanup_batch_size as i64),
1517 RuntimeConfigValue::Int(defaults.announce_cache_cleanup_batch_size as i64),
1518 RuntimeConfigApplyMode::Immediate,
1519 "Number of announce-cache entries processed per cleanup tick.",
1520 )),
1521 "global.discovery_cleanup_interval_ticks" => Some(make_entry(
1522 key,
1523 RuntimeConfigValue::Int(self.discovery_cleanup_interval_ticks as i64),
1524 RuntimeConfigValue::Int(defaults.discovery_cleanup_interval_ticks as i64),
1525 RuntimeConfigApplyMode::Immediate,
1526 "Tick interval between discovered-interface cleanup passes.",
1527 )),
1528 "global.management_announce_interval_secs" => Some(make_entry(
1529 key,
1530 RuntimeConfigValue::Float(self.management_announce_interval_secs),
1531 RuntimeConfigValue::Float(defaults.management_announce_interval_secs),
1532 RuntimeConfigApplyMode::Immediate,
1533 "Interval between management announces in seconds.",
1534 )),
1535 "global.direct_connect_policy" => Some(make_entry(
1536 key,
1537 RuntimeConfigValue::String(Self::holepunch_policy_name(
1538 self.holepunch_manager.policy(),
1539 )),
1540 RuntimeConfigValue::String(Self::holepunch_policy_name(
1541 defaults.direct_connect_policy,
1542 )),
1543 RuntimeConfigApplyMode::Immediate,
1544 "Policy for incoming direct-connect proposals.",
1545 )),
1546 #[cfg(feature = "rns-hooks")]
1547 "provider.queue_max_events" => {
1548 let value = self
1549 .provider_bridge
1550 .as_ref()
1551 .map(|b| b.queue_max_events())
1552 .unwrap_or(defaults.provider_queue_max_events);
1553 Some(make_entry(
1554 key,
1555 RuntimeConfigValue::Int(value as i64),
1556 RuntimeConfigValue::Int(defaults.provider_queue_max_events as i64),
1557 RuntimeConfigApplyMode::Immediate,
1558 "Max queued events in the provider bridge.",
1559 ))
1560 }
1561 #[cfg(feature = "rns-hooks")]
1562 "provider.queue_max_bytes" => {
1563 let value = self
1564 .provider_bridge
1565 .as_ref()
1566 .map(|b| b.queue_max_bytes())
1567 .unwrap_or(defaults.provider_queue_max_bytes);
1568 Some(make_entry(
1569 key,
1570 RuntimeConfigValue::Int(value as i64),
1571 RuntimeConfigValue::Int(defaults.provider_queue_max_bytes as i64),
1572 RuntimeConfigApplyMode::Immediate,
1573 "Max queued bytes in the provider bridge.",
1574 ))
1575 }
1576 _ => {
1577 #[cfg(feature = "iface-backbone")]
1578 if let Some(entry) = self.backbone_runtime_entry(key) {
1579 return Some(entry);
1580 }
1581 #[cfg(feature = "iface-backbone")]
1582 if let Some(entry) = self.backbone_client_runtime_entry(key) {
1583 return Some(entry);
1584 }
1585 #[cfg(feature = "iface-tcp")]
1586 if let Some(entry) = self.tcp_server_runtime_entry(key) {
1587 return Some(entry);
1588 }
1589 #[cfg(feature = "iface-tcp")]
1590 if let Some(entry) = self.tcp_client_runtime_entry(key) {
1591 return Some(entry);
1592 }
1593 #[cfg(feature = "iface-udp")]
1594 if let Some(entry) = self.udp_runtime_entry(key) {
1595 return Some(entry);
1596 }
1597 #[cfg(feature = "iface-auto")]
1598 if let Some(entry) = self.auto_runtime_entry(key) {
1599 return Some(entry);
1600 }
1601 #[cfg(feature = "iface-i2p")]
1602 if let Some(entry) = self.i2p_runtime_entry(key) {
1603 return Some(entry);
1604 }
1605 #[cfg(feature = "iface-pipe")]
1606 if let Some(entry) = self.pipe_runtime_entry(key) {
1607 return Some(entry);
1608 }
1609 #[cfg(feature = "iface-rnode")]
1610 if let Some(entry) = self.rnode_runtime_entry(key) {
1611 return Some(entry);
1612 }
1613 if let Some(entry) = self.generic_interface_runtime_entry(key) {
1614 return Some(entry);
1615 }
1616 None
1617 }
1618 }
1619 }
1620
1621 fn list_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
1622 let mut entries: Vec<RuntimeConfigEntry> = [
1623 "global.tick_interval_ms",
1624 "global.known_destinations_ttl_secs",
1625 "global.rate_limiter_ttl_secs",
1626 "global.known_destinations_cleanup_interval_ticks",
1627 "global.announce_cache_cleanup_interval_ticks",
1628 "global.announce_cache_cleanup_batch_size",
1629 "global.discovery_cleanup_interval_ticks",
1630 "global.management_announce_interval_secs",
1631 "global.direct_connect_policy",
1632 ]
1633 .into_iter()
1634 .filter_map(|key| self.runtime_config_entry(key))
1635 .collect();
1636
1637 #[cfg(feature = "rns-hooks")]
1638 {
1639 entries.extend(
1640 ["provider.queue_max_events", "provider.queue_max_bytes"]
1641 .into_iter()
1642 .filter_map(|key| self.runtime_config_entry(key)),
1643 );
1644 }
1645 #[cfg(feature = "iface-backbone")]
1646 {
1647 entries.extend(self.list_backbone_runtime_config());
1648 entries.extend(self.list_backbone_client_runtime_config());
1649 }
1650 #[cfg(feature = "iface-tcp")]
1651 {
1652 entries.extend(self.list_tcp_server_runtime_config());
1653 entries.extend(self.list_tcp_client_runtime_config());
1654 }
1655 #[cfg(feature = "iface-udp")]
1656 {
1657 entries.extend(self.list_udp_runtime_config());
1658 }
1659 #[cfg(feature = "iface-auto")]
1660 {
1661 entries.extend(self.list_auto_runtime_config());
1662 }
1663 #[cfg(feature = "iface-i2p")]
1664 {
1665 entries.extend(self.list_i2p_runtime_config());
1666 }
1667 #[cfg(feature = "iface-pipe")]
1668 {
1669 entries.extend(self.list_pipe_runtime_config());
1670 }
1671 #[cfg(feature = "iface-rnode")]
1672 {
1673 entries.extend(self.list_rnode_runtime_config());
1674 }
1675 entries.extend(self.list_generic_interface_runtime_config());
1676
1677 entries
1678 }
1679
1680 fn holepunch_policy_name(policy: crate::event::HolePunchPolicy) -> String {
1681 match policy {
1682 crate::event::HolePunchPolicy::Reject => "reject".to_string(),
1683 crate::event::HolePunchPolicy::AcceptAll => "accept_all".to_string(),
1684 crate::event::HolePunchPolicy::AskApp => "ask_app".to_string(),
1685 }
1686 }
1687
1688 fn parse_holepunch_policy(value: &RuntimeConfigValue) -> Option<crate::event::HolePunchPolicy> {
1689 match value {
1690 RuntimeConfigValue::String(s) => match s.to_ascii_lowercase().as_str() {
1691 "reject" => Some(crate::event::HolePunchPolicy::Reject),
1692 "accept_all" | "acceptall" => Some(crate::event::HolePunchPolicy::AcceptAll),
1693 "ask_app" | "askapp" => Some(crate::event::HolePunchPolicy::AskApp),
1694 _ => None,
1695 },
1696 _ => None,
1697 }
1698 }
1699
1700 fn expect_u64(value: RuntimeConfigValue, key: &str) -> Result<u64, RuntimeConfigError> {
1701 match value {
1702 RuntimeConfigValue::Int(v) if v >= 0 => Ok(v as u64),
1703 RuntimeConfigValue::Int(_) => Err(RuntimeConfigError {
1704 code: RuntimeConfigErrorCode::InvalidValue,
1705 message: format!("{} must be >= 0", key),
1706 }),
1707 _ => Err(RuntimeConfigError {
1708 code: RuntimeConfigErrorCode::InvalidType,
1709 message: format!("{} expects an integer", key),
1710 }),
1711 }
1712 }
1713
1714 fn expect_f64(value: RuntimeConfigValue, key: &str) -> Result<f64, RuntimeConfigError> {
1715 match value {
1716 RuntimeConfigValue::Float(v) if v >= 0.0 => Ok(v),
1717 RuntimeConfigValue::Int(v) if v >= 0 => Ok(v as f64),
1718 RuntimeConfigValue::Float(_) | RuntimeConfigValue::Int(_) => Err(RuntimeConfigError {
1719 code: RuntimeConfigErrorCode::InvalidValue,
1720 message: format!("{} must be >= 0", key),
1721 }),
1722 _ => Err(RuntimeConfigError {
1723 code: RuntimeConfigErrorCode::InvalidType,
1724 message: format!("{} expects a numeric value", key),
1725 }),
1726 }
1727 }
1728
1729 fn expect_i64(value: RuntimeConfigValue, key: &str) -> Result<i64, RuntimeConfigError> {
1730 match value {
1731 RuntimeConfigValue::Int(v) => Ok(v),
1732 _ => Err(RuntimeConfigError {
1733 code: RuntimeConfigErrorCode::InvalidType,
1734 message: format!("{} expects an integer", key),
1735 }),
1736 }
1737 }
1738
1739 fn expect_bool(value: RuntimeConfigValue, key: &str) -> Result<bool, RuntimeConfigError> {
1740 match value {
1741 RuntimeConfigValue::Bool(v) => Ok(v),
1742 _ => Err(RuntimeConfigError {
1743 code: RuntimeConfigErrorCode::InvalidType,
1744 message: format!("{} expects a boolean", key),
1745 }),
1746 }
1747 }
1748
1749 fn expect_string(value: RuntimeConfigValue, key: &str) -> Result<String, RuntimeConfigError> {
1750 match value {
1751 RuntimeConfigValue::String(v) => Ok(v),
1752 _ => Err(RuntimeConfigError {
1753 code: RuntimeConfigErrorCode::InvalidType,
1754 message: format!("{} expects a string", key),
1755 }),
1756 }
1757 }
1758
1759 fn expect_optional_f64(
1760 value: RuntimeConfigValue,
1761 key: &str,
1762 ) -> Result<Option<f64>, RuntimeConfigError> {
1763 match value {
1764 RuntimeConfigValue::Null => Ok(None),
1765 RuntimeConfigValue::Float(v) => Ok(Some(v)),
1766 RuntimeConfigValue::Int(v) => Ok(Some(v as f64)),
1767 _ => Err(RuntimeConfigError {
1768 code: RuntimeConfigErrorCode::InvalidType,
1769 message: format!("{} expects a numeric value or null", key),
1770 }),
1771 }
1772 }
1773
1774 fn expect_optional_string(
1775 value: RuntimeConfigValue,
1776 key: &str,
1777 ) -> Result<Option<String>, RuntimeConfigError> {
1778 match value {
1779 RuntimeConfigValue::Null => Ok(None),
1780 RuntimeConfigValue::String(v) => Ok(Some(v)),
1781 _ => Err(RuntimeConfigError {
1782 code: RuntimeConfigErrorCode::InvalidType,
1783 message: format!("{} expects a string or null", key),
1784 }),
1785 }
1786 }
1787
1788 #[cfg(feature = "iface-backbone")]
1789 fn split_backbone_runtime_key<'a>(
1790 &self,
1791 key: &'a str,
1792 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
1793 let rest = key.strip_prefix("backbone.").ok_or(RuntimeConfigError {
1794 code: RuntimeConfigErrorCode::UnknownKey,
1795 message: format!("unknown runtime-config key '{}'", key),
1796 })?;
1797 rest.split_once('.').ok_or(RuntimeConfigError {
1798 code: RuntimeConfigErrorCode::UnknownKey,
1799 message: format!("unknown runtime-config key '{}'", key),
1800 })
1801 }
1802
1803 #[cfg(feature = "iface-backbone")]
1804 fn set_optional_duration(
1805 value: RuntimeConfigValue,
1806 key: &str,
1807 ) -> Result<Option<Duration>, RuntimeConfigError> {
1808 let secs = Self::expect_f64(value, key)?;
1809 if secs == 0.0 {
1810 Ok(None)
1811 } else {
1812 Ok(Some(Duration::from_secs_f64(secs)))
1813 }
1814 }
1815
1816 #[cfg(feature = "iface-backbone")]
1817 fn set_optional_usize(
1818 value: RuntimeConfigValue,
1819 key: &str,
1820 ) -> Result<Option<usize>, RuntimeConfigError> {
1821 let raw = Self::expect_u64(value, key)?;
1822 if raw == 0 {
1823 Ok(None)
1824 } else {
1825 Ok(Some(raw as usize))
1826 }
1827 }
1828
1829 #[cfg(feature = "iface-backbone")]
1830 fn set_backbone_runtime_config(
1831 &mut self,
1832 key: &str,
1833 value: RuntimeConfigValue,
1834 ) -> Result<(), RuntimeConfigError> {
1835 let (name, setting) = self.split_backbone_runtime_key(key)?;
1836 if matches!(
1837 setting,
1838 "discoverable"
1839 | "discovery_name"
1840 | "announce_interval_secs"
1841 | "reachable_on"
1842 | "stamp_value"
1843 | "latitude"
1844 | "longitude"
1845 | "height"
1846 ) {
1847 return self.set_backbone_discovery_runtime_config(key, value);
1848 }
1849 let handle = self.backbone_runtime.get(name).ok_or(RuntimeConfigError {
1850 code: RuntimeConfigErrorCode::NotFound,
1851 message: format!("backbone interface '{}' not found", name),
1852 })?;
1853 let mut runtime = handle.runtime.lock().unwrap();
1854 match setting {
1855 "idle_timeout_secs" => {
1856 runtime.idle_timeout = Self::set_optional_duration(value, key)?;
1857 Ok(())
1858 }
1859 "write_stall_timeout_secs" => {
1860 runtime.write_stall_timeout = Self::set_optional_duration(value, key)?;
1861 Ok(())
1862 }
1863 "max_penalty_duration_secs" => {
1864 runtime.abuse.max_penalty_duration = Self::set_optional_duration(value, key)?;
1865 Ok(())
1866 }
1867 "max_connections" => {
1868 runtime.max_connections = Self::set_optional_usize(value, key)?;
1869 Ok(())
1870 }
1871 _ => Err(RuntimeConfigError {
1872 code: RuntimeConfigErrorCode::UnknownKey,
1873 message: format!("unknown runtime-config key '{}'", key),
1874 }),
1875 }
1876 }
1877
1878 #[cfg(feature = "iface-backbone")]
1879 fn split_backbone_discovery_runtime_key<'a>(
1880 &self,
1881 key: &'a str,
1882 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
1883 let rest = key.strip_prefix("backbone.").ok_or(RuntimeConfigError {
1884 code: RuntimeConfigErrorCode::UnknownKey,
1885 message: format!("unknown runtime-config key '{}'", key),
1886 })?;
1887 rest.split_once('.').ok_or(RuntimeConfigError {
1888 code: RuntimeConfigErrorCode::UnknownKey,
1889 message: format!("unknown runtime-config key '{}'", key),
1890 })
1891 }
1892
1893 #[cfg(feature = "iface-backbone")]
1894 fn set_backbone_discovery_runtime_config(
1895 &mut self,
1896 key: &str,
1897 value: RuntimeConfigValue,
1898 ) -> Result<(), RuntimeConfigError> {
1899 let (name, setting) = self.split_backbone_discovery_runtime_key(key)?;
1900 let handle = self
1901 .backbone_discovery_runtime
1902 .get_mut(name)
1903 .ok_or(RuntimeConfigError {
1904 code: RuntimeConfigErrorCode::NotFound,
1905 message: format!("backbone interface '{}' not found", name),
1906 })?;
1907 match setting {
1908 "discoverable" => {
1909 handle.current.discoverable = Self::expect_bool(value, key)?;
1910 }
1911 "discovery_name" => {
1912 handle.current.config.discovery_name = Self::expect_string(value, key)?;
1913 }
1914 "announce_interval_secs" => {
1915 let secs = Self::expect_u64(value, key)?;
1916 if secs < 300 {
1917 return Err(RuntimeConfigError {
1918 code: RuntimeConfigErrorCode::InvalidValue,
1919 message: format!("{} must be >= 300", key),
1920 });
1921 }
1922 handle.current.config.announce_interval = secs;
1923 }
1924 "reachable_on" => {
1925 handle.current.config.reachable_on = Self::expect_optional_string(value, key)?;
1926 }
1927 "stamp_value" => {
1928 let raw = Self::expect_u64(value, key)?;
1929 if raw > u8::MAX as u64 {
1930 return Err(RuntimeConfigError {
1931 code: RuntimeConfigErrorCode::InvalidValue,
1932 message: format!("{} must be <= {}", key, u8::MAX),
1933 });
1934 }
1935 handle.current.config.stamp_value = raw as u8;
1936 }
1937 "latitude" => {
1938 handle.current.config.latitude = Self::expect_optional_f64(value, key)?;
1939 }
1940 "longitude" => {
1941 handle.current.config.longitude = Self::expect_optional_f64(value, key)?;
1942 }
1943 "height" => {
1944 handle.current.config.height = Self::expect_optional_f64(value, key)?;
1945 }
1946 _ => {
1947 return Err(RuntimeConfigError {
1948 code: RuntimeConfigErrorCode::UnknownKey,
1949 message: format!("unknown runtime-config key '{}'", key),
1950 });
1951 }
1952 }
1953 self.sync_backbone_discovery_runtime(name)
1954 }
1955
1956 #[cfg(feature = "iface-backbone")]
1957 fn reset_backbone_discovery_runtime_config(
1958 &mut self,
1959 key: &str,
1960 ) -> Result<(), RuntimeConfigError> {
1961 let (name, setting) = self.split_backbone_discovery_runtime_key(key)?;
1962 let handle = self
1963 .backbone_discovery_runtime
1964 .get_mut(name)
1965 .ok_or(RuntimeConfigError {
1966 code: RuntimeConfigErrorCode::NotFound,
1967 message: format!("backbone interface '{}' not found", name),
1968 })?;
1969 match setting {
1970 "discoverable" => handle.current.discoverable = handle.startup.discoverable,
1971 "discovery_name" => {
1972 handle.current.config.discovery_name = handle.startup.config.discovery_name.clone()
1973 }
1974 "announce_interval_secs" => {
1975 handle.current.config.announce_interval = handle.startup.config.announce_interval
1976 }
1977 "reachable_on" => {
1978 handle.current.config.reachable_on = handle.startup.config.reachable_on.clone()
1979 }
1980 "stamp_value" => handle.current.config.stamp_value = handle.startup.config.stamp_value,
1981 "latitude" => handle.current.config.latitude = handle.startup.config.latitude,
1982 "longitude" => handle.current.config.longitude = handle.startup.config.longitude,
1983 "height" => handle.current.config.height = handle.startup.config.height,
1984 _ => {
1985 return Err(RuntimeConfigError {
1986 code: RuntimeConfigErrorCode::UnknownKey,
1987 message: format!("unknown runtime-config key '{}'", key),
1988 });
1989 }
1990 }
1991 self.sync_backbone_discovery_runtime(name)
1992 }
1993
1994 #[cfg(feature = "iface-backbone")]
1995 fn reset_backbone_runtime_config(&mut self, key: &str) -> Result<(), RuntimeConfigError> {
1996 let (name, setting) = self.split_backbone_runtime_key(key)?;
1997 if matches!(
1998 setting,
1999 "discoverable"
2000 | "discovery_name"
2001 | "announce_interval_secs"
2002 | "reachable_on"
2003 | "stamp_value"
2004 | "latitude"
2005 | "longitude"
2006 | "height"
2007 ) {
2008 return self.reset_backbone_discovery_runtime_config(key);
2009 }
2010 let handle = self.backbone_runtime.get(name).ok_or(RuntimeConfigError {
2011 code: RuntimeConfigErrorCode::NotFound,
2012 message: format!("backbone interface '{}' not found", name),
2013 })?;
2014 let mut runtime = handle.runtime.lock().unwrap();
2015 let startup = handle.startup.clone();
2016 match setting {
2017 "idle_timeout_secs" => runtime.idle_timeout = startup.idle_timeout,
2018 "write_stall_timeout_secs" => runtime.write_stall_timeout = startup.write_stall_timeout,
2019 "max_penalty_duration_secs" => {
2020 runtime.abuse.max_penalty_duration = startup.abuse.max_penalty_duration
2021 }
2022 "max_connections" => runtime.max_connections = startup.max_connections,
2023 _ => {
2024 return Err(RuntimeConfigError {
2025 code: RuntimeConfigErrorCode::UnknownKey,
2026 message: format!("unknown runtime-config key '{}'", key),
2027 })
2028 }
2029 }
2030 Ok(())
2031 }
2032
2033 #[cfg(feature = "iface-backbone")]
2034 fn list_backbone_client_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
2035 let mut entries = Vec::new();
2036 let mut names: Vec<&String> = self.backbone_client_runtime.keys().collect();
2037 names.sort();
2038 for name in names {
2039 for suffix in [
2040 "connect_timeout_secs",
2041 "reconnect_wait_secs",
2042 "max_reconnect_tries",
2043 ] {
2044 let key = format!("backbone_client.{}.{}", name, suffix);
2045 if let Some(entry) = self.backbone_client_runtime_entry(&key) {
2046 entries.push(entry);
2047 }
2048 }
2049 }
2050 entries
2051 }
2052
2053 #[cfg(feature = "iface-backbone")]
2054 fn backbone_client_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
2055 let rest = key.strip_prefix("backbone_client.")?;
2056 let (name, setting) = rest.split_once('.')?;
2057 let handle = self.backbone_client_runtime.get(name)?;
2058 let current = handle.runtime.lock().unwrap().clone();
2059 let startup = handle.startup.clone();
2060 let make_entry = |value: RuntimeConfigValue,
2061 default: RuntimeConfigValue,
2062 description: &str|
2063 -> RuntimeConfigEntry {
2064 RuntimeConfigEntry {
2065 key: key.to_string(),
2066 source: if value == default {
2067 RuntimeConfigSource::Startup
2068 } else {
2069 RuntimeConfigSource::RuntimeOverride
2070 },
2071 value,
2072 default,
2073 apply_mode: RuntimeConfigApplyMode::NextReconnect,
2074 description: Some(description.to_string()),
2075 }
2076 };
2077 match setting {
2078 "connect_timeout_secs" => Some(make_entry(
2079 RuntimeConfigValue::Float(current.connect_timeout.as_secs_f64()),
2080 RuntimeConfigValue::Float(startup.connect_timeout.as_secs_f64()),
2081 "Backbone client connect timeout in seconds; applies on the next reconnect.",
2082 )),
2083 "reconnect_wait_secs" => Some(make_entry(
2084 RuntimeConfigValue::Float(current.reconnect_wait.as_secs_f64()),
2085 RuntimeConfigValue::Float(startup.reconnect_wait.as_secs_f64()),
2086 "Delay between backbone client reconnect attempts in seconds.",
2087 )),
2088 "max_reconnect_tries" => Some(make_entry(
2089 RuntimeConfigValue::Int(current.max_reconnect_tries.unwrap_or(0) as i64),
2090 RuntimeConfigValue::Int(startup.max_reconnect_tries.unwrap_or(0) as i64),
2091 "Maximum backbone client reconnect attempts; 0 disables the cap.",
2092 )),
2093 _ => None,
2094 }
2095 }
2096
2097 #[cfg(feature = "iface-backbone")]
2098 fn split_backbone_client_runtime_key<'a>(
2099 &self,
2100 key: &'a str,
2101 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
2102 let rest = key
2103 .strip_prefix("backbone_client.")
2104 .ok_or(RuntimeConfigError {
2105 code: RuntimeConfigErrorCode::UnknownKey,
2106 message: format!("unknown runtime-config key '{}'", key),
2107 })?;
2108 rest.split_once('.').ok_or(RuntimeConfigError {
2109 code: RuntimeConfigErrorCode::UnknownKey,
2110 message: format!("unknown runtime-config key '{}'", key),
2111 })
2112 }
2113
2114 #[cfg(feature = "iface-backbone")]
2115 fn set_backbone_client_runtime_config(
2116 &mut self,
2117 key: &str,
2118 value: RuntimeConfigValue,
2119 ) -> Result<(), RuntimeConfigError> {
2120 let (name, setting) = self.split_backbone_client_runtime_key(key)?;
2121 let handle = self
2122 .backbone_client_runtime
2123 .get(name)
2124 .ok_or(RuntimeConfigError {
2125 code: RuntimeConfigErrorCode::NotFound,
2126 message: format!("backbone client interface '{}' not found", name),
2127 })?;
2128 let mut runtime = handle.runtime.lock().unwrap();
2129 match setting {
2130 "connect_timeout_secs" => {
2131 runtime.connect_timeout = Duration::from_secs_f64(Self::expect_f64(value, key)?);
2132 Ok(())
2133 }
2134 "reconnect_wait_secs" => {
2135 runtime.reconnect_wait = Duration::from_secs_f64(Self::expect_f64(value, key)?);
2136 Ok(())
2137 }
2138 "max_reconnect_tries" => {
2139 runtime.max_reconnect_tries = match Self::expect_u64(value, key)? {
2140 0 => None,
2141 raw => Some(raw as u32),
2142 };
2143 Ok(())
2144 }
2145 _ => Err(RuntimeConfigError {
2146 code: RuntimeConfigErrorCode::UnknownKey,
2147 message: format!("unknown runtime-config key '{}'", key),
2148 }),
2149 }
2150 }
2151
2152 #[cfg(feature = "iface-backbone")]
2153 fn reset_backbone_client_runtime_config(
2154 &mut self,
2155 key: &str,
2156 ) -> Result<(), RuntimeConfigError> {
2157 let (name, setting) = self.split_backbone_client_runtime_key(key)?;
2158 let handle = self
2159 .backbone_client_runtime
2160 .get(name)
2161 .ok_or(RuntimeConfigError {
2162 code: RuntimeConfigErrorCode::NotFound,
2163 message: format!("backbone client interface '{}' not found", name),
2164 })?;
2165 let mut runtime = handle.runtime.lock().unwrap();
2166 let startup = handle.startup.clone();
2167 match setting {
2168 "connect_timeout_secs" => runtime.connect_timeout = startup.connect_timeout,
2169 "reconnect_wait_secs" => runtime.reconnect_wait = startup.reconnect_wait,
2170 "max_reconnect_tries" => runtime.max_reconnect_tries = startup.max_reconnect_tries,
2171 _ => {
2172 return Err(RuntimeConfigError {
2173 code: RuntimeConfigErrorCode::UnknownKey,
2174 message: format!("unknown runtime-config key '{}'", key),
2175 })
2176 }
2177 }
2178 Ok(())
2179 }
2180
2181 #[cfg(feature = "iface-tcp")]
2182 fn list_tcp_server_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
2183 let mut entries = Vec::new();
2184 let mut names: Vec<&String> = self.tcp_server_runtime.keys().collect();
2185 names.sort();
2186 for name in names {
2187 for suffix in [
2188 "max_connections",
2189 "discoverable",
2190 "discovery_name",
2191 "announce_interval_secs",
2192 "reachable_on",
2193 "stamp_value",
2194 "latitude",
2195 "longitude",
2196 "height",
2197 ] {
2198 let key = format!("tcp_server.{}.{}", name, suffix);
2199 if let Some(entry) = self.tcp_server_runtime_entry(&key) {
2200 entries.push(entry);
2201 }
2202 }
2203 }
2204 entries
2205 }
2206
2207 #[cfg(feature = "iface-tcp")]
2208 fn list_tcp_client_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
2209 let mut entries = Vec::new();
2210 let mut names: Vec<&String> = self.tcp_client_runtime.keys().collect();
2211 names.sort();
2212 for name in names {
2213 for suffix in [
2214 "connect_timeout_secs",
2215 "reconnect_wait_secs",
2216 "max_reconnect_tries",
2217 ] {
2218 let key = format!("tcp_client.{}.{}", name, suffix);
2219 if let Some(entry) = self.tcp_client_runtime_entry(&key) {
2220 entries.push(entry);
2221 }
2222 }
2223 }
2224 entries
2225 }
2226
2227 #[cfg(feature = "iface-tcp")]
2228 fn tcp_client_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
2229 let rest = key.strip_prefix("tcp_client.")?;
2230 let (name, setting) = rest.split_once('.')?;
2231 let handle = self.tcp_client_runtime.get(name)?;
2232 let current = handle.runtime.lock().unwrap().clone();
2233 let startup = handle.startup.clone();
2234 let make_entry = |value: RuntimeConfigValue,
2235 default: RuntimeConfigValue,
2236 description: &str|
2237 -> RuntimeConfigEntry {
2238 RuntimeConfigEntry {
2239 key: key.to_string(),
2240 source: if value == default {
2241 RuntimeConfigSource::Startup
2242 } else {
2243 RuntimeConfigSource::RuntimeOverride
2244 },
2245 value,
2246 default,
2247 apply_mode: RuntimeConfigApplyMode::NextReconnect,
2248 description: Some(description.to_string()),
2249 }
2250 };
2251 match setting {
2252 "connect_timeout_secs" => Some(make_entry(
2253 RuntimeConfigValue::Float(current.connect_timeout.as_secs_f64()),
2254 RuntimeConfigValue::Float(startup.connect_timeout.as_secs_f64()),
2255 "TCP client connect timeout in seconds; applies on the next reconnect.",
2256 )),
2257 "reconnect_wait_secs" => Some(make_entry(
2258 RuntimeConfigValue::Float(current.reconnect_wait.as_secs_f64()),
2259 RuntimeConfigValue::Float(startup.reconnect_wait.as_secs_f64()),
2260 "Delay between TCP client reconnect attempts in seconds.",
2261 )),
2262 "max_reconnect_tries" => Some(make_entry(
2263 RuntimeConfigValue::Int(current.max_reconnect_tries.unwrap_or(0) as i64),
2264 RuntimeConfigValue::Int(startup.max_reconnect_tries.unwrap_or(0) as i64),
2265 "Maximum TCP client reconnect attempts; 0 disables the cap.",
2266 )),
2267 _ => None,
2268 }
2269 }
2270
2271 #[cfg(feature = "iface-tcp")]
2272 fn split_tcp_client_runtime_key<'a>(
2273 &self,
2274 key: &'a str,
2275 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
2276 let rest = key.strip_prefix("tcp_client.").ok_or(RuntimeConfigError {
2277 code: RuntimeConfigErrorCode::UnknownKey,
2278 message: format!("unknown runtime-config key '{}'", key),
2279 })?;
2280 rest.split_once('.').ok_or(RuntimeConfigError {
2281 code: RuntimeConfigErrorCode::UnknownKey,
2282 message: format!("unknown runtime-config key '{}'", key),
2283 })
2284 }
2285
2286 #[cfg(feature = "iface-tcp")]
2287 fn set_tcp_client_runtime_config(
2288 &mut self,
2289 key: &str,
2290 value: RuntimeConfigValue,
2291 ) -> Result<(), RuntimeConfigError> {
2292 let (name, setting) = self.split_tcp_client_runtime_key(key)?;
2293 let handle = self
2294 .tcp_client_runtime
2295 .get(name)
2296 .ok_or(RuntimeConfigError {
2297 code: RuntimeConfigErrorCode::NotFound,
2298 message: format!("tcp client interface '{}' not found", name),
2299 })?;
2300 let mut runtime = handle.runtime.lock().unwrap();
2301 match setting {
2302 "connect_timeout_secs" => {
2303 runtime.connect_timeout = Duration::from_secs_f64(Self::expect_f64(value, key)?);
2304 Ok(())
2305 }
2306 "reconnect_wait_secs" => {
2307 runtime.reconnect_wait = Duration::from_secs_f64(Self::expect_f64(value, key)?);
2308 Ok(())
2309 }
2310 "max_reconnect_tries" => {
2311 runtime.max_reconnect_tries = match Self::expect_u64(value, key)? {
2312 0 => None,
2313 raw => Some(raw as u32),
2314 };
2315 Ok(())
2316 }
2317 _ => Err(RuntimeConfigError {
2318 code: RuntimeConfigErrorCode::UnknownKey,
2319 message: format!("unknown runtime-config key '{}'", key),
2320 }),
2321 }
2322 }
2323
2324 #[cfg(feature = "iface-tcp")]
2325 fn reset_tcp_client_runtime_config(&mut self, key: &str) -> Result<(), RuntimeConfigError> {
2326 let (name, setting) = self.split_tcp_client_runtime_key(key)?;
2327 let handle = self
2328 .tcp_client_runtime
2329 .get(name)
2330 .ok_or(RuntimeConfigError {
2331 code: RuntimeConfigErrorCode::NotFound,
2332 message: format!("tcp client interface '{}' not found", name),
2333 })?;
2334 let mut runtime = handle.runtime.lock().unwrap();
2335 let startup = handle.startup.clone();
2336 match setting {
2337 "connect_timeout_secs" => runtime.connect_timeout = startup.connect_timeout,
2338 "reconnect_wait_secs" => runtime.reconnect_wait = startup.reconnect_wait,
2339 "max_reconnect_tries" => runtime.max_reconnect_tries = startup.max_reconnect_tries,
2340 _ => {
2341 return Err(RuntimeConfigError {
2342 code: RuntimeConfigErrorCode::UnknownKey,
2343 message: format!("unknown runtime-config key '{}'", key),
2344 })
2345 }
2346 }
2347 Ok(())
2348 }
2349
2350 #[cfg(feature = "iface-udp")]
2351 fn list_udp_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
2352 let mut entries = Vec::new();
2353 let mut names: Vec<&String> = self.udp_runtime.keys().collect();
2354 names.sort();
2355 for name in names {
2356 for suffix in ["forward_ip", "forward_port"] {
2357 let key = format!("udp.{}.{}", name, suffix);
2358 if let Some(entry) = self.udp_runtime_entry(&key) {
2359 entries.push(entry);
2360 }
2361 }
2362 }
2363 entries
2364 }
2365
2366 #[cfg(feature = "iface-udp")]
2367 fn udp_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
2368 let rest = key.strip_prefix("udp.")?;
2369 let (name, setting) = rest.split_once('.')?;
2370 let handle = self.udp_runtime.get(name)?;
2371 let current = handle.runtime.lock().unwrap().clone();
2372 let startup = handle.startup.clone();
2373 let make_entry = |value: RuntimeConfigValue,
2374 default: RuntimeConfigValue,
2375 description: &str|
2376 -> RuntimeConfigEntry {
2377 RuntimeConfigEntry {
2378 key: key.to_string(),
2379 source: if value == default {
2380 RuntimeConfigSource::Startup
2381 } else {
2382 RuntimeConfigSource::RuntimeOverride
2383 },
2384 value,
2385 default,
2386 apply_mode: RuntimeConfigApplyMode::Immediate,
2387 description: Some(description.to_string()),
2388 }
2389 };
2390 match setting {
2391 "forward_ip" => Some(make_entry(
2392 current
2393 .forward_ip
2394 .clone()
2395 .map(RuntimeConfigValue::String)
2396 .unwrap_or(RuntimeConfigValue::Null),
2397 startup
2398 .forward_ip
2399 .clone()
2400 .map(RuntimeConfigValue::String)
2401 .unwrap_or(RuntimeConfigValue::Null),
2402 "Outbound UDP destination IP or hostname; null clears it.",
2403 )),
2404 "forward_port" => Some(make_entry(
2405 current
2406 .forward_port
2407 .map(|value| RuntimeConfigValue::Int(value as i64))
2408 .unwrap_or(RuntimeConfigValue::Null),
2409 startup
2410 .forward_port
2411 .map(|value| RuntimeConfigValue::Int(value as i64))
2412 .unwrap_or(RuntimeConfigValue::Null),
2413 "Outbound UDP destination port; null clears it.",
2414 )),
2415 _ => None,
2416 }
2417 }
2418
2419 #[cfg(feature = "iface-udp")]
2420 fn split_udp_runtime_key<'a>(
2421 &self,
2422 key: &'a str,
2423 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
2424 let rest = key.strip_prefix("udp.").ok_or(RuntimeConfigError {
2425 code: RuntimeConfigErrorCode::UnknownKey,
2426 message: format!("unknown runtime-config key '{}'", key),
2427 })?;
2428 rest.split_once('.').ok_or(RuntimeConfigError {
2429 code: RuntimeConfigErrorCode::UnknownKey,
2430 message: format!("unknown runtime-config key '{}'", key),
2431 })
2432 }
2433
2434 #[cfg(feature = "iface-udp")]
2435 fn set_udp_runtime_config(
2436 &mut self,
2437 key: &str,
2438 value: RuntimeConfigValue,
2439 ) -> Result<(), RuntimeConfigError> {
2440 let (name, setting) = self.split_udp_runtime_key(key)?;
2441 let handle = self.udp_runtime.get(name).ok_or(RuntimeConfigError {
2442 code: RuntimeConfigErrorCode::NotFound,
2443 message: format!("udp interface '{}' not found", name),
2444 })?;
2445 let mut runtime = handle.runtime.lock().unwrap();
2446 match setting {
2447 "forward_ip" => {
2448 runtime.forward_ip = Self::expect_optional_string(value, key)?;
2449 Ok(())
2450 }
2451 "forward_port" => {
2452 runtime.forward_port = match value {
2453 RuntimeConfigValue::Null => None,
2454 other => Some(Self::expect_u64(other, key)? as u16),
2455 };
2456 Ok(())
2457 }
2458 _ => Err(RuntimeConfigError {
2459 code: RuntimeConfigErrorCode::UnknownKey,
2460 message: format!("unknown runtime-config key '{}'", key),
2461 }),
2462 }
2463 }
2464
2465 #[cfg(feature = "iface-udp")]
2466 fn reset_udp_runtime_config(&mut self, key: &str) -> Result<(), RuntimeConfigError> {
2467 let (name, setting) = self.split_udp_runtime_key(key)?;
2468 let handle = self.udp_runtime.get(name).ok_or(RuntimeConfigError {
2469 code: RuntimeConfigErrorCode::NotFound,
2470 message: format!("udp interface '{}' not found", name),
2471 })?;
2472 let mut runtime = handle.runtime.lock().unwrap();
2473 let startup = handle.startup.clone();
2474 match setting {
2475 "forward_ip" => runtime.forward_ip = startup.forward_ip,
2476 "forward_port" => runtime.forward_port = startup.forward_port,
2477 _ => {
2478 return Err(RuntimeConfigError {
2479 code: RuntimeConfigErrorCode::UnknownKey,
2480 message: format!("unknown runtime-config key '{}'", key),
2481 })
2482 }
2483 }
2484 Ok(())
2485 }
2486
2487 #[cfg(feature = "iface-auto")]
2488 fn list_auto_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
2489 let mut entries = Vec::new();
2490 let mut names: Vec<&String> = self.auto_runtime.keys().collect();
2491 names.sort();
2492 for name in names {
2493 for suffix in [
2494 "announce_interval_secs",
2495 "peer_timeout_secs",
2496 "peer_job_interval_secs",
2497 ] {
2498 let key = format!("auto.{}.{}", name, suffix);
2499 if let Some(entry) = self.auto_runtime_entry(&key) {
2500 entries.push(entry);
2501 }
2502 }
2503 }
2504 entries
2505 }
2506
2507 #[cfg(feature = "iface-auto")]
2508 fn auto_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
2509 let rest = key.strip_prefix("auto.")?;
2510 let (name, setting) = rest.split_once('.')?;
2511 let handle = self.auto_runtime.get(name)?;
2512 let current = handle.runtime.lock().unwrap().clone();
2513 let startup = handle.startup.clone();
2514 let make_entry = |value: RuntimeConfigValue,
2515 default: RuntimeConfigValue,
2516 description: &str|
2517 -> RuntimeConfigEntry {
2518 RuntimeConfigEntry {
2519 key: key.to_string(),
2520 source: if value == default {
2521 RuntimeConfigSource::Startup
2522 } else {
2523 RuntimeConfigSource::RuntimeOverride
2524 },
2525 value,
2526 default,
2527 apply_mode: RuntimeConfigApplyMode::Immediate,
2528 description: Some(description.to_string()),
2529 }
2530 };
2531 match setting {
2532 "announce_interval_secs" => Some(make_entry(
2533 RuntimeConfigValue::Float(current.announce_interval_secs),
2534 RuntimeConfigValue::Float(startup.announce_interval_secs),
2535 "Interval between multicast discovery announces in seconds.",
2536 )),
2537 "peer_timeout_secs" => Some(make_entry(
2538 RuntimeConfigValue::Float(current.peer_timeout_secs),
2539 RuntimeConfigValue::Float(startup.peer_timeout_secs),
2540 "How long an Auto peer may stay quiet before being culled.",
2541 )),
2542 "peer_job_interval_secs" => Some(make_entry(
2543 RuntimeConfigValue::Float(current.peer_job_interval_secs),
2544 RuntimeConfigValue::Float(startup.peer_job_interval_secs),
2545 "Interval between Auto peer maintenance passes.",
2546 )),
2547 _ => None,
2548 }
2549 }
2550
2551 #[cfg(feature = "iface-auto")]
2552 fn split_auto_runtime_key<'a>(
2553 &self,
2554 key: &'a str,
2555 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
2556 let rest = key.strip_prefix("auto.").ok_or(RuntimeConfigError {
2557 code: RuntimeConfigErrorCode::UnknownKey,
2558 message: format!("unknown runtime-config key '{}'", key),
2559 })?;
2560 rest.split_once('.').ok_or(RuntimeConfigError {
2561 code: RuntimeConfigErrorCode::UnknownKey,
2562 message: format!("unknown runtime-config key '{}'", key),
2563 })
2564 }
2565
2566 #[cfg(feature = "iface-auto")]
2567 fn set_auto_runtime_config(
2568 &mut self,
2569 key: &str,
2570 value: RuntimeConfigValue,
2571 ) -> Result<(), RuntimeConfigError> {
2572 let (name, setting) = self.split_auto_runtime_key(key)?;
2573 let handle = self.auto_runtime.get(name).ok_or(RuntimeConfigError {
2574 code: RuntimeConfigErrorCode::NotFound,
2575 message: format!("auto interface '{}' not found", name),
2576 })?;
2577 let mut runtime = handle.runtime.lock().unwrap();
2578 match setting {
2579 "announce_interval_secs" => {
2580 runtime.announce_interval_secs = Self::expect_f64(value, key)?.max(0.1)
2581 }
2582 "peer_timeout_secs" => {
2583 runtime.peer_timeout_secs = Self::expect_f64(value, key)?.max(0.1)
2584 }
2585 "peer_job_interval_secs" => {
2586 runtime.peer_job_interval_secs = Self::expect_f64(value, key)?.max(0.1)
2587 }
2588 _ => {
2589 return Err(RuntimeConfigError {
2590 code: RuntimeConfigErrorCode::UnknownKey,
2591 message: format!("unknown runtime-config key '{}'", key),
2592 });
2593 }
2594 }
2595 Ok(())
2596 }
2597
2598 #[cfg(feature = "iface-auto")]
2599 fn reset_auto_runtime_config(&mut self, key: &str) -> Result<(), RuntimeConfigError> {
2600 let (name, setting) = self.split_auto_runtime_key(key)?;
2601 let handle = self.auto_runtime.get(name).ok_or(RuntimeConfigError {
2602 code: RuntimeConfigErrorCode::NotFound,
2603 message: format!("auto interface '{}' not found", name),
2604 })?;
2605 let mut runtime = handle.runtime.lock().unwrap();
2606 let startup = handle.startup.clone();
2607 match setting {
2608 "announce_interval_secs" => {
2609 runtime.announce_interval_secs = startup.announce_interval_secs
2610 }
2611 "peer_timeout_secs" => runtime.peer_timeout_secs = startup.peer_timeout_secs,
2612 "peer_job_interval_secs" => {
2613 runtime.peer_job_interval_secs = startup.peer_job_interval_secs
2614 }
2615 _ => {
2616 return Err(RuntimeConfigError {
2617 code: RuntimeConfigErrorCode::UnknownKey,
2618 message: format!("unknown runtime-config key '{}'", key),
2619 });
2620 }
2621 }
2622 Ok(())
2623 }
2624
2625 #[cfg(feature = "iface-i2p")]
2626 fn list_i2p_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
2627 let mut entries = Vec::new();
2628 let mut names: Vec<&String> = self.i2p_runtime.keys().collect();
2629 names.sort();
2630 for name in names {
2631 let key = format!("i2p.{}.reconnect_wait_secs", name);
2632 if let Some(entry) = self.i2p_runtime_entry(&key) {
2633 entries.push(entry);
2634 }
2635 }
2636 entries
2637 }
2638
2639 #[cfg(feature = "iface-i2p")]
2640 fn i2p_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
2641 let rest = key.strip_prefix("i2p.")?;
2642 let (name, setting) = rest.split_once('.')?;
2643 let handle = self.i2p_runtime.get(name)?;
2644 let current = handle.runtime.lock().unwrap().clone();
2645 let startup = handle.startup.clone();
2646 match setting {
2647 "reconnect_wait_secs" => Some(RuntimeConfigEntry {
2648 key: key.to_string(),
2649 source: if current.reconnect_wait == startup.reconnect_wait {
2650 RuntimeConfigSource::Startup
2651 } else {
2652 RuntimeConfigSource::RuntimeOverride
2653 },
2654 value: RuntimeConfigValue::Float(current.reconnect_wait.as_secs_f64()),
2655 default: RuntimeConfigValue::Float(startup.reconnect_wait.as_secs_f64()),
2656 apply_mode: RuntimeConfigApplyMode::NextReconnect,
2657 description: Some(
2658 "Delay before retrying outbound I2P peer connections.".to_string(),
2659 ),
2660 }),
2661 _ => None,
2662 }
2663 }
2664
2665 #[cfg(feature = "iface-i2p")]
2666 fn split_i2p_runtime_key<'a>(
2667 &self,
2668 key: &'a str,
2669 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
2670 let rest = key.strip_prefix("i2p.").ok_or(RuntimeConfigError {
2671 code: RuntimeConfigErrorCode::UnknownKey,
2672 message: format!("unknown runtime-config key '{}'", key),
2673 })?;
2674 rest.split_once('.').ok_or(RuntimeConfigError {
2675 code: RuntimeConfigErrorCode::UnknownKey,
2676 message: format!("unknown runtime-config key '{}'", key),
2677 })
2678 }
2679
2680 #[cfg(feature = "iface-i2p")]
2681 fn set_i2p_runtime_config(
2682 &mut self,
2683 key: &str,
2684 value: RuntimeConfigValue,
2685 ) -> Result<(), RuntimeConfigError> {
2686 let (name, setting) = self.split_i2p_runtime_key(key)?;
2687 let handle = self.i2p_runtime.get(name).ok_or(RuntimeConfigError {
2688 code: RuntimeConfigErrorCode::NotFound,
2689 message: format!("i2p interface '{}' not found", name),
2690 })?;
2691 let mut runtime = handle.runtime.lock().unwrap();
2692 match setting {
2693 "reconnect_wait_secs" => {
2694 runtime.reconnect_wait =
2695 Duration::from_secs_f64(Self::expect_f64(value, key)?.max(0.1));
2696 Ok(())
2697 }
2698 _ => Err(RuntimeConfigError {
2699 code: RuntimeConfigErrorCode::UnknownKey,
2700 message: format!("unknown runtime-config key '{}'", key),
2701 }),
2702 }
2703 }
2704
2705 #[cfg(feature = "iface-i2p")]
2706 fn reset_i2p_runtime_config(&mut self, key: &str) -> Result<(), RuntimeConfigError> {
2707 let (name, setting) = self.split_i2p_runtime_key(key)?;
2708 let handle = self.i2p_runtime.get(name).ok_or(RuntimeConfigError {
2709 code: RuntimeConfigErrorCode::NotFound,
2710 message: format!("i2p interface '{}' not found", name),
2711 })?;
2712 let mut runtime = handle.runtime.lock().unwrap();
2713 let startup = handle.startup.clone();
2714 match setting {
2715 "reconnect_wait_secs" => runtime.reconnect_wait = startup.reconnect_wait,
2716 _ => {
2717 return Err(RuntimeConfigError {
2718 code: RuntimeConfigErrorCode::UnknownKey,
2719 message: format!("unknown runtime-config key '{}'", key),
2720 });
2721 }
2722 }
2723 Ok(())
2724 }
2725
2726 #[cfg(feature = "iface-pipe")]
2727 fn list_pipe_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
2728 let mut entries = Vec::new();
2729 let mut names: Vec<&String> = self.pipe_runtime.keys().collect();
2730 names.sort();
2731 for name in names {
2732 let key = format!("pipe.{}.respawn_delay_secs", name);
2733 if let Some(entry) = self.pipe_runtime_entry(&key) {
2734 entries.push(entry);
2735 }
2736 }
2737 entries
2738 }
2739
2740 #[cfg(feature = "iface-pipe")]
2741 fn pipe_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
2742 let rest = key.strip_prefix("pipe.")?;
2743 let (name, setting) = rest.split_once('.')?;
2744 let handle = self.pipe_runtime.get(name)?;
2745 let current = handle.runtime.lock().unwrap().clone();
2746 let startup = handle.startup.clone();
2747 match setting {
2748 "respawn_delay_secs" => Some(RuntimeConfigEntry {
2749 key: key.to_string(),
2750 source: if current.respawn_delay == startup.respawn_delay {
2751 RuntimeConfigSource::Startup
2752 } else {
2753 RuntimeConfigSource::RuntimeOverride
2754 },
2755 value: RuntimeConfigValue::Float(current.respawn_delay.as_secs_f64()),
2756 default: RuntimeConfigValue::Float(startup.respawn_delay.as_secs_f64()),
2757 apply_mode: RuntimeConfigApplyMode::NextReconnect,
2758 description: Some(
2759 "Delay before respawning the pipe subprocess after exit.".to_string(),
2760 ),
2761 }),
2762 _ => None,
2763 }
2764 }
2765
2766 #[cfg(feature = "iface-pipe")]
2767 fn split_pipe_runtime_key<'a>(
2768 &self,
2769 key: &'a str,
2770 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
2771 let rest = key.strip_prefix("pipe.").ok_or(RuntimeConfigError {
2772 code: RuntimeConfigErrorCode::UnknownKey,
2773 message: format!("unknown runtime-config key '{}'", key),
2774 })?;
2775 rest.split_once('.').ok_or(RuntimeConfigError {
2776 code: RuntimeConfigErrorCode::UnknownKey,
2777 message: format!("unknown runtime-config key '{}'", key),
2778 })
2779 }
2780
2781 #[cfg(feature = "iface-pipe")]
2782 fn set_pipe_runtime_config(
2783 &mut self,
2784 key: &str,
2785 value: RuntimeConfigValue,
2786 ) -> Result<(), RuntimeConfigError> {
2787 let (name, setting) = self.split_pipe_runtime_key(key)?;
2788 let handle = self.pipe_runtime.get(name).ok_or(RuntimeConfigError {
2789 code: RuntimeConfigErrorCode::NotFound,
2790 message: format!("pipe interface '{}' not found", name),
2791 })?;
2792 let mut runtime = handle.runtime.lock().unwrap();
2793 match setting {
2794 "respawn_delay_secs" => {
2795 runtime.respawn_delay =
2796 Duration::from_secs_f64(Self::expect_f64(value, key)?.max(0.1));
2797 Ok(())
2798 }
2799 _ => Err(RuntimeConfigError {
2800 code: RuntimeConfigErrorCode::UnknownKey,
2801 message: format!("unknown runtime-config key '{}'", key),
2802 }),
2803 }
2804 }
2805
2806 #[cfg(feature = "iface-pipe")]
2807 fn reset_pipe_runtime_config(&mut self, key: &str) -> Result<(), RuntimeConfigError> {
2808 let (name, setting) = self.split_pipe_runtime_key(key)?;
2809 let handle = self.pipe_runtime.get(name).ok_or(RuntimeConfigError {
2810 code: RuntimeConfigErrorCode::NotFound,
2811 message: format!("pipe interface '{}' not found", name),
2812 })?;
2813 let mut runtime = handle.runtime.lock().unwrap();
2814 let startup = handle.startup.clone();
2815 match setting {
2816 "respawn_delay_secs" => runtime.respawn_delay = startup.respawn_delay,
2817 _ => {
2818 return Err(RuntimeConfigError {
2819 code: RuntimeConfigErrorCode::UnknownKey,
2820 message: format!("unknown runtime-config key '{}'", key),
2821 });
2822 }
2823 }
2824 Ok(())
2825 }
2826
2827 #[cfg(feature = "iface-rnode")]
2828 fn list_rnode_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
2829 let mut entries = Vec::new();
2830 let mut names: Vec<&String> = self.rnode_runtime.keys().collect();
2831 names.sort();
2832 for name in names {
2833 for suffix in [
2834 "frequency_hz",
2835 "bandwidth_hz",
2836 "txpower_dbm",
2837 "spreading_factor",
2838 "coding_rate",
2839 "st_alock_pct",
2840 "lt_alock_pct",
2841 ] {
2842 let key = format!("rnode.{}.{}", name, suffix);
2843 if let Some(entry) = self.rnode_runtime_entry(&key) {
2844 entries.push(entry);
2845 }
2846 }
2847 }
2848 entries
2849 }
2850
2851 #[cfg(feature = "iface-rnode")]
2852 fn rnode_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
2853 let rest = key.strip_prefix("rnode.")?;
2854 let (name, setting) = rest.split_once('.')?;
2855 let handle = self.rnode_runtime.get(name)?;
2856 let current = handle.runtime.lock().unwrap().clone();
2857 let startup = handle.startup.clone();
2858 let make_entry = |value: RuntimeConfigValue,
2859 default: RuntimeConfigValue,
2860 description: &str|
2861 -> RuntimeConfigEntry {
2862 RuntimeConfigEntry {
2863 key: key.to_string(),
2864 source: if value == default {
2865 RuntimeConfigSource::Startup
2866 } else {
2867 RuntimeConfigSource::RuntimeOverride
2868 },
2869 value,
2870 default,
2871 apply_mode: RuntimeConfigApplyMode::Immediate,
2872 description: Some(description.to_string()),
2873 }
2874 };
2875 match setting {
2876 "frequency_hz" => Some(make_entry(
2877 RuntimeConfigValue::Int(current.sub.frequency as i64),
2878 RuntimeConfigValue::Int(startup.sub.frequency as i64),
2879 "RNode radio frequency in Hz.",
2880 )),
2881 "bandwidth_hz" => Some(make_entry(
2882 RuntimeConfigValue::Int(current.sub.bandwidth as i64),
2883 RuntimeConfigValue::Int(startup.sub.bandwidth as i64),
2884 "RNode radio bandwidth in Hz.",
2885 )),
2886 "txpower_dbm" => Some(make_entry(
2887 RuntimeConfigValue::Int(current.sub.txpower as i64),
2888 RuntimeConfigValue::Int(startup.sub.txpower as i64),
2889 "RNode transmit power in dBm.",
2890 )),
2891 "spreading_factor" => Some(make_entry(
2892 RuntimeConfigValue::Int(current.sub.spreading_factor as i64),
2893 RuntimeConfigValue::Int(startup.sub.spreading_factor as i64),
2894 "RNode LoRa spreading factor.",
2895 )),
2896 "coding_rate" => Some(make_entry(
2897 RuntimeConfigValue::Int(current.sub.coding_rate as i64),
2898 RuntimeConfigValue::Int(startup.sub.coding_rate as i64),
2899 "RNode LoRa coding rate.",
2900 )),
2901 "st_alock_pct" => Some(make_entry(
2902 current
2903 .sub
2904 .st_alock
2905 .map(|value| RuntimeConfigValue::Float(value as f64))
2906 .unwrap_or(RuntimeConfigValue::Null),
2907 startup
2908 .sub
2909 .st_alock
2910 .map(|value| RuntimeConfigValue::Float(value as f64))
2911 .unwrap_or(RuntimeConfigValue::Null),
2912 "RNode short-term airtime lock percent; null clears it.",
2913 )),
2914 "lt_alock_pct" => Some(make_entry(
2915 current
2916 .sub
2917 .lt_alock
2918 .map(|value| RuntimeConfigValue::Float(value as f64))
2919 .unwrap_or(RuntimeConfigValue::Null),
2920 startup
2921 .sub
2922 .lt_alock
2923 .map(|value| RuntimeConfigValue::Float(value as f64))
2924 .unwrap_or(RuntimeConfigValue::Null),
2925 "RNode long-term airtime lock percent; null clears it.",
2926 )),
2927 _ => None,
2928 }
2929 }
2930
2931 #[cfg(feature = "iface-rnode")]
2932 fn split_rnode_runtime_key<'a>(
2933 &self,
2934 key: &'a str,
2935 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
2936 let rest = key.strip_prefix("rnode.").ok_or(RuntimeConfigError {
2937 code: RuntimeConfigErrorCode::UnknownKey,
2938 message: format!("unknown runtime-config key '{}'", key),
2939 })?;
2940 rest.split_once('.').ok_or(RuntimeConfigError {
2941 code: RuntimeConfigErrorCode::UnknownKey,
2942 message: format!("unknown runtime-config key '{}'", key),
2943 })
2944 }
2945
2946 #[cfg(feature = "iface-rnode")]
2947 fn apply_rnode_runtime(runtime: &mut RNodeRuntime) -> Result<(), RuntimeConfigError> {
2948 if let Some(err) = validate_sub_config(&runtime.sub) {
2949 return Err(RuntimeConfigError {
2950 code: RuntimeConfigErrorCode::InvalidValue,
2951 message: err,
2952 });
2953 }
2954 if let Some(writer) = runtime.writer.clone() {
2955 crate::interface::rnode::configure_subinterface(&writer, 0, &runtime.sub, false)
2956 .map_err(|e| RuntimeConfigError {
2957 code: RuntimeConfigErrorCode::ApplyFailed,
2958 message: format!("failed to apply RNode config: {}", e),
2959 })?;
2960 }
2961 Ok(())
2962 }
2963
2964 #[cfg(feature = "iface-rnode")]
2965 fn set_rnode_runtime_config(
2966 &mut self,
2967 key: &str,
2968 value: RuntimeConfigValue,
2969 ) -> Result<(), RuntimeConfigError> {
2970 let (name, setting) = self.split_rnode_runtime_key(key)?;
2971 let handle = self.rnode_runtime.get(name).ok_or(RuntimeConfigError {
2972 code: RuntimeConfigErrorCode::NotFound,
2973 message: format!("rnode interface '{}' not found", name),
2974 })?;
2975 let mut runtime = handle.runtime.lock().unwrap();
2976 let old = runtime.sub.clone();
2977 match setting {
2978 "frequency_hz" => runtime.sub.frequency = Self::expect_u64(value, key)? as u32,
2979 "bandwidth_hz" => runtime.sub.bandwidth = Self::expect_u64(value, key)? as u32,
2980 "txpower_dbm" => runtime.sub.txpower = Self::expect_i64(value, key)? as i8,
2981 "spreading_factor" => {
2982 runtime.sub.spreading_factor = Self::expect_u64(value, key)? as u8
2983 }
2984 "coding_rate" => runtime.sub.coding_rate = Self::expect_u64(value, key)? as u8,
2985 "st_alock_pct" => {
2986 runtime.sub.st_alock = match value {
2987 RuntimeConfigValue::Null => None,
2988 other => Some(Self::expect_f64(other, key)? as f32),
2989 };
2990 }
2991 "lt_alock_pct" => {
2992 runtime.sub.lt_alock = match value {
2993 RuntimeConfigValue::Null => None,
2994 other => Some(Self::expect_f64(other, key)? as f32),
2995 };
2996 }
2997 _ => {
2998 return Err(RuntimeConfigError {
2999 code: RuntimeConfigErrorCode::UnknownKey,
3000 message: format!("unknown runtime-config key '{}'", key),
3001 });
3002 }
3003 }
3004 if let Err(err) = Self::apply_rnode_runtime(&mut runtime) {
3005 runtime.sub = old;
3006 return Err(err);
3007 }
3008 Ok(())
3009 }
3010
3011 #[cfg(feature = "iface-rnode")]
3012 fn reset_rnode_runtime_config(&mut self, key: &str) -> Result<(), RuntimeConfigError> {
3013 let (name, setting) = self.split_rnode_runtime_key(key)?;
3014 let handle = self.rnode_runtime.get(name).ok_or(RuntimeConfigError {
3015 code: RuntimeConfigErrorCode::NotFound,
3016 message: format!("rnode interface '{}' not found", name),
3017 })?;
3018 let mut runtime = handle.runtime.lock().unwrap();
3019 let old = runtime.sub.clone();
3020 let startup = handle.startup.clone();
3021 match setting {
3022 "frequency_hz" => runtime.sub.frequency = startup.sub.frequency,
3023 "bandwidth_hz" => runtime.sub.bandwidth = startup.sub.bandwidth,
3024 "txpower_dbm" => runtime.sub.txpower = startup.sub.txpower,
3025 "spreading_factor" => runtime.sub.spreading_factor = startup.sub.spreading_factor,
3026 "coding_rate" => runtime.sub.coding_rate = startup.sub.coding_rate,
3027 "st_alock_pct" => runtime.sub.st_alock = startup.sub.st_alock,
3028 "lt_alock_pct" => runtime.sub.lt_alock = startup.sub.lt_alock,
3029 _ => {
3030 return Err(RuntimeConfigError {
3031 code: RuntimeConfigErrorCode::UnknownKey,
3032 message: format!("unknown runtime-config key '{}'", key),
3033 });
3034 }
3035 }
3036 if let Err(err) = Self::apply_rnode_runtime(&mut runtime) {
3037 runtime.sub = old;
3038 return Err(err);
3039 }
3040 Ok(())
3041 }
3042
3043 fn list_generic_interface_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
3044 let mut entries = Vec::new();
3045 let mut names: Vec<String> = self
3046 .interfaces
3047 .values()
3048 .map(|entry| entry.info.name.clone())
3049 .collect();
3050 names.sort();
3051 names.dedup();
3052 for name in names {
3053 for suffix in [
3054 "enabled",
3055 "mode",
3056 "announce_rate_target",
3057 "announce_rate_grace",
3058 "announce_rate_penalty",
3059 "announce_cap",
3060 "ingress_control",
3061 "ic_max_held_announces",
3062 "ic_burst_hold",
3063 "ic_burst_freq_new",
3064 "ic_burst_freq",
3065 "ic_new_time",
3066 "ic_burst_penalty",
3067 "ic_held_release_interval",
3068 ] {
3069 let key = format!("interface.{}.{}", name, suffix);
3070 if let Some(entry) = self.generic_interface_runtime_entry(&key) {
3071 entries.push(entry);
3072 }
3073 }
3074 if self.interface_ifac_runtime.contains_key(&name) {
3075 for suffix in ["ifac_netname", "ifac_passphrase", "ifac_size_bytes"] {
3076 let key = format!("interface.{}.{}", name, suffix);
3077 if let Some(entry) = self.generic_interface_runtime_entry(&key) {
3078 entries.push(entry);
3079 }
3080 }
3081 }
3082 }
3083 entries
3084 }
3085
3086 fn generic_interface_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
3087 let rest = key.strip_prefix("interface.")?;
3088 let (name, setting) = rest.rsplit_once('.')?;
3089 let make_entry = |value: RuntimeConfigValue,
3090 default: RuntimeConfigValue,
3091 apply_mode: RuntimeConfigApplyMode,
3092 description: &str|
3093 -> RuntimeConfigEntry {
3094 RuntimeConfigEntry {
3095 key: key.to_string(),
3096 source: if value == default {
3097 RuntimeConfigSource::Startup
3098 } else {
3099 RuntimeConfigSource::RuntimeOverride
3100 },
3101 value,
3102 default,
3103 apply_mode,
3104 description: Some(description.to_string()),
3105 }
3106 };
3107 match setting {
3108 "enabled" => {
3109 let entry = self
3110 .interfaces
3111 .values()
3112 .find(|entry| entry.info.name == name)?;
3113 Some(make_entry(
3114 RuntimeConfigValue::Bool(entry.enabled),
3115 RuntimeConfigValue::Bool(true),
3116 RuntimeConfigApplyMode::Immediate,
3117 "Administrative enable/disable state for this interface.",
3118 ))
3119 }
3120 "ifac_netname" => {
3121 let current = self.interface_ifac_runtime.get(name)?;
3122 let startup = self.interface_ifac_runtime_defaults.get(name)?;
3123 Some(make_entry(
3124 current
3125 .netname
3126 .clone()
3127 .map(RuntimeConfigValue::String)
3128 .unwrap_or(RuntimeConfigValue::Null),
3129 startup
3130 .netname
3131 .clone()
3132 .map(RuntimeConfigValue::String)
3133 .unwrap_or(RuntimeConfigValue::Null),
3134 RuntimeConfigApplyMode::Immediate,
3135 "IFAC network name for this interface; null clears it.",
3136 ))
3137 }
3138 "ifac_passphrase" => {
3139 let current = self.interface_ifac_runtime.get(name)?;
3140 let startup = self.interface_ifac_runtime_defaults.get(name)?;
3141 let current_value = current
3142 .netkey
3143 .as_ref()
3144 .map(|_| RuntimeConfigValue::String("<redacted>".to_string()))
3145 .unwrap_or(RuntimeConfigValue::Null);
3146 let default_value = startup
3147 .netkey
3148 .as_ref()
3149 .map(|_| RuntimeConfigValue::String("<redacted>".to_string()))
3150 .unwrap_or(RuntimeConfigValue::Null);
3151 Some(RuntimeConfigEntry {
3152 key: key.to_string(),
3153 source: if current.netkey == startup.netkey {
3154 RuntimeConfigSource::Startup
3155 } else {
3156 RuntimeConfigSource::RuntimeOverride
3157 },
3158 value: current_value,
3159 default: default_value,
3160 apply_mode: RuntimeConfigApplyMode::Immediate,
3161 description: Some(
3162 "IFAC passphrase for this interface; write-only, set a string to change it or null to clear it."
3163 .to_string(),
3164 ),
3165 })
3166 }
3167 "ifac_size_bytes" => {
3168 let current = self.interface_ifac_runtime.get(name)?;
3169 let startup = self.interface_ifac_runtime_defaults.get(name)?;
3170 Some(make_entry(
3171 RuntimeConfigValue::Int(current.size as i64),
3172 RuntimeConfigValue::Int(startup.size as i64),
3173 RuntimeConfigApplyMode::Immediate,
3174 "IFAC size in bytes; applies when IFAC is enabled.",
3175 ))
3176 }
3177 "mode" => {
3178 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3179 Some(make_entry(
3180 RuntimeConfigValue::String(Self::interface_mode_name(current.mode)),
3181 RuntimeConfigValue::String(Self::interface_mode_name(startup.mode)),
3182 RuntimeConfigApplyMode::Immediate,
3183 "Routing mode for this interface.",
3184 ))
3185 }
3186 "announce_rate_target" => {
3187 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3188 Some(make_entry(
3189 current
3190 .announce_rate_target
3191 .map(RuntimeConfigValue::Float)
3192 .unwrap_or(RuntimeConfigValue::Null),
3193 startup
3194 .announce_rate_target
3195 .map(RuntimeConfigValue::Float)
3196 .unwrap_or(RuntimeConfigValue::Null),
3197 RuntimeConfigApplyMode::Immediate,
3198 "Optional announce rate target in announces/sec; null disables it.",
3199 ))
3200 }
3201 "announce_rate_grace" => {
3202 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3203 Some(make_entry(
3204 RuntimeConfigValue::Int(current.announce_rate_grace as i64),
3205 RuntimeConfigValue::Int(startup.announce_rate_grace as i64),
3206 RuntimeConfigApplyMode::Immediate,
3207 "Announce rate grace period in announces.",
3208 ))
3209 }
3210 "announce_rate_penalty" => {
3211 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3212 Some(make_entry(
3213 RuntimeConfigValue::Float(current.announce_rate_penalty),
3214 RuntimeConfigValue::Float(startup.announce_rate_penalty),
3215 RuntimeConfigApplyMode::Immediate,
3216 "Announce rate penalty multiplier.",
3217 ))
3218 }
3219 "announce_cap" => {
3220 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3221 Some(make_entry(
3222 RuntimeConfigValue::Float(current.announce_cap),
3223 RuntimeConfigValue::Float(startup.announce_cap),
3224 RuntimeConfigApplyMode::Immediate,
3225 "Fraction of bitrate reserved for announces.",
3226 ))
3227 }
3228 "ingress_control" => {
3229 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3230 Some(make_entry(
3231 RuntimeConfigValue::Bool(current.ingress_control.enabled),
3232 RuntimeConfigValue::Bool(startup.ingress_control.enabled),
3233 RuntimeConfigApplyMode::Immediate,
3234 "Whether ingress control is enabled for this interface.",
3235 ))
3236 }
3237 "ic_max_held_announces" => {
3238 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3239 Some(make_entry(
3240 RuntimeConfigValue::Int(current.ingress_control.max_held_announces as i64),
3241 RuntimeConfigValue::Int(startup.ingress_control.max_held_announces as i64),
3242 RuntimeConfigApplyMode::Immediate,
3243 "Maximum held announces retained while ingress control is limiting this interface.",
3244 ))
3245 }
3246 "ic_burst_hold" => {
3247 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3248 Some(make_entry(
3249 RuntimeConfigValue::Float(current.ingress_control.burst_hold),
3250 RuntimeConfigValue::Float(startup.ingress_control.burst_hold),
3251 RuntimeConfigApplyMode::Immediate,
3252 "Seconds to keep ingress-control burst state active before releasing held announces.",
3253 ))
3254 }
3255 "ic_burst_freq_new" => {
3256 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3257 Some(make_entry(
3258 RuntimeConfigValue::Float(current.ingress_control.burst_freq_new),
3259 RuntimeConfigValue::Float(startup.ingress_control.burst_freq_new),
3260 RuntimeConfigApplyMode::Immediate,
3261 "Announce frequency threshold for new interfaces.",
3262 ))
3263 }
3264 "ic_burst_freq" => {
3265 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3266 Some(make_entry(
3267 RuntimeConfigValue::Float(current.ingress_control.burst_freq),
3268 RuntimeConfigValue::Float(startup.ingress_control.burst_freq),
3269 RuntimeConfigApplyMode::Immediate,
3270 "Announce frequency threshold for established interfaces.",
3271 ))
3272 }
3273 "ic_new_time" => {
3274 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3275 Some(make_entry(
3276 RuntimeConfigValue::Float(current.ingress_control.new_time),
3277 RuntimeConfigValue::Float(startup.ingress_control.new_time),
3278 RuntimeConfigApplyMode::Immediate,
3279 "Seconds after interface start that ingress control uses the new-interface burst threshold.",
3280 ))
3281 }
3282 "ic_burst_penalty" => {
3283 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3284 Some(make_entry(
3285 RuntimeConfigValue::Float(current.ingress_control.burst_penalty),
3286 RuntimeConfigValue::Float(startup.ingress_control.burst_penalty),
3287 RuntimeConfigApplyMode::Immediate,
3288 "Seconds to wait after a burst before releasing held announces.",
3289 ))
3290 }
3291 "ic_held_release_interval" => {
3292 let (_, current, startup) = self.interface_runtime_infos_by_name(name)?;
3293 Some(make_entry(
3294 RuntimeConfigValue::Float(current.ingress_control.held_release_interval),
3295 RuntimeConfigValue::Float(startup.ingress_control.held_release_interval),
3296 RuntimeConfigApplyMode::Immediate,
3297 "Seconds between held announce releases.",
3298 ))
3299 }
3300 _ => None,
3301 }
3302 }
3303
3304 fn split_generic_interface_runtime_key<'a>(
3305 &self,
3306 key: &'a str,
3307 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
3308 let rest = key.strip_prefix("interface.").ok_or(RuntimeConfigError {
3309 code: RuntimeConfigErrorCode::UnknownKey,
3310 message: format!("unknown runtime-config key '{}'", key),
3311 })?;
3312 rest.rsplit_once('.').ok_or(RuntimeConfigError {
3313 code: RuntimeConfigErrorCode::UnknownKey,
3314 message: format!("unknown runtime-config key '{}'", key),
3315 })
3316 }
3317
3318 fn interface_runtime_infos_by_name(
3319 &self,
3320 name: &str,
3321 ) -> Option<(
3322 rns_core::transport::types::InterfaceId,
3323 &rns_core::transport::types::InterfaceInfo,
3324 &rns_core::transport::types::InterfaceInfo,
3325 )> {
3326 let (id, entry) = self
3327 .interfaces
3328 .iter()
3329 .find(|(_, entry)| entry.info.name == name)?;
3330 let startup = self.interface_runtime_defaults.get(name)?;
3331 Some((*id, &entry.info, startup))
3332 }
3333
3334 fn interface_mode_name(mode: u8) -> String {
3335 match mode {
3336 rns_core::constants::MODE_FULL => "full".to_string(),
3337 rns_core::constants::MODE_ACCESS_POINT => "access_point".to_string(),
3338 rns_core::constants::MODE_POINT_TO_POINT => "point_to_point".to_string(),
3339 rns_core::constants::MODE_ROAMING => "roaming".to_string(),
3340 rns_core::constants::MODE_BOUNDARY => "boundary".to_string(),
3341 rns_core::constants::MODE_GATEWAY => "gateway".to_string(),
3342 _ => mode.to_string(),
3343 }
3344 }
3345
3346 fn parse_interface_mode(value: &RuntimeConfigValue) -> Option<u8> {
3347 match value {
3348 RuntimeConfigValue::Int(v) if *v >= 0 && *v <= u8::MAX as i64 => Some(*v as u8),
3349 RuntimeConfigValue::String(s) => match s.to_ascii_lowercase().as_str() {
3350 "full" => Some(rns_core::constants::MODE_FULL),
3351 "access_point" | "accesspoint" | "ap" => {
3352 Some(rns_core::constants::MODE_ACCESS_POINT)
3353 }
3354 "point_to_point" | "pointtopoint" | "ptp" => {
3355 Some(rns_core::constants::MODE_POINT_TO_POINT)
3356 }
3357 "roaming" => Some(rns_core::constants::MODE_ROAMING),
3358 "boundary" => Some(rns_core::constants::MODE_BOUNDARY),
3359 "gateway" | "gw" => Some(rns_core::constants::MODE_GATEWAY),
3360 _ => None,
3361 },
3362 _ => None,
3363 }
3364 }
3365
3366 fn apply_interface_ifac_runtime(entry: &mut InterfaceEntry, config: &IfacRuntimeConfig) {
3367 entry.ifac = if config.netname.is_some() || config.netkey.is_some() {
3368 Some(ifac::derive_ifac(
3369 config.netname.as_deref(),
3370 config.netkey.as_deref(),
3371 config.size,
3372 ))
3373 } else {
3374 None
3375 };
3376 }
3377
3378 fn set_generic_interface_runtime_config(
3379 &mut self,
3380 key: &str,
3381 value: RuntimeConfigValue,
3382 ) -> Result<(), RuntimeConfigError> {
3383 let (name, setting) = self.split_generic_interface_runtime_key(key)?;
3384 let (id, _) = self
3385 .interfaces
3386 .iter()
3387 .find(|(_, entry)| entry.info.name == name)
3388 .map(|(id, entry)| (*id, entry))
3389 .ok_or(RuntimeConfigError {
3390 code: RuntimeConfigErrorCode::NotFound,
3391 message: format!("interface '{}' not found", name),
3392 })?;
3393 let entry = self.interfaces.get_mut(&id).unwrap();
3394 match setting {
3395 "enabled" => {
3396 entry.enabled = Self::expect_bool(value, key)?;
3397 }
3398 "ifac_netname" => {
3399 let runtime =
3400 self.interface_ifac_runtime
3401 .get_mut(name)
3402 .ok_or(RuntimeConfigError {
3403 code: RuntimeConfigErrorCode::UnknownKey,
3404 message: format!("unknown runtime-config key '{}'", key),
3405 })?;
3406 runtime.netname = match value {
3407 RuntimeConfigValue::Null => None,
3408 RuntimeConfigValue::String(value) => Some(value),
3409 _ => {
3410 return Err(RuntimeConfigError {
3411 code: RuntimeConfigErrorCode::InvalidType,
3412 message: format!("{} expects a string or null", key),
3413 })
3414 }
3415 };
3416 Self::apply_interface_ifac_runtime(entry, runtime);
3417 }
3418 "ifac_passphrase" => {
3419 let runtime =
3420 self.interface_ifac_runtime
3421 .get_mut(name)
3422 .ok_or(RuntimeConfigError {
3423 code: RuntimeConfigErrorCode::UnknownKey,
3424 message: format!("unknown runtime-config key '{}'", key),
3425 })?;
3426 runtime.netkey = match value {
3427 RuntimeConfigValue::Null => None,
3428 RuntimeConfigValue::String(value) => Some(value),
3429 _ => {
3430 return Err(RuntimeConfigError {
3431 code: RuntimeConfigErrorCode::InvalidType,
3432 message: format!("{} expects a string or null", key),
3433 })
3434 }
3435 };
3436 Self::apply_interface_ifac_runtime(entry, runtime);
3437 }
3438 "ifac_size_bytes" => {
3439 let runtime =
3440 self.interface_ifac_runtime
3441 .get_mut(name)
3442 .ok_or(RuntimeConfigError {
3443 code: RuntimeConfigErrorCode::UnknownKey,
3444 message: format!("unknown runtime-config key '{}'", key),
3445 })?;
3446 runtime.size =
3447 (Self::expect_u64(value, key)? as usize).max(crate::ifac::IFAC_MIN_SIZE);
3448 Self::apply_interface_ifac_runtime(entry, runtime);
3449 }
3450 "mode" => {
3451 entry.info.mode = Self::parse_interface_mode(&value).ok_or(RuntimeConfigError {
3452 code: RuntimeConfigErrorCode::InvalidValue,
3453 message: format!("{} must be a valid interface mode", key),
3454 })?;
3455 }
3456 "announce_rate_target" => {
3457 entry.info.announce_rate_target = match value {
3458 RuntimeConfigValue::Null => None,
3459 RuntimeConfigValue::Float(v) if v >= 0.0 => Some(v),
3460 RuntimeConfigValue::Int(v) if v >= 0 => Some(v as f64),
3461 RuntimeConfigValue::Float(_) | RuntimeConfigValue::Int(_) => {
3462 return Err(RuntimeConfigError {
3463 code: RuntimeConfigErrorCode::InvalidValue,
3464 message: format!("{} must be >= 0", key),
3465 })
3466 }
3467 _ => {
3468 return Err(RuntimeConfigError {
3469 code: RuntimeConfigErrorCode::InvalidType,
3470 message: format!("{} expects a numeric value or null", key),
3471 })
3472 }
3473 };
3474 }
3475 "announce_rate_grace" => {
3476 entry.info.announce_rate_grace = Self::expect_u64(value, key)? as u32
3477 }
3478 "announce_rate_penalty" => {
3479 entry.info.announce_rate_penalty = Self::expect_f64(value, key)?
3480 }
3481 "announce_cap" => entry.info.announce_cap = Self::expect_f64(value, key)?,
3482 "ingress_control" => {
3483 entry.info.ingress_control.enabled = Self::expect_bool(value, key)?
3484 }
3485 "ic_max_held_announces" => {
3486 entry.info.ingress_control.max_held_announces =
3487 Self::expect_u64(value, key)? as usize
3488 }
3489 "ic_burst_hold" => {
3490 entry.info.ingress_control.burst_hold = Self::expect_f64(value, key)?
3491 }
3492 "ic_burst_freq_new" => {
3493 entry.info.ingress_control.burst_freq_new = Self::expect_f64(value, key)?
3494 }
3495 "ic_burst_freq" => {
3496 entry.info.ingress_control.burst_freq = Self::expect_f64(value, key)?
3497 }
3498 "ic_new_time" => entry.info.ingress_control.new_time = Self::expect_f64(value, key)?,
3499 "ic_burst_penalty" => {
3500 entry.info.ingress_control.burst_penalty = Self::expect_f64(value, key)?
3501 }
3502 "ic_held_release_interval" => {
3503 entry.info.ingress_control.held_release_interval = Self::expect_f64(value, key)?
3504 }
3505 _ => {
3506 return Err(RuntimeConfigError {
3507 code: RuntimeConfigErrorCode::UnknownKey,
3508 message: format!("unknown runtime-config key '{}'", key),
3509 })
3510 }
3511 }
3512 let info = entry.info.clone();
3513 self.engine.register_interface(info);
3514 Ok(())
3515 }
3516
3517 fn reset_generic_interface_runtime_config(
3518 &mut self,
3519 key: &str,
3520 ) -> Result<(), RuntimeConfigError> {
3521 let (name, setting) = self.split_generic_interface_runtime_key(key)?;
3522 let startup =
3523 self.interface_runtime_defaults
3524 .get(name)
3525 .cloned()
3526 .ok_or(RuntimeConfigError {
3527 code: RuntimeConfigErrorCode::NotFound,
3528 message: format!("interface '{}' not found", name),
3529 })?;
3530 let entry = self
3531 .interfaces
3532 .values_mut()
3533 .find(|entry| entry.info.name == name)
3534 .ok_or(RuntimeConfigError {
3535 code: RuntimeConfigErrorCode::NotFound,
3536 message: format!("interface '{}' not found", name),
3537 })?;
3538 match setting {
3539 "enabled" => entry.enabled = true,
3540 "ifac_netname" => {
3541 let startup_ifac =
3542 self.interface_ifac_runtime_defaults
3543 .get(name)
3544 .ok_or(RuntimeConfigError {
3545 code: RuntimeConfigErrorCode::UnknownKey,
3546 message: format!("unknown runtime-config key '{}'", key),
3547 })?;
3548 let runtime =
3549 self.interface_ifac_runtime
3550 .get_mut(name)
3551 .ok_or(RuntimeConfigError {
3552 code: RuntimeConfigErrorCode::UnknownKey,
3553 message: format!("unknown runtime-config key '{}'", key),
3554 })?;
3555 runtime.netname = startup_ifac.netname.clone();
3556 Self::apply_interface_ifac_runtime(entry, runtime);
3557 }
3558 "ifac_passphrase" => {
3559 let startup_ifac =
3560 self.interface_ifac_runtime_defaults
3561 .get(name)
3562 .ok_or(RuntimeConfigError {
3563 code: RuntimeConfigErrorCode::UnknownKey,
3564 message: format!("unknown runtime-config key '{}'", key),
3565 })?;
3566 let runtime =
3567 self.interface_ifac_runtime
3568 .get_mut(name)
3569 .ok_or(RuntimeConfigError {
3570 code: RuntimeConfigErrorCode::UnknownKey,
3571 message: format!("unknown runtime-config key '{}'", key),
3572 })?;
3573 runtime.netkey = startup_ifac.netkey.clone();
3574 Self::apply_interface_ifac_runtime(entry, runtime);
3575 }
3576 "ifac_size_bytes" => {
3577 let startup_ifac =
3578 self.interface_ifac_runtime_defaults
3579 .get(name)
3580 .ok_or(RuntimeConfigError {
3581 code: RuntimeConfigErrorCode::UnknownKey,
3582 message: format!("unknown runtime-config key '{}'", key),
3583 })?;
3584 let runtime =
3585 self.interface_ifac_runtime
3586 .get_mut(name)
3587 .ok_or(RuntimeConfigError {
3588 code: RuntimeConfigErrorCode::UnknownKey,
3589 message: format!("unknown runtime-config key '{}'", key),
3590 })?;
3591 runtime.size = startup_ifac.size;
3592 Self::apply_interface_ifac_runtime(entry, runtime);
3593 }
3594 "mode" => entry.info.mode = startup.mode,
3595 "announce_rate_target" => {
3596 entry.info.announce_rate_target = startup.announce_rate_target
3597 }
3598 "announce_rate_grace" => entry.info.announce_rate_grace = startup.announce_rate_grace,
3599 "announce_rate_penalty" => {
3600 entry.info.announce_rate_penalty = startup.announce_rate_penalty
3601 }
3602 "announce_cap" => entry.info.announce_cap = startup.announce_cap,
3603 "ingress_control" => {
3604 entry.info.ingress_control.enabled = startup.ingress_control.enabled
3605 }
3606 "ic_max_held_announces" => {
3607 entry.info.ingress_control.max_held_announces =
3608 startup.ingress_control.max_held_announces
3609 }
3610 "ic_burst_hold" => {
3611 entry.info.ingress_control.burst_hold = startup.ingress_control.burst_hold
3612 }
3613 "ic_burst_freq_new" => {
3614 entry.info.ingress_control.burst_freq_new = startup.ingress_control.burst_freq_new
3615 }
3616 "ic_burst_freq" => {
3617 entry.info.ingress_control.burst_freq = startup.ingress_control.burst_freq
3618 }
3619 "ic_new_time" => entry.info.ingress_control.new_time = startup.ingress_control.new_time,
3620 "ic_burst_penalty" => {
3621 entry.info.ingress_control.burst_penalty = startup.ingress_control.burst_penalty
3622 }
3623 "ic_held_release_interval" => {
3624 entry.info.ingress_control.held_release_interval =
3625 startup.ingress_control.held_release_interval
3626 }
3627 _ => {
3628 return Err(RuntimeConfigError {
3629 code: RuntimeConfigErrorCode::UnknownKey,
3630 message: format!("unknown runtime-config key '{}'", key),
3631 })
3632 }
3633 }
3634 let info = entry.info.clone();
3635 self.engine.register_interface(info);
3636 Ok(())
3637 }
3638
3639 #[cfg(feature = "iface-tcp")]
3640 fn tcp_server_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
3641 let rest = key.strip_prefix("tcp_server.")?;
3642 let (name, setting) = rest.split_once('.')?;
3643 if matches!(
3644 setting,
3645 "discoverable"
3646 | "discovery_name"
3647 | "announce_interval_secs"
3648 | "reachable_on"
3649 | "stamp_value"
3650 | "latitude"
3651 | "longitude"
3652 | "height"
3653 ) {
3654 let handle = self.tcp_server_discovery_runtime.get(name)?;
3655 let current = &handle.current;
3656 let startup = &handle.startup;
3657 let make_entry = |value: RuntimeConfigValue,
3658 default: RuntimeConfigValue,
3659 apply_mode: RuntimeConfigApplyMode,
3660 description: &str|
3661 -> RuntimeConfigEntry {
3662 RuntimeConfigEntry {
3663 key: key.to_string(),
3664 source: if value == default {
3665 RuntimeConfigSource::Startup
3666 } else {
3667 RuntimeConfigSource::RuntimeOverride
3668 },
3669 value,
3670 default,
3671 apply_mode,
3672 description: Some(description.to_string()),
3673 }
3674 };
3675 return match setting {
3676 "discoverable" => Some(make_entry(
3677 RuntimeConfigValue::Bool(current.discoverable),
3678 RuntimeConfigValue::Bool(startup.discoverable),
3679 RuntimeConfigApplyMode::Immediate,
3680 "Whether this TCP server interface is advertised through interface discovery.",
3681 )),
3682 "discovery_name" => Some(make_entry(
3683 RuntimeConfigValue::String(current.config.discovery_name.clone()),
3684 RuntimeConfigValue::String(startup.config.discovery_name.clone()),
3685 RuntimeConfigApplyMode::Immediate,
3686 "Human-readable discovery name advertised for this TCP server interface.",
3687 )),
3688 "announce_interval_secs" => Some(make_entry(
3689 RuntimeConfigValue::Int(current.config.announce_interval as i64),
3690 RuntimeConfigValue::Int(startup.config.announce_interval as i64),
3691 RuntimeConfigApplyMode::Immediate,
3692 "Discovery announce interval for this TCP server interface in seconds.",
3693 )),
3694 "reachable_on" => Some(make_entry(
3695 current
3696 .config
3697 .reachable_on
3698 .clone()
3699 .map(RuntimeConfigValue::String)
3700 .unwrap_or(RuntimeConfigValue::Null),
3701 startup
3702 .config
3703 .reachable_on
3704 .clone()
3705 .map(RuntimeConfigValue::String)
3706 .unwrap_or(RuntimeConfigValue::Null),
3707 RuntimeConfigApplyMode::Immediate,
3708 "Reachable hostname or IP advertised for this TCP server interface; null clears it.",
3709 )),
3710 "stamp_value" => Some(make_entry(
3711 RuntimeConfigValue::Int(current.config.stamp_value as i64),
3712 RuntimeConfigValue::Int(startup.config.stamp_value as i64),
3713 RuntimeConfigApplyMode::Immediate,
3714 "Discovery proof-of-work stamp cost for this TCP server interface.",
3715 )),
3716 "latitude" => Some(make_entry(
3717 current
3718 .config
3719 .latitude
3720 .map(RuntimeConfigValue::Float)
3721 .unwrap_or(RuntimeConfigValue::Null),
3722 startup
3723 .config
3724 .latitude
3725 .map(RuntimeConfigValue::Float)
3726 .unwrap_or(RuntimeConfigValue::Null),
3727 RuntimeConfigApplyMode::Immediate,
3728 "Latitude advertised for this TCP server interface; null clears it.",
3729 )),
3730 "longitude" => Some(make_entry(
3731 current
3732 .config
3733 .longitude
3734 .map(RuntimeConfigValue::Float)
3735 .unwrap_or(RuntimeConfigValue::Null),
3736 startup
3737 .config
3738 .longitude
3739 .map(RuntimeConfigValue::Float)
3740 .unwrap_or(RuntimeConfigValue::Null),
3741 RuntimeConfigApplyMode::Immediate,
3742 "Longitude advertised for this TCP server interface; null clears it.",
3743 )),
3744 "height" => Some(make_entry(
3745 current
3746 .config
3747 .height
3748 .map(RuntimeConfigValue::Float)
3749 .unwrap_or(RuntimeConfigValue::Null),
3750 startup
3751 .config
3752 .height
3753 .map(RuntimeConfigValue::Float)
3754 .unwrap_or(RuntimeConfigValue::Null),
3755 RuntimeConfigApplyMode::Immediate,
3756 "Height advertised for this TCP server interface; null clears it.",
3757 )),
3758 _ => None,
3759 };
3760 }
3761
3762 let handle = self.tcp_server_runtime.get(name)?;
3763 let current = handle.runtime.lock().unwrap().clone();
3764 let startup = handle.startup.clone();
3765 match setting {
3766 "max_connections" => Some(RuntimeConfigEntry {
3767 key: key.to_string(),
3768 value: RuntimeConfigValue::Int(current.max_connections.unwrap_or(0) as i64),
3769 default: RuntimeConfigValue::Int(startup.max_connections.unwrap_or(0) as i64),
3770 source: if current.max_connections == startup.max_connections {
3771 RuntimeConfigSource::Startup
3772 } else {
3773 RuntimeConfigSource::RuntimeOverride
3774 },
3775 apply_mode: RuntimeConfigApplyMode::NewConnectionsOnly,
3776 description: Some(
3777 "Maximum simultaneous inbound TCP server connections; 0 disables the cap."
3778 .to_string(),
3779 ),
3780 }),
3781 _ => None,
3782 }
3783 }
3784
3785 #[cfg(feature = "iface-tcp")]
3786 fn split_tcp_server_runtime_key<'a>(
3787 &self,
3788 key: &'a str,
3789 ) -> Result<(&'a str, &'a str), RuntimeConfigError> {
3790 let rest = key.strip_prefix("tcp_server.").ok_or(RuntimeConfigError {
3791 code: RuntimeConfigErrorCode::UnknownKey,
3792 message: format!("unknown runtime-config key '{}'", key),
3793 })?;
3794 rest.split_once('.').ok_or(RuntimeConfigError {
3795 code: RuntimeConfigErrorCode::UnknownKey,
3796 message: format!("unknown runtime-config key '{}'", key),
3797 })
3798 }
3799
3800 #[cfg(feature = "iface-tcp")]
3801 fn set_tcp_server_runtime_config(
3802 &mut self,
3803 key: &str,
3804 value: RuntimeConfigValue,
3805 ) -> Result<(), RuntimeConfigError> {
3806 let (name, setting) = self.split_tcp_server_runtime_key(key)?;
3807 if matches!(
3808 setting,
3809 "discoverable"
3810 | "discovery_name"
3811 | "announce_interval_secs"
3812 | "reachable_on"
3813 | "stamp_value"
3814 | "latitude"
3815 | "longitude"
3816 | "height"
3817 ) {
3818 return self.set_tcp_server_discovery_runtime_config(key, value);
3819 }
3820 let handle = self
3821 .tcp_server_runtime
3822 .get(name)
3823 .ok_or(RuntimeConfigError {
3824 code: RuntimeConfigErrorCode::NotFound,
3825 message: format!("tcp server interface '{}' not found", name),
3826 })?;
3827 let mut runtime = handle.runtime.lock().unwrap();
3828 match setting {
3829 "max_connections" => {
3830 runtime.max_connections = Self::set_optional_usize(value, key)?;
3831 Ok(())
3832 }
3833 _ => Err(RuntimeConfigError {
3834 code: RuntimeConfigErrorCode::UnknownKey,
3835 message: format!("unknown runtime-config key '{}'", key),
3836 }),
3837 }
3838 }
3839
3840 #[cfg(feature = "iface-tcp")]
3841 fn reset_tcp_server_runtime_config(&mut self, key: &str) -> Result<(), RuntimeConfigError> {
3842 let (name, setting) = self.split_tcp_server_runtime_key(key)?;
3843 if matches!(
3844 setting,
3845 "discoverable"
3846 | "discovery_name"
3847 | "announce_interval_secs"
3848 | "reachable_on"
3849 | "stamp_value"
3850 | "latitude"
3851 | "longitude"
3852 | "height"
3853 ) {
3854 return self.reset_tcp_server_discovery_runtime_config(key);
3855 }
3856 let handle = self
3857 .tcp_server_runtime
3858 .get(name)
3859 .ok_or(RuntimeConfigError {
3860 code: RuntimeConfigErrorCode::NotFound,
3861 message: format!("tcp server interface '{}' not found", name),
3862 })?;
3863 let mut runtime = handle.runtime.lock().unwrap();
3864 let startup = handle.startup.clone();
3865 match setting {
3866 "max_connections" => runtime.max_connections = startup.max_connections,
3867 _ => {
3868 return Err(RuntimeConfigError {
3869 code: RuntimeConfigErrorCode::UnknownKey,
3870 message: format!("unknown runtime-config key '{}'", key),
3871 })
3872 }
3873 }
3874 Ok(())
3875 }
3876
3877 #[cfg(feature = "iface-tcp")]
3878 fn set_tcp_server_discovery_runtime_config(
3879 &mut self,
3880 key: &str,
3881 value: RuntimeConfigValue,
3882 ) -> Result<(), RuntimeConfigError> {
3883 let (name, setting) = self.split_tcp_server_runtime_key(key)?;
3884 let handle = self
3885 .tcp_server_discovery_runtime
3886 .get_mut(name)
3887 .ok_or(RuntimeConfigError {
3888 code: RuntimeConfigErrorCode::NotFound,
3889 message: format!("tcp server interface '{}' not found", name),
3890 })?;
3891 match setting {
3892 "discoverable" => handle.current.discoverable = Self::expect_bool(value, key)?,
3893 "discovery_name" => {
3894 handle.current.config.discovery_name = Self::expect_string(value, key)?
3895 }
3896 "announce_interval_secs" => {
3897 let secs = Self::expect_u64(value, key)?;
3898 if secs < 300 {
3899 return Err(RuntimeConfigError {
3900 code: RuntimeConfigErrorCode::InvalidValue,
3901 message: format!("{} must be >= 300", key),
3902 });
3903 }
3904 handle.current.config.announce_interval = secs;
3905 }
3906 "reachable_on" => {
3907 handle.current.config.reachable_on = Self::expect_optional_string(value, key)?
3908 }
3909 "stamp_value" => {
3910 let raw = Self::expect_u64(value, key)?;
3911 if raw > u8::MAX as u64 {
3912 return Err(RuntimeConfigError {
3913 code: RuntimeConfigErrorCode::InvalidValue,
3914 message: format!("{} must be <= {}", key, u8::MAX),
3915 });
3916 }
3917 handle.current.config.stamp_value = raw as u8;
3918 }
3919 "latitude" => handle.current.config.latitude = Self::expect_optional_f64(value, key)?,
3920 "longitude" => handle.current.config.longitude = Self::expect_optional_f64(value, key)?,
3921 "height" => handle.current.config.height = Self::expect_optional_f64(value, key)?,
3922 _ => {
3923 return Err(RuntimeConfigError {
3924 code: RuntimeConfigErrorCode::UnknownKey,
3925 message: format!("unknown runtime-config key '{}'", key),
3926 })
3927 }
3928 }
3929 self.sync_tcp_server_discovery_runtime(name)
3930 }
3931
3932 #[cfg(feature = "iface-tcp")]
3933 fn reset_tcp_server_discovery_runtime_config(
3934 &mut self,
3935 key: &str,
3936 ) -> Result<(), RuntimeConfigError> {
3937 let (name, setting) = self.split_tcp_server_runtime_key(key)?;
3938 let handle = self
3939 .tcp_server_discovery_runtime
3940 .get_mut(name)
3941 .ok_or(RuntimeConfigError {
3942 code: RuntimeConfigErrorCode::NotFound,
3943 message: format!("tcp server interface '{}' not found", name),
3944 })?;
3945 match setting {
3946 "discoverable" => handle.current.discoverable = handle.startup.discoverable,
3947 "discovery_name" => {
3948 handle.current.config.discovery_name = handle.startup.config.discovery_name.clone()
3949 }
3950 "announce_interval_secs" => {
3951 handle.current.config.announce_interval = handle.startup.config.announce_interval
3952 }
3953 "reachable_on" => {
3954 handle.current.config.reachable_on = handle.startup.config.reachable_on.clone()
3955 }
3956 "stamp_value" => handle.current.config.stamp_value = handle.startup.config.stamp_value,
3957 "latitude" => handle.current.config.latitude = handle.startup.config.latitude,
3958 "longitude" => handle.current.config.longitude = handle.startup.config.longitude,
3959 "height" => handle.current.config.height = handle.startup.config.height,
3960 _ => {
3961 return Err(RuntimeConfigError {
3962 code: RuntimeConfigErrorCode::UnknownKey,
3963 message: format!("unknown runtime-config key '{}'", key),
3964 })
3965 }
3966 }
3967 self.sync_tcp_server_discovery_runtime(name)
3968 }
3969
3970 #[cfg(feature = "iface-backbone")]
3971 fn list_backbone_runtime_config(&self) -> Vec<RuntimeConfigEntry> {
3972 let mut entries = Vec::new();
3973 let mut names: Vec<&String> = self.backbone_runtime.keys().collect();
3974 names.sort();
3975 for name in names {
3976 for suffix in [
3977 "idle_timeout_secs",
3978 "write_stall_timeout_secs",
3979 "max_penalty_duration_secs",
3980 "max_connections",
3981 ] {
3982 let key = format!("backbone.{}.{}", name, suffix);
3983 if let Some(entry) = self.backbone_runtime_entry(&key) {
3984 entries.push(entry);
3985 }
3986 }
3987 for suffix in [
3988 "discoverable",
3989 "discovery_name",
3990 "announce_interval_secs",
3991 "reachable_on",
3992 "stamp_value",
3993 "latitude",
3994 "longitude",
3995 "height",
3996 ] {
3997 let key = format!("backbone.{}.{}", name, suffix);
3998 if let Some(entry) = self.backbone_runtime_entry(&key) {
3999 entries.push(entry);
4000 }
4001 }
4002 }
4003 entries
4004 }
4005
4006 #[cfg(feature = "iface-backbone")]
4007 fn backbone_runtime_entry(&self, key: &str) -> Option<RuntimeConfigEntry> {
4008 let rest = key.strip_prefix("backbone.")?;
4009 let (name, setting) = rest.split_once('.')?;
4010
4011 let make_entry = |value: RuntimeConfigValue,
4012 default: RuntimeConfigValue,
4013 apply_mode: RuntimeConfigApplyMode,
4014 description: &str| RuntimeConfigEntry {
4015 key: key.to_string(),
4016 source: if value == default {
4017 RuntimeConfigSource::Startup
4018 } else {
4019 RuntimeConfigSource::RuntimeOverride
4020 },
4021 value,
4022 default,
4023 apply_mode,
4024 description: Some(description.to_string()),
4025 };
4026
4027 if matches!(
4028 setting,
4029 "discoverable"
4030 | "discovery_name"
4031 | "announce_interval_secs"
4032 | "reachable_on"
4033 | "stamp_value"
4034 | "latitude"
4035 | "longitude"
4036 | "height"
4037 ) {
4038 let handle = self.backbone_discovery_runtime.get(name)?;
4039 let current = &handle.current;
4040 let startup = &handle.startup;
4041 return match setting {
4042 "discoverable" => Some(make_entry(
4043 RuntimeConfigValue::Bool(current.discoverable),
4044 RuntimeConfigValue::Bool(startup.discoverable),
4045 RuntimeConfigApplyMode::Immediate,
4046 "Whether this backbone interface is advertised through interface discovery.",
4047 )),
4048 "discovery_name" => Some(make_entry(
4049 RuntimeConfigValue::String(current.config.discovery_name.clone()),
4050 RuntimeConfigValue::String(startup.config.discovery_name.clone()),
4051 RuntimeConfigApplyMode::Immediate,
4052 "Human-readable discovery name advertised for this backbone interface.",
4053 )),
4054 "announce_interval_secs" => Some(make_entry(
4055 RuntimeConfigValue::Int(current.config.announce_interval as i64),
4056 RuntimeConfigValue::Int(startup.config.announce_interval as i64),
4057 RuntimeConfigApplyMode::Immediate,
4058 "Discovery announce interval for this backbone interface in seconds.",
4059 )),
4060 "reachable_on" => Some(make_entry(
4061 current
4062 .config
4063 .reachable_on
4064 .clone()
4065 .map(RuntimeConfigValue::String)
4066 .unwrap_or(RuntimeConfigValue::Null),
4067 startup
4068 .config
4069 .reachable_on
4070 .clone()
4071 .map(RuntimeConfigValue::String)
4072 .unwrap_or(RuntimeConfigValue::Null),
4073 RuntimeConfigApplyMode::Immediate,
4074 "Reachable hostname or IP advertised for this backbone interface; null clears it.",
4075 )),
4076 "stamp_value" => Some(make_entry(
4077 RuntimeConfigValue::Int(current.config.stamp_value as i64),
4078 RuntimeConfigValue::Int(startup.config.stamp_value as i64),
4079 RuntimeConfigApplyMode::Immediate,
4080 "Discovery proof-of-work stamp cost for this backbone interface.",
4081 )),
4082 "latitude" => Some(make_entry(
4083 current
4084 .config
4085 .latitude
4086 .map(RuntimeConfigValue::Float)
4087 .unwrap_or(RuntimeConfigValue::Null),
4088 startup
4089 .config
4090 .latitude
4091 .map(RuntimeConfigValue::Float)
4092 .unwrap_or(RuntimeConfigValue::Null),
4093 RuntimeConfigApplyMode::Immediate,
4094 "Latitude advertised for this backbone interface; null clears it.",
4095 )),
4096 "longitude" => Some(make_entry(
4097 current
4098 .config
4099 .longitude
4100 .map(RuntimeConfigValue::Float)
4101 .unwrap_or(RuntimeConfigValue::Null),
4102 startup
4103 .config
4104 .longitude
4105 .map(RuntimeConfigValue::Float)
4106 .unwrap_or(RuntimeConfigValue::Null),
4107 RuntimeConfigApplyMode::Immediate,
4108 "Longitude advertised for this backbone interface; null clears it.",
4109 )),
4110 "height" => Some(make_entry(
4111 current
4112 .config
4113 .height
4114 .map(RuntimeConfigValue::Float)
4115 .unwrap_or(RuntimeConfigValue::Null),
4116 startup
4117 .config
4118 .height
4119 .map(RuntimeConfigValue::Float)
4120 .unwrap_or(RuntimeConfigValue::Null),
4121 RuntimeConfigApplyMode::Immediate,
4122 "Height advertised for this backbone interface; null clears it.",
4123 )),
4124 _ => None,
4125 };
4126 }
4127
4128 if let Some(handle) = self.backbone_runtime.get(name) {
4129 let current = handle.runtime.lock().unwrap().clone();
4130 let startup = handle.startup.clone();
4131 return match setting {
4132 "idle_timeout_secs" => Some(make_entry(
4133 RuntimeConfigValue::Float(current.idle_timeout.map(|d| d.as_secs_f64()).unwrap_or(0.0)),
4134 RuntimeConfigValue::Float(startup.idle_timeout.map(|d| d.as_secs_f64()).unwrap_or(0.0)),
4135 RuntimeConfigApplyMode::Immediate,
4136 "Disconnect silent inbound peers after this many seconds; 0 disables the timeout.",
4137 )),
4138 "write_stall_timeout_secs" => Some(make_entry(
4139 RuntimeConfigValue::Float(current.write_stall_timeout.map(|d| d.as_secs_f64()).unwrap_or(0.0)),
4140 RuntimeConfigValue::Float(startup.write_stall_timeout.map(|d| d.as_secs_f64()).unwrap_or(0.0)),
4141 RuntimeConfigApplyMode::Immediate,
4142 "Disconnect peers whose send buffer remains unwritable for this many seconds; 0 disables the timeout.",
4143 )),
4144 "max_penalty_duration_secs" => Some(make_entry(
4145 RuntimeConfigValue::Float(current.abuse.max_penalty_duration.map(|d| d.as_secs_f64()).unwrap_or(0.0)),
4146 RuntimeConfigValue::Float(startup.abuse.max_penalty_duration.map(|d| d.as_secs_f64()).unwrap_or(0.0)),
4147 RuntimeConfigApplyMode::Immediate,
4148 "Maximum accepted backbone blacklist duration; 0 means no cap.",
4149 )),
4150 "max_connections" => Some(make_entry(
4151 RuntimeConfigValue::Int(current.max_connections.unwrap_or(0) as i64),
4152 RuntimeConfigValue::Int(startup.max_connections.unwrap_or(0) as i64),
4153 RuntimeConfigApplyMode::NewConnectionsOnly,
4154 "Maximum simultaneous inbound backbone connections; 0 disables the cap.",
4155 )),
4156 _ => None,
4157 };
4158 }
4159
4160 None
4161 }
4162
4163 #[cfg(feature = "rns-hooks")]
4164 fn forward_hook_side_effects(&mut self, attach_point: &str, exec: &rns_hooks::ExecuteResult) {
4165 if !exec.injected_actions.is_empty() {
4166 self.dispatch_all(convert_injected_actions(exec.injected_actions.clone()));
4167 }
4168 if let Some(ref bridge) = self.provider_bridge {
4169 for event in &exec.provider_events {
4170 bridge.emit_event(
4171 attach_point,
4172 event.hook_name.clone(),
4173 event.payload_type.clone(),
4174 event.payload.clone(),
4175 );
4176 }
4177 }
4178 }
4179
4180 #[cfg(feature = "rns-hooks")]
4181 fn collect_hook_side_effects(
4182 &mut self,
4183 attach_point: &str,
4184 exec: &rns_hooks::ExecuteResult,
4185 out: &mut Vec<TransportAction>,
4186 ) {
4187 if !exec.injected_actions.is_empty() {
4188 out.extend(convert_injected_actions(exec.injected_actions.clone()));
4189 }
4190 if let Some(ref bridge) = self.provider_bridge {
4191 for event in &exec.provider_events {
4192 bridge.emit_event(
4193 attach_point,
4194 event.hook_name.clone(),
4195 event.payload_type.clone(),
4196 event.payload.clone(),
4197 );
4198 }
4199 }
4200 }
4201
4202 pub fn set_probe_config(
4204 &mut self,
4205 addrs: Vec<std::net::SocketAddr>,
4206 protocol: rns_core::holepunch::ProbeProtocol,
4207 device: Option<String>,
4208 ) {
4209 self.holepunch_manager = HolePunchManager::new(addrs, protocol, device);
4210 }
4211
4212 pub fn run(&mut self) {
4214 loop {
4215 let event = match self.rx.recv() {
4216 Ok(e) => e,
4217 Err(_) => break, };
4219
4220 match event {
4221 Event::Frame { interface_id, data } => {
4222 if data.len() > 2 && (data[0] & 0x03) == 0x01 {
4224 log::debug!(
4225 "Announce:frame from iface {} (len={}, flags=0x{:02x})",
4226 interface_id.0,
4227 data.len(),
4228 data[0]
4229 );
4230 }
4231 if let Some(entry) = self.interfaces.get(&interface_id) {
4232 if !entry.enabled || !entry.online {
4233 continue;
4234 }
4235 }
4236 if let Some(entry) = self.interfaces.get_mut(&interface_id) {
4238 entry.stats.rxb += data.len() as u64;
4239 entry.stats.rx_packets += 1;
4240 }
4241
4242 let packet = if let Some(entry) = self.interfaces.get(&interface_id) {
4244 if let Some(ref ifac_state) = entry.ifac {
4245 match ifac::unmask_inbound(&data, ifac_state) {
4247 Some(unmasked) => unmasked,
4248 None => {
4249 log::debug!("[{}] IFAC rejected packet", interface_id.0);
4250 continue;
4251 }
4252 }
4253 } else {
4254 if data.len() > 2 && data[0] & 0x80 == 0x80 {
4256 log::debug!(
4257 "[{}] dropping packet with IFAC flag on non-IFAC interface",
4258 interface_id.0
4259 );
4260 continue;
4261 }
4262 data
4263 }
4264 } else {
4265 data
4266 };
4267
4268 #[cfg(feature = "rns-hooks")]
4270 {
4271 let pkt_ctx = rns_hooks::PacketContext {
4272 flags: if packet.is_empty() { 0 } else { packet[0] },
4273 hops: if packet.len() > 1 { packet[1] } else { 0 },
4274 destination_hash: extract_dest_hash(&packet),
4275 context: 0,
4276 packet_hash: [0; 32],
4277 interface_id: interface_id.0,
4278 data_offset: 0,
4279 data_len: packet.len() as u32,
4280 };
4281 let ctx = HookContext::Packet {
4282 ctx: &pkt_ctx,
4283 raw: &packet,
4284 };
4285 let now = time::now();
4286 let engine_ref = EngineRef {
4287 engine: &self.engine,
4288 interfaces: &self.interfaces,
4289 link_manager: &self.link_manager,
4290 now,
4291 };
4292 let provider_events_enabled = self.provider_events_enabled();
4293 {
4294 let exec = run_hook_inner(
4295 &mut self.hook_slots[HookPoint::PreIngress as usize].programs,
4296 &self.hook_manager,
4297 &engine_ref,
4298 &ctx,
4299 now,
4300 provider_events_enabled,
4301 );
4302 if let Some(ref e) = exec {
4303 self.forward_hook_side_effects("PreIngress", e);
4304 if e.hook_result.as_ref().map_or(false, |r| r.is_drop()) {
4305 continue;
4306 }
4307 }
4308 }
4309 }
4310
4311 if packet.len() > 2 && (packet[0] & 0x03) == 0x01 {
4313 let now = time::now();
4314 if let Some(entry) = self.interfaces.get_mut(&interface_id) {
4315 entry.stats.record_incoming_announce(now);
4316 }
4317 }
4318
4319 if let Some(entry) = self.interfaces.get(&interface_id) {
4321 self.engine.update_interface_freq(
4322 interface_id,
4323 entry.stats.incoming_announce_freq(),
4324 );
4325 }
4326
4327 let actions = if self.async_announce_verification {
4328 let mut announce_queue = self
4329 .announce_verify_queue
4330 .lock()
4331 .unwrap_or_else(|poisoned| poisoned.into_inner());
4332 self.engine.handle_inbound_with_announce_queue(
4333 &packet,
4334 interface_id,
4335 time::now(),
4336 &mut self.rng,
4337 Some(&mut announce_queue),
4338 )
4339 } else {
4340 self.engine.handle_inbound(
4341 &packet,
4342 interface_id,
4343 time::now(),
4344 &mut self.rng,
4345 )
4346 };
4347
4348 #[cfg(feature = "rns-hooks")]
4350 {
4351 let pkt_ctx2 = rns_hooks::PacketContext {
4352 flags: if packet.is_empty() { 0 } else { packet[0] },
4353 hops: if packet.len() > 1 { packet[1] } else { 0 },
4354 destination_hash: extract_dest_hash(&packet),
4355 context: 0,
4356 packet_hash: [0; 32],
4357 interface_id: interface_id.0,
4358 data_offset: 0,
4359 data_len: packet.len() as u32,
4360 };
4361 let ctx = HookContext::Packet {
4362 ctx: &pkt_ctx2,
4363 raw: &packet,
4364 };
4365 let now = time::now();
4366 let engine_ref = EngineRef {
4367 engine: &self.engine,
4368 interfaces: &self.interfaces,
4369 link_manager: &self.link_manager,
4370 now,
4371 };
4372 let provider_events_enabled = self.provider_events_enabled();
4373 if let Some(ref e) = run_hook_inner(
4374 &mut self.hook_slots[HookPoint::PreDispatch as usize].programs,
4375 &self.hook_manager,
4376 &engine_ref,
4377 &ctx,
4378 now,
4379 provider_events_enabled,
4380 ) {
4381 self.forward_hook_side_effects("PreDispatch", e);
4382 }
4383 }
4384
4385 self.dispatch_all(actions);
4386 }
4387 Event::AnnounceVerified {
4388 key,
4389 validated,
4390 sig_cache_key,
4391 } => {
4392 let pending = {
4393 let mut announce_queue = self
4394 .announce_verify_queue
4395 .lock()
4396 .unwrap_or_else(|poisoned| poisoned.into_inner());
4397 announce_queue.complete_success(&key)
4398 };
4399 if let Some(pending) = pending {
4400 let actions = self.engine.complete_verified_announce(
4401 pending,
4402 validated,
4403 sig_cache_key,
4404 time::now(),
4405 &mut self.rng,
4406 );
4407 self.dispatch_all(actions);
4408 }
4409 }
4410 Event::AnnounceVerifyFailed { key, .. } => {
4411 let mut announce_queue = self
4412 .announce_verify_queue
4413 .lock()
4414 .unwrap_or_else(|poisoned| poisoned.into_inner());
4415 let _ = announce_queue.complete_failure(&key);
4416 }
4417 Event::Tick => {
4418 #[cfg(feature = "rns-hooks")]
4420 {
4421 let ctx = HookContext::Tick;
4422 let now = time::now();
4423 let engine_ref = EngineRef {
4424 engine: &self.engine,
4425 interfaces: &self.interfaces,
4426 link_manager: &self.link_manager,
4427 now,
4428 };
4429 let provider_events_enabled = self.provider_events_enabled();
4430 if let Some(ref e) = run_hook_inner(
4431 &mut self.hook_slots[HookPoint::Tick as usize].programs,
4432 &self.hook_manager,
4433 &engine_ref,
4434 &ctx,
4435 now,
4436 provider_events_enabled,
4437 ) {
4438 self.forward_hook_side_effects("Tick", e);
4439 }
4440 }
4441
4442 let now = time::now();
4443 for (id, entry) in &self.interfaces {
4445 self.engine
4446 .update_interface_freq(*id, entry.stats.incoming_announce_freq());
4447 }
4448 let actions = self.engine.tick(now, &mut self.rng);
4449 self.dispatch_all(actions);
4450 let link_actions = self.link_manager.tick(&mut self.rng);
4452 self.dispatch_link_actions(link_actions);
4453 self.enforce_drain_deadline();
4454 {
4456 let tx = self.get_event_sender();
4457 let hp_actions = self.holepunch_manager.tick(&tx);
4458 self.dispatch_holepunch_actions(hp_actions);
4459 }
4460 self.tick_management_announces(now);
4462 self.sent_packets
4464 .retain(|_, (_, sent_time)| now - *sent_time < 60.0);
4465 self.completed_proofs
4467 .retain(|_, (_, received)| now - *received < 120.0);
4468
4469 self.tick_discovery_announcer(now);
4470
4471 self.memory_stats_counter += 1;
4473 if self.memory_stats_counter >= 300 {
4474 self.memory_stats_counter = 0;
4475 self.log_memory_stats();
4476 }
4477
4478 if self.discover_interfaces {
4480 self.discovery_cleanup_counter += 1;
4481 if self.discovery_cleanup_counter >= self.discovery_cleanup_interval_ticks {
4482 self.discovery_cleanup_counter = 0;
4483 if let Ok(removed) = self.discovered_interfaces.cleanup() {
4484 if removed > 0 {
4485 log::info!(
4486 "Discovery cleanup: removed {} stale entries",
4487 removed
4488 );
4489 }
4490 }
4491 }
4492 }
4493
4494 self.cache_cleanup_counter += 1;
4496 if self.cache_cleanup_counter >= self.known_destinations_cleanup_interval_ticks
4497 {
4498 self.cache_cleanup_counter = 0;
4499
4500 let active_dests = self.engine.active_destination_hashes();
4501
4502 let now = time::now();
4505 let ttl = self.known_destinations_ttl;
4506 let kd_before = self.known_destinations.len();
4507 self.known_destinations.retain(|k, announced| {
4508 active_dests.contains(k)
4509 || self.local_destinations.contains_key(k)
4510 || now - announced.received_at < ttl
4511 });
4512 let kd_removed = kd_before - self.known_destinations.len();
4513 let kd_evicted = self.enforce_known_destination_cap(false);
4514
4515 let rl_removed = self.engine.cull_rate_limiter(
4517 &active_dests,
4518 now,
4519 self.rate_limiter_ttl_secs,
4520 );
4521
4522 if kd_removed > 0 || kd_evicted > 0 || rl_removed > 0 {
4523 log::info!(
4524 "Memory cleanup: removed {} known_destinations, evicted {} known_destinations, {} rate_limiter entries",
4525 kd_removed, kd_evicted, rl_removed
4526 );
4527 }
4528 }
4529
4530 self.announce_cache_cleanup_counter += 1;
4532 if self.announce_cache_cleanup_counter
4533 >= self.announce_cache_cleanup_interval_ticks
4534 {
4535 self.announce_cache_cleanup_counter = 0;
4536 if self.announce_cache.is_some()
4537 && self.cache_cleanup_active_hashes.is_none()
4538 {
4539 self.cache_cleanup_active_hashes =
4540 Some(self.engine.active_packet_hashes());
4541 self.cache_cleanup_entries = None;
4542 self.cache_cleanup_removed = 0;
4543 }
4544 }
4545
4546 if self.cache_cleanup_active_hashes.is_some() {
4548 if let Some(ref cache) = self.announce_cache {
4549 if self.cache_cleanup_entries.is_none() {
4550 match cache.entries() {
4551 Ok(entries) => self.cache_cleanup_entries = Some(entries),
4552 Err(e) => {
4553 log::warn!(
4554 "Announce cache cleanup failed to open directory: {}",
4555 e
4556 );
4557 self.cache_cleanup_active_hashes = None;
4558 self.cache_cleanup_entries = None;
4559 }
4560 }
4561 }
4562 }
4563
4564 if let Some(ref cache) = self.announce_cache {
4565 let active_hashes = self.cache_cleanup_active_hashes.as_ref().unwrap();
4566 let entries = match self.cache_cleanup_entries.as_mut() {
4567 Some(entries) => entries,
4568 None => continue,
4569 };
4570 match cache.clean_batch(
4571 active_hashes,
4572 entries,
4573 self.announce_cache_cleanup_batch_size,
4574 ) {
4575 Ok((removed, finished)) => {
4576 self.cache_cleanup_removed += removed;
4577 if finished {
4578 if self.cache_cleanup_removed > 0 {
4579 log::info!(
4580 "Announce cache cleanup complete: removed {} stale files",
4581 self.cache_cleanup_removed
4582 );
4583 }
4584 self.cache_cleanup_active_hashes = None;
4585 self.cache_cleanup_entries = None;
4586 }
4587 }
4588 Err(e) => {
4589 log::warn!("Announce cache cleanup failed: {}", e);
4590 self.cache_cleanup_active_hashes = None;
4591 self.cache_cleanup_entries = None;
4592 }
4593 }
4594 } else {
4595 self.cache_cleanup_active_hashes = None;
4596 self.cache_cleanup_entries = None;
4597 }
4598 }
4599 }
4600 Event::BeginDrain { timeout } => {
4601 self.begin_drain(timeout);
4602 }
4603 Event::InterfaceUp(id, new_writer, info) => {
4604 let wants_tunnel;
4605 let mut replay_shared_announces = false;
4606 if let Some(mut info) = info {
4607 log::info!("[{}] dynamic interface registered", id.0);
4609 wants_tunnel = info.wants_tunnel;
4610 let iface_type = infer_interface_type(&info.name);
4611 info.started = time::now();
4613 self.register_interface_runtime_defaults(&info);
4614 self.engine.register_interface(info.clone());
4615 if let Some(writer) = new_writer {
4616 let (writer, async_writer_metrics) =
4617 self.wrap_interface_writer(id, &info.name, writer);
4618 self.interfaces.insert(
4619 id,
4620 InterfaceEntry {
4621 id,
4622 info,
4623 writer,
4624 async_writer_metrics: Some(async_writer_metrics),
4625 enabled: true,
4626 online: true,
4627 dynamic: true,
4628 ifac: None,
4629 stats: InterfaceStats {
4630 started: time::now(),
4631 ..Default::default()
4632 },
4633 interface_type: iface_type,
4634 send_retry_at: None,
4635 send_retry_backoff: Duration::ZERO,
4636 },
4637 );
4638 }
4639 self.callbacks.on_interface_up(id);
4640 #[cfg(feature = "rns-hooks")]
4641 {
4642 let ctx = HookContext::Interface { interface_id: id.0 };
4643 let now = time::now();
4644 let engine_ref = EngineRef {
4645 engine: &self.engine,
4646 interfaces: &self.interfaces,
4647 link_manager: &self.link_manager,
4648 now,
4649 };
4650 let provider_events_enabled = self.provider_events_enabled();
4651 if let Some(ref e) = run_hook_inner(
4652 &mut self.hook_slots[HookPoint::InterfaceUp as usize].programs,
4653 &self.hook_manager,
4654 &engine_ref,
4655 &ctx,
4656 now,
4657 provider_events_enabled,
4658 ) {
4659 self.forward_hook_side_effects("InterfaceUp", e);
4660 }
4661 }
4662 } else {
4663 let is_local_client = self
4665 .interfaces
4666 .get(&id)
4667 .map(|entry| entry.info.is_local_client)
4668 .unwrap_or(false);
4669 replay_shared_announces = is_local_client
4670 && self.shared_reconnect_pending.remove(&id).unwrap_or(false);
4671 let interface_name = self
4672 .interfaces
4673 .get(&id)
4674 .map(|entry| entry.info.name.clone())
4675 .unwrap_or_else(|| format!("iface-{}", id.0));
4676 let wrapped_writer = if let Some(writer) = new_writer {
4677 Some(self.wrap_interface_writer(id, &interface_name, writer))
4678 } else {
4679 None
4680 };
4681 if let Some(entry) = self.interfaces.get_mut(&id) {
4682 log::info!("[{}] interface online", id.0);
4683 wants_tunnel = entry.info.wants_tunnel;
4684 entry.online = true;
4685 if let Some((writer, async_writer_metrics)) = wrapped_writer {
4686 log::info!("[{}] writer refreshed after reconnect", id.0);
4687 entry.writer = writer;
4688 entry.async_writer_metrics = Some(async_writer_metrics);
4689 }
4690 self.callbacks.on_interface_up(id);
4691 #[cfg(feature = "rns-hooks")]
4692 {
4693 let ctx = HookContext::Interface { interface_id: id.0 };
4694 let now = time::now();
4695 let engine_ref = EngineRef {
4696 engine: &self.engine,
4697 interfaces: &self.interfaces,
4698 link_manager: &self.link_manager,
4699 now,
4700 };
4701 let provider_events_enabled = self.provider_events_enabled();
4702 if let Some(ref e) = run_hook_inner(
4703 &mut self.hook_slots[HookPoint::InterfaceUp as usize].programs,
4704 &self.hook_manager,
4705 &engine_ref,
4706 &ctx,
4707 now,
4708 provider_events_enabled,
4709 ) {
4710 self.forward_hook_side_effects("InterfaceUp", e);
4711 }
4712 }
4713 } else {
4714 wants_tunnel = false;
4715 }
4716 }
4717
4718 if wants_tunnel {
4720 self.synthesize_tunnel_for_interface(id);
4721 }
4722 if replay_shared_announces {
4723 self.replay_shared_announces();
4724 }
4725 }
4726 Event::InterfaceDown(id) => {
4727 if let Some(entry) = self.interfaces.get(&id) {
4729 if let Some(tunnel_id) = entry.info.tunnel_id {
4730 self.engine.void_tunnel_interface(&tunnel_id);
4731 }
4732 }
4733
4734 if let Some(entry) = self.interfaces.get(&id) {
4735 let is_dynamic = entry.dynamic;
4736 let is_local_client = entry.info.is_local_client;
4737 let interface_name = entry.info.name.clone();
4738 if is_dynamic {
4739 log::info!("[{}] dynamic interface removed", id.0);
4741 self.interface_runtime_defaults.remove(&interface_name);
4742 self.engine.deregister_interface(id);
4743 self.interfaces.remove(&id);
4744 } else {
4745 log::info!("[{}] interface offline", id.0);
4747 self.interfaces.get_mut(&id).unwrap().online = false;
4748 if is_local_client {
4749 self.handle_shared_interface_down(id);
4750 }
4751 }
4752 self.callbacks.on_interface_down(id);
4753 #[cfg(feature = "rns-hooks")]
4754 {
4755 let ctx = HookContext::Interface { interface_id: id.0 };
4756 let now = time::now();
4757 let engine_ref = EngineRef {
4758 engine: &self.engine,
4759 interfaces: &self.interfaces,
4760 link_manager: &self.link_manager,
4761 now,
4762 };
4763 let provider_events_enabled = self.provider_events_enabled();
4764 if let Some(ref e) = run_hook_inner(
4765 &mut self.hook_slots[HookPoint::InterfaceDown as usize].programs,
4766 &self.hook_manager,
4767 &engine_ref,
4768 &ctx,
4769 now,
4770 provider_events_enabled,
4771 ) {
4772 self.forward_hook_side_effects("InterfaceDown", e);
4773 }
4774 }
4775 }
4776 }
4777 Event::SendOutbound {
4778 raw,
4779 dest_type,
4780 attached_interface,
4781 } => {
4782 if self.is_draining() {
4783 self.reject_new_work("send outbound packet");
4784 continue;
4785 }
4786 match RawPacket::unpack(&raw) {
4787 Ok(packet) => {
4788 let is_announce = packet.flags.packet_type
4789 == rns_core::constants::PACKET_TYPE_ANNOUNCE;
4790 if is_announce {
4791 log::debug!("SendOutbound: ANNOUNCE for {:02x?} (len={}, dest_type={}, attached={:?})",
4792 &packet.destination_hash[..4], raw.len(), dest_type, attached_interface);
4793 }
4794 if packet.flags.packet_type == rns_core::constants::PACKET_TYPE_DATA {
4796 self.sent_packets.insert(
4797 packet.packet_hash,
4798 (packet.destination_hash, time::now()),
4799 );
4800 }
4801 let actions = self.engine.handle_outbound(
4802 &packet,
4803 dest_type,
4804 attached_interface,
4805 time::now(),
4806 );
4807 if is_announce {
4808 log::debug!(
4809 "SendOutbound: announce routed to {} actions: {:?}",
4810 actions.len(),
4811 actions
4812 .iter()
4813 .map(|a| match a {
4814 TransportAction::SendOnInterface {
4815 interface, ..
4816 } => format!("SendOn({})", interface.0),
4817 TransportAction::BroadcastOnAllInterfaces {
4818 ..
4819 } => "BroadcastAll".to_string(),
4820 _ => "other".to_string(),
4821 })
4822 .collect::<Vec<_>>()
4823 );
4824 }
4825 self.dispatch_all(actions);
4826 }
4827 Err(e) => {
4828 log::warn!("SendOutbound: failed to unpack packet: {:?}", e);
4829 }
4830 }
4831 }
4832 Event::RegisterDestination {
4833 dest_hash,
4834 dest_type,
4835 } => {
4836 self.engine.register_destination(dest_hash, dest_type);
4837 self.local_destinations.insert(dest_hash, dest_type);
4838 }
4839 Event::StoreSharedAnnounce {
4840 dest_hash,
4841 name_hash,
4842 identity_prv_key,
4843 app_data,
4844 } => {
4845 self.shared_announces.insert(
4846 dest_hash,
4847 SharedAnnounceRecord {
4848 name_hash,
4849 identity_prv_key,
4850 app_data,
4851 },
4852 );
4853 }
4854 Event::DeregisterDestination { dest_hash } => {
4855 self.engine.deregister_destination(&dest_hash);
4856 self.local_destinations.remove(&dest_hash);
4857 self.shared_announces.remove(&dest_hash);
4858 }
4859 Event::Query(request, response_tx) => {
4860 let response = self.handle_query_mut(request);
4861 let _ = response_tx.send(response);
4862 }
4863 Event::DeregisterLinkDestination { dest_hash } => {
4864 self.link_manager.deregister_link_destination(&dest_hash);
4865 }
4866 Event::RegisterLinkDestination {
4867 dest_hash,
4868 sig_prv_bytes,
4869 sig_pub_bytes,
4870 resource_strategy,
4871 } => {
4872 let sig_prv =
4873 rns_crypto::ed25519::Ed25519PrivateKey::from_bytes(&sig_prv_bytes);
4874 let strat = match resource_strategy {
4875 1 => crate::link_manager::ResourceStrategy::AcceptAll,
4876 2 => crate::link_manager::ResourceStrategy::AcceptApp,
4877 _ => crate::link_manager::ResourceStrategy::AcceptNone,
4878 };
4879 self.link_manager.register_link_destination(
4880 dest_hash,
4881 sig_prv,
4882 sig_pub_bytes,
4883 strat,
4884 );
4885 self.engine
4887 .register_destination(dest_hash, rns_core::constants::DESTINATION_SINGLE);
4888 self.local_destinations
4889 .insert(dest_hash, rns_core::constants::DESTINATION_SINGLE);
4890 }
4891 Event::RegisterRequestHandler {
4892 path,
4893 allowed_list,
4894 handler,
4895 } => {
4896 self.link_manager.register_request_handler(
4897 &path,
4898 allowed_list,
4899 move |link_id, p, data, remote| handler(link_id, p, data, remote),
4900 );
4901 }
4902 Event::CreateLink {
4903 dest_hash,
4904 dest_sig_pub_bytes,
4905 response_tx,
4906 } => {
4907 if self.is_draining() {
4908 self.reject_new_work("create link");
4909 let _ = (dest_hash, dest_sig_pub_bytes);
4910 let _ = response_tx.send([0u8; 16]);
4911 continue;
4912 }
4913 let hops = self.engine.hops_to(&dest_hash).unwrap_or(0);
4914 let mtu = self
4915 .engine
4916 .next_hop_interface(&dest_hash)
4917 .and_then(|iface_id| self.interfaces.get(&iface_id))
4918 .map(|entry| entry.info.mtu)
4919 .unwrap_or(rns_core::constants::MTU as u32);
4920 let (link_id, link_actions) = self.link_manager.create_link(
4921 &dest_hash,
4922 &dest_sig_pub_bytes,
4923 hops,
4924 mtu,
4925 &mut self.rng,
4926 );
4927 let _ = response_tx.send(link_id);
4928 self.dispatch_link_actions(link_actions);
4929 }
4930 Event::SendRequest {
4931 link_id,
4932 path,
4933 data,
4934 } => {
4935 if self.is_draining() {
4936 self.reject_new_work("send link request");
4937 let _ = (link_id, path, data);
4938 continue;
4939 }
4940 let link_actions =
4941 self.link_manager
4942 .send_request(&link_id, &path, &data, &mut self.rng);
4943 self.dispatch_link_actions(link_actions);
4944 }
4945 Event::IdentifyOnLink {
4946 link_id,
4947 identity_prv_key,
4948 } => {
4949 if self.is_draining() {
4950 self.reject_new_work("identify on link");
4951 let _ = (link_id, identity_prv_key);
4952 continue;
4953 }
4954 let identity =
4955 rns_crypto::identity::Identity::from_private_key(&identity_prv_key);
4956 let link_actions =
4957 self.link_manager
4958 .identify(&link_id, &identity, &mut self.rng);
4959 self.dispatch_link_actions(link_actions);
4960 }
4961 Event::TeardownLink { link_id } => {
4962 let link_actions = self.link_manager.teardown_link(&link_id);
4963 self.dispatch_link_actions(link_actions);
4964 }
4965 Event::SendResource {
4966 link_id,
4967 data,
4968 metadata,
4969 } => {
4970 if self.is_draining() {
4971 self.reject_new_work("send resource");
4972 let _ = (link_id, data, metadata);
4973 continue;
4974 }
4975 let link_actions = self.link_manager.send_resource(
4976 &link_id,
4977 &data,
4978 metadata.as_deref(),
4979 &mut self.rng,
4980 );
4981 self.dispatch_link_actions(link_actions);
4982 }
4983 Event::SetResourceStrategy { link_id, strategy } => {
4984 use crate::link_manager::ResourceStrategy;
4985 let strat = match strategy {
4986 0 => ResourceStrategy::AcceptNone,
4987 1 => ResourceStrategy::AcceptAll,
4988 2 => ResourceStrategy::AcceptApp,
4989 _ => ResourceStrategy::AcceptNone,
4990 };
4991 self.link_manager.set_resource_strategy(&link_id, strat);
4992 }
4993 Event::AcceptResource {
4994 link_id,
4995 resource_hash,
4996 accept,
4997 } => {
4998 if self.is_draining() && accept {
4999 self.reject_new_work("accept resource");
5000 let _ = (link_id, resource_hash, accept);
5001 continue;
5002 }
5003 let link_actions = self.link_manager.accept_resource(
5004 &link_id,
5005 &resource_hash,
5006 accept,
5007 &mut self.rng,
5008 );
5009 self.dispatch_link_actions(link_actions);
5010 }
5011 Event::SendChannelMessage {
5012 link_id,
5013 msgtype,
5014 payload,
5015 response_tx,
5016 } => {
5017 if self.is_draining() {
5018 self.reject_new_work("send channel message");
5019 let _ = response_tx.send(Err(self.drain_error("send channel message")));
5020 continue;
5021 }
5022 match self.link_manager.send_channel_message(
5023 &link_id,
5024 msgtype,
5025 &payload,
5026 &mut self.rng,
5027 ) {
5028 Ok(link_actions) => {
5029 self.dispatch_link_actions(link_actions);
5030 let _ = response_tx.send(Ok(()));
5031 }
5032 Err(err) => {
5033 let _ = response_tx.send(Err(err));
5034 }
5035 }
5036 }
5037 Event::SendOnLink {
5038 link_id,
5039 data,
5040 context,
5041 } => {
5042 if self.is_draining() {
5043 self.reject_new_work("send link payload");
5044 let _ = (link_id, data, context);
5045 continue;
5046 }
5047 let link_actions =
5048 self.link_manager
5049 .send_on_link(&link_id, &data, context, &mut self.rng);
5050 self.dispatch_link_actions(link_actions);
5051 }
5052 Event::RequestPath { dest_hash } => {
5053 if self.is_draining() {
5054 self.reject_new_work("request path");
5055 let _ = dest_hash;
5056 continue;
5057 }
5058 self.handle_request_path(dest_hash);
5059 }
5060 Event::RegisterProofStrategy {
5061 dest_hash,
5062 strategy,
5063 signing_key,
5064 } => {
5065 let identity = signing_key
5066 .map(|key| rns_crypto::identity::Identity::from_private_key(&key));
5067 self.proof_strategies
5068 .insert(dest_hash, (strategy, identity));
5069 }
5070 Event::ProposeDirectConnect { link_id } => {
5071 if self.is_draining() {
5072 self.reject_new_work("propose direct connect");
5073 let _ = link_id;
5074 continue;
5075 }
5076 let derived_key = self.link_manager.get_derived_key(&link_id);
5077 if let Some(dk) = derived_key {
5078 let tx = self.get_event_sender();
5079 let hp_actions =
5080 self.holepunch_manager
5081 .propose(link_id, &dk, &mut self.rng, &tx);
5082 self.dispatch_holepunch_actions(hp_actions);
5083 } else {
5084 log::warn!(
5085 "Cannot propose direct connect: no derived key for link {:02x?}",
5086 &link_id[..4]
5087 );
5088 }
5089 }
5090 Event::SetDirectConnectPolicy { policy } => {
5091 self.holepunch_manager.set_policy(policy);
5092 }
5093 Event::HolePunchProbeResult {
5094 link_id,
5095 session_id,
5096 observed_addr,
5097 socket,
5098 probe_server,
5099 } => {
5100 let hp_actions = self.holepunch_manager.handle_probe_result(
5101 link_id,
5102 session_id,
5103 observed_addr,
5104 socket,
5105 probe_server,
5106 );
5107 self.dispatch_holepunch_actions(hp_actions);
5108 }
5109 Event::HolePunchProbeFailed {
5110 link_id,
5111 session_id,
5112 } => {
5113 let hp_actions = self
5114 .holepunch_manager
5115 .handle_probe_failed(link_id, session_id);
5116 self.dispatch_holepunch_actions(hp_actions);
5117 }
5118 Event::LoadHook {
5119 name,
5120 wasm_bytes,
5121 attach_point,
5122 priority,
5123 response_tx,
5124 } => {
5125 #[cfg(feature = "rns-hooks")]
5126 {
5127 let result = (|| -> Result<(), String> {
5128 let point_idx = crate::config::parse_hook_point(&attach_point)
5129 .ok_or_else(|| format!("unknown hook point '{}'", attach_point))?;
5130 let mgr = self
5131 .hook_manager
5132 .as_ref()
5133 .ok_or_else(|| "hook manager not available".to_string())?;
5134 let program = mgr
5135 .compile(name.clone(), &wasm_bytes, priority)
5136 .map_err(|e| format!("compile error: {}", e))?;
5137 self.hook_slots[point_idx].attach(program);
5138 log::info!(
5139 "Loaded hook '{}' at point {} (priority {})",
5140 name,
5141 attach_point,
5142 priority
5143 );
5144 Ok(())
5145 })();
5146 let _ = response_tx.send(result);
5147 }
5148 #[cfg(not(feature = "rns-hooks"))]
5149 {
5150 let _ = (name, wasm_bytes, attach_point, priority);
5151 let _ = response_tx.send(Err("hooks not enabled".to_string()));
5152 }
5153 }
5154 Event::UnloadHook {
5155 name,
5156 attach_point,
5157 response_tx,
5158 } => {
5159 #[cfg(feature = "rns-hooks")]
5160 {
5161 let result = (|| -> Result<(), String> {
5162 let point_idx = crate::config::parse_hook_point(&attach_point)
5163 .ok_or_else(|| format!("unknown hook point '{}'", attach_point))?;
5164 match self.hook_slots[point_idx].detach(&name) {
5165 Some(_) => {
5166 log::info!(
5167 "Unloaded hook '{}' from point {}",
5168 name,
5169 attach_point
5170 );
5171 Ok(())
5172 }
5173 None => Err(format!(
5174 "hook '{}' not found at point '{}'",
5175 name, attach_point
5176 )),
5177 }
5178 })();
5179 let _ = response_tx.send(result);
5180 }
5181 #[cfg(not(feature = "rns-hooks"))]
5182 {
5183 let _ = (name, attach_point);
5184 let _ = response_tx.send(Err("hooks not enabled".to_string()));
5185 }
5186 }
5187 Event::ReloadHook {
5188 name,
5189 attach_point,
5190 wasm_bytes,
5191 response_tx,
5192 } => {
5193 #[cfg(feature = "rns-hooks")]
5194 {
5195 let result = (|| -> Result<(), String> {
5196 let point_idx = crate::config::parse_hook_point(&attach_point)
5197 .ok_or_else(|| format!("unknown hook point '{}'", attach_point))?;
5198 let old =
5199 self.hook_slots[point_idx].detach(&name).ok_or_else(|| {
5200 format!("hook '{}' not found at point '{}'", name, attach_point)
5201 })?;
5202 let priority = old.priority;
5203 let mgr = match self.hook_manager.as_ref() {
5204 Some(m) => m,
5205 None => {
5206 self.hook_slots[point_idx].attach(old);
5207 return Err("hook manager not available".to_string());
5208 }
5209 };
5210 match mgr.compile(name.clone(), &wasm_bytes, priority) {
5211 Ok(program) => {
5212 self.hook_slots[point_idx].attach(program);
5213 log::info!(
5214 "Reloaded hook '{}' at point {} (priority {})",
5215 name,
5216 attach_point,
5217 priority
5218 );
5219 Ok(())
5220 }
5221 Err(e) => {
5222 self.hook_slots[point_idx].attach(old);
5223 Err(format!("compile error: {}", e))
5224 }
5225 }
5226 })();
5227 let _ = response_tx.send(result);
5228 }
5229 #[cfg(not(feature = "rns-hooks"))]
5230 {
5231 let _ = (name, attach_point, wasm_bytes);
5232 let _ = response_tx.send(Err("hooks not enabled".to_string()));
5233 }
5234 }
5235 Event::SetHookEnabled {
5236 name,
5237 attach_point,
5238 enabled,
5239 response_tx,
5240 } => {
5241 #[cfg(feature = "rns-hooks")]
5242 {
5243 let result = self.update_hook_program(&name, &attach_point, |program| {
5244 program.enabled = enabled;
5245 });
5246 if result.is_ok() {
5247 log::info!(
5248 "{} hook '{}' at point {}",
5249 if enabled { "Enabled" } else { "Disabled" },
5250 name,
5251 attach_point,
5252 );
5253 }
5254 let _ = response_tx.send(result);
5255 }
5256 #[cfg(not(feature = "rns-hooks"))]
5257 {
5258 let _ = (name, attach_point, enabled);
5259 let _ = response_tx.send(Err("hooks not enabled".to_string()));
5260 }
5261 }
5262 Event::SetHookPriority {
5263 name,
5264 attach_point,
5265 priority,
5266 response_tx,
5267 } => {
5268 #[cfg(feature = "rns-hooks")]
5269 {
5270 let result = self.update_hook_program(&name, &attach_point, |program| {
5271 program.priority = priority;
5272 });
5273 if result.is_ok() {
5274 let point_idx = crate::config::parse_hook_point(&attach_point)
5275 .expect("validated hook point");
5276 self.hook_slots[point_idx]
5277 .programs
5278 .sort_by(|a, b| b.priority.cmp(&a.priority));
5279 log::info!(
5280 "Updated hook '{}' at point {} to priority {}",
5281 name,
5282 attach_point,
5283 priority,
5284 );
5285 }
5286 let _ = response_tx.send(result);
5287 }
5288 #[cfg(not(feature = "rns-hooks"))]
5289 {
5290 let _ = (name, attach_point, priority);
5291 let _ = response_tx.send(Err("hooks not enabled".to_string()));
5292 }
5293 }
5294 Event::ListHooks { response_tx } => {
5295 #[cfg(feature = "rns-hooks")]
5296 {
5297 let hook_point_names = [
5298 "PreIngress",
5299 "PreDispatch",
5300 "AnnounceReceived",
5301 "PathUpdated",
5302 "AnnounceRetransmit",
5303 "LinkRequestReceived",
5304 "LinkEstablished",
5305 "LinkClosed",
5306 "InterfaceUp",
5307 "InterfaceDown",
5308 "InterfaceConfigChanged",
5309 "BackbonePeerConnected",
5310 "BackbonePeerDisconnected",
5311 "BackbonePeerIdleTimeout",
5312 "BackbonePeerWriteStall",
5313 "BackbonePeerPenalty",
5314 "SendOnInterface",
5315 "BroadcastOnAllInterfaces",
5316 "DeliverLocal",
5317 "TunnelSynthesize",
5318 "Tick",
5319 ];
5320 let mut infos = Vec::new();
5321 for (idx, slot) in self.hook_slots.iter().enumerate() {
5322 let point_name = hook_point_names.get(idx).unwrap_or(&"Unknown");
5323 for prog in &slot.programs {
5324 infos.push(crate::event::HookInfo {
5325 name: prog.name.clone(),
5326 attach_point: point_name.to_string(),
5327 priority: prog.priority,
5328 enabled: prog.enabled,
5329 consecutive_traps: prog.consecutive_traps,
5330 });
5331 }
5332 }
5333 let _ = response_tx.send(infos);
5334 }
5335 #[cfg(not(feature = "rns-hooks"))]
5336 {
5337 let _ = response_tx.send(Vec::new());
5338 }
5339 }
5340 Event::InterfaceConfigChanged(id) => {
5341 #[cfg(feature = "rns-hooks")]
5342 {
5343 let ctx = HookContext::Interface { interface_id: id.0 };
5344 let now = time::now();
5345 let engine_ref = EngineRef {
5346 engine: &self.engine,
5347 interfaces: &self.interfaces,
5348 link_manager: &self.link_manager,
5349 now,
5350 };
5351 let provider_events_enabled = self.provider_events_enabled();
5352 if let Some(ref e) = run_hook_inner(
5353 &mut self.hook_slots[HookPoint::InterfaceConfigChanged as usize]
5354 .programs,
5355 &self.hook_manager,
5356 &engine_ref,
5357 &ctx,
5358 now,
5359 provider_events_enabled,
5360 ) {
5361 self.forward_hook_side_effects("InterfaceConfigChanged", e);
5362 }
5363 }
5364 #[cfg(not(feature = "rns-hooks"))]
5365 let _ = id;
5366 }
5367 Event::BackbonePeerConnected {
5368 server_interface_id,
5369 peer_interface_id,
5370 peer_ip,
5371 peer_port,
5372 } => {
5373 #[cfg(feature = "rns-hooks")]
5374 {
5375 self.run_backbone_peer_hook(
5376 "BackbonePeerConnected",
5377 HookPoint::BackbonePeerConnected,
5378 &BackbonePeerHookEvent {
5379 server_interface_id,
5380 peer_interface_id: Some(peer_interface_id),
5381 peer_ip,
5382 peer_port,
5383 connected_for: Duration::ZERO,
5384 had_received_data: false,
5385 penalty_level: 0,
5386 blacklist_for: Duration::ZERO,
5387 },
5388 );
5389 }
5390 #[cfg(not(feature = "rns-hooks"))]
5391 let _ = (server_interface_id, peer_interface_id, peer_ip, peer_port);
5392 }
5393 Event::BackbonePeerDisconnected {
5394 server_interface_id,
5395 peer_interface_id,
5396 peer_ip,
5397 peer_port,
5398 connected_for,
5399 had_received_data,
5400 } => {
5401 #[cfg(feature = "rns-hooks")]
5402 {
5403 self.run_backbone_peer_hook(
5404 "BackbonePeerDisconnected",
5405 HookPoint::BackbonePeerDisconnected,
5406 &BackbonePeerHookEvent {
5407 server_interface_id,
5408 peer_interface_id: Some(peer_interface_id),
5409 peer_ip,
5410 peer_port,
5411 connected_for,
5412 had_received_data,
5413 penalty_level: 0,
5414 blacklist_for: Duration::ZERO,
5415 },
5416 );
5417 }
5418 #[cfg(not(feature = "rns-hooks"))]
5419 let _ = (
5420 server_interface_id,
5421 peer_interface_id,
5422 peer_ip,
5423 peer_port,
5424 connected_for,
5425 had_received_data,
5426 );
5427 }
5428 Event::BackbonePeerIdleTimeout {
5429 server_interface_id,
5430 peer_interface_id,
5431 peer_ip,
5432 peer_port,
5433 connected_for,
5434 } => {
5435 #[cfg(feature = "rns-hooks")]
5436 {
5437 self.run_backbone_peer_hook(
5438 "BackbonePeerIdleTimeout",
5439 HookPoint::BackbonePeerIdleTimeout,
5440 &BackbonePeerHookEvent {
5441 server_interface_id,
5442 peer_interface_id: Some(peer_interface_id),
5443 peer_ip,
5444 peer_port,
5445 connected_for,
5446 had_received_data: false,
5447 penalty_level: 0,
5448 blacklist_for: Duration::ZERO,
5449 },
5450 );
5451 }
5452 #[cfg(not(feature = "rns-hooks"))]
5453 let _ = (
5454 server_interface_id,
5455 peer_interface_id,
5456 peer_ip,
5457 peer_port,
5458 connected_for,
5459 );
5460 }
5461 Event::BackbonePeerWriteStall {
5462 server_interface_id,
5463 peer_interface_id,
5464 peer_ip,
5465 peer_port,
5466 connected_for,
5467 } => {
5468 #[cfg(feature = "rns-hooks")]
5469 {
5470 self.run_backbone_peer_hook(
5471 "BackbonePeerWriteStall",
5472 HookPoint::BackbonePeerWriteStall,
5473 &BackbonePeerHookEvent {
5474 server_interface_id,
5475 peer_interface_id: Some(peer_interface_id),
5476 peer_ip,
5477 peer_port,
5478 connected_for,
5479 had_received_data: false,
5480 penalty_level: 0,
5481 blacklist_for: Duration::ZERO,
5482 },
5483 );
5484 }
5485 #[cfg(not(feature = "rns-hooks"))]
5486 let _ = (
5487 server_interface_id,
5488 peer_interface_id,
5489 peer_ip,
5490 peer_port,
5491 connected_for,
5492 );
5493 }
5494 Event::BackbonePeerPenalty {
5495 server_interface_id,
5496 peer_ip,
5497 penalty_level,
5498 blacklist_for,
5499 } => {
5500 #[cfg(feature = "rns-hooks")]
5501 {
5502 self.run_backbone_peer_hook(
5503 "BackbonePeerPenalty",
5504 HookPoint::BackbonePeerPenalty,
5505 &BackbonePeerHookEvent {
5506 server_interface_id,
5507 peer_interface_id: None,
5508 peer_ip,
5509 peer_port: 0,
5510 connected_for: Duration::ZERO,
5511 had_received_data: false,
5512 penalty_level,
5513 blacklist_for,
5514 },
5515 );
5516 }
5517 #[cfg(not(feature = "rns-hooks"))]
5518 let _ = (server_interface_id, peer_ip, penalty_level, blacklist_for);
5519 }
5520 Event::Shutdown => {
5521 self.lifecycle_state = LifecycleState::Stopped;
5522 break;
5523 }
5524 }
5525 }
5526 }
5527
5528 fn handle_query(&self, request: QueryRequest) -> QueryResponse {
5530 match request {
5531 QueryRequest::InterfaceStats => {
5532 let mut interfaces = Vec::new();
5533 let mut total_rxb: u64 = 0;
5534 let mut total_txb: u64 = 0;
5535 for entry in self.interfaces.values() {
5536 total_rxb += entry.stats.rxb;
5537 total_txb += entry.stats.txb;
5538 interfaces.push(SingleInterfaceStat {
5539 id: entry.info.id.0,
5540 name: entry.info.name.clone(),
5541 status: entry.online && entry.enabled,
5542 mode: entry.info.mode,
5543 rxb: entry.stats.rxb,
5544 txb: entry.stats.txb,
5545 rx_packets: entry.stats.rx_packets,
5546 tx_packets: entry.stats.tx_packets,
5547 bitrate: entry.info.bitrate,
5548 ifac_size: entry.ifac.as_ref().map(|s| s.size),
5549 started: entry.stats.started,
5550 ia_freq: entry.stats.incoming_announce_freq(),
5551 oa_freq: entry.stats.outgoing_announce_freq(),
5552 interface_type: entry.interface_type.clone(),
5553 });
5554 }
5555 interfaces.sort_by(|a, b| a.name.cmp(&b.name));
5557 QueryResponse::InterfaceStats(InterfaceStatsResponse {
5558 interfaces,
5559 transport_id: self.engine.identity_hash().copied(),
5560 transport_enabled: self.engine.transport_enabled(),
5561 transport_uptime: time::now() - self.started,
5562 total_rxb,
5563 total_txb,
5564 probe_responder: self.probe_responder_hash,
5565 })
5566 }
5567 QueryRequest::BackboneInterfaces => {
5568 QueryResponse::BackboneInterfaces(self.list_backbone_interfaces())
5569 }
5570 QueryRequest::ProviderBridgeStats => {
5571 #[cfg(feature = "rns-hooks")]
5572 {
5573 QueryResponse::ProviderBridgeStats(
5574 self.provider_bridge.as_ref().map(|bridge| bridge.stats()),
5575 )
5576 }
5577 #[cfg(not(feature = "rns-hooks"))]
5578 {
5579 QueryResponse::ProviderBridgeStats(None::<crate::event::ProviderBridgeStats>)
5580 }
5581 }
5582 QueryRequest::DrainStatus => QueryResponse::DrainStatus(self.drain_status()),
5583 QueryRequest::PathTable { max_hops } => {
5584 let entries: Vec<PathTableEntry> = self
5585 .engine
5586 .path_table_entries()
5587 .filter(|(_, entry)| max_hops.map_or(true, |max| entry.hops <= max))
5588 .map(|(hash, entry)| {
5589 let iface_name = self
5590 .interfaces
5591 .get(&entry.receiving_interface)
5592 .map(|e| e.info.name.clone())
5593 .or_else(|| {
5594 self.engine
5595 .interface_info(&entry.receiving_interface)
5596 .map(|i| i.name.clone())
5597 })
5598 .unwrap_or_default();
5599 PathTableEntry {
5600 hash: *hash,
5601 timestamp: entry.timestamp,
5602 via: entry.next_hop,
5603 hops: entry.hops,
5604 expires: entry.expires,
5605 interface: entry.receiving_interface,
5606 interface_name: iface_name,
5607 }
5608 })
5609 .collect();
5610 QueryResponse::PathTable(entries)
5611 }
5612 QueryRequest::RateTable => {
5613 let entries: Vec<RateTableEntry> = self
5614 .engine
5615 .rate_limiter()
5616 .entries()
5617 .map(|(hash, entry)| RateTableEntry {
5618 hash: *hash,
5619 last: entry.last,
5620 rate_violations: entry.rate_violations,
5621 blocked_until: entry.blocked_until,
5622 timestamps: entry.timestamps.clone(),
5623 })
5624 .collect();
5625 QueryResponse::RateTable(entries)
5626 }
5627 QueryRequest::NextHop { dest_hash } => {
5628 let resp = self
5629 .engine
5630 .next_hop(&dest_hash)
5631 .map(|next_hop| NextHopResponse {
5632 next_hop,
5633 hops: self.engine.hops_to(&dest_hash).unwrap_or(0),
5634 interface: self
5635 .engine
5636 .next_hop_interface(&dest_hash)
5637 .unwrap_or(InterfaceId(0)),
5638 });
5639 QueryResponse::NextHop(resp)
5640 }
5641 QueryRequest::NextHopIfName { dest_hash } => {
5642 let name = self
5643 .engine
5644 .next_hop_interface(&dest_hash)
5645 .and_then(|id| self.interfaces.get(&id))
5646 .map(|entry| entry.info.name.clone());
5647 QueryResponse::NextHopIfName(name)
5648 }
5649 QueryRequest::LinkCount => QueryResponse::LinkCount(
5650 self.engine.link_table_count() + self.link_manager.link_count(),
5651 ),
5652 QueryRequest::DropPath { .. } => {
5653 QueryResponse::DropPath(false)
5655 }
5656 QueryRequest::DropAllVia { .. } => QueryResponse::DropAllVia(0),
5657 QueryRequest::DropAnnounceQueues => QueryResponse::DropAnnounceQueues,
5658 QueryRequest::TransportIdentity => {
5659 QueryResponse::TransportIdentity(self.engine.identity_hash().copied())
5660 }
5661 QueryRequest::GetBlackholed => {
5662 let now = time::now();
5663 let entries: Vec<BlackholeInfo> = self
5664 .engine
5665 .blackholed_entries()
5666 .filter(|(_, e)| e.expires == 0.0 || e.expires > now)
5667 .map(|(hash, entry)| BlackholeInfo {
5668 identity_hash: *hash,
5669 created: entry.created,
5670 expires: entry.expires,
5671 reason: entry.reason.clone(),
5672 })
5673 .collect();
5674 QueryResponse::Blackholed(entries)
5675 }
5676 QueryRequest::BlackholeIdentity { .. } | QueryRequest::UnblackholeIdentity { .. } => {
5677 QueryResponse::BlackholeResult(false)
5679 }
5680 QueryRequest::InjectPath { .. } => {
5681 QueryResponse::InjectPath(false)
5683 }
5684 QueryRequest::InjectIdentity { .. } => {
5685 QueryResponse::InjectIdentity(false)
5687 }
5688 QueryRequest::HasPath { dest_hash } => {
5689 QueryResponse::HasPath(self.engine.has_path(&dest_hash))
5690 }
5691 QueryRequest::HopsTo { dest_hash } => {
5692 QueryResponse::HopsTo(self.engine.hops_to(&dest_hash))
5693 }
5694 QueryRequest::RecallIdentity { dest_hash } => {
5695 QueryResponse::RecallIdentity(self.known_destinations.get(&dest_hash).cloned())
5696 }
5697 QueryRequest::LocalDestinations => {
5698 let entries: Vec<LocalDestinationEntry> = self
5699 .local_destinations
5700 .iter()
5701 .map(|(hash, dest_type)| LocalDestinationEntry {
5702 hash: *hash,
5703 dest_type: *dest_type,
5704 })
5705 .collect();
5706 QueryResponse::LocalDestinations(entries)
5707 }
5708 QueryRequest::Links => QueryResponse::Links(self.link_manager.link_entries()),
5709 QueryRequest::Resources => {
5710 QueryResponse::Resources(self.link_manager.resource_entries())
5711 }
5712 QueryRequest::DiscoveredInterfaces {
5713 only_available,
5714 only_transport,
5715 } => {
5716 let mut interfaces = self.discovered_interfaces.list().unwrap_or_default();
5717 crate::discovery::filter_and_sort_interfaces(
5718 &mut interfaces,
5719 only_available,
5720 only_transport,
5721 );
5722 QueryResponse::DiscoveredInterfaces(interfaces)
5723 }
5724 QueryRequest::ListRuntimeConfig => {
5725 QueryResponse::RuntimeConfigList(self.list_runtime_config())
5726 }
5727 QueryRequest::GetRuntimeConfig { key } => {
5728 QueryResponse::RuntimeConfigEntry(self.runtime_config_entry(&key))
5729 }
5730 QueryRequest::BackbonePeerState { interface_name } => QueryResponse::BackbonePeerState(
5731 self.list_backbone_peer_state(interface_name.as_deref()),
5732 ),
5733 QueryRequest::SendProbe { .. } => QueryResponse::SendProbe(None),
5735 QueryRequest::CheckProof { .. } => QueryResponse::CheckProof(None),
5736 QueryRequest::SetRuntimeConfig { .. } => {
5737 QueryResponse::RuntimeConfigSet(Err(RuntimeConfigError {
5738 code: RuntimeConfigErrorCode::Unsupported,
5739 message: "mutating runtime config is handled separately".to_string(),
5740 }))
5741 }
5742 QueryRequest::ResetRuntimeConfig { .. } => {
5743 QueryResponse::RuntimeConfigReset(Err(RuntimeConfigError {
5744 code: RuntimeConfigErrorCode::Unsupported,
5745 message: "mutating runtime config is handled separately".to_string(),
5746 }))
5747 }
5748 QueryRequest::ClearBackbonePeerState { .. } => {
5749 QueryResponse::ClearBackbonePeerState(false)
5750 }
5751 QueryRequest::BlacklistBackbonePeer { .. } => {
5752 QueryResponse::BlacklistBackbonePeer(false)
5753 }
5754 }
5755 }
5756
5757 fn handle_query_mut(&mut self, request: QueryRequest) -> QueryResponse {
5759 match request {
5760 QueryRequest::BlackholeIdentity {
5761 identity_hash,
5762 duration_hours,
5763 reason,
5764 } => {
5765 let now = time::now();
5766 self.engine
5767 .blackhole_identity(identity_hash, now, duration_hours, reason);
5768 QueryResponse::BlackholeResult(true)
5769 }
5770 QueryRequest::UnblackholeIdentity { identity_hash } => {
5771 let result = self.engine.unblackhole_identity(&identity_hash);
5772 QueryResponse::UnblackholeResult(result)
5773 }
5774 QueryRequest::DropPath { dest_hash } => {
5775 QueryResponse::DropPath(self.engine.drop_path(&dest_hash))
5776 }
5777 QueryRequest::DropAllVia { transport_hash } => {
5778 QueryResponse::DropAllVia(self.engine.drop_all_via(&transport_hash))
5779 }
5780 QueryRequest::DropAnnounceQueues => {
5781 self.engine.drop_announce_queues();
5782 QueryResponse::DropAnnounceQueues
5783 }
5784 QueryRequest::ClearBackbonePeerState {
5785 interface_name,
5786 peer_ip,
5787 } => QueryResponse::ClearBackbonePeerState(
5788 self.clear_backbone_peer_state(&interface_name, peer_ip),
5789 ),
5790 QueryRequest::BlacklistBackbonePeer {
5791 interface_name,
5792 peer_ip,
5793 duration,
5794 reason,
5795 penalty_level,
5796 } => QueryResponse::BlacklistBackbonePeer(self.blacklist_backbone_peer(
5797 &interface_name,
5798 peer_ip,
5799 duration,
5800 reason,
5801 penalty_level,
5802 )),
5803 QueryRequest::DrainStatus => QueryResponse::DrainStatus(self.drain_status()),
5804 QueryRequest::InjectPath {
5805 dest_hash,
5806 next_hop,
5807 hops,
5808 expires,
5809 interface_name,
5810 packet_hash,
5811 } => {
5812 let iface_id = self
5814 .interfaces
5815 .iter()
5816 .find(|(_, entry)| entry.info.name == interface_name)
5817 .map(|(id, _)| *id);
5818 match iface_id {
5819 Some(id) => {
5820 let entry = PathEntry {
5821 timestamp: time::now(),
5822 next_hop,
5823 hops,
5824 expires,
5825 random_blobs: Vec::new(),
5826 receiving_interface: id,
5827 packet_hash,
5828 announce_raw: None,
5829 };
5830 self.engine.inject_path(dest_hash, entry);
5831 QueryResponse::InjectPath(true)
5832 }
5833 None => QueryResponse::InjectPath(false),
5834 }
5835 }
5836 QueryRequest::InjectIdentity {
5837 dest_hash,
5838 identity_hash,
5839 public_key,
5840 app_data,
5841 hops,
5842 received_at,
5843 } => {
5844 self.upsert_known_destination(
5845 dest_hash,
5846 crate::destination::AnnouncedIdentity {
5847 dest_hash: rns_core::types::DestHash(dest_hash),
5848 identity_hash: rns_core::types::IdentityHash(identity_hash),
5849 public_key,
5850 app_data,
5851 hops,
5852 received_at,
5853 receiving_interface: rns_core::transport::types::InterfaceId(0),
5854 },
5855 );
5856 QueryResponse::InjectIdentity(true)
5857 }
5858 QueryRequest::SendProbe {
5859 dest_hash,
5860 payload_size,
5861 } => {
5862 let announced = self.known_destinations.get(&dest_hash).cloned();
5864 match announced {
5865 Some(recalled) => {
5866 let remote_id =
5868 rns_crypto::identity::Identity::from_public_key(&recalled.public_key);
5869 let mut payload = vec![0u8; payload_size];
5870 self.rng.fill_bytes(&mut payload);
5871 match remote_id.encrypt(&payload, &mut self.rng) {
5872 Ok(ciphertext) => {
5873 let flags = rns_core::packet::PacketFlags {
5875 header_type: rns_core::constants::HEADER_1,
5876 context_flag: rns_core::constants::FLAG_UNSET,
5877 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
5878 destination_type: rns_core::constants::DESTINATION_SINGLE,
5879 packet_type: rns_core::constants::PACKET_TYPE_DATA,
5880 };
5881 match RawPacket::pack(
5882 flags,
5883 0,
5884 &dest_hash,
5885 None,
5886 rns_core::constants::CONTEXT_NONE,
5887 &ciphertext,
5888 ) {
5889 Ok(packet) => {
5890 let packet_hash = packet.packet_hash;
5891 let hops = self.engine.hops_to(&dest_hash).unwrap_or(0);
5892 self.sent_packets
5894 .insert(packet_hash, (dest_hash, time::now()));
5895 let actions = self.engine.handle_outbound(
5897 &packet,
5898 rns_core::constants::DESTINATION_SINGLE,
5899 None,
5900 time::now(),
5901 );
5902 self.dispatch_all(actions);
5903 log::debug!(
5904 "Sent probe ({} bytes) to {:02x?}",
5905 payload_size,
5906 &dest_hash[..4],
5907 );
5908 QueryResponse::SendProbe(Some((packet_hash, hops)))
5909 }
5910 Err(_) => {
5911 log::warn!("Failed to pack probe packet");
5912 QueryResponse::SendProbe(None)
5913 }
5914 }
5915 }
5916 Err(_) => {
5917 log::warn!("Failed to encrypt probe payload");
5918 QueryResponse::SendProbe(None)
5919 }
5920 }
5921 }
5922 None => {
5923 log::debug!("No known identity for probe dest {:02x?}", &dest_hash[..4]);
5924 QueryResponse::SendProbe(None)
5925 }
5926 }
5927 }
5928 QueryRequest::CheckProof { packet_hash } => {
5929 match self.completed_proofs.remove(&packet_hash) {
5930 Some((rtt, _received)) => QueryResponse::CheckProof(Some(rtt)),
5931 None => QueryResponse::CheckProof(None),
5932 }
5933 }
5934 QueryRequest::SetRuntimeConfig { key, value } => {
5935 let result = match key.as_str() {
5936 "global.tick_interval_ms" => match Self::expect_u64(value, &key) {
5937 Ok(value) => {
5938 let clamped = value.clamp(100, 10_000);
5939 self.tick_interval_ms.store(clamped, Ordering::Relaxed);
5940 Ok(())
5941 }
5942 Err(err) => Err(err),
5943 },
5944 "global.known_destinations_ttl_secs" => match Self::expect_f64(value, &key) {
5945 Ok(value) => {
5946 self.known_destinations_ttl = value;
5947 Ok(())
5948 }
5949 Err(err) => Err(err),
5950 },
5951 "global.rate_limiter_ttl_secs" => match Self::expect_f64(value, &key) {
5952 Ok(value) if value >= 0.0 => {
5953 self.rate_limiter_ttl_secs = value;
5954 Ok(())
5955 }
5956 Ok(_) => Err(RuntimeConfigError {
5957 code: RuntimeConfigErrorCode::InvalidValue,
5958 message: format!("{} must be >= 0", key),
5959 }),
5960 Err(err) => Err(err),
5961 },
5962 "global.known_destinations_cleanup_interval_ticks" => {
5963 match Self::expect_u64(value, &key) {
5964 Ok(value) if value > 0 => {
5965 self.known_destinations_cleanup_interval_ticks = value as u32;
5966 Ok(())
5967 }
5968 Ok(_) => Err(RuntimeConfigError {
5969 code: RuntimeConfigErrorCode::InvalidValue,
5970 message: format!("{} must be >= 1", key),
5971 }),
5972 Err(err) => Err(err),
5973 }
5974 }
5975 "global.announce_cache_cleanup_interval_ticks" => {
5976 match Self::expect_u64(value, &key) {
5977 Ok(value) if value > 0 => {
5978 self.announce_cache_cleanup_interval_ticks = value as u32;
5979 Ok(())
5980 }
5981 Ok(_) => Err(RuntimeConfigError {
5982 code: RuntimeConfigErrorCode::InvalidValue,
5983 message: format!("{} must be >= 1", key),
5984 }),
5985 Err(err) => Err(err),
5986 }
5987 }
5988 "global.announce_cache_cleanup_batch_size" => {
5989 match Self::expect_u64(value, &key) {
5990 Ok(value) if value > 0 => {
5991 self.announce_cache_cleanup_batch_size = value as usize;
5992 Ok(())
5993 }
5994 Ok(_) => Err(RuntimeConfigError {
5995 code: RuntimeConfigErrorCode::InvalidValue,
5996 message: format!("{} must be >= 1", key),
5997 }),
5998 Err(err) => Err(err),
5999 }
6000 }
6001 "global.discovery_cleanup_interval_ticks" => {
6002 match Self::expect_u64(value, &key) {
6003 Ok(value) if value > 0 => {
6004 self.discovery_cleanup_interval_ticks = value as u32;
6005 Ok(())
6006 }
6007 Ok(_) => Err(RuntimeConfigError {
6008 code: RuntimeConfigErrorCode::InvalidValue,
6009 message: format!("{} must be >= 1", key),
6010 }),
6011 Err(err) => Err(err),
6012 }
6013 }
6014 "global.management_announce_interval_secs" => {
6015 match Self::expect_f64(value, &key) {
6016 Ok(value) => {
6017 self.management_announce_interval_secs = value;
6018 Ok(())
6019 }
6020 Err(err) => Err(err),
6021 }
6022 }
6023 "global.direct_connect_policy" => {
6024 let policy = match Self::parse_holepunch_policy(&value) {
6025 Some(policy) => policy,
6026 None => {
6027 return QueryResponse::RuntimeConfigSet(Err(RuntimeConfigError {
6028 code: RuntimeConfigErrorCode::InvalidValue,
6029 message: format!(
6030 "{} must be one of: reject, accept_all, ask_app",
6031 key
6032 ),
6033 }))
6034 }
6035 };
6036 self.holepunch_manager.set_policy(policy);
6037 Ok(())
6038 }
6039 #[cfg(feature = "rns-hooks")]
6040 "provider.queue_max_events" => match Self::expect_u64(value, &key) {
6041 Ok(v) if v > 0 => {
6042 if let Some(ref bridge) = self.provider_bridge {
6043 bridge.set_queue_max_events(v as usize);
6044 }
6045 Ok(())
6046 }
6047 Ok(_) => Err(RuntimeConfigError {
6048 code: RuntimeConfigErrorCode::InvalidValue,
6049 message: format!("{} must be >= 1", key),
6050 }),
6051 Err(err) => Err(err),
6052 },
6053 #[cfg(feature = "rns-hooks")]
6054 "provider.queue_max_bytes" => match Self::expect_u64(value, &key) {
6055 Ok(v) if v > 0 => {
6056 if let Some(ref bridge) = self.provider_bridge {
6057 bridge.set_queue_max_bytes(v as usize);
6058 }
6059 Ok(())
6060 }
6061 Ok(_) => Err(RuntimeConfigError {
6062 code: RuntimeConfigErrorCode::InvalidValue,
6063 message: format!("{} must be >= 1", key),
6064 }),
6065 Err(err) => Err(err),
6066 },
6067 #[cfg(feature = "iface-backbone")]
6068 _ if key.starts_with("backbone.") => {
6069 self.set_backbone_runtime_config(&key, value)
6070 }
6071 #[cfg(feature = "iface-backbone")]
6072 _ if key.starts_with("backbone_client.") => {
6073 self.set_backbone_client_runtime_config(&key, value)
6074 }
6075 #[cfg(feature = "iface-tcp")]
6076 _ if key.starts_with("tcp_server.") => {
6077 self.set_tcp_server_runtime_config(&key, value)
6078 }
6079 #[cfg(feature = "iface-tcp")]
6080 _ if key.starts_with("tcp_client.") => {
6081 self.set_tcp_client_runtime_config(&key, value)
6082 }
6083 #[cfg(feature = "iface-udp")]
6084 _ if key.starts_with("udp.") => self.set_udp_runtime_config(&key, value),
6085 #[cfg(feature = "iface-auto")]
6086 _ if key.starts_with("auto.") => self.set_auto_runtime_config(&key, value),
6087 #[cfg(feature = "iface-i2p")]
6088 _ if key.starts_with("i2p.") => self.set_i2p_runtime_config(&key, value),
6089 #[cfg(feature = "iface-pipe")]
6090 _ if key.starts_with("pipe.") => self.set_pipe_runtime_config(&key, value),
6091 #[cfg(feature = "iface-rnode")]
6092 _ if key.starts_with("rnode.") => self.set_rnode_runtime_config(&key, value),
6093 _ if key.starts_with("interface.") => {
6094 self.set_generic_interface_runtime_config(&key, value)
6095 }
6096 _ => {
6097 return QueryResponse::RuntimeConfigSet(Err(RuntimeConfigError {
6098 code: RuntimeConfigErrorCode::UnknownKey,
6099 message: format!("unknown runtime-config key '{}'", key),
6100 }))
6101 }
6102 };
6103
6104 QueryResponse::RuntimeConfigSet(match result {
6105 Ok(()) => self.runtime_config_entry(&key).ok_or(RuntimeConfigError {
6106 code: RuntimeConfigErrorCode::ApplyFailed,
6107 message: format!("failed to read back runtime-config key '{}'", key),
6108 }),
6109 Err(err) => Err(err),
6110 })
6111 }
6112 QueryRequest::ResetRuntimeConfig { key } => {
6113 let defaults = self.runtime_config_defaults;
6114 let result = match key.as_str() {
6115 "global.tick_interval_ms" => {
6116 self.tick_interval_ms
6117 .store(defaults.tick_interval_ms, Ordering::Relaxed);
6118 Ok(())
6119 }
6120 "global.known_destinations_ttl_secs" => {
6121 self.known_destinations_ttl = defaults.known_destinations_ttl;
6122 Ok(())
6123 }
6124 "global.rate_limiter_ttl_secs" => {
6125 self.rate_limiter_ttl_secs = defaults.rate_limiter_ttl_secs;
6126 Ok(())
6127 }
6128 "global.known_destinations_cleanup_interval_ticks" => {
6129 self.known_destinations_cleanup_interval_ticks =
6130 defaults.known_destinations_cleanup_interval_ticks;
6131 Ok(())
6132 }
6133 "global.announce_cache_cleanup_interval_ticks" => {
6134 self.announce_cache_cleanup_interval_ticks =
6135 defaults.announce_cache_cleanup_interval_ticks;
6136 Ok(())
6137 }
6138 "global.announce_cache_cleanup_batch_size" => {
6139 self.announce_cache_cleanup_batch_size =
6140 defaults.announce_cache_cleanup_batch_size;
6141 Ok(())
6142 }
6143 "global.discovery_cleanup_interval_ticks" => {
6144 self.discovery_cleanup_interval_ticks =
6145 defaults.discovery_cleanup_interval_ticks;
6146 Ok(())
6147 }
6148 "global.management_announce_interval_secs" => {
6149 self.management_announce_interval_secs =
6150 defaults.management_announce_interval_secs;
6151 Ok(())
6152 }
6153 "global.direct_connect_policy" => {
6154 self.holepunch_manager
6155 .set_policy(defaults.direct_connect_policy);
6156 Ok(())
6157 }
6158 #[cfg(feature = "rns-hooks")]
6159 "provider.queue_max_events" => {
6160 if let Some(ref bridge) = self.provider_bridge {
6161 bridge.set_queue_max_events(defaults.provider_queue_max_events);
6162 }
6163 Ok(())
6164 }
6165 #[cfg(feature = "rns-hooks")]
6166 "provider.queue_max_bytes" => {
6167 if let Some(ref bridge) = self.provider_bridge {
6168 bridge.set_queue_max_bytes(defaults.provider_queue_max_bytes);
6169 }
6170 Ok(())
6171 }
6172 #[cfg(feature = "iface-backbone")]
6173 _ if key.starts_with("backbone.") => self.reset_backbone_runtime_config(&key),
6174 #[cfg(feature = "iface-backbone")]
6175 _ if key.starts_with("backbone_client.") => {
6176 self.reset_backbone_client_runtime_config(&key)
6177 }
6178 #[cfg(feature = "iface-tcp")]
6179 _ if key.starts_with("tcp_server.") => {
6180 self.reset_tcp_server_runtime_config(&key)
6181 }
6182 #[cfg(feature = "iface-tcp")]
6183 _ if key.starts_with("tcp_client.") => {
6184 self.reset_tcp_client_runtime_config(&key)
6185 }
6186 #[cfg(feature = "iface-udp")]
6187 _ if key.starts_with("udp.") => self.reset_udp_runtime_config(&key),
6188 #[cfg(feature = "iface-auto")]
6189 _ if key.starts_with("auto.") => self.reset_auto_runtime_config(&key),
6190 #[cfg(feature = "iface-i2p")]
6191 _ if key.starts_with("i2p.") => self.reset_i2p_runtime_config(&key),
6192 #[cfg(feature = "iface-pipe")]
6193 _ if key.starts_with("pipe.") => self.reset_pipe_runtime_config(&key),
6194 #[cfg(feature = "iface-rnode")]
6195 _ if key.starts_with("rnode.") => self.reset_rnode_runtime_config(&key),
6196 _ if key.starts_with("interface.") => {
6197 self.reset_generic_interface_runtime_config(&key)
6198 }
6199 _ => {
6200 return QueryResponse::RuntimeConfigReset(Err(RuntimeConfigError {
6201 code: RuntimeConfigErrorCode::UnknownKey,
6202 message: format!("unknown runtime-config key '{}'", key),
6203 }))
6204 }
6205 };
6206
6207 QueryResponse::RuntimeConfigReset(match result {
6208 Ok(()) => self.runtime_config_entry(&key).ok_or(RuntimeConfigError {
6209 code: RuntimeConfigErrorCode::ApplyFailed,
6210 message: format!("failed to read back runtime-config key '{}'", key),
6211 }),
6212 Err(err) => Err(err),
6213 })
6214 }
6215 other => self.handle_query(other),
6216 }
6217 }
6218
6219 fn handle_tunnel_synth_delivery(&mut self, raw: &[u8]) {
6221 let packet = match RawPacket::unpack(raw) {
6223 Ok(p) => p,
6224 Err(_) => return,
6225 };
6226
6227 match rns_core::transport::tunnel::validate_tunnel_synthesize_data(&packet.data) {
6228 Ok(validated) => {
6229 let iface_id = self
6232 .interfaces
6233 .iter()
6234 .find(|(_, entry)| entry.info.wants_tunnel && entry.online && entry.enabled)
6235 .map(|(id, _)| *id);
6236
6237 if let Some(iface) = iface_id {
6238 let now = time::now();
6239 let tunnel_actions = self.engine.handle_tunnel(validated.tunnel_id, iface, now);
6240 self.dispatch_all(tunnel_actions);
6241 }
6242 }
6243 Err(e) => {
6244 log::debug!("Tunnel synthesis validation failed: {}", e);
6245 }
6246 }
6247 }
6248
6249 fn synthesize_tunnel_for_interface(&mut self, interface: InterfaceId) {
6253 if let Some(ref identity) = self.transport_identity {
6254 let actions = self
6255 .engine
6256 .synthesize_tunnel(identity, interface, &mut self.rng);
6257 self.dispatch_all(actions);
6258 }
6259 }
6260
6261 fn handle_request_path(&mut self, dest_hash: [u8; 16]) {
6263 let mut data = Vec::with_capacity(48);
6265 data.extend_from_slice(&dest_hash);
6266
6267 if self.engine.transport_enabled() {
6268 if let Some(id_hash) = self.engine.identity_hash() {
6269 data.extend_from_slice(id_hash);
6270 }
6271 }
6272
6273 let mut tag = [0u8; 16];
6275 self.rng.fill_bytes(&mut tag);
6276 data.extend_from_slice(&tag);
6277
6278 let flags = rns_core::packet::PacketFlags {
6280 header_type: rns_core::constants::HEADER_1,
6281 context_flag: rns_core::constants::FLAG_UNSET,
6282 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
6283 destination_type: rns_core::constants::DESTINATION_PLAIN,
6284 packet_type: rns_core::constants::PACKET_TYPE_DATA,
6285 };
6286
6287 if let Ok(packet) = RawPacket::pack(
6288 flags,
6289 0,
6290 &self.path_request_dest,
6291 None,
6292 rns_core::constants::CONTEXT_NONE,
6293 &data,
6294 ) {
6295 let actions = self.engine.handle_outbound(
6296 &packet,
6297 rns_core::constants::DESTINATION_PLAIN,
6298 None,
6299 time::now(),
6300 );
6301 self.dispatch_all(actions);
6302 }
6303 }
6304
6305 fn maybe_generate_proof(&mut self, dest_hash: [u8; 16], packet_hash: &[u8; 32]) {
6308 use rns_core::types::ProofStrategy;
6309
6310 let (strategy, identity) = match self.proof_strategies.get(&dest_hash) {
6311 Some((s, id)) => (*s, id.as_ref()),
6312 None => return,
6313 };
6314
6315 let should_prove = match strategy {
6316 ProofStrategy::ProveAll => true,
6317 ProofStrategy::ProveApp => self.callbacks.on_proof_requested(
6318 rns_core::types::DestHash(dest_hash),
6319 rns_core::types::PacketHash(*packet_hash),
6320 ),
6321 ProofStrategy::ProveNone => false,
6322 };
6323
6324 if !should_prove {
6325 return;
6326 }
6327
6328 let identity = match identity {
6329 Some(id) => id,
6330 None => {
6331 log::warn!(
6332 "Cannot generate proof for {:02x?}: no signing key",
6333 &dest_hash[..4]
6334 );
6335 return;
6336 }
6337 };
6338
6339 let signature = match identity.sign(packet_hash) {
6341 Ok(sig) => sig,
6342 Err(e) => {
6343 log::warn!("Failed to sign proof for {:02x?}: {:?}", &dest_hash[..4], e);
6344 return;
6345 }
6346 };
6347
6348 let mut proof_data = Vec::with_capacity(96);
6350 proof_data.extend_from_slice(packet_hash);
6351 proof_data.extend_from_slice(&signature);
6352
6353 let mut proof_dest = [0u8; 16];
6359 proof_dest.copy_from_slice(&packet_hash[..16]);
6360
6361 let flags = rns_core::packet::PacketFlags {
6362 header_type: rns_core::constants::HEADER_1,
6363 context_flag: rns_core::constants::FLAG_UNSET,
6364 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
6365 destination_type: rns_core::constants::DESTINATION_SINGLE,
6366 packet_type: rns_core::constants::PACKET_TYPE_PROOF,
6367 };
6368
6369 if let Ok(packet) = RawPacket::pack(
6370 flags,
6371 0,
6372 &proof_dest,
6373 None,
6374 rns_core::constants::CONTEXT_NONE,
6375 &proof_data,
6376 ) {
6377 let actions = self.engine.handle_outbound(
6378 &packet,
6379 rns_core::constants::DESTINATION_SINGLE,
6380 None,
6381 time::now(),
6382 );
6383 self.dispatch_all(actions);
6384 log::debug!(
6385 "Generated proof for packet on dest {:02x?}",
6386 &dest_hash[..4]
6387 );
6388 }
6389 }
6390
6391 fn handle_inbound_proof(
6393 &mut self,
6394 dest_hash: [u8; 16],
6395 proof_data: &[u8],
6396 _raw_packet_hash: &[u8; 32],
6397 ) {
6398 let (tracked_hash, signature): ([u8; 32], &[u8]) = if proof_data.len() >= 96 {
6402 let mut tracked_hash = [0u8; 32];
6403 tracked_hash.copy_from_slice(&proof_data[..32]);
6404 (tracked_hash, &proof_data[32..96])
6405 } else if proof_data.len() == 64 {
6406 let mut candidates = self
6407 .sent_packets
6408 .iter()
6409 .filter_map(|(packet_hash, _)| {
6410 if packet_hash[..16] == dest_hash {
6411 Some(*packet_hash)
6412 } else {
6413 None
6414 }
6415 })
6416 .collect::<Vec<_>>();
6417
6418 if candidates.is_empty() {
6419 log::debug!(
6420 "Implicit proof for unknown packet prefix {:02x?} on dest {:02x?}",
6421 &dest_hash[..4],
6422 &dest_hash[..4]
6423 );
6424 return;
6425 }
6426
6427 if candidates.len() > 1 {
6430 candidates.sort_by(|a, b| {
6431 let ta = self.sent_packets.get(a).map(|(_, t)| *t).unwrap_or_default();
6432 let tb = self.sent_packets.get(b).map(|(_, t)| *t).unwrap_or_default();
6433 tb.partial_cmp(&ta).unwrap_or(core::cmp::Ordering::Equal)
6434 });
6435 log::debug!(
6436 "Implicit proof matched {} candidates for prefix {:02x?}; using newest",
6437 candidates.len(),
6438 &dest_hash[..4]
6439 );
6440 }
6441
6442 (candidates[0], &proof_data[..64])
6443 } else {
6444 log::debug!("Unsupported proof length: {} bytes", proof_data.len());
6445 return;
6446 };
6447
6448 if let Some((tracked_dest, sent_time)) = self.sent_packets.remove(&tracked_hash) {
6450 if let Some(announced) = self.known_destinations.get(&tracked_dest) {
6453 let identity =
6454 rns_crypto::identity::Identity::from_public_key(&announced.public_key);
6455 let mut sig = [0u8; 64];
6456 sig.copy_from_slice(signature);
6457 if !identity.verify(&sig, &tracked_hash) {
6458 log::debug!("Proof signature invalid for {:02x?}", &tracked_hash[..4],);
6459 return;
6460 }
6461 } else {
6462 log::debug!(
6463 "No known identity for dest {:02x?}, accepting proof without signature check",
6464 &tracked_dest[..4],
6465 );
6466 }
6467
6468 let now = time::now();
6469 let rtt = now - sent_time;
6470 log::debug!(
6471 "Proof received for {:02x?} rtt={:.3}s",
6472 &tracked_hash[..4],
6473 rtt,
6474 );
6475 self.completed_proofs.insert(tracked_hash, (rtt, now));
6476 self.callbacks.on_proof(
6477 rns_core::types::DestHash(tracked_dest),
6478 rns_core::types::PacketHash(tracked_hash),
6479 rtt,
6480 );
6481 } else {
6482 log::debug!(
6483 "Proof for unknown packet {:02x?} on dest {:02x?}",
6484 &tracked_hash[..4],
6485 &dest_hash[..4],
6486 );
6487 }
6488 }
6489
6490 fn interface_send_deferred(entry: &InterfaceEntry, now: Instant) -> bool {
6491 matches!(entry.send_retry_at, Some(retry_at) if now < retry_at)
6492 }
6493
6494 fn record_send_result(
6495 entry: &mut InterfaceEntry,
6496 result: &std::io::Result<()>,
6497 context: &str,
6498 interface_id: InterfaceId,
6499 ) {
6500 match result {
6501 Ok(()) => {
6502 entry.send_retry_at = None;
6503 entry.send_retry_backoff = Duration::ZERO;
6504 }
6505 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
6506 let next_backoff = if entry.send_retry_backoff.is_zero() {
6507 SEND_RETRY_BACKOFF_MIN
6508 } else {
6509 (entry.send_retry_backoff * 2).min(SEND_RETRY_BACKOFF_MAX)
6510 };
6511 entry.send_retry_backoff = next_backoff;
6512 entry.send_retry_at = Some(Instant::now() + next_backoff);
6513 log::debug!(
6514 "[{}] {} deferred after WouldBlock; retry in {:?}",
6515 interface_id.0,
6516 context,
6517 next_backoff
6518 );
6519 }
6520 Err(e) => {
6521 entry.send_retry_at = None;
6522 entry.send_retry_backoff = Duration::ZERO;
6523 log::warn!("[{}] {} failed: {}", interface_id.0, context, e);
6524 }
6525 }
6526 }
6527
6528 fn dispatch_all(&mut self, actions: Vec<TransportAction>) {
6530 #[cfg(feature = "rns-hooks")]
6531 let mut hook_injected: Vec<TransportAction> = Vec::new();
6532
6533 for action in actions {
6534 match action {
6535 TransportAction::SendOnInterface { interface, raw } => {
6536 #[cfg(feature = "rns-hooks")]
6537 {
6538 let pkt_ctx = rns_hooks::PacketContext {
6539 flags: if raw.is_empty() { 0 } else { raw[0] },
6540 hops: if raw.len() > 1 { raw[1] } else { 0 },
6541 destination_hash: extract_dest_hash(&raw),
6542 context: 0,
6543 packet_hash: [0; 32],
6544 interface_id: interface.0,
6545 data_offset: 0,
6546 data_len: raw.len() as u32,
6547 };
6548 let ctx = HookContext::Packet {
6549 ctx: &pkt_ctx,
6550 raw: &raw,
6551 };
6552 let now = time::now();
6553 let engine_ref = EngineRef {
6554 engine: &self.engine,
6555 interfaces: &self.interfaces,
6556 link_manager: &self.link_manager,
6557 now,
6558 };
6559 let provider_events_enabled = self.provider_events_enabled();
6560 {
6561 let exec = run_hook_inner(
6562 &mut self.hook_slots[HookPoint::SendOnInterface as usize].programs,
6563 &self.hook_manager,
6564 &engine_ref,
6565 &ctx,
6566 now,
6567 provider_events_enabled,
6568 );
6569 if let Some(ref e) = exec {
6570 self.collect_hook_side_effects(
6571 "SendOnInterface",
6572 e,
6573 &mut hook_injected,
6574 );
6575 if e.hook_result.as_ref().map_or(false, |r| r.is_drop()) {
6576 continue;
6577 }
6578 }
6579 }
6580 }
6581 let is_announce = raw.len() > 2 && (raw[0] & 0x03) == 0x01;
6582 if is_announce {
6583 log::debug!(
6584 "Announce:dispatching to iface {} (len={}, online={})",
6585 interface.0,
6586 raw.len(),
6587 self.interfaces
6588 .get(&interface)
6589 .map(|e| e.online && e.enabled)
6590 .unwrap_or(false)
6591 );
6592 }
6593 if let Some(entry) = self.interfaces.get_mut(&interface) {
6594 if entry.online && entry.enabled {
6595 if Self::interface_send_deferred(entry, Instant::now()) {
6596 continue;
6597 }
6598 let data = if let Some(ref ifac_state) = entry.ifac {
6599 ifac::mask_outbound(&raw, ifac_state)
6600 } else {
6601 raw
6602 };
6603 entry.stats.txb += data.len() as u64;
6605 entry.stats.tx_packets += 1;
6606 if is_announce {
6607 entry.stats.record_outgoing_announce(time::now());
6608 }
6609 let send_result = entry.writer.send_frame(&data);
6610 let sent_ok = send_result.is_ok();
6611 Self::record_send_result(entry, &send_result, "send", interface);
6612 if sent_ok && is_announce {
6613 let header_type = (data[0] >> 6) & 0x03;
6616 let dest_start = if header_type == 1 { 18usize } else { 2usize };
6617 let dest_preview = if data.len() >= dest_start + 4 {
6618 format!("{:02x?}", &data[dest_start..dest_start + 4])
6619 } else {
6620 "??".into()
6621 };
6622 log::debug!(
6623 "Announce:SENT on iface {} (len={}, h={}, dest=[{}])",
6624 interface.0,
6625 data.len(),
6626 header_type,
6627 dest_preview
6628 );
6629 }
6630 }
6631 }
6632 }
6633 TransportAction::BroadcastOnAllInterfaces { raw, exclude } => {
6634 #[cfg(feature = "rns-hooks")]
6635 {
6636 let pkt_ctx = rns_hooks::PacketContext {
6637 flags: if raw.is_empty() { 0 } else { raw[0] },
6638 hops: if raw.len() > 1 { raw[1] } else { 0 },
6639 destination_hash: extract_dest_hash(&raw),
6640 context: 0,
6641 packet_hash: [0; 32],
6642 interface_id: 0,
6643 data_offset: 0,
6644 data_len: raw.len() as u32,
6645 };
6646 let ctx = HookContext::Packet {
6647 ctx: &pkt_ctx,
6648 raw: &raw,
6649 };
6650 let now = time::now();
6651 let engine_ref = EngineRef {
6652 engine: &self.engine,
6653 interfaces: &self.interfaces,
6654 link_manager: &self.link_manager,
6655 now,
6656 };
6657 let provider_events_enabled = self.provider_events_enabled();
6658 {
6659 let exec = run_hook_inner(
6660 &mut self.hook_slots[HookPoint::BroadcastOnAllInterfaces as usize]
6661 .programs,
6662 &self.hook_manager,
6663 &engine_ref,
6664 &ctx,
6665 now,
6666 provider_events_enabled,
6667 );
6668 if let Some(ref e) = exec {
6669 self.collect_hook_side_effects(
6670 "BroadcastOnAllInterfaces",
6671 e,
6672 &mut hook_injected,
6673 );
6674 if e.hook_result.as_ref().map_or(false, |r| r.is_drop()) {
6675 continue;
6676 }
6677 }
6678 }
6679 }
6680 let is_announce = raw.len() > 2 && (raw[0] & 0x03) == 0x01;
6681 for entry in self.interfaces.values_mut() {
6682 if entry.online && entry.enabled && Some(entry.id) != exclude {
6683 if Self::interface_send_deferred(entry, Instant::now()) {
6684 continue;
6685 }
6686 let data = if let Some(ref ifac_state) = entry.ifac {
6687 ifac::mask_outbound(&raw, ifac_state)
6688 } else {
6689 raw.clone()
6690 };
6691 entry.stats.txb += data.len() as u64;
6693 entry.stats.tx_packets += 1;
6694 if is_announce {
6695 entry.stats.record_outgoing_announce(time::now());
6696 }
6697 let send_result = entry.writer.send_frame(&data);
6698 Self::record_send_result(entry, &send_result, "broadcast", entry.id);
6699 }
6700 }
6701 }
6702 TransportAction::DeliverLocal {
6703 destination_hash,
6704 raw,
6705 packet_hash,
6706 receiving_interface,
6707 } => {
6708 #[cfg(feature = "rns-hooks")]
6709 {
6710 let pkt_ctx = rns_hooks::PacketContext {
6711 flags: 0,
6712 hops: 0,
6713 destination_hash,
6714 context: 0,
6715 packet_hash,
6716 interface_id: receiving_interface.0,
6717 data_offset: 0,
6718 data_len: raw.len() as u32,
6719 };
6720 let ctx = HookContext::Packet {
6721 ctx: &pkt_ctx,
6722 raw: &raw,
6723 };
6724 let now = time::now();
6725 let engine_ref = EngineRef {
6726 engine: &self.engine,
6727 interfaces: &self.interfaces,
6728 link_manager: &self.link_manager,
6729 now,
6730 };
6731 let provider_events_enabled = self.provider_events_enabled();
6732 {
6733 let exec = run_hook_inner(
6734 &mut self.hook_slots[HookPoint::DeliverLocal as usize].programs,
6735 &self.hook_manager,
6736 &engine_ref,
6737 &ctx,
6738 now,
6739 provider_events_enabled,
6740 );
6741 if let Some(ref e) = exec {
6742 self.collect_hook_side_effects(
6743 "DeliverLocal",
6744 e,
6745 &mut hook_injected,
6746 );
6747 if e.hook_result.as_ref().map_or(false, |r| r.is_drop()) {
6748 continue;
6749 }
6750 }
6751 }
6752 }
6753 if destination_hash == self.tunnel_synth_dest {
6754 self.handle_tunnel_synth_delivery(&raw);
6756 } else if destination_hash == self.path_request_dest {
6757 if let Ok(packet) = RawPacket::unpack(&raw) {
6759 let actions = self.engine.handle_path_request(
6760 &packet.data,
6761 receiving_interface,
6762 time::now(),
6763 );
6764 self.dispatch_all(actions);
6765 }
6766 } else if self.link_manager.is_link_destination(&destination_hash) {
6767 let link_actions = self.link_manager.handle_local_delivery(
6769 destination_hash,
6770 &raw,
6771 packet_hash,
6772 receiving_interface,
6773 &mut self.rng,
6774 );
6775 if link_actions.is_empty() {
6776 if let Ok(packet) = RawPacket::unpack(&raw) {
6780 if packet.flags.packet_type
6781 == rns_core::constants::PACKET_TYPE_PROOF
6782 {
6783 self.handle_inbound_proof(
6784 destination_hash,
6785 &packet.data,
6786 &packet_hash,
6787 );
6788 continue;
6789 }
6790 }
6791 self.maybe_generate_proof(destination_hash, &packet_hash);
6792 self.callbacks.on_local_delivery(
6793 rns_core::types::DestHash(destination_hash),
6794 raw,
6795 rns_core::types::PacketHash(packet_hash),
6796 );
6797 } else {
6798 self.dispatch_link_actions(link_actions);
6799 }
6800 } else {
6801 if let Ok(packet) = RawPacket::unpack(&raw) {
6803 if packet.flags.packet_type == rns_core::constants::PACKET_TYPE_PROOF {
6804 self.handle_inbound_proof(
6805 destination_hash,
6806 &packet.data,
6807 &packet_hash,
6808 );
6809 continue;
6810 }
6811 }
6812
6813 self.maybe_generate_proof(destination_hash, &packet_hash);
6815
6816 self.callbacks.on_local_delivery(
6817 rns_core::types::DestHash(destination_hash),
6818 raw,
6819 rns_core::types::PacketHash(packet_hash),
6820 );
6821 }
6822 }
6823 TransportAction::AnnounceReceived {
6824 destination_hash,
6825 identity_hash,
6826 public_key,
6827 name_hash,
6828 app_data,
6829 hops,
6830 receiving_interface,
6831 ..
6832 } => {
6833 #[cfg(feature = "rns-hooks")]
6834 {
6835 let ctx = HookContext::Announce {
6836 destination_hash,
6837 hops,
6838 interface_id: receiving_interface.0,
6839 };
6840 let now = time::now();
6841 let engine_ref = EngineRef {
6842 engine: &self.engine,
6843 interfaces: &self.interfaces,
6844 link_manager: &self.link_manager,
6845 now,
6846 };
6847 let provider_events_enabled = self.provider_events_enabled();
6848 {
6849 let exec = run_hook_inner(
6850 &mut self.hook_slots[HookPoint::AnnounceReceived as usize].programs,
6851 &self.hook_manager,
6852 &engine_ref,
6853 &ctx,
6854 now,
6855 provider_events_enabled,
6856 );
6857 if let Some(ref e) = exec {
6858 self.collect_hook_side_effects(
6859 "AnnounceReceived",
6860 e,
6861 &mut hook_injected,
6862 );
6863 if e.hook_result.as_ref().map_or(false, |r| r.is_drop()) {
6864 continue;
6865 }
6866 }
6867 }
6868 }
6869
6870 if name_hash == self.discovery_name_hash {
6874 if self.discover_interfaces {
6875 if let Some(ref app_data) = app_data {
6876 if let Some(mut discovered) =
6877 crate::discovery::parse_interface_announce(
6878 app_data,
6879 &identity_hash,
6880 hops,
6881 self.discovery_required_value,
6882 )
6883 {
6884 if let Ok(Some(existing)) =
6886 self.discovered_interfaces.load(&discovered.discovery_hash)
6887 {
6888 discovered.discovered = existing.discovered;
6889 discovered.heard_count = existing.heard_count + 1;
6890 }
6891 if let Err(e) = self.discovered_interfaces.store(&discovered) {
6892 log::warn!("Failed to store discovered interface: {}", e);
6893 } else {
6894 log::debug!(
6895 "Discovered interface '{}' ({}) at {}:{} [stamp={}]",
6896 discovered.name,
6897 discovered.interface_type,
6898 discovered.reachable_on.as_deref().unwrap_or("?"),
6899 discovered
6900 .port
6901 .map(|p| p.to_string())
6902 .unwrap_or_else(|| "?".into()),
6903 discovered.stamp_value,
6904 );
6905 }
6906 }
6907 }
6908 }
6909 }
6911
6912 let announced = crate::destination::AnnouncedIdentity {
6914 dest_hash: rns_core::types::DestHash(destination_hash),
6915 identity_hash: rns_core::types::IdentityHash(identity_hash),
6916 public_key,
6917 app_data: app_data.clone(),
6918 hops,
6919 received_at: time::now(),
6920 receiving_interface,
6921 };
6922 self.upsert_known_destination(destination_hash, announced.clone());
6923 log::info!(
6924 "Announce:validated dest={:02x}{:02x}{:02x}{:02x}.. hops={}",
6925 destination_hash[0],
6926 destination_hash[1],
6927 destination_hash[2],
6928 destination_hash[3],
6929 hops,
6930 );
6931 self.callbacks.on_announce(announced);
6932 }
6933 TransportAction::PathUpdated {
6934 destination_hash,
6935 hops,
6936 interface,
6937 ..
6938 } => {
6939 #[cfg(feature = "rns-hooks")]
6940 {
6941 let ctx = HookContext::Announce {
6942 destination_hash,
6943 hops,
6944 interface_id: interface.0,
6945 };
6946 let now = time::now();
6947 let engine_ref = EngineRef {
6948 engine: &self.engine,
6949 interfaces: &self.interfaces,
6950 link_manager: &self.link_manager,
6951 now,
6952 };
6953 let provider_events_enabled = self.provider_events_enabled();
6954 if let Some(ref e) = run_hook_inner(
6955 &mut self.hook_slots[HookPoint::PathUpdated as usize].programs,
6956 &self.hook_manager,
6957 &engine_ref,
6958 &ctx,
6959 now,
6960 provider_events_enabled,
6961 ) {
6962 self.collect_hook_side_effects("PathUpdated", e, &mut hook_injected);
6963 }
6964 }
6965 #[cfg(not(feature = "rns-hooks"))]
6966 let _ = interface;
6967
6968 self.callbacks
6969 .on_path_updated(rns_core::types::DestHash(destination_hash), hops);
6970 }
6971 TransportAction::ForwardToLocalClients { raw, exclude } => {
6972 for entry in self.interfaces.values_mut() {
6973 if entry.online
6974 && entry.enabled
6975 && entry.info.is_local_client
6976 && Some(entry.id) != exclude
6977 {
6978 if Self::interface_send_deferred(entry, Instant::now()) {
6979 continue;
6980 }
6981 let data = if let Some(ref ifac_state) = entry.ifac {
6982 ifac::mask_outbound(&raw, ifac_state)
6983 } else {
6984 raw.clone()
6985 };
6986 entry.stats.txb += data.len() as u64;
6987 entry.stats.tx_packets += 1;
6988 let send_result = entry.writer.send_frame(&data);
6989 Self::record_send_result(
6990 entry,
6991 &send_result,
6992 "forward to local client",
6993 entry.id,
6994 );
6995 }
6996 }
6997 }
6998 TransportAction::ForwardPlainBroadcast {
6999 raw,
7000 to_local,
7001 exclude,
7002 } => {
7003 for entry in self.interfaces.values_mut() {
7004 if entry.online
7005 && entry.enabled
7006 && entry.info.is_local_client == to_local
7007 && Some(entry.id) != exclude
7008 {
7009 if Self::interface_send_deferred(entry, Instant::now()) {
7010 continue;
7011 }
7012 let data = if let Some(ref ifac_state) = entry.ifac {
7013 ifac::mask_outbound(&raw, ifac_state)
7014 } else {
7015 raw.clone()
7016 };
7017 entry.stats.txb += data.len() as u64;
7018 entry.stats.tx_packets += 1;
7019 let send_result = entry.writer.send_frame(&data);
7020 Self::record_send_result(
7021 entry,
7022 &send_result,
7023 "forward plain broadcast",
7024 entry.id,
7025 );
7026 }
7027 }
7028 }
7029 TransportAction::CacheAnnounce { packet_hash, raw } => {
7030 if let Some(ref cache) = self.announce_cache {
7031 if let Err(e) = cache.store(&packet_hash, &raw, None) {
7032 log::warn!("Failed to cache announce: {}", e);
7033 }
7034 }
7035 }
7036 TransportAction::TunnelSynthesize {
7037 interface,
7038 data,
7039 dest_hash,
7040 } => {
7041 #[cfg(feature = "rns-hooks")]
7042 {
7043 let pkt_ctx = rns_hooks::PacketContext {
7044 flags: 0,
7045 hops: 0,
7046 destination_hash: dest_hash,
7047 context: 0,
7048 packet_hash: [0; 32],
7049 interface_id: interface.0,
7050 data_offset: 0,
7051 data_len: data.len() as u32,
7052 };
7053 let ctx = HookContext::Packet {
7054 ctx: &pkt_ctx,
7055 raw: &data,
7056 };
7057 let now = time::now();
7058 let engine_ref = EngineRef {
7059 engine: &self.engine,
7060 interfaces: &self.interfaces,
7061 link_manager: &self.link_manager,
7062 now,
7063 };
7064 let provider_events_enabled = self.provider_events_enabled();
7065 {
7066 let exec = run_hook_inner(
7067 &mut self.hook_slots[HookPoint::TunnelSynthesize as usize].programs,
7068 &self.hook_manager,
7069 &engine_ref,
7070 &ctx,
7071 now,
7072 provider_events_enabled,
7073 );
7074 if let Some(ref e) = exec {
7075 self.collect_hook_side_effects(
7076 "TunnelSynthesize",
7077 e,
7078 &mut hook_injected,
7079 );
7080 if e.hook_result.as_ref().map_or(false, |r| r.is_drop()) {
7081 continue;
7082 }
7083 }
7084 }
7085 }
7086 let flags = rns_core::packet::PacketFlags {
7088 header_type: rns_core::constants::HEADER_1,
7089 context_flag: rns_core::constants::FLAG_UNSET,
7090 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
7091 destination_type: rns_core::constants::DESTINATION_PLAIN,
7092 packet_type: rns_core::constants::PACKET_TYPE_DATA,
7093 };
7094 if let Ok(packet) = rns_core::packet::RawPacket::pack(
7095 flags,
7096 0,
7097 &dest_hash,
7098 None,
7099 rns_core::constants::CONTEXT_NONE,
7100 &data,
7101 ) {
7102 if let Some(entry) = self.interfaces.get_mut(&interface) {
7103 if entry.online && entry.enabled {
7104 let raw = if let Some(ref ifac_state) = entry.ifac {
7105 ifac::mask_outbound(&packet.raw, ifac_state)
7106 } else {
7107 packet.raw
7108 };
7109 entry.stats.txb += raw.len() as u64;
7110 entry.stats.tx_packets += 1;
7111 if let Err(e) = entry.writer.send_frame(&raw) {
7112 log::warn!(
7113 "[{}] tunnel synthesize send failed: {}",
7114 entry.info.id.0,
7115 e
7116 );
7117 }
7118 }
7119 }
7120 }
7121 }
7122 TransportAction::TunnelEstablished {
7123 tunnel_id,
7124 interface,
7125 } => {
7126 log::info!(
7127 "Tunnel established: {:02x?} on interface {}",
7128 &tunnel_id[..4],
7129 interface.0
7130 );
7131 }
7132 TransportAction::AnnounceRetransmit {
7133 destination_hash,
7134 hops,
7135 interface,
7136 } => {
7137 #[cfg(feature = "rns-hooks")]
7138 {
7139 let ctx = HookContext::Announce {
7140 destination_hash,
7141 hops,
7142 interface_id: interface.map(|i| i.0).unwrap_or(0),
7143 };
7144 let now = time::now();
7145 let engine_ref = EngineRef {
7146 engine: &self.engine,
7147 interfaces: &self.interfaces,
7148 link_manager: &self.link_manager,
7149 now,
7150 };
7151 let provider_events_enabled = self.provider_events_enabled();
7152 if let Some(ref e) = run_hook_inner(
7153 &mut self.hook_slots[HookPoint::AnnounceRetransmit as usize].programs,
7154 &self.hook_manager,
7155 &engine_ref,
7156 &ctx,
7157 now,
7158 provider_events_enabled,
7159 ) {
7160 self.collect_hook_side_effects(
7161 "AnnounceRetransmit",
7162 e,
7163 &mut hook_injected,
7164 );
7165 }
7166 }
7167 #[cfg(not(feature = "rns-hooks"))]
7168 {
7169 let _ = (destination_hash, hops, interface);
7170 }
7171 }
7172 TransportAction::LinkRequestReceived {
7173 link_id,
7174 destination_hash: _,
7175 receiving_interface,
7176 } => {
7177 #[cfg(feature = "rns-hooks")]
7178 {
7179 let ctx = HookContext::Link {
7180 link_id,
7181 interface_id: receiving_interface.0,
7182 };
7183 let now = time::now();
7184 let engine_ref = EngineRef {
7185 engine: &self.engine,
7186 interfaces: &self.interfaces,
7187 link_manager: &self.link_manager,
7188 now,
7189 };
7190 let provider_events_enabled = self.provider_events_enabled();
7191 if let Some(ref e) = run_hook_inner(
7192 &mut self.hook_slots[HookPoint::LinkRequestReceived as usize].programs,
7193 &self.hook_manager,
7194 &engine_ref,
7195 &ctx,
7196 now,
7197 provider_events_enabled,
7198 ) {
7199 self.collect_hook_side_effects(
7200 "LinkRequestReceived",
7201 e,
7202 &mut hook_injected,
7203 );
7204 }
7205 }
7206 #[cfg(not(feature = "rns-hooks"))]
7207 {
7208 let _ = (link_id, receiving_interface);
7209 }
7210 }
7211 TransportAction::LinkEstablished { link_id, interface } => {
7212 #[cfg(feature = "rns-hooks")]
7213 {
7214 let ctx = HookContext::Link {
7215 link_id,
7216 interface_id: interface.0,
7217 };
7218 let now = time::now();
7219 let engine_ref = EngineRef {
7220 engine: &self.engine,
7221 interfaces: &self.interfaces,
7222 link_manager: &self.link_manager,
7223 now,
7224 };
7225 let provider_events_enabled = self.provider_events_enabled();
7226 if let Some(ref e) = run_hook_inner(
7227 &mut self.hook_slots[HookPoint::LinkEstablished as usize].programs,
7228 &self.hook_manager,
7229 &engine_ref,
7230 &ctx,
7231 now,
7232 provider_events_enabled,
7233 ) {
7234 self.collect_hook_side_effects(
7235 "LinkEstablished",
7236 e,
7237 &mut hook_injected,
7238 );
7239 }
7240 }
7241 #[cfg(not(feature = "rns-hooks"))]
7242 {
7243 let _ = (link_id, interface);
7244 }
7245 }
7246 TransportAction::LinkClosed { link_id } => {
7247 #[cfg(feature = "rns-hooks")]
7248 {
7249 let ctx = HookContext::Link {
7250 link_id,
7251 interface_id: 0,
7252 };
7253 let now = time::now();
7254 let engine_ref = EngineRef {
7255 engine: &self.engine,
7256 interfaces: &self.interfaces,
7257 link_manager: &self.link_manager,
7258 now,
7259 };
7260 let provider_events_enabled = self.provider_events_enabled();
7261 if let Some(ref e) = run_hook_inner(
7262 &mut self.hook_slots[HookPoint::LinkClosed as usize].programs,
7263 &self.hook_manager,
7264 &engine_ref,
7265 &ctx,
7266 now,
7267 provider_events_enabled,
7268 ) {
7269 self.collect_hook_side_effects("LinkClosed", e, &mut hook_injected);
7270 }
7271 }
7272 #[cfg(not(feature = "rns-hooks"))]
7273 {
7274 let _ = link_id;
7275 }
7276 }
7277 }
7278 }
7279
7280 #[cfg(feature = "rns-hooks")]
7282 if !hook_injected.is_empty() {
7283 self.dispatch_all(hook_injected);
7284 }
7285 }
7286
7287 fn dispatch_link_actions(&mut self, actions: Vec<LinkManagerAction>) {
7289 #[cfg(feature = "rns-hooks")]
7290 let mut hook_injected: Vec<TransportAction> = Vec::new();
7291
7292 for action in actions {
7293 match action {
7294 LinkManagerAction::SendPacket {
7295 mut raw,
7296 dest_type,
7297 mut attached_interface,
7298 } => {
7299 if dest_type == rns_core::constants::DESTINATION_LINK
7300 && attached_interface.is_none()
7301 {
7302 if let Ok(packet) = RawPacket::unpack(&raw) {
7303 let link_id = packet.destination_hash;
7304 if let Some((iface, transport_id)) =
7305 self.link_manager.get_link_route_hint(&link_id)
7306 {
7307 attached_interface = Some(iface);
7308 if packet.flags.header_type == rns_core::constants::HEADER_1 {
7309 if let Some(next_hop) = transport_id {
7310 raw = inject_transport_header(&packet.raw, &next_hop);
7311 log::debug!(
7312 "Link SendPacket rewrite: link={:02x?} iface={} header=1->2 tid={:02x?}",
7313 &link_id[..4],
7314 iface.0,
7315 &next_hop[..4]
7316 );
7317 } else {
7318 log::debug!(
7319 "Link SendPacket route: link={:02x?} iface={} header=1 (no transport_id)",
7320 &link_id[..4],
7321 iface.0
7322 );
7323 }
7324 }
7325 } else {
7326 log::debug!(
7327 "Link SendPacket no route hint: link={:02x?}",
7328 &link_id[..4]
7329 );
7330 }
7331 }
7332 }
7333
7334 match RawPacket::unpack(&raw) {
7336 Ok(packet) => {
7337 if packet.flags.packet_type == rns_core::constants::PACKET_TYPE_DATA {
7338 self.sent_packets.insert(
7339 packet.packet_hash,
7340 (packet.destination_hash, time::now()),
7341 );
7342 }
7343 let transport_actions = self.engine.handle_outbound(
7344 &packet,
7345 dest_type,
7346 attached_interface,
7347 time::now(),
7348 );
7349 self.dispatch_all(transport_actions);
7350 }
7351 Err(e) => {
7352 log::warn!("LinkManager SendPacket: failed to unpack: {:?}", e);
7353 }
7354 }
7355 }
7356 LinkManagerAction::LinkEstablished {
7357 link_id,
7358 dest_hash,
7359 rtt,
7360 is_initiator,
7361 } => {
7362 #[cfg(feature = "rns-hooks")]
7363 {
7364 let ctx = HookContext::Link {
7365 link_id,
7366 interface_id: 0,
7367 };
7368 let now = time::now();
7369 let engine_ref = EngineRef {
7370 engine: &self.engine,
7371 interfaces: &self.interfaces,
7372 link_manager: &self.link_manager,
7373 now,
7374 };
7375 let provider_events_enabled = self.provider_events_enabled();
7376 if let Some(ref e) = run_hook_inner(
7377 &mut self.hook_slots[HookPoint::LinkEstablished as usize].programs,
7378 &self.hook_manager,
7379 &engine_ref,
7380 &ctx,
7381 now,
7382 provider_events_enabled,
7383 ) {
7384 self.collect_hook_side_effects(
7385 "LinkEstablished",
7386 e,
7387 &mut hook_injected,
7388 );
7389 }
7390 }
7391 log::info!(
7392 "Link established: {:02x?} rtt={:.3}s initiator={}",
7393 &link_id[..4],
7394 rtt,
7395 is_initiator,
7396 );
7397 self.callbacks.on_link_established(
7398 rns_core::types::LinkId(link_id),
7399 rns_core::types::DestHash(dest_hash),
7400 rtt,
7401 is_initiator,
7402 );
7403 }
7404 LinkManagerAction::LinkClosed { link_id, reason } => {
7405 #[cfg(feature = "rns-hooks")]
7406 {
7407 let ctx = HookContext::Link {
7408 link_id,
7409 interface_id: 0,
7410 };
7411 let now = time::now();
7412 let engine_ref = EngineRef {
7413 engine: &self.engine,
7414 interfaces: &self.interfaces,
7415 link_manager: &self.link_manager,
7416 now,
7417 };
7418 let provider_events_enabled = self.provider_events_enabled();
7419 if let Some(ref e) = run_hook_inner(
7420 &mut self.hook_slots[HookPoint::LinkClosed as usize].programs,
7421 &self.hook_manager,
7422 &engine_ref,
7423 &ctx,
7424 now,
7425 provider_events_enabled,
7426 ) {
7427 self.collect_hook_side_effects("LinkClosed", e, &mut hook_injected);
7428 }
7429 }
7430 log::info!("Link closed: {:02x?} reason={:?}", &link_id[..4], reason);
7431 self.holepunch_manager.link_closed(&link_id);
7432 self.callbacks
7433 .on_link_closed(rns_core::types::LinkId(link_id), reason);
7434 }
7435 LinkManagerAction::RemoteIdentified {
7436 link_id,
7437 identity_hash,
7438 public_key,
7439 } => {
7440 log::debug!(
7441 "Remote identified on link {:02x?}: {:02x?}",
7442 &link_id[..4],
7443 &identity_hash[..4],
7444 );
7445 self.callbacks.on_remote_identified(
7446 rns_core::types::LinkId(link_id),
7447 rns_core::types::IdentityHash(identity_hash),
7448 public_key,
7449 );
7450 }
7451 LinkManagerAction::RegisterLinkDest { link_id } => {
7452 self.engine
7454 .register_destination(link_id, rns_core::constants::DESTINATION_LINK);
7455 }
7456 LinkManagerAction::DeregisterLinkDest { link_id } => {
7457 self.engine.deregister_destination(&link_id);
7458 }
7459 LinkManagerAction::ManagementRequest {
7460 link_id,
7461 path_hash,
7462 data,
7463 request_id,
7464 remote_identity,
7465 } => {
7466 self.handle_management_request(
7467 link_id,
7468 path_hash,
7469 data,
7470 request_id,
7471 remote_identity,
7472 );
7473 }
7474 LinkManagerAction::ResourceReceived {
7475 link_id,
7476 data,
7477 metadata,
7478 } => {
7479 self.callbacks.on_resource_received(
7480 rns_core::types::LinkId(link_id),
7481 data,
7482 metadata,
7483 );
7484 }
7485 LinkManagerAction::ResourceCompleted { link_id } => {
7486 self.callbacks
7487 .on_resource_completed(rns_core::types::LinkId(link_id));
7488 }
7489 LinkManagerAction::ResourceFailed { link_id, error } => {
7490 log::debug!("Resource failed on link {:02x?}: {}", &link_id[..4], error);
7491 self.callbacks
7492 .on_resource_failed(rns_core::types::LinkId(link_id), error);
7493 }
7494 LinkManagerAction::ResourceProgress {
7495 link_id,
7496 received,
7497 total,
7498 } => {
7499 self.callbacks.on_resource_progress(
7500 rns_core::types::LinkId(link_id),
7501 received,
7502 total,
7503 );
7504 }
7505 LinkManagerAction::ResourceAcceptQuery {
7506 link_id,
7507 resource_hash,
7508 transfer_size,
7509 has_metadata,
7510 } => {
7511 let accept = self.callbacks.on_resource_accept_query(
7512 rns_core::types::LinkId(link_id),
7513 resource_hash.clone(),
7514 transfer_size,
7515 has_metadata,
7516 );
7517 let accept_actions = self.link_manager.accept_resource(
7518 &link_id,
7519 &resource_hash,
7520 accept,
7521 &mut self.rng,
7522 );
7523 self.dispatch_link_actions(accept_actions);
7525 }
7526 LinkManagerAction::ChannelMessageReceived {
7527 link_id,
7528 msgtype,
7529 payload,
7530 } => {
7531 if HolePunchManager::is_holepunch_message(msgtype) {
7533 let derived_key = self.link_manager.get_derived_key(&link_id);
7534 let tx = self.get_event_sender();
7535 let (handled, hp_actions) = self.holepunch_manager.handle_signal(
7536 link_id,
7537 msgtype,
7538 payload,
7539 derived_key.as_deref(),
7540 &tx,
7541 );
7542 if handled {
7543 self.dispatch_holepunch_actions(hp_actions);
7544 }
7545 } else {
7546 self.callbacks.on_channel_message(
7547 rns_core::types::LinkId(link_id),
7548 msgtype,
7549 payload,
7550 );
7551 }
7552 }
7553 LinkManagerAction::LinkDataReceived {
7554 link_id,
7555 context,
7556 data,
7557 } => {
7558 self.callbacks
7559 .on_link_data(rns_core::types::LinkId(link_id), context, data);
7560 }
7561 LinkManagerAction::ResponseReceived {
7562 link_id,
7563 request_id,
7564 data,
7565 } => {
7566 self.callbacks
7567 .on_response(rns_core::types::LinkId(link_id), request_id, data);
7568 }
7569 LinkManagerAction::LinkRequestReceived {
7570 link_id,
7571 receiving_interface,
7572 } => {
7573 #[cfg(feature = "rns-hooks")]
7574 {
7575 let ctx = HookContext::Link {
7576 link_id,
7577 interface_id: receiving_interface.0,
7578 };
7579 let now = time::now();
7580 let engine_ref = EngineRef {
7581 engine: &self.engine,
7582 interfaces: &self.interfaces,
7583 link_manager: &self.link_manager,
7584 now,
7585 };
7586 let provider_events_enabled = self.provider_events_enabled();
7587 if let Some(ref e) = run_hook_inner(
7588 &mut self.hook_slots[HookPoint::LinkRequestReceived as usize].programs,
7589 &self.hook_manager,
7590 &engine_ref,
7591 &ctx,
7592 now,
7593 provider_events_enabled,
7594 ) {
7595 self.collect_hook_side_effects(
7596 "LinkRequestReceived",
7597 e,
7598 &mut hook_injected,
7599 );
7600 }
7601 }
7602 #[cfg(not(feature = "rns-hooks"))]
7603 {
7604 let _ = (link_id, receiving_interface);
7605 }
7606 }
7607 }
7608 }
7609
7610 #[cfg(feature = "rns-hooks")]
7612 if !hook_injected.is_empty() {
7613 self.dispatch_all(hook_injected);
7614 }
7615 }
7616
7617 fn dispatch_holepunch_actions(&mut self, actions: Vec<HolePunchManagerAction>) {
7619 for action in actions {
7620 match action {
7621 HolePunchManagerAction::SendChannelMessage {
7622 link_id,
7623 msgtype,
7624 payload,
7625 } => {
7626 if let Ok(link_actions) = self.link_manager.send_channel_message(
7627 &link_id,
7628 msgtype,
7629 &payload,
7630 &mut self.rng,
7631 ) {
7632 self.dispatch_link_actions(link_actions);
7633 }
7634 }
7635 HolePunchManagerAction::DirectConnectEstablished {
7636 link_id,
7637 session_id,
7638 interface_id,
7639 rtt,
7640 mtu,
7641 } => {
7642 log::info!(
7643 "Direct connection established for link {:02x?} session {:02x?} iface {} rtt={:.1}ms mtu={}",
7644 &link_id[..4], &session_id[..4], interface_id.0, rtt * 1000.0, mtu
7645 );
7646 self.engine
7648 .redirect_path(&link_id, interface_id, time::now());
7649 self.link_manager.set_link_rtt(&link_id, rtt);
7651 self.link_manager.set_link_mtu(&link_id, mtu);
7652 self.link_manager.record_link_inbound(&link_id);
7655 self.link_manager.flush_channel_tx(&link_id);
7657 self.callbacks.on_direct_connect_established(
7658 rns_core::types::LinkId(link_id),
7659 interface_id,
7660 );
7661 }
7662 HolePunchManagerAction::DirectConnectFailed {
7663 link_id,
7664 session_id,
7665 reason,
7666 } => {
7667 log::debug!(
7668 "Direct connection failed for link {:02x?} session {:02x?} reason={}",
7669 &link_id[..4],
7670 &session_id[..4],
7671 reason
7672 );
7673 self.callbacks
7674 .on_direct_connect_failed(rns_core::types::LinkId(link_id), reason);
7675 }
7676 }
7677 }
7678 }
7679
7680 fn get_event_sender(&self) -> crate::event::EventSender {
7685 self.event_tx.clone()
7689 }
7690
7691 const MANAGEMENT_ANNOUNCE_DELAY: f64 = 5.0;
7693
7694 fn tick_discovery_announcer(&mut self, now: f64) {
7696 let announcer = match self.interface_announcer.as_mut() {
7697 Some(a) => a,
7698 None => return,
7699 };
7700
7701 announcer.maybe_start(now);
7702
7703 let stamp_result = match announcer.poll_ready() {
7704 Some(r) => r,
7705 None => return,
7706 };
7707
7708 if !announcer.contains_interface(&stamp_result.interface_name) {
7709 log::debug!(
7710 "Discovery: dropping completed stamp for removed interface '{}'",
7711 stamp_result.interface_name
7712 );
7713 return;
7714 }
7715
7716 let identity = match self.transport_identity.as_ref() {
7717 Some(id) => id,
7718 None => {
7719 log::warn!("Discovery: stamp ready but no transport identity");
7720 return;
7721 }
7722 };
7723
7724 let identity_hash = identity.hash();
7726 let disc_dest = rns_core::destination::destination_hash(
7727 crate::discovery::APP_NAME,
7728 &["discovery", "interface"],
7729 Some(&identity_hash),
7730 );
7731 let name_hash = self.discovery_name_hash;
7732 let mut random_hash = [0u8; 10];
7733 self.rng.fill_bytes(&mut random_hash);
7734
7735 let (announce_data, _) = match rns_core::announce::AnnounceData::pack(
7736 identity,
7737 &disc_dest,
7738 &name_hash,
7739 &random_hash,
7740 None,
7741 Some(&stamp_result.app_data),
7742 ) {
7743 Ok(v) => v,
7744 Err(e) => {
7745 log::warn!("Discovery: failed to pack announce: {}", e);
7746 return;
7747 }
7748 };
7749
7750 let flags = rns_core::packet::PacketFlags {
7751 header_type: rns_core::constants::HEADER_1,
7752 context_flag: rns_core::constants::FLAG_UNSET,
7753 transport_type: rns_core::constants::TRANSPORT_BROADCAST,
7754 destination_type: rns_core::constants::DESTINATION_SINGLE,
7755 packet_type: rns_core::constants::PACKET_TYPE_ANNOUNCE,
7756 };
7757
7758 let packet = match RawPacket::pack(
7759 flags,
7760 0,
7761 &disc_dest,
7762 None,
7763 rns_core::constants::CONTEXT_NONE,
7764 &announce_data,
7765 ) {
7766 Ok(p) => p,
7767 Err(e) => {
7768 log::warn!("Discovery: failed to pack packet: {}", e);
7769 return;
7770 }
7771 };
7772
7773 let outbound_actions = self.engine.handle_outbound(
7774 &packet,
7775 rns_core::constants::DESTINATION_SINGLE,
7776 None,
7777 now,
7778 );
7779 log::debug!(
7780 "Discovery announce sent for interface '{}' ({} actions, dest={:02x?})",
7781 stamp_result.interface_name,
7782 outbound_actions.len(),
7783 &disc_dest[..4],
7784 );
7785 self.dispatch_all(outbound_actions);
7786 }
7787
7788 fn rss_mb() -> Option<f64> {
7790 let statm = std::fs::read_to_string("/proc/self/statm").ok()?;
7791 let rss_pages: u64 = statm.split_whitespace().nth(1)?.parse().ok()?;
7792 Some(rss_pages as f64 * 4096.0 / (1024.0 * 1024.0))
7793 }
7794
7795 fn parse_proc_kib(contents: &str, key: &str) -> Option<u64> {
7796 contents.lines().find_map(|line| {
7797 let value = line.strip_prefix(key)?;
7798 value.split_whitespace().next()?.parse().ok()
7799 })
7800 }
7801
7802 fn proc_status_mb() -> Option<(f64, f64, f64, f64)> {
7803 let status = std::fs::read_to_string("/proc/self/status").ok()?;
7804 let vm_rss = Self::parse_proc_kib(&status, "VmRSS:")? as f64 / 1024.0;
7805 let vm_hwm = Self::parse_proc_kib(&status, "VmHWM:")? as f64 / 1024.0;
7806 let vm_data = Self::parse_proc_kib(&status, "VmData:")? as f64 / 1024.0;
7807 let vm_swap = Self::parse_proc_kib(&status, "VmSwap:").unwrap_or(0) as f64 / 1024.0;
7808 Some((vm_rss, vm_hwm, vm_data, vm_swap))
7809 }
7810
7811 fn smaps_rollup_mb() -> Option<(f64, f64, f64, f64, f64, f64, f64, f64)> {
7812 let smaps = std::fs::read_to_string("/proc/self/smaps_rollup").ok()?;
7813 let rss_kib = Self::parse_proc_kib(&smaps, "Rss:")?;
7814 let anon_kib = Self::parse_proc_kib(&smaps, "Anonymous:")?;
7815 let shared_clean_kib = Self::parse_proc_kib(&smaps, "Shared_Clean:").unwrap_or(0);
7816 let shared_dirty_kib = Self::parse_proc_kib(&smaps, "Shared_Dirty:").unwrap_or(0);
7817 let private_clean_kib = Self::parse_proc_kib(&smaps, "Private_Clean:").unwrap_or(0);
7818 let private_dirty_kib = Self::parse_proc_kib(&smaps, "Private_Dirty:").unwrap_or(0);
7819 let swap_kib = Self::parse_proc_kib(&smaps, "Swap:").unwrap_or(0);
7820 let file_est_kib = rss_kib.saturating_sub(anon_kib);
7821 Some((
7822 rss_kib as f64 / 1024.0,
7823 anon_kib as f64 / 1024.0,
7824 file_est_kib as f64 / 1024.0,
7825 shared_clean_kib as f64 / 1024.0,
7826 shared_dirty_kib as f64 / 1024.0,
7827 private_clean_kib as f64 / 1024.0,
7828 private_dirty_kib as f64 / 1024.0,
7829 swap_kib as f64 / 1024.0,
7830 ))
7831 }
7832
7833 fn log_memory_stats(&self) {
7835 let rss = Self::rss_mb()
7836 .map(|v| format!("{:.1}", v))
7837 .unwrap_or_else(|| "N/A".into());
7838 let (vm_rss, vm_hwm, vm_data, vm_swap) = Self::proc_status_mb()
7839 .map(|(rss, hwm, data, swap)| {
7840 (
7841 format!("{rss:.1}"),
7842 format!("{hwm:.1}"),
7843 format!("{data:.1}"),
7844 format!("{swap:.1}"),
7845 )
7846 })
7847 .unwrap_or_else(|| ("N/A".into(), "N/A".into(), "N/A".into(), "N/A".into()));
7848 let (
7849 smaps_rss,
7850 smaps_anon,
7851 smaps_file_est,
7852 smaps_shared_clean,
7853 smaps_shared_dirty,
7854 smaps_private_clean,
7855 smaps_private_dirty,
7856 smaps_swap,
7857 ) = Self::smaps_rollup_mb()
7858 .map(
7859 |(
7860 rss,
7861 anon,
7862 file_est,
7863 shared_clean,
7864 shared_dirty,
7865 private_clean,
7866 private_dirty,
7867 swap,
7868 )| {
7869 (
7870 format!("{rss:.1}"),
7871 format!("{anon:.1}"),
7872 format!("{file_est:.1}"),
7873 format!("{shared_clean:.1}"),
7874 format!("{shared_dirty:.1}"),
7875 format!("{private_clean:.1}"),
7876 format!("{private_dirty:.1}"),
7877 format!("{swap:.1}"),
7878 )
7879 },
7880 )
7881 .unwrap_or_else(|| {
7882 (
7883 "N/A".into(),
7884 "N/A".into(),
7885 "N/A".into(),
7886 "N/A".into(),
7887 "N/A".into(),
7888 "N/A".into(),
7889 "N/A".into(),
7890 "N/A".into(),
7891 )
7892 });
7893 log::info!(
7894 "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={}",
7895 rss,
7896 vm_rss,
7897 vm_hwm,
7898 vm_data,
7899 vm_swap,
7900 smaps_rss,
7901 smaps_anon,
7902 smaps_file_est,
7903 smaps_shared_clean,
7904 smaps_shared_dirty,
7905 smaps_private_clean,
7906 smaps_private_dirty,
7907 smaps_swap,
7908 self.known_destinations.len(),
7909 self.known_destinations_cap_evict_count,
7910 self.engine.path_table_count(),
7911 self.engine.path_destination_cap_evict_count(),
7912 self.engine.announce_table_count(),
7913 self.engine.reverse_table_count(),
7914 self.engine.link_table_count(),
7915 self.engine.held_announces_count(),
7916 self.engine.packet_hashlist_len(),
7917 self.engine.announce_sig_cache_len(),
7918 self.announce_verify_queue
7919 .lock()
7920 .map(|queue| queue.len())
7921 .unwrap_or(0),
7922 self.engine.rate_limiter_count(),
7923 self.engine.blackholed_count(),
7924 self.engine.tunnel_count(),
7925 self.engine.announce_queue_count(),
7926 self.engine.nonempty_announce_queue_count(),
7927 self.engine.queued_announce_count(),
7928 self.engine.queued_announce_bytes(),
7929 self.engine.announce_queue_interface_cap_drop_count(),
7930 self.engine.discovery_pr_tags_count(),
7931 self.engine.discovery_path_requests_count(),
7932 self.sent_packets.len(),
7933 self.completed_proofs.len(),
7934 self.local_destinations.len(),
7935 self.shared_announces.len(),
7936 self.link_manager.link_count(),
7937 self.holepunch_manager.session_count(),
7938 self.proof_strategies.len(),
7939 );
7940 }
7941
7942 fn tick_management_announces(&mut self, now: f64) {
7944 if self.transport_identity.is_none() {
7945 return;
7946 }
7947
7948 let uptime = now - self.started;
7949
7950 if !self.initial_announce_sent {
7952 if uptime < Self::MANAGEMENT_ANNOUNCE_DELAY {
7953 return;
7954 }
7955 self.initial_announce_sent = true;
7956 self.emit_management_announces(now);
7957 return;
7958 }
7959
7960 if now - self.last_management_announce >= self.management_announce_interval_secs {
7962 self.emit_management_announces(now);
7963 }
7964 }
7965
7966 fn emit_management_announces(&mut self, now: f64) {
7968 use crate::management;
7969
7970 self.last_management_announce = now;
7971
7972 let identity = match self.transport_identity {
7973 Some(ref id) => id,
7974 None => return,
7975 };
7976
7977 let mgmt_raw = if self.management_config.enable_remote_management {
7979 management::build_management_announce(identity, &mut self.rng)
7980 } else {
7981 None
7982 };
7983
7984 let bh_raw = if self.management_config.publish_blackhole {
7985 management::build_blackhole_announce(identity, &mut self.rng)
7986 } else {
7987 None
7988 };
7989
7990 let probe_raw = if self.probe_responder_hash.is_some() {
7991 management::build_probe_announce(identity, &mut self.rng)
7992 } else {
7993 None
7994 };
7995
7996 if let Some(raw) = mgmt_raw {
7997 if let Ok(packet) = RawPacket::unpack(&raw) {
7998 let actions = self.engine.handle_outbound(
7999 &packet,
8000 rns_core::constants::DESTINATION_SINGLE,
8001 None,
8002 now,
8003 );
8004 self.dispatch_all(actions);
8005 log::debug!("Emitted management destination announce");
8006 }
8007 }
8008
8009 if let Some(raw) = bh_raw {
8010 if let Ok(packet) = RawPacket::unpack(&raw) {
8011 let actions = self.engine.handle_outbound(
8012 &packet,
8013 rns_core::constants::DESTINATION_SINGLE,
8014 None,
8015 now,
8016 );
8017 self.dispatch_all(actions);
8018 log::debug!("Emitted blackhole info announce");
8019 }
8020 }
8021
8022 if let Some(raw) = probe_raw {
8023 if let Ok(packet) = RawPacket::unpack(&raw) {
8024 let actions = self.engine.handle_outbound(
8025 &packet,
8026 rns_core::constants::DESTINATION_SINGLE,
8027 None,
8028 now,
8029 );
8030 self.dispatch_all(actions);
8031 log::debug!("Emitted probe responder announce");
8032 }
8033 }
8034 }
8035
8036 fn handle_management_request(
8038 &mut self,
8039 link_id: [u8; 16],
8040 path_hash: [u8; 16],
8041 data: Vec<u8>,
8042 request_id: [u8; 16],
8043 remote_identity: Option<([u8; 16], [u8; 64])>,
8044 ) {
8045 use crate::management;
8046
8047 let is_restricted = path_hash == management::status_path_hash()
8049 || path_hash == management::path_path_hash();
8050
8051 if is_restricted && !self.management_config.remote_management_allowed.is_empty() {
8052 match remote_identity {
8053 Some((identity_hash, _)) => {
8054 if !self
8055 .management_config
8056 .remote_management_allowed
8057 .contains(&identity_hash)
8058 {
8059 log::debug!("Management request denied: identity not in allowed list");
8060 return;
8061 }
8062 }
8063 None => {
8064 log::debug!("Management request denied: peer not identified");
8065 return;
8066 }
8067 }
8068 }
8069
8070 let response_data = if path_hash == management::status_path_hash() {
8071 {
8072 let views: Vec<&dyn management::InterfaceStatusView> = self
8073 .interfaces
8074 .values()
8075 .map(|e| e as &dyn management::InterfaceStatusView)
8076 .collect();
8077 management::handle_status_request(
8078 &data,
8079 &self.engine,
8080 &views,
8081 self.started,
8082 self.probe_responder_hash,
8083 )
8084 }
8085 } else if path_hash == management::path_path_hash() {
8086 management::handle_path_request(&data, &self.engine)
8087 } else if path_hash == management::list_path_hash() {
8088 management::handle_blackhole_list_request(&self.engine)
8089 } else {
8090 log::warn!("Unknown management path_hash: {:02x?}", &path_hash[..4]);
8091 None
8092 };
8093
8094 if let Some(response) = response_data {
8095 let actions = self.link_manager.send_management_response(
8096 &link_id,
8097 &request_id,
8098 &response,
8099 &mut self.rng,
8100 );
8101 self.dispatch_link_actions(actions);
8102 }
8103 }
8104}
8105
8106#[cfg(test)]
8107mod tests {
8108 use super::*;
8109 use crate::event;
8110 use crate::interface::Writer;
8111 use rns_core::announce::AnnounceData;
8112 use rns_core::constants;
8113 use rns_core::packet::PacketFlags;
8114 use rns_core::transport::types::InterfaceInfo;
8115 use rns_crypto::identity::Identity;
8116 use std::io;
8117 use std::sync::mpsc;
8118 use std::sync::{Arc, Mutex};
8119 use std::thread;
8120 use std::time::{Duration, Instant};
8121
8122 struct MockWriter {
8123 sent: Arc<Mutex<Vec<Vec<u8>>>>,
8124 }
8125
8126 impl MockWriter {
8127 fn new() -> (Self, Arc<Mutex<Vec<Vec<u8>>>>) {
8128 let sent = Arc::new(Mutex::new(Vec::new()));
8129 (MockWriter { sent: sent.clone() }, sent)
8130 }
8131 }
8132
8133 impl Writer for MockWriter {
8134 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
8135 self.sent.lock().unwrap().push(data.to_vec());
8136 Ok(())
8137 }
8138 }
8139
8140 struct BlockingWriter {
8141 entered_tx: std::sync::mpsc::Sender<()>,
8142 release_rx: std::sync::mpsc::Receiver<()>,
8143 }
8144
8145 impl Writer for BlockingWriter {
8146 fn send_frame(&mut self, _data: &[u8]) -> io::Result<()> {
8147 let _ = self.entered_tx.send(());
8148 let _ = self.release_rx.recv();
8149 Ok(())
8150 }
8151 }
8152
8153 struct WouldBlockWriter {
8154 attempts: Arc<Mutex<usize>>,
8155 }
8156
8157 impl WouldBlockWriter {
8158 fn new() -> (Self, Arc<Mutex<usize>>) {
8159 let attempts = Arc::new(Mutex::new(0));
8160 (
8161 WouldBlockWriter {
8162 attempts: attempts.clone(),
8163 },
8164 attempts,
8165 )
8166 }
8167 }
8168
8169 impl Writer for WouldBlockWriter {
8170 fn send_frame(&mut self, _data: &[u8]) -> io::Result<()> {
8171 *self.attempts.lock().unwrap() += 1;
8172 Err(io::Error::new(
8173 io::ErrorKind::WouldBlock,
8174 "intentional stall",
8175 ))
8176 }
8177 }
8178
8179 fn wait_for_sent_len(sent: &Arc<Mutex<Vec<Vec<u8>>>>, expected: usize) {
8180 let deadline = Instant::now() + Duration::from_millis(200);
8181 while Instant::now() < deadline {
8182 if sent.lock().unwrap().len() == expected {
8183 return;
8184 }
8185 thread::sleep(Duration::from_millis(5));
8186 }
8187 assert_eq!(sent.lock().unwrap().len(), expected);
8188 }
8189
8190 use rns_core::types::{DestHash, IdentityHash, LinkId as TypedLinkId, PacketHash};
8191
8192 struct MockCallbacks {
8193 announces: Arc<Mutex<Vec<(DestHash, u8)>>>,
8194 paths: Arc<Mutex<Vec<(DestHash, u8)>>>,
8195 deliveries: Arc<Mutex<Vec<DestHash>>>,
8196 iface_ups: Arc<Mutex<Vec<InterfaceId>>>,
8197 iface_downs: Arc<Mutex<Vec<InterfaceId>>>,
8198 link_established: Arc<Mutex<Vec<(TypedLinkId, f64, bool)>>>,
8199 link_closed: Arc<Mutex<Vec<TypedLinkId>>>,
8200 remote_identified: Arc<Mutex<Vec<(TypedLinkId, IdentityHash)>>>,
8201 resources_received: Arc<Mutex<Vec<(TypedLinkId, Vec<u8>)>>>,
8202 resource_completed: Arc<Mutex<Vec<TypedLinkId>>>,
8203 resource_failed: Arc<Mutex<Vec<(TypedLinkId, String)>>>,
8204 channel_messages: Arc<Mutex<Vec<(TypedLinkId, u16, Vec<u8>)>>>,
8205 link_data: Arc<Mutex<Vec<(TypedLinkId, u8, Vec<u8>)>>>,
8206 responses: Arc<Mutex<Vec<(TypedLinkId, [u8; 16], Vec<u8>)>>>,
8207 proofs: Arc<Mutex<Vec<(DestHash, PacketHash, f64)>>>,
8208 proof_requested: Arc<Mutex<Vec<(DestHash, PacketHash)>>>,
8209 }
8210
8211 impl MockCallbacks {
8212 fn new() -> (
8213 Self,
8214 Arc<Mutex<Vec<(DestHash, u8)>>>,
8215 Arc<Mutex<Vec<(DestHash, u8)>>>,
8216 Arc<Mutex<Vec<DestHash>>>,
8217 Arc<Mutex<Vec<InterfaceId>>>,
8218 Arc<Mutex<Vec<InterfaceId>>>,
8219 ) {
8220 let announces = Arc::new(Mutex::new(Vec::new()));
8221 let paths = Arc::new(Mutex::new(Vec::new()));
8222 let deliveries = Arc::new(Mutex::new(Vec::new()));
8223 let iface_ups = Arc::new(Mutex::new(Vec::new()));
8224 let iface_downs = Arc::new(Mutex::new(Vec::new()));
8225 (
8226 MockCallbacks {
8227 announces: announces.clone(),
8228 paths: paths.clone(),
8229 deliveries: deliveries.clone(),
8230 iface_ups: iface_ups.clone(),
8231 iface_downs: iface_downs.clone(),
8232 link_established: Arc::new(Mutex::new(Vec::new())),
8233 link_closed: Arc::new(Mutex::new(Vec::new())),
8234 remote_identified: Arc::new(Mutex::new(Vec::new())),
8235 resources_received: Arc::new(Mutex::new(Vec::new())),
8236 resource_completed: Arc::new(Mutex::new(Vec::new())),
8237 resource_failed: Arc::new(Mutex::new(Vec::new())),
8238 channel_messages: Arc::new(Mutex::new(Vec::new())),
8239 link_data: Arc::new(Mutex::new(Vec::new())),
8240 responses: Arc::new(Mutex::new(Vec::new())),
8241 proofs: Arc::new(Mutex::new(Vec::new())),
8242 proof_requested: Arc::new(Mutex::new(Vec::new())),
8243 },
8244 announces,
8245 paths,
8246 deliveries,
8247 iface_ups,
8248 iface_downs,
8249 )
8250 }
8251
8252 fn with_link_tracking() -> (
8253 Self,
8254 Arc<Mutex<Vec<(TypedLinkId, f64, bool)>>>,
8255 Arc<Mutex<Vec<TypedLinkId>>>,
8256 Arc<Mutex<Vec<(TypedLinkId, IdentityHash)>>>,
8257 ) {
8258 let link_established = Arc::new(Mutex::new(Vec::new()));
8259 let link_closed = Arc::new(Mutex::new(Vec::new()));
8260 let remote_identified = Arc::new(Mutex::new(Vec::new()));
8261 (
8262 MockCallbacks {
8263 announces: Arc::new(Mutex::new(Vec::new())),
8264 paths: Arc::new(Mutex::new(Vec::new())),
8265 deliveries: Arc::new(Mutex::new(Vec::new())),
8266 iface_ups: Arc::new(Mutex::new(Vec::new())),
8267 iface_downs: Arc::new(Mutex::new(Vec::new())),
8268 link_established: link_established.clone(),
8269 link_closed: link_closed.clone(),
8270 remote_identified: remote_identified.clone(),
8271 resources_received: Arc::new(Mutex::new(Vec::new())),
8272 resource_completed: Arc::new(Mutex::new(Vec::new())),
8273 resource_failed: Arc::new(Mutex::new(Vec::new())),
8274 channel_messages: Arc::new(Mutex::new(Vec::new())),
8275 link_data: Arc::new(Mutex::new(Vec::new())),
8276 responses: Arc::new(Mutex::new(Vec::new())),
8277 proofs: Arc::new(Mutex::new(Vec::new())),
8278 proof_requested: Arc::new(Mutex::new(Vec::new())),
8279 },
8280 link_established,
8281 link_closed,
8282 remote_identified,
8283 )
8284 }
8285 }
8286
8287 fn new_test_driver() -> Driver {
8288 let transport_config = TransportConfig {
8289 transport_enabled: false,
8290 identity_hash: None,
8291 prefer_shorter_path: false,
8292 max_paths_per_destination: 1,
8293 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
8294 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
8295 max_path_destinations: usize::MAX,
8296 max_tunnel_destinations_total: usize::MAX,
8297 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
8298 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
8299 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
8300 announce_sig_cache_enabled: true,
8301 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
8302 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
8303 announce_queue_max_entries: 256,
8304 announce_queue_max_interfaces: 1024,
8305 };
8306 let (callbacks, _, _, _, _, _) = MockCallbacks::new();
8307 let (tx, rx) = event::channel();
8308 let mut driver = Driver::new(transport_config, rx, tx, Box::new(callbacks));
8309 driver.set_tick_interval_handle(Arc::new(AtomicU64::new(1000)));
8310 driver
8311 }
8312
8313 fn make_announced_identity(
8314 dest_hash: [u8; 16],
8315 received_at: f64,
8316 receiving_interface: InterfaceId,
8317 ) -> crate::destination::AnnouncedIdentity {
8318 crate::destination::AnnouncedIdentity {
8319 dest_hash: rns_core::types::DestHash(dest_hash),
8320 identity_hash: rns_core::types::IdentityHash([dest_hash[0]; 16]),
8321 public_key: [dest_hash[0]; 64],
8322 app_data: None,
8323 hops: 1,
8324 received_at,
8325 receiving_interface,
8326 }
8327 }
8328
8329 #[cfg(feature = "iface-backbone")]
8330 fn register_test_backbone(driver: &mut Driver, name: &str) {
8331 let startup = BackboneServerRuntime {
8332 max_connections: Some(8),
8333 idle_timeout: Some(Duration::from_secs(10)),
8334 write_stall_timeout: Some(Duration::from_secs(30)),
8335 abuse: BackboneAbuseConfig {
8336 max_penalty_duration: Some(Duration::from_secs(3600)),
8337 },
8338 };
8339 let peer_state = Arc::new(std::sync::Mutex::new(
8340 crate::interface::backbone::BackbonePeerMonitor::new(),
8341 ));
8342 driver.register_backbone_runtime(BackboneRuntimeConfigHandle {
8343 interface_name: name.to_string(),
8344 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
8345 startup,
8346 });
8347 driver.register_backbone_peer_state(BackbonePeerStateHandle {
8348 interface_id: InterfaceId(1),
8349 interface_name: name.to_string(),
8350 peer_state,
8351 });
8352 }
8353
8354 #[cfg(feature = "iface-backbone")]
8355 fn register_test_backbone_client(driver: &mut Driver, name: &str) {
8356 let startup = BackboneClientRuntime {
8357 reconnect_wait: Duration::from_secs(5),
8358 max_reconnect_tries: Some(3),
8359 connect_timeout: Duration::from_secs(5),
8360 };
8361 driver.register_backbone_client_runtime(BackboneClientRuntimeConfigHandle {
8362 interface_name: name.to_string(),
8363 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
8364 startup,
8365 });
8366 }
8367
8368 #[cfg(feature = "iface-backbone")]
8369 fn register_test_backbone_discovery(driver: &mut Driver, name: &str, discoverable: bool) {
8370 let startup = BackboneDiscoveryRuntime {
8371 discoverable,
8372 config: crate::discovery::DiscoveryConfig {
8373 discovery_name: name.to_string(),
8374 announce_interval: 3600,
8375 stamp_value: crate::discovery::DEFAULT_STAMP_VALUE,
8376 reachable_on: None,
8377 interface_type: "BackboneInterface".to_string(),
8378 listen_port: Some(4242),
8379 latitude: None,
8380 longitude: None,
8381 height: None,
8382 },
8383 transport_enabled: true,
8384 ifac_netname: None,
8385 ifac_netkey: None,
8386 };
8387 driver.register_backbone_discovery_runtime(BackboneDiscoveryRuntimeHandle {
8388 interface_name: name.to_string(),
8389 current: startup.clone(),
8390 startup,
8391 });
8392 }
8393
8394 #[cfg(feature = "iface-tcp")]
8395 fn register_test_tcp_server(driver: &mut Driver, name: &str) {
8396 let startup = TcpServerRuntime {
8397 max_connections: Some(4),
8398 };
8399 driver.register_tcp_server_runtime(TcpServerRuntimeConfigHandle {
8400 interface_name: name.to_string(),
8401 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
8402 startup,
8403 });
8404 }
8405
8406 #[cfg(feature = "iface-tcp")]
8407 fn register_test_tcp_server_discovery(driver: &mut Driver, name: &str, discoverable: bool) {
8408 let startup = TcpServerDiscoveryRuntime {
8409 discoverable,
8410 config: crate::discovery::DiscoveryConfig {
8411 discovery_name: name.to_string(),
8412 announce_interval: 3600,
8413 stamp_value: crate::discovery::DEFAULT_STAMP_VALUE,
8414 reachable_on: None,
8415 interface_type: "TCPServerInterface".to_string(),
8416 listen_port: Some(4242),
8417 latitude: None,
8418 longitude: None,
8419 height: None,
8420 },
8421 transport_enabled: true,
8422 ifac_netname: None,
8423 ifac_netkey: None,
8424 };
8425 driver.register_tcp_server_discovery_runtime(TcpServerDiscoveryRuntimeHandle {
8426 interface_name: name.to_string(),
8427 current: startup.clone(),
8428 startup,
8429 });
8430 }
8431
8432 #[cfg(feature = "iface-tcp")]
8433 fn register_test_tcp_client(driver: &mut Driver, name: &str) {
8434 let startup = crate::interface::tcp::TcpClientRuntime {
8435 target_host: "127.0.0.1".into(),
8436 target_port: 4242,
8437 reconnect_wait: Duration::from_secs(5),
8438 max_reconnect_tries: Some(3),
8439 connect_timeout: Duration::from_secs(5),
8440 };
8441 driver.register_tcp_client_runtime(crate::interface::tcp::TcpClientRuntimeConfigHandle {
8442 interface_name: name.to_string(),
8443 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
8444 startup,
8445 });
8446 }
8447
8448 #[cfg(feature = "iface-udp")]
8449 fn register_test_udp(driver: &mut Driver, name: &str) {
8450 let startup = UdpRuntime {
8451 forward_ip: Some("127.0.0.1".into()),
8452 forward_port: Some(4242),
8453 };
8454 driver.register_udp_runtime(UdpRuntimeConfigHandle {
8455 interface_name: name.to_string(),
8456 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
8457 startup,
8458 });
8459 }
8460
8461 fn register_test_generic_interface(driver: &mut Driver, id: u64, name: &str) {
8462 let mut info = make_interface_info(id);
8463 info.name = name.to_string();
8464 info.mode = rns_core::constants::MODE_FULL;
8465 info.announce_rate_target = Some(1.5);
8466 info.announce_rate_grace = 2;
8467 info.announce_rate_penalty = 0.25;
8468 info.announce_cap = 0.05;
8469 info.ingress_control.enabled = true;
8470 driver.register_interface_runtime_defaults(&info);
8471 driver.register_interface_ifac_runtime(
8472 &info.name,
8473 IfacRuntimeConfig {
8474 netname: None,
8475 netkey: None,
8476 size: 16,
8477 },
8478 );
8479 driver.engine.register_interface(info.clone());
8480 let (writer, _) = MockWriter::new();
8481 driver.interfaces.insert(
8482 InterfaceId(id),
8483 InterfaceEntry {
8484 id: InterfaceId(id),
8485 info,
8486 writer: Box::new(writer),
8487 async_writer_metrics: None,
8488 enabled: true,
8489 online: true,
8490 dynamic: false,
8491 ifac: None,
8492 stats: InterfaceStats {
8493 started: time::now(),
8494 ..Default::default()
8495 },
8496 interface_type: "TestInterface".to_string(),
8497 send_retry_at: None,
8498 send_retry_backoff: Duration::ZERO,
8499 },
8500 );
8501 }
8502
8503 #[cfg(feature = "iface-auto")]
8504 fn register_test_auto(driver: &mut Driver, name: &str) {
8505 let startup = AutoRuntime {
8506 announce_interval_secs: 1.6,
8507 peer_timeout_secs: 22.0,
8508 peer_job_interval_secs: 4.0,
8509 };
8510 driver.register_auto_runtime(AutoRuntimeConfigHandle {
8511 interface_name: name.to_string(),
8512 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
8513 startup,
8514 });
8515 }
8516
8517 #[cfg(feature = "iface-i2p")]
8518 fn register_test_i2p(driver: &mut Driver, name: &str) {
8519 let startup = I2pRuntime {
8520 reconnect_wait: Duration::from_secs(15),
8521 };
8522 driver.register_i2p_runtime(I2pRuntimeConfigHandle {
8523 interface_name: name.to_string(),
8524 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
8525 startup,
8526 });
8527 }
8528
8529 #[cfg(feature = "iface-pipe")]
8530 fn register_test_pipe(driver: &mut Driver, name: &str) {
8531 let startup = PipeRuntime {
8532 respawn_delay: Duration::from_secs(5),
8533 };
8534 driver.register_pipe_runtime(PipeRuntimeConfigHandle {
8535 interface_name: name.to_string(),
8536 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
8537 startup,
8538 });
8539 }
8540
8541 #[cfg(feature = "iface-rnode")]
8542 fn register_test_rnode(driver: &mut Driver, name: &str) {
8543 let startup = RNodeRuntime {
8544 sub: RNodeSubConfig {
8545 name: name.to_string(),
8546 frequency: 868_000_000,
8547 bandwidth: 125_000,
8548 txpower: 7,
8549 spreading_factor: 8,
8550 coding_rate: 5,
8551 flow_control: false,
8552 st_alock: None,
8553 lt_alock: None,
8554 },
8555 writer: None,
8556 };
8557 driver.register_rnode_runtime(RNodeRuntimeConfigHandle {
8558 interface_name: name.to_string(),
8559 runtime: Arc::new(std::sync::Mutex::new(startup.clone())),
8560 startup,
8561 });
8562 }
8563
8564 impl Callbacks for MockCallbacks {
8565 fn on_announce(&mut self, announced: crate::destination::AnnouncedIdentity) {
8566 self.announces
8567 .lock()
8568 .unwrap()
8569 .push((announced.dest_hash, announced.hops));
8570 }
8571
8572 fn on_path_updated(&mut self, dest_hash: DestHash, hops: u8) {
8573 self.paths.lock().unwrap().push((dest_hash, hops));
8574 }
8575
8576 fn on_local_delivery(
8577 &mut self,
8578 dest_hash: DestHash,
8579 _raw: Vec<u8>,
8580 _packet_hash: PacketHash,
8581 ) {
8582 self.deliveries.lock().unwrap().push(dest_hash);
8583 }
8584
8585 fn on_interface_up(&mut self, id: InterfaceId) {
8586 self.iface_ups.lock().unwrap().push(id);
8587 }
8588
8589 fn on_interface_down(&mut self, id: InterfaceId) {
8590 self.iface_downs.lock().unwrap().push(id);
8591 }
8592
8593 fn on_link_established(
8594 &mut self,
8595 link_id: TypedLinkId,
8596 _dest_hash: DestHash,
8597 rtt: f64,
8598 is_initiator: bool,
8599 ) {
8600 self.link_established
8601 .lock()
8602 .unwrap()
8603 .push((link_id, rtt, is_initiator));
8604 }
8605
8606 fn on_link_closed(
8607 &mut self,
8608 link_id: TypedLinkId,
8609 _reason: Option<rns_core::link::TeardownReason>,
8610 ) {
8611 self.link_closed.lock().unwrap().push(link_id);
8612 }
8613
8614 fn on_remote_identified(
8615 &mut self,
8616 link_id: TypedLinkId,
8617 identity_hash: IdentityHash,
8618 _public_key: [u8; 64],
8619 ) {
8620 self.remote_identified
8621 .lock()
8622 .unwrap()
8623 .push((link_id, identity_hash));
8624 }
8625
8626 fn on_resource_received(
8627 &mut self,
8628 link_id: TypedLinkId,
8629 data: Vec<u8>,
8630 _metadata: Option<Vec<u8>>,
8631 ) {
8632 self.resources_received
8633 .lock()
8634 .unwrap()
8635 .push((link_id, data));
8636 }
8637
8638 fn on_resource_completed(&mut self, link_id: TypedLinkId) {
8639 self.resource_completed.lock().unwrap().push(link_id);
8640 }
8641
8642 fn on_resource_failed(&mut self, link_id: TypedLinkId, error: String) {
8643 self.resource_failed.lock().unwrap().push((link_id, error));
8644 }
8645
8646 fn on_channel_message(&mut self, link_id: TypedLinkId, msgtype: u16, payload: Vec<u8>) {
8647 self.channel_messages
8648 .lock()
8649 .unwrap()
8650 .push((link_id, msgtype, payload));
8651 }
8652
8653 fn on_link_data(&mut self, link_id: TypedLinkId, context: u8, data: Vec<u8>) {
8654 self.link_data
8655 .lock()
8656 .unwrap()
8657 .push((link_id, context, data));
8658 }
8659
8660 fn on_response(&mut self, link_id: TypedLinkId, request_id: [u8; 16], data: Vec<u8>) {
8661 self.responses
8662 .lock()
8663 .unwrap()
8664 .push((link_id, request_id, data));
8665 }
8666
8667 fn on_proof(&mut self, dest_hash: DestHash, packet_hash: PacketHash, rtt: f64) {
8668 self.proofs
8669 .lock()
8670 .unwrap()
8671 .push((dest_hash, packet_hash, rtt));
8672 }
8673
8674 fn on_proof_requested(&mut self, dest_hash: DestHash, packet_hash: PacketHash) -> bool {
8675 self.proof_requested
8676 .lock()
8677 .unwrap()
8678 .push((dest_hash, packet_hash));
8679 true
8680 }
8681 }
8682
8683 fn make_interface_info(id: u64) -> InterfaceInfo {
8684 InterfaceInfo {
8685 id: InterfaceId(id),
8686 name: format!("test-{}", id),
8687 mode: constants::MODE_FULL,
8688 out_capable: true,
8689 in_capable: true,
8690 bitrate: None,
8691 announce_rate_target: None,
8692 announce_rate_grace: 0,
8693 announce_rate_penalty: 0.0,
8694 announce_cap: rns_core::constants::ANNOUNCE_CAP,
8695 is_local_client: false,
8696 wants_tunnel: false,
8697 tunnel_id: None,
8698 mtu: constants::MTU as u32,
8699 ia_freq: 0.0,
8700 started: 0.0,
8701 ingress_control: rns_core::transport::types::IngressControlConfig::disabled(),
8702 }
8703 }
8704
8705 fn make_entry(id: u64, writer: Box<dyn Writer>, online: bool) -> InterfaceEntry {
8706 InterfaceEntry {
8707 id: InterfaceId(id),
8708 info: make_interface_info(id),
8709 writer,
8710 async_writer_metrics: None,
8711 enabled: true,
8712 online,
8713 dynamic: false,
8714 ifac: None,
8715 stats: InterfaceStats::default(),
8716 interface_type: String::new(),
8717 send_retry_at: None,
8718 send_retry_backoff: Duration::ZERO,
8719 }
8720 }
8721
8722 fn build_announce_packet(identity: &Identity) -> Vec<u8> {
8724 let dest_hash =
8725 rns_core::destination::destination_hash("test", &["app"], Some(identity.hash()));
8726 let name_hash = rns_core::destination::name_hash("test", &["app"]);
8727 let random_hash = [0x42u8; 10];
8728
8729 let (announce_data, _has_ratchet) =
8730 AnnounceData::pack(identity, &dest_hash, &name_hash, &random_hash, None, None).unwrap();
8731
8732 let flags = PacketFlags {
8733 header_type: constants::HEADER_1,
8734 context_flag: constants::FLAG_UNSET,
8735 transport_type: constants::TRANSPORT_BROADCAST,
8736 destination_type: constants::DESTINATION_SINGLE,
8737 packet_type: constants::PACKET_TYPE_ANNOUNCE,
8738 };
8739
8740 let packet = RawPacket::pack(
8741 flags,
8742 0,
8743 &dest_hash,
8744 None,
8745 constants::CONTEXT_NONE,
8746 &announce_data,
8747 )
8748 .unwrap();
8749 packet.raw
8750 }
8751
8752 #[test]
8753 fn process_inbound_frame() {
8754 let (tx, rx) = event::channel();
8755 let (cbs, announces, _, _, _, _) = MockCallbacks::new();
8756 let mut driver = Driver::new(
8757 TransportConfig {
8758 transport_enabled: false,
8759 identity_hash: None,
8760 prefer_shorter_path: false,
8761 max_paths_per_destination: 1,
8762 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
8763 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
8764 max_path_destinations: usize::MAX,
8765 max_tunnel_destinations_total: usize::MAX,
8766 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
8767 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
8768 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
8769 announce_sig_cache_enabled: true,
8770 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
8771 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
8772 announce_queue_max_entries: 256,
8773 announce_queue_max_interfaces: 1024,
8774 },
8775 rx,
8776 tx.clone(),
8777 Box::new(cbs),
8778 );
8779 let info = make_interface_info(1);
8780 driver.engine.register_interface(info.clone());
8781 let (writer, _sent) = MockWriter::new();
8782 driver
8783 .interfaces
8784 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
8785
8786 let identity = Identity::new(&mut OsRng);
8787 let announce_raw = build_announce_packet(&identity);
8788
8789 tx.send(Event::Frame {
8791 interface_id: InterfaceId(1),
8792 data: announce_raw,
8793 })
8794 .unwrap();
8795 tx.send(Event::Shutdown).unwrap();
8796 driver.run();
8797
8798 assert_eq!(announces.lock().unwrap().len(), 1);
8799 }
8800
8801 #[test]
8802 fn dispatch_send() {
8803 let (tx, rx) = event::channel();
8804 let (cbs, _, _, _, _, _) = MockCallbacks::new();
8805 let mut driver = Driver::new(
8806 TransportConfig {
8807 transport_enabled: false,
8808 identity_hash: None,
8809 prefer_shorter_path: false,
8810 max_paths_per_destination: 1,
8811 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
8812 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
8813 max_path_destinations: usize::MAX,
8814 max_tunnel_destinations_total: usize::MAX,
8815 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
8816 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
8817 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
8818 announce_sig_cache_enabled: true,
8819 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
8820 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
8821 announce_queue_max_entries: 256,
8822 announce_queue_max_interfaces: 1024,
8823 },
8824 rx,
8825 tx.clone(),
8826 Box::new(cbs),
8827 );
8828 let (writer, sent) = MockWriter::new();
8829 driver
8830 .interfaces
8831 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
8832
8833 driver.dispatch_all(vec![TransportAction::SendOnInterface {
8834 interface: InterfaceId(1),
8835 raw: vec![0x01, 0x02, 0x03],
8836 }]);
8837
8838 assert_eq!(sent.lock().unwrap().len(), 1);
8839 assert_eq!(sent.lock().unwrap()[0], vec![0x01, 0x02, 0x03]);
8840
8841 drop(tx);
8842 }
8843
8844 #[test]
8845 fn dispatch_broadcast() {
8846 let (tx, rx) = event::channel();
8847 let (cbs, _, _, _, _, _) = MockCallbacks::new();
8848 let mut driver = Driver::new(
8849 TransportConfig {
8850 transport_enabled: false,
8851 identity_hash: None,
8852 prefer_shorter_path: false,
8853 max_paths_per_destination: 1,
8854 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
8855 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
8856 max_path_destinations: usize::MAX,
8857 max_tunnel_destinations_total: usize::MAX,
8858 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
8859 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
8860 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
8861 announce_sig_cache_enabled: true,
8862 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
8863 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
8864 announce_queue_max_entries: 256,
8865 announce_queue_max_interfaces: 1024,
8866 },
8867 rx,
8868 tx.clone(),
8869 Box::new(cbs),
8870 );
8871
8872 let (w1, sent1) = MockWriter::new();
8873 let (w2, sent2) = MockWriter::new();
8874 driver
8875 .interfaces
8876 .insert(InterfaceId(1), make_entry(1, Box::new(w1), true));
8877 driver
8878 .interfaces
8879 .insert(InterfaceId(2), make_entry(2, Box::new(w2), true));
8880
8881 driver.dispatch_all(vec![TransportAction::BroadcastOnAllInterfaces {
8882 raw: vec![0xAA],
8883 exclude: None,
8884 }]);
8885
8886 assert_eq!(sent1.lock().unwrap().len(), 1);
8887 assert_eq!(sent2.lock().unwrap().len(), 1);
8888
8889 drop(tx);
8890 }
8891
8892 #[test]
8893 fn dispatch_broadcast_exclude() {
8894 let (tx, rx) = event::channel();
8895 let (cbs, _, _, _, _, _) = MockCallbacks::new();
8896 let mut driver = Driver::new(
8897 TransportConfig {
8898 transport_enabled: false,
8899 identity_hash: None,
8900 prefer_shorter_path: false,
8901 max_paths_per_destination: 1,
8902 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
8903 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
8904 max_path_destinations: usize::MAX,
8905 max_tunnel_destinations_total: usize::MAX,
8906 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
8907 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
8908 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
8909 announce_sig_cache_enabled: true,
8910 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
8911 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
8912 announce_queue_max_entries: 256,
8913 announce_queue_max_interfaces: 1024,
8914 },
8915 rx,
8916 tx.clone(),
8917 Box::new(cbs),
8918 );
8919
8920 let (w1, sent1) = MockWriter::new();
8921 let (w2, sent2) = MockWriter::new();
8922 driver
8923 .interfaces
8924 .insert(InterfaceId(1), make_entry(1, Box::new(w1), true));
8925 driver
8926 .interfaces
8927 .insert(InterfaceId(2), make_entry(2, Box::new(w2), true));
8928
8929 driver.dispatch_all(vec![TransportAction::BroadcastOnAllInterfaces {
8930 raw: vec![0xBB],
8931 exclude: Some(InterfaceId(1)),
8932 }]);
8933
8934 assert_eq!(sent1.lock().unwrap().len(), 0); assert_eq!(sent2.lock().unwrap().len(), 1);
8936
8937 drop(tx);
8938 }
8939
8940 #[test]
8941 fn tick_event() {
8942 let (tx, rx) = event::channel();
8943 let (cbs, _, _, _, _, _) = MockCallbacks::new();
8944 let mut driver = Driver::new(
8945 TransportConfig {
8946 transport_enabled: true,
8947 identity_hash: Some([0x42; 16]),
8948 prefer_shorter_path: false,
8949 max_paths_per_destination: 1,
8950 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
8951 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
8952 max_path_destinations: usize::MAX,
8953 max_tunnel_destinations_total: usize::MAX,
8954 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
8955 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
8956 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
8957 announce_sig_cache_enabled: true,
8958 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
8959 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
8960 announce_queue_max_entries: 256,
8961 announce_queue_max_interfaces: 1024,
8962 },
8963 rx,
8964 tx.clone(),
8965 Box::new(cbs),
8966 );
8967 let info = make_interface_info(1);
8968 driver.engine.register_interface(info.clone());
8969 let (writer, _sent) = MockWriter::new();
8970 driver
8971 .interfaces
8972 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
8973
8974 tx.send(Event::Tick).unwrap();
8976 tx.send(Event::Shutdown).unwrap();
8977 driver.run();
8978 }
8980
8981 #[test]
8982 fn shutdown_event() {
8983 let (tx, rx) = event::channel();
8984 let (cbs, _, _, _, _, _) = MockCallbacks::new();
8985 let mut driver = Driver::new(
8986 TransportConfig {
8987 transport_enabled: false,
8988 identity_hash: None,
8989 prefer_shorter_path: false,
8990 max_paths_per_destination: 1,
8991 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
8992 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
8993 max_path_destinations: usize::MAX,
8994 max_tunnel_destinations_total: usize::MAX,
8995 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
8996 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
8997 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
8998 announce_sig_cache_enabled: true,
8999 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9000 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9001 announce_queue_max_entries: 256,
9002 announce_queue_max_interfaces: 1024,
9003 },
9004 rx,
9005 tx.clone(),
9006 Box::new(cbs),
9007 );
9008
9009 tx.send(Event::Shutdown).unwrap();
9010 driver.run(); }
9012
9013 #[test]
9014 fn begin_drain_updates_driver_status() {
9015 let (tx, rx) = event::channel();
9016 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9017 let mut driver = Driver::new(
9018 TransportConfig {
9019 transport_enabled: false,
9020 identity_hash: None,
9021 prefer_shorter_path: false,
9022 max_paths_per_destination: 1,
9023 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9024 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9025 max_path_destinations: usize::MAX,
9026 max_tunnel_destinations_total: usize::MAX,
9027 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9028 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9029 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9030 announce_sig_cache_enabled: true,
9031 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9032 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9033 announce_queue_max_entries: 256,
9034 announce_queue_max_interfaces: 1024,
9035 },
9036 rx,
9037 tx,
9038 Box::new(cbs),
9039 );
9040
9041 driver.begin_drain(Duration::from_secs(3));
9042
9043 let QueryResponse::DrainStatus(status) = driver.handle_query(QueryRequest::DrainStatus)
9044 else {
9045 panic!("expected drain status response");
9046 };
9047 assert_eq!(status.state, LifecycleState::Draining);
9048 assert!(status.drain_complete);
9049 assert!(status.drain_age_seconds.is_some());
9050 assert!(status.deadline_remaining_seconds.is_some());
9051 assert_eq!(
9052 status.detail.as_deref(),
9053 Some("node is draining existing work; no active links, resource transfers, hole-punch sessions, or queued writer/provider work remain")
9054 );
9055 }
9056
9057 #[test]
9058 fn begin_drain_with_pending_link_reports_incomplete_status() {
9059 let (tx, rx) = event::channel();
9060 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9061 let mut driver = Driver::new(
9062 TransportConfig {
9063 transport_enabled: false,
9064 identity_hash: None,
9065 prefer_shorter_path: false,
9066 max_paths_per_destination: 1,
9067 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9068 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9069 max_path_destinations: usize::MAX,
9070 max_tunnel_destinations_total: usize::MAX,
9071 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9072 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9073 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9074 announce_sig_cache_enabled: true,
9075 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9076 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9077 announce_queue_max_entries: 256,
9078 announce_queue_max_interfaces: 1024,
9079 },
9080 rx,
9081 tx,
9082 Box::new(cbs),
9083 );
9084
9085 let _ = driver.link_manager.create_link(
9086 &[0xDD; 16],
9087 &[0x11; 32],
9088 1,
9089 rns_core::constants::MTU as u32,
9090 &mut OsRng,
9091 );
9092
9093 driver.begin_drain(Duration::from_secs(3));
9094
9095 let QueryResponse::DrainStatus(status) = driver.handle_query(QueryRequest::DrainStatus)
9096 else {
9097 panic!("expected drain status response");
9098 };
9099 assert_eq!(status.state, LifecycleState::Draining);
9100 assert!(!status.drain_complete);
9101 assert!(status
9102 .detail
9103 .unwrap_or_default()
9104 .contains("1 link(s) still active"));
9105 }
9106
9107 #[test]
9108 fn begin_drain_with_queued_writer_frames_reports_incomplete_status() {
9109 let (tx, rx) = event::channel();
9110 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9111 let mut driver = Driver::new(
9112 TransportConfig {
9113 transport_enabled: false,
9114 identity_hash: None,
9115 prefer_shorter_path: false,
9116 max_paths_per_destination: 1,
9117 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9118 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9119 max_path_destinations: usize::MAX,
9120 max_tunnel_destinations_total: usize::MAX,
9121 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9122 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9123 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9124 announce_sig_cache_enabled: true,
9125 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9126 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9127 announce_queue_max_entries: 256,
9128 announce_queue_max_interfaces: 1024,
9129 },
9130 rx,
9131 tx,
9132 Box::new(cbs),
9133 );
9134
9135 let info = make_interface_info(77);
9136 let (entered_tx, entered_rx) = std::sync::mpsc::channel();
9137 let (release_tx, release_rx) = std::sync::mpsc::channel();
9138 let (writer, async_writer_metrics) = crate::interface::wrap_async_writer(
9139 Box::new(BlockingWriter {
9140 entered_tx,
9141 release_rx,
9142 }),
9143 InterfaceId(77),
9144 &info.name,
9145 driver.event_tx.clone(),
9146 1,
9147 );
9148
9149 driver.interfaces.insert(
9150 InterfaceId(77),
9151 InterfaceEntry {
9152 id: InterfaceId(77),
9153 info,
9154 writer,
9155 async_writer_metrics: Some(async_writer_metrics),
9156 enabled: true,
9157 online: true,
9158 dynamic: false,
9159 ifac: None,
9160 stats: InterfaceStats::default(),
9161 interface_type: "TestInterface".to_string(),
9162 send_retry_at: None,
9163 send_retry_backoff: Duration::ZERO,
9164 },
9165 );
9166
9167 driver.dispatch_all(vec![TransportAction::SendOnInterface {
9168 interface: InterfaceId(77),
9169 raw: vec![0x01],
9170 }]);
9171 entered_rx.recv_timeout(Duration::from_secs(1)).unwrap();
9172 driver.dispatch_all(vec![TransportAction::SendOnInterface {
9173 interface: InterfaceId(77),
9174 raw: vec![0x02],
9175 }]);
9176
9177 driver.begin_drain(Duration::from_secs(3));
9178
9179 let QueryResponse::DrainStatus(status) = driver.handle_query(QueryRequest::DrainStatus)
9180 else {
9181 panic!("expected drain status response");
9182 };
9183 assert_eq!(status.state, LifecycleState::Draining);
9184 assert!(!status.drain_complete);
9185 assert_eq!(status.interface_writer_queued_frames, 1);
9186 assert!(status
9187 .detail
9188 .unwrap_or_default()
9189 .contains("queued interface writer frame"));
9190
9191 let _ = release_tx.send(());
9192 }
9193
9194 #[test]
9195 fn enforce_drain_deadline_tears_down_remaining_links() {
9196 let (tx, rx) = event::channel();
9197 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9198 let mut driver = Driver::new(
9199 TransportConfig {
9200 transport_enabled: false,
9201 identity_hash: None,
9202 prefer_shorter_path: false,
9203 max_paths_per_destination: 1,
9204 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9205 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9206 max_path_destinations: usize::MAX,
9207 max_tunnel_destinations_total: usize::MAX,
9208 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9209 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9210 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9211 announce_sig_cache_enabled: true,
9212 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9213 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9214 announce_queue_max_entries: 256,
9215 announce_queue_max_interfaces: 1024,
9216 },
9217 rx,
9218 tx,
9219 Box::new(cbs),
9220 );
9221
9222 let _ = driver.link_manager.create_link(
9223 &[0xDD; 16],
9224 &[0x11; 32],
9225 1,
9226 rns_core::constants::MTU as u32,
9227 &mut OsRng,
9228 );
9229 driver.begin_drain(Duration::ZERO);
9230
9231 driver.enforce_drain_deadline();
9232
9233 assert_eq!(driver.lifecycle_state, LifecycleState::Stopping);
9234 assert_eq!(driver.link_manager.link_count(), 0);
9235 let QueryResponse::DrainStatus(status) = driver.handle_query(QueryRequest::DrainStatus)
9236 else {
9237 panic!("expected drain status response");
9238 };
9239 assert!(status.drain_complete);
9240 assert_eq!(status.state, LifecycleState::Stopping);
9241 }
9242
9243 #[test]
9244 fn begin_drain_with_holepunch_session_reports_incomplete_status_and_deadline_aborts_it() {
9245 let (tx, rx) = event::channel();
9246 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9247 let mut driver = Driver::new(
9248 TransportConfig {
9249 transport_enabled: false,
9250 identity_hash: None,
9251 prefer_shorter_path: false,
9252 max_paths_per_destination: 1,
9253 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9254 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9255 max_path_destinations: usize::MAX,
9256 max_tunnel_destinations_total: usize::MAX,
9257 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9258 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9259 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9260 announce_sig_cache_enabled: true,
9261 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9262 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9263 announce_queue_max_entries: 256,
9264 announce_queue_max_interfaces: 1024,
9265 },
9266 rx,
9267 tx,
9268 Box::new(cbs),
9269 );
9270 driver.holepunch_manager = crate::holepunch::orchestrator::HolePunchManager::new(
9271 vec!["127.0.0.1:4343".parse().unwrap()],
9272 rns_core::holepunch::ProbeProtocol::Rnsp,
9273 None,
9274 );
9275
9276 let _ = driver.holepunch_manager.propose(
9277 [0x44; 16],
9278 &[0xAA; 32],
9279 &mut OsRng,
9280 &driver.get_event_sender(),
9281 );
9282 assert_eq!(driver.holepunch_manager.session_count(), 1);
9283
9284 driver.begin_drain(Duration::from_secs(3));
9285
9286 let QueryResponse::DrainStatus(status) = driver.handle_query(QueryRequest::DrainStatus)
9287 else {
9288 panic!("expected drain status response");
9289 };
9290 assert_eq!(status.state, LifecycleState::Draining);
9291 assert!(!status.drain_complete);
9292 assert!(status
9293 .detail
9294 .unwrap_or_default()
9295 .contains("1 hole-punch session(s) still active"));
9296
9297 driver.begin_drain(Duration::ZERO);
9298 driver.enforce_drain_deadline();
9299
9300 assert_eq!(driver.holepunch_manager.session_count(), 0);
9301 let QueryResponse::DrainStatus(status) = driver.handle_query(QueryRequest::DrainStatus)
9302 else {
9303 panic!("expected drain status response");
9304 };
9305 assert!(status.drain_complete);
9306 assert_eq!(status.state, LifecycleState::Stopping);
9307 }
9308
9309 #[test]
9310 fn begin_drain_event_is_processed_by_run_loop() {
9311 let (tx, rx) = event::channel();
9312 let tx_query = tx.clone();
9313 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9314 let mut driver = Driver::new(
9315 TransportConfig {
9316 transport_enabled: false,
9317 identity_hash: None,
9318 prefer_shorter_path: false,
9319 max_paths_per_destination: 1,
9320 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9321 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9322 max_path_destinations: usize::MAX,
9323 max_tunnel_destinations_total: usize::MAX,
9324 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9325 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9326 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9327 announce_sig_cache_enabled: true,
9328 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9329 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9330 announce_queue_max_entries: 256,
9331 announce_queue_max_interfaces: 1024,
9332 },
9333 rx,
9334 tx.clone(),
9335 Box::new(cbs),
9336 );
9337
9338 let handle = std::thread::spawn(move || driver.run());
9339 tx.send(Event::BeginDrain {
9340 timeout: Duration::from_secs(2),
9341 })
9342 .unwrap();
9343 let (resp_tx, resp_rx) = std::sync::mpsc::channel();
9344 tx_query
9345 .send(Event::Query(QueryRequest::DrainStatus, resp_tx))
9346 .unwrap();
9347 let status = match resp_rx.recv().unwrap() {
9348 QueryResponse::DrainStatus(status) => status,
9349 other => panic!("expected drain status response, got {:?}", other),
9350 };
9351 assert_eq!(status.state, LifecycleState::Draining);
9352 tx_query.send(Event::Shutdown).unwrap();
9353 handle.join().unwrap();
9354 }
9355
9356 #[test]
9357 fn send_channel_message_returns_error_while_draining() {
9358 let (tx, rx) = event::channel();
9359 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9360 let mut driver = Driver::new(
9361 TransportConfig {
9362 transport_enabled: false,
9363 identity_hash: None,
9364 prefer_shorter_path: false,
9365 max_paths_per_destination: 1,
9366 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9367 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9368 max_path_destinations: usize::MAX,
9369 max_tunnel_destinations_total: usize::MAX,
9370 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9371 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9372 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9373 announce_sig_cache_enabled: true,
9374 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9375 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9376 announce_queue_max_entries: 256,
9377 announce_queue_max_interfaces: 1024,
9378 },
9379 rx,
9380 tx.clone(),
9381 Box::new(cbs),
9382 );
9383
9384 tx.send(Event::BeginDrain {
9385 timeout: Duration::from_secs(2),
9386 })
9387 .unwrap();
9388 let (resp_tx, resp_rx) = mpsc::channel();
9389 tx.send(Event::SendChannelMessage {
9390 link_id: [0xAA; 16],
9391 msgtype: 7,
9392 payload: b"drain".to_vec(),
9393 response_tx: resp_tx,
9394 })
9395 .unwrap();
9396 tx.send(Event::Shutdown).unwrap();
9397 driver.run();
9398
9399 let response = resp_rx.recv().unwrap();
9400 assert_eq!(
9401 response,
9402 Err("cannot send channel message while node is draining".into())
9403 );
9404 }
9405
9406 #[test]
9407 fn send_outbound_is_ignored_while_draining() {
9408 let (tx, rx) = event::channel();
9409 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9410 let mut driver = Driver::new(
9411 TransportConfig {
9412 transport_enabled: false,
9413 identity_hash: None,
9414 prefer_shorter_path: false,
9415 max_paths_per_destination: 1,
9416 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9417 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9418 max_path_destinations: usize::MAX,
9419 max_tunnel_destinations_total: usize::MAX,
9420 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9421 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9422 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9423 announce_sig_cache_enabled: true,
9424 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9425 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9426 announce_queue_max_entries: 256,
9427 announce_queue_max_interfaces: 1024,
9428 },
9429 rx,
9430 tx.clone(),
9431 Box::new(cbs),
9432 );
9433 let identity = Identity::new(&mut OsRng);
9434 let info = make_interface_info(1);
9435 driver.engine.register_interface(info);
9436 let (writer, sent) = MockWriter::new();
9437 driver
9438 .interfaces
9439 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
9440
9441 tx.send(Event::BeginDrain {
9442 timeout: Duration::from_secs(2),
9443 })
9444 .unwrap();
9445 tx.send(Event::SendOutbound {
9446 raw: build_announce_packet(&identity),
9447 dest_type: constants::DESTINATION_SINGLE,
9448 attached_interface: None,
9449 })
9450 .unwrap();
9451 tx.send(Event::Shutdown).unwrap();
9452 driver.run();
9453
9454 assert!(sent.lock().unwrap().is_empty());
9455 assert!(driver.sent_packets.is_empty());
9456 }
9457
9458 #[test]
9459 fn request_path_is_ignored_while_draining() {
9460 let (tx, rx) = event::channel();
9461 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9462 let mut driver = Driver::new(
9463 TransportConfig {
9464 transport_enabled: false,
9465 identity_hash: None,
9466 prefer_shorter_path: false,
9467 max_paths_per_destination: 1,
9468 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9469 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9470 max_path_destinations: usize::MAX,
9471 max_tunnel_destinations_total: usize::MAX,
9472 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9473 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9474 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9475 announce_sig_cache_enabled: true,
9476 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9477 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9478 announce_queue_max_entries: 256,
9479 announce_queue_max_interfaces: 1024,
9480 },
9481 rx,
9482 tx.clone(),
9483 Box::new(cbs),
9484 );
9485 let info = make_interface_info(1);
9486 driver.engine.register_interface(info);
9487 let (writer, sent) = MockWriter::new();
9488 driver
9489 .interfaces
9490 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
9491
9492 tx.send(Event::BeginDrain {
9493 timeout: Duration::from_secs(2),
9494 })
9495 .unwrap();
9496 tx.send(Event::RequestPath {
9497 dest_hash: [0xAA; 16],
9498 })
9499 .unwrap();
9500 tx.send(Event::Shutdown).unwrap();
9501 driver.run();
9502
9503 assert!(sent.lock().unwrap().is_empty());
9504 }
9505
9506 #[test]
9507 fn create_link_returns_zero_link_id_while_draining() {
9508 let (tx, rx) = event::channel();
9509 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9510 let mut driver = Driver::new(
9511 TransportConfig {
9512 transport_enabled: false,
9513 identity_hash: None,
9514 prefer_shorter_path: false,
9515 max_paths_per_destination: 1,
9516 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9517 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9518 max_path_destinations: usize::MAX,
9519 max_tunnel_destinations_total: usize::MAX,
9520 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9521 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9522 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9523 announce_sig_cache_enabled: true,
9524 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9525 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9526 announce_queue_max_entries: 256,
9527 announce_queue_max_interfaces: 1024,
9528 },
9529 rx,
9530 tx.clone(),
9531 Box::new(cbs),
9532 );
9533
9534 tx.send(Event::BeginDrain {
9535 timeout: Duration::from_secs(2),
9536 })
9537 .unwrap();
9538 let (resp_tx, resp_rx) = mpsc::channel();
9539 tx.send(Event::CreateLink {
9540 dest_hash: [0xAB; 16],
9541 dest_sig_pub_bytes: [0xCD; 32],
9542 response_tx: resp_tx,
9543 })
9544 .unwrap();
9545 tx.send(Event::Shutdown).unwrap();
9546 driver.run();
9547
9548 assert_eq!(resp_rx.recv().unwrap(), [0u8; 16]);
9549 }
9550
9551 #[test]
9552 fn announce_callback() {
9553 let (tx, rx) = event::channel();
9554 let (cbs, announces, paths, _, _, _) = MockCallbacks::new();
9555 let mut driver = Driver::new(
9556 TransportConfig {
9557 transport_enabled: false,
9558 identity_hash: None,
9559 prefer_shorter_path: false,
9560 max_paths_per_destination: 1,
9561 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9562 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9563 max_path_destinations: usize::MAX,
9564 max_tunnel_destinations_total: usize::MAX,
9565 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9566 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9567 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9568 announce_sig_cache_enabled: true,
9569 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9570 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9571 announce_queue_max_entries: 256,
9572 announce_queue_max_interfaces: 1024,
9573 },
9574 rx,
9575 tx.clone(),
9576 Box::new(cbs),
9577 );
9578 let info = make_interface_info(1);
9579 driver.engine.register_interface(info.clone());
9580 let (writer, _sent) = MockWriter::new();
9581 driver
9582 .interfaces
9583 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
9584
9585 let identity = Identity::new(&mut OsRng);
9586 let announce_raw = build_announce_packet(&identity);
9587
9588 tx.send(Event::Frame {
9589 interface_id: InterfaceId(1),
9590 data: announce_raw,
9591 })
9592 .unwrap();
9593 tx.send(Event::Shutdown).unwrap();
9594 driver.run();
9595
9596 let ann = announces.lock().unwrap();
9597 assert_eq!(ann.len(), 1);
9598 assert_eq!(ann[0].1, 1);
9600
9601 let p = paths.lock().unwrap();
9602 assert_eq!(p.len(), 1);
9603 }
9604
9605 #[test]
9606 fn dispatch_skips_offline_interface() {
9607 let (tx, rx) = event::channel();
9608 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9609 let mut driver = Driver::new(
9610 TransportConfig {
9611 transport_enabled: false,
9612 identity_hash: None,
9613 prefer_shorter_path: false,
9614 max_paths_per_destination: 1,
9615 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9616 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9617 max_path_destinations: usize::MAX,
9618 max_tunnel_destinations_total: usize::MAX,
9619 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9620 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9621 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9622 announce_sig_cache_enabled: true,
9623 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9624 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9625 announce_queue_max_entries: 256,
9626 announce_queue_max_interfaces: 1024,
9627 },
9628 rx,
9629 tx.clone(),
9630 Box::new(cbs),
9631 );
9632
9633 let (w1, sent1) = MockWriter::new();
9634 let (w2, sent2) = MockWriter::new();
9635 driver
9636 .interfaces
9637 .insert(InterfaceId(1), make_entry(1, Box::new(w1), false)); driver
9639 .interfaces
9640 .insert(InterfaceId(2), make_entry(2, Box::new(w2), true));
9641
9642 driver.dispatch_all(vec![TransportAction::SendOnInterface {
9644 interface: InterfaceId(1),
9645 raw: vec![0x01],
9646 }]);
9647 assert_eq!(sent1.lock().unwrap().len(), 0);
9648
9649 driver.dispatch_all(vec![TransportAction::BroadcastOnAllInterfaces {
9651 raw: vec![0x02],
9652 exclude: None,
9653 }]);
9654 assert_eq!(sent1.lock().unwrap().len(), 0); assert_eq!(sent2.lock().unwrap().len(), 1);
9656
9657 drop(tx);
9658 }
9659
9660 #[test]
9661 fn interface_up_refreshes_writer() {
9662 let (tx, rx) = event::channel();
9663 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9664 let mut driver = Driver::new(
9665 TransportConfig {
9666 transport_enabled: false,
9667 identity_hash: None,
9668 prefer_shorter_path: false,
9669 max_paths_per_destination: 1,
9670 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9671 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9672 max_path_destinations: usize::MAX,
9673 max_tunnel_destinations_total: usize::MAX,
9674 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9675 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9676 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9677 announce_sig_cache_enabled: true,
9678 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9679 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9680 announce_queue_max_entries: 256,
9681 announce_queue_max_interfaces: 1024,
9682 },
9683 rx,
9684 tx.clone(),
9685 Box::new(cbs),
9686 );
9687
9688 let (w_old, sent_old) = MockWriter::new();
9689 driver
9690 .interfaces
9691 .insert(InterfaceId(1), make_entry(1, Box::new(w_old), false));
9692
9693 let (w_new, sent_new) = MockWriter::new();
9695 tx.send(Event::InterfaceUp(
9696 InterfaceId(1),
9697 Some(Box::new(w_new)),
9698 None,
9699 ))
9700 .unwrap();
9701 tx.send(Event::Shutdown).unwrap();
9702 driver.run();
9703
9704 assert!(driver.interfaces[&InterfaceId(1)].online);
9706
9707 driver.dispatch_all(vec![TransportAction::SendOnInterface {
9709 interface: InterfaceId(1),
9710 raw: vec![0xFF],
9711 }]);
9712
9713 assert_eq!(sent_old.lock().unwrap().len(), 0);
9715 wait_for_sent_len(&sent_new, 1);
9717 assert_eq!(sent_new.lock().unwrap()[0], vec![0xFF]);
9718
9719 drop(tx);
9720 }
9721
9722 #[test]
9723 fn dynamic_interface_register() {
9724 let (tx, rx) = event::channel();
9725 let (cbs, _, _, _, iface_ups, _) = MockCallbacks::new();
9726 let mut driver = Driver::new(
9727 TransportConfig {
9728 transport_enabled: false,
9729 identity_hash: None,
9730 prefer_shorter_path: false,
9731 max_paths_per_destination: 1,
9732 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9733 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9734 max_path_destinations: usize::MAX,
9735 max_tunnel_destinations_total: usize::MAX,
9736 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9737 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9738 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9739 announce_sig_cache_enabled: true,
9740 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9741 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9742 announce_queue_max_entries: 256,
9743 announce_queue_max_interfaces: 1024,
9744 },
9745 rx,
9746 tx.clone(),
9747 Box::new(cbs),
9748 );
9749
9750 let info = make_interface_info(100);
9751 let (writer, sent) = MockWriter::new();
9752
9753 tx.send(Event::InterfaceUp(
9755 InterfaceId(100),
9756 Some(Box::new(writer)),
9757 Some(info),
9758 ))
9759 .unwrap();
9760 tx.send(Event::Shutdown).unwrap();
9761 driver.run();
9762
9763 assert!(driver.interfaces.contains_key(&InterfaceId(100)));
9765 assert!(driver.interfaces[&InterfaceId(100)].online);
9766 assert!(driver.interfaces[&InterfaceId(100)].dynamic);
9767
9768 assert_eq!(iface_ups.lock().unwrap().len(), 1);
9770 assert_eq!(iface_ups.lock().unwrap()[0], InterfaceId(100));
9771
9772 driver.dispatch_all(vec![TransportAction::SendOnInterface {
9774 interface: InterfaceId(100),
9775 raw: vec![0x42],
9776 }]);
9777 wait_for_sent_len(&sent, 1);
9778
9779 drop(tx);
9780 }
9781
9782 #[test]
9783 fn dynamic_interface_deregister() {
9784 let (tx, rx) = event::channel();
9785 let (cbs, _, _, _, _, iface_downs) = MockCallbacks::new();
9786 let mut driver = Driver::new(
9787 TransportConfig {
9788 transport_enabled: false,
9789 identity_hash: None,
9790 prefer_shorter_path: false,
9791 max_paths_per_destination: 1,
9792 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9793 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9794 max_path_destinations: usize::MAX,
9795 max_tunnel_destinations_total: usize::MAX,
9796 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9797 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9798 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9799 announce_sig_cache_enabled: true,
9800 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9801 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9802 announce_queue_max_entries: 256,
9803 announce_queue_max_interfaces: 1024,
9804 },
9805 rx,
9806 tx.clone(),
9807 Box::new(cbs),
9808 );
9809
9810 let info = make_interface_info(200);
9812 driver.engine.register_interface(info.clone());
9813 let (writer, _sent) = MockWriter::new();
9814 driver.interfaces.insert(
9815 InterfaceId(200),
9816 InterfaceEntry {
9817 id: InterfaceId(200),
9818 info,
9819 writer: Box::new(writer),
9820 async_writer_metrics: None,
9821 enabled: true,
9822 online: true,
9823 dynamic: true,
9824 ifac: None,
9825 stats: InterfaceStats::default(),
9826 interface_type: String::new(),
9827 send_retry_at: None,
9828 send_retry_backoff: Duration::ZERO,
9829 },
9830 );
9831
9832 tx.send(Event::InterfaceDown(InterfaceId(200))).unwrap();
9834 tx.send(Event::Shutdown).unwrap();
9835 driver.run();
9836
9837 assert!(!driver.interfaces.contains_key(&InterfaceId(200)));
9838 assert_eq!(iface_downs.lock().unwrap().len(), 1);
9839 assert_eq!(iface_downs.lock().unwrap()[0], InterfaceId(200));
9840 }
9841
9842 #[test]
9843 fn send_wouldblock_is_backed_off_between_dispatches() {
9844 let (tx, rx) = event::channel();
9845 let (cbs, ..) = MockCallbacks::new();
9846 let mut driver = Driver::new(
9847 TransportConfig {
9848 transport_enabled: false,
9849 identity_hash: None,
9850 prefer_shorter_path: false,
9851 max_paths_per_destination: 1,
9852 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9853 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9854 max_path_destinations: usize::MAX,
9855 max_tunnel_destinations_total: usize::MAX,
9856 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9857 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9858 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9859 announce_sig_cache_enabled: true,
9860 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9861 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9862 announce_queue_max_entries: 256,
9863 announce_queue_max_interfaces: 1024,
9864 },
9865 rx,
9866 tx,
9867 Box::new(cbs),
9868 );
9869 let (writer, attempts) = WouldBlockWriter::new();
9870 driver
9871 .interfaces
9872 .insert(InterfaceId(7), make_entry(7, Box::new(writer), true));
9873
9874 let action = TransportAction::SendOnInterface {
9875 interface: InterfaceId(7),
9876 raw: vec![0x01, 0x00, 0x42],
9877 };
9878 driver.dispatch_all(vec![action.clone()]);
9879 assert_eq!(*attempts.lock().unwrap(), 1);
9880
9881 driver.dispatch_all(vec![action.clone()]);
9882 assert_eq!(
9883 *attempts.lock().unwrap(),
9884 1,
9885 "second dispatch should be deferred during backoff"
9886 );
9887
9888 let entry = driver.interfaces.get_mut(&InterfaceId(7)).unwrap();
9889 entry.send_retry_at = Some(Instant::now() - Duration::from_millis(1));
9890 driver.dispatch_all(vec![action]);
9891 assert_eq!(*attempts.lock().unwrap(), 2);
9892 }
9893
9894 #[test]
9895 fn interface_callbacks_fire() {
9896 let (tx, rx) = event::channel();
9897 let (cbs, _, _, _, iface_ups, iface_downs) = MockCallbacks::new();
9898 let mut driver = Driver::new(
9899 TransportConfig {
9900 transport_enabled: false,
9901 identity_hash: None,
9902 prefer_shorter_path: false,
9903 max_paths_per_destination: 1,
9904 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9905 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9906 max_path_destinations: usize::MAX,
9907 max_tunnel_destinations_total: usize::MAX,
9908 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9909 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9910 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9911 announce_sig_cache_enabled: true,
9912 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9913 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9914 announce_queue_max_entries: 256,
9915 announce_queue_max_interfaces: 1024,
9916 },
9917 rx,
9918 tx.clone(),
9919 Box::new(cbs),
9920 );
9921
9922 let (writer, _) = MockWriter::new();
9924 driver
9925 .interfaces
9926 .insert(InterfaceId(1), make_entry(1, Box::new(writer), false));
9927
9928 tx.send(Event::InterfaceUp(InterfaceId(1), None, None))
9929 .unwrap();
9930 tx.send(Event::InterfaceDown(InterfaceId(1))).unwrap();
9931 tx.send(Event::Shutdown).unwrap();
9932 driver.run();
9933
9934 assert_eq!(iface_ups.lock().unwrap().len(), 1);
9935 assert_eq!(iface_downs.lock().unwrap().len(), 1);
9936 assert!(driver.interfaces.contains_key(&InterfaceId(1)));
9938 assert!(!driver.interfaces[&InterfaceId(1)].online);
9939 }
9940
9941 #[test]
9946 fn frame_updates_rx_stats() {
9947 let (tx, rx) = event::channel();
9948 let (cbs, _, _, _, _, _) = MockCallbacks::new();
9949 let mut driver = Driver::new(
9950 TransportConfig {
9951 transport_enabled: false,
9952 identity_hash: None,
9953 prefer_shorter_path: false,
9954 max_paths_per_destination: 1,
9955 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
9956 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
9957 max_path_destinations: usize::MAX,
9958 max_tunnel_destinations_total: usize::MAX,
9959 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
9960 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
9961 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
9962 announce_sig_cache_enabled: true,
9963 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
9964 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
9965 announce_queue_max_entries: 256,
9966 announce_queue_max_interfaces: 1024,
9967 },
9968 rx,
9969 tx.clone(),
9970 Box::new(cbs),
9971 );
9972 let info = make_interface_info(1);
9973 driver.engine.register_interface(info.clone());
9974 let (writer, _sent) = MockWriter::new();
9975 driver
9976 .interfaces
9977 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
9978
9979 let identity = Identity::new(&mut OsRng);
9980 let announce_raw = build_announce_packet(&identity);
9981 let announce_len = announce_raw.len() as u64;
9982
9983 tx.send(Event::Frame {
9984 interface_id: InterfaceId(1),
9985 data: announce_raw,
9986 })
9987 .unwrap();
9988 tx.send(Event::Shutdown).unwrap();
9989 driver.run();
9990
9991 let stats = &driver.interfaces[&InterfaceId(1)].stats;
9992 assert_eq!(stats.rxb, announce_len);
9993 assert_eq!(stats.rx_packets, 1);
9994 }
9995
9996 #[test]
9997 fn send_updates_tx_stats() {
9998 let (tx, rx) = event::channel();
9999 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10000 let mut driver = Driver::new(
10001 TransportConfig {
10002 transport_enabled: false,
10003 identity_hash: None,
10004 prefer_shorter_path: false,
10005 max_paths_per_destination: 1,
10006 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10007 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10008 max_path_destinations: usize::MAX,
10009 max_tunnel_destinations_total: usize::MAX,
10010 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10011 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10012 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10013 announce_sig_cache_enabled: true,
10014 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10015 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10016 announce_queue_max_entries: 256,
10017 announce_queue_max_interfaces: 1024,
10018 },
10019 rx,
10020 tx.clone(),
10021 Box::new(cbs),
10022 );
10023 let (writer, _sent) = MockWriter::new();
10024 driver
10025 .interfaces
10026 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10027
10028 driver.dispatch_all(vec![TransportAction::SendOnInterface {
10029 interface: InterfaceId(1),
10030 raw: vec![0x01, 0x02, 0x03],
10031 }]);
10032
10033 let stats = &driver.interfaces[&InterfaceId(1)].stats;
10034 assert_eq!(stats.txb, 3);
10035 assert_eq!(stats.tx_packets, 1);
10036
10037 drop(tx);
10038 }
10039
10040 #[test]
10041 fn broadcast_updates_tx_stats() {
10042 let (tx, rx) = event::channel();
10043 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10044 let mut driver = Driver::new(
10045 TransportConfig {
10046 transport_enabled: false,
10047 identity_hash: None,
10048 prefer_shorter_path: false,
10049 max_paths_per_destination: 1,
10050 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10051 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10052 max_path_destinations: usize::MAX,
10053 max_tunnel_destinations_total: usize::MAX,
10054 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10055 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10056 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10057 announce_sig_cache_enabled: true,
10058 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10059 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10060 announce_queue_max_entries: 256,
10061 announce_queue_max_interfaces: 1024,
10062 },
10063 rx,
10064 tx.clone(),
10065 Box::new(cbs),
10066 );
10067 let (w1, _s1) = MockWriter::new();
10068 let (w2, _s2) = MockWriter::new();
10069 driver
10070 .interfaces
10071 .insert(InterfaceId(1), make_entry(1, Box::new(w1), true));
10072 driver
10073 .interfaces
10074 .insert(InterfaceId(2), make_entry(2, Box::new(w2), true));
10075
10076 driver.dispatch_all(vec![TransportAction::BroadcastOnAllInterfaces {
10077 raw: vec![0xAA, 0xBB],
10078 exclude: None,
10079 }]);
10080
10081 assert_eq!(driver.interfaces[&InterfaceId(1)].stats.txb, 2);
10083 assert_eq!(driver.interfaces[&InterfaceId(1)].stats.tx_packets, 1);
10084 assert_eq!(driver.interfaces[&InterfaceId(2)].stats.txb, 2);
10085 assert_eq!(driver.interfaces[&InterfaceId(2)].stats.tx_packets, 1);
10086
10087 drop(tx);
10088 }
10089
10090 #[test]
10091 fn query_interface_stats() {
10092 let (tx, rx) = event::channel();
10093 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10094 let mut driver = Driver::new(
10095 TransportConfig {
10096 transport_enabled: true,
10097 identity_hash: Some([0x42; 16]),
10098 prefer_shorter_path: false,
10099 max_paths_per_destination: 1,
10100 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10101 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10102 max_path_destinations: usize::MAX,
10103 max_tunnel_destinations_total: usize::MAX,
10104 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10105 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10106 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10107 announce_sig_cache_enabled: true,
10108 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10109 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10110 announce_queue_max_entries: 256,
10111 announce_queue_max_interfaces: 1024,
10112 },
10113 rx,
10114 tx.clone(),
10115 Box::new(cbs),
10116 );
10117 let (writer, _sent) = MockWriter::new();
10118 driver
10119 .interfaces
10120 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10121
10122 let (resp_tx, resp_rx) = mpsc::channel();
10123 tx.send(Event::Query(QueryRequest::InterfaceStats, resp_tx))
10124 .unwrap();
10125 tx.send(Event::Shutdown).unwrap();
10126 driver.run();
10127
10128 let resp = resp_rx.recv().unwrap();
10129 match resp {
10130 QueryResponse::InterfaceStats(stats) => {
10131 assert_eq!(stats.interfaces.len(), 1);
10132 assert_eq!(stats.interfaces[0].name, "test-1");
10133 assert!(stats.interfaces[0].status);
10134 assert_eq!(stats.transport_id, Some([0x42; 16]));
10135 assert!(stats.transport_enabled);
10136 }
10137 _ => panic!("unexpected response"),
10138 }
10139 }
10140
10141 #[test]
10142 fn query_path_table() {
10143 let (tx, rx) = event::channel();
10144 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10145 let mut driver = Driver::new(
10146 TransportConfig {
10147 transport_enabled: false,
10148 identity_hash: None,
10149 prefer_shorter_path: false,
10150 max_paths_per_destination: 1,
10151 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10152 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10153 max_path_destinations: usize::MAX,
10154 max_tunnel_destinations_total: usize::MAX,
10155 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10156 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10157 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10158 announce_sig_cache_enabled: true,
10159 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10160 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10161 announce_queue_max_entries: 256,
10162 announce_queue_max_interfaces: 1024,
10163 },
10164 rx,
10165 tx.clone(),
10166 Box::new(cbs),
10167 );
10168 let info = make_interface_info(1);
10169 driver.engine.register_interface(info);
10170 let (writer, _sent) = MockWriter::new();
10171 driver
10172 .interfaces
10173 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10174
10175 let identity = Identity::new(&mut OsRng);
10177 let announce_raw = build_announce_packet(&identity);
10178 tx.send(Event::Frame {
10179 interface_id: InterfaceId(1),
10180 data: announce_raw,
10181 })
10182 .unwrap();
10183
10184 let (resp_tx, resp_rx) = mpsc::channel();
10185 tx.send(Event::Query(
10186 QueryRequest::PathTable { max_hops: None },
10187 resp_tx,
10188 ))
10189 .unwrap();
10190 tx.send(Event::Shutdown).unwrap();
10191 driver.run();
10192
10193 let resp = resp_rx.recv().unwrap();
10194 match resp {
10195 QueryResponse::PathTable(entries) => {
10196 assert_eq!(entries.len(), 1);
10197 assert_eq!(entries[0].hops, 1);
10198 }
10199 _ => panic!("unexpected response"),
10200 }
10201 }
10202
10203 #[test]
10204 fn query_drop_path() {
10205 let (tx, rx) = event::channel();
10206 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10207 let mut driver = Driver::new(
10208 TransportConfig {
10209 transport_enabled: false,
10210 identity_hash: None,
10211 prefer_shorter_path: false,
10212 max_paths_per_destination: 1,
10213 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10214 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10215 max_path_destinations: usize::MAX,
10216 max_tunnel_destinations_total: usize::MAX,
10217 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10218 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10219 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10220 announce_sig_cache_enabled: true,
10221 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10222 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10223 announce_queue_max_entries: 256,
10224 announce_queue_max_interfaces: 1024,
10225 },
10226 rx,
10227 tx.clone(),
10228 Box::new(cbs),
10229 );
10230 let info = make_interface_info(1);
10231 driver.engine.register_interface(info);
10232 let (writer, _sent) = MockWriter::new();
10233 driver
10234 .interfaces
10235 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10236
10237 let identity = Identity::new(&mut OsRng);
10239 let announce_raw = build_announce_packet(&identity);
10240 let dest_hash =
10241 rns_core::destination::destination_hash("test", &["app"], Some(identity.hash()));
10242
10243 tx.send(Event::Frame {
10244 interface_id: InterfaceId(1),
10245 data: announce_raw,
10246 })
10247 .unwrap();
10248
10249 let (resp_tx, resp_rx) = mpsc::channel();
10250 tx.send(Event::Query(QueryRequest::DropPath { dest_hash }, resp_tx))
10251 .unwrap();
10252 tx.send(Event::Shutdown).unwrap();
10253 driver.run();
10254
10255 let resp = resp_rx.recv().unwrap();
10256 match resp {
10257 QueryResponse::DropPath(dropped) => {
10258 assert!(dropped);
10259 }
10260 _ => panic!("unexpected response"),
10261 }
10262 }
10263
10264 #[test]
10265 fn send_outbound_event() {
10266 let (tx, rx) = event::channel();
10267 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10268 let mut driver = Driver::new(
10269 TransportConfig {
10270 transport_enabled: false,
10271 identity_hash: None,
10272 prefer_shorter_path: false,
10273 max_paths_per_destination: 1,
10274 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10275 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10276 max_path_destinations: usize::MAX,
10277 max_tunnel_destinations_total: usize::MAX,
10278 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10279 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10280 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10281 announce_sig_cache_enabled: true,
10282 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10283 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10284 announce_queue_max_entries: 256,
10285 announce_queue_max_interfaces: 1024,
10286 },
10287 rx,
10288 tx.clone(),
10289 Box::new(cbs),
10290 );
10291 let (writer, sent) = MockWriter::new();
10292 let info = make_interface_info(1);
10293 driver.engine.register_interface(info);
10294 driver
10295 .interfaces
10296 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10297
10298 let dest = [0xAA; 16];
10300 let flags = PacketFlags {
10301 header_type: constants::HEADER_1,
10302 context_flag: constants::FLAG_UNSET,
10303 transport_type: constants::TRANSPORT_BROADCAST,
10304 destination_type: constants::DESTINATION_PLAIN,
10305 packet_type: constants::PACKET_TYPE_DATA,
10306 };
10307 let packet =
10308 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
10309
10310 tx.send(Event::SendOutbound {
10311 raw: packet.raw,
10312 dest_type: constants::DESTINATION_PLAIN,
10313 attached_interface: None,
10314 })
10315 .unwrap();
10316 tx.send(Event::Shutdown).unwrap();
10317 driver.run();
10318
10319 assert_eq!(sent.lock().unwrap().len(), 1);
10321 }
10322
10323 #[test]
10324 fn register_destination_and_deliver() {
10325 let (tx, rx) = event::channel();
10326 let (cbs, _, _, deliveries, _, _) = MockCallbacks::new();
10327 let mut driver = Driver::new(
10328 TransportConfig {
10329 transport_enabled: false,
10330 identity_hash: None,
10331 prefer_shorter_path: false,
10332 max_paths_per_destination: 1,
10333 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10334 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10335 max_path_destinations: usize::MAX,
10336 max_tunnel_destinations_total: usize::MAX,
10337 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10338 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10339 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10340 announce_sig_cache_enabled: true,
10341 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10342 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10343 announce_queue_max_entries: 256,
10344 announce_queue_max_interfaces: 1024,
10345 },
10346 rx,
10347 tx.clone(),
10348 Box::new(cbs),
10349 );
10350 let info = make_interface_info(1);
10351 driver.engine.register_interface(info);
10352 let (writer, _sent) = MockWriter::new();
10353 driver
10354 .interfaces
10355 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10356
10357 let dest = [0xBB; 16];
10358
10359 tx.send(Event::RegisterDestination {
10361 dest_hash: dest,
10362 dest_type: constants::DESTINATION_SINGLE,
10363 })
10364 .unwrap();
10365
10366 let flags = PacketFlags {
10367 header_type: constants::HEADER_1,
10368 context_flag: constants::FLAG_UNSET,
10369 transport_type: constants::TRANSPORT_BROADCAST,
10370 destination_type: constants::DESTINATION_SINGLE,
10371 packet_type: constants::PACKET_TYPE_DATA,
10372 };
10373 let packet =
10374 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"data").unwrap();
10375 tx.send(Event::Frame {
10376 interface_id: InterfaceId(1),
10377 data: packet.raw,
10378 })
10379 .unwrap();
10380 tx.send(Event::Shutdown).unwrap();
10381 driver.run();
10382
10383 assert_eq!(deliveries.lock().unwrap().len(), 1);
10384 assert_eq!(deliveries.lock().unwrap()[0], DestHash(dest));
10385 }
10386
10387 #[test]
10388 fn query_transport_identity() {
10389 let (tx, rx) = event::channel();
10390 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10391 let mut driver = Driver::new(
10392 TransportConfig {
10393 transport_enabled: true,
10394 identity_hash: Some([0xAA; 16]),
10395 prefer_shorter_path: false,
10396 max_paths_per_destination: 1,
10397 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10398 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10399 max_path_destinations: usize::MAX,
10400 max_tunnel_destinations_total: usize::MAX,
10401 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10402 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10403 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10404 announce_sig_cache_enabled: true,
10405 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10406 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10407 announce_queue_max_entries: 256,
10408 announce_queue_max_interfaces: 1024,
10409 },
10410 rx,
10411 tx.clone(),
10412 Box::new(cbs),
10413 );
10414
10415 let (resp_tx, resp_rx) = mpsc::channel();
10416 tx.send(Event::Query(QueryRequest::TransportIdentity, resp_tx))
10417 .unwrap();
10418 tx.send(Event::Shutdown).unwrap();
10419 driver.run();
10420
10421 match resp_rx.recv().unwrap() {
10422 QueryResponse::TransportIdentity(Some(hash)) => {
10423 assert_eq!(hash, [0xAA; 16]);
10424 }
10425 _ => panic!("unexpected response"),
10426 }
10427 }
10428
10429 #[test]
10430 fn query_link_count() {
10431 let (tx, rx) = event::channel();
10432 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10433 let mut driver = Driver::new(
10434 TransportConfig {
10435 transport_enabled: false,
10436 identity_hash: None,
10437 prefer_shorter_path: false,
10438 max_paths_per_destination: 1,
10439 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10440 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10441 max_path_destinations: usize::MAX,
10442 max_tunnel_destinations_total: usize::MAX,
10443 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10444 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10445 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10446 announce_sig_cache_enabled: true,
10447 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10448 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10449 announce_queue_max_entries: 256,
10450 announce_queue_max_interfaces: 1024,
10451 },
10452 rx,
10453 tx.clone(),
10454 Box::new(cbs),
10455 );
10456
10457 let (resp_tx, resp_rx) = mpsc::channel();
10458 tx.send(Event::Query(QueryRequest::LinkCount, resp_tx))
10459 .unwrap();
10460 tx.send(Event::Shutdown).unwrap();
10461 driver.run();
10462
10463 match resp_rx.recv().unwrap() {
10464 QueryResponse::LinkCount(count) => assert_eq!(count, 0),
10465 _ => panic!("unexpected response"),
10466 }
10467 }
10468
10469 #[test]
10470 fn query_rate_table() {
10471 let (tx, rx) = event::channel();
10472 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10473 let mut driver = Driver::new(
10474 TransportConfig {
10475 transport_enabled: false,
10476 identity_hash: None,
10477 prefer_shorter_path: false,
10478 max_paths_per_destination: 1,
10479 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10480 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10481 max_path_destinations: usize::MAX,
10482 max_tunnel_destinations_total: usize::MAX,
10483 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10484 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10485 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10486 announce_sig_cache_enabled: true,
10487 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10488 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10489 announce_queue_max_entries: 256,
10490 announce_queue_max_interfaces: 1024,
10491 },
10492 rx,
10493 tx.clone(),
10494 Box::new(cbs),
10495 );
10496
10497 let (resp_tx, resp_rx) = mpsc::channel();
10498 tx.send(Event::Query(QueryRequest::RateTable, resp_tx))
10499 .unwrap();
10500 tx.send(Event::Shutdown).unwrap();
10501 driver.run();
10502
10503 match resp_rx.recv().unwrap() {
10504 QueryResponse::RateTable(entries) => assert!(entries.is_empty()),
10505 _ => panic!("unexpected response"),
10506 }
10507 }
10508
10509 #[test]
10510 fn query_next_hop() {
10511 let (tx, rx) = event::channel();
10512 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10513 let mut driver = Driver::new(
10514 TransportConfig {
10515 transport_enabled: false,
10516 identity_hash: None,
10517 prefer_shorter_path: false,
10518 max_paths_per_destination: 1,
10519 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10520 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10521 max_path_destinations: usize::MAX,
10522 max_tunnel_destinations_total: usize::MAX,
10523 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10524 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10525 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10526 announce_sig_cache_enabled: true,
10527 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10528 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10529 announce_queue_max_entries: 256,
10530 announce_queue_max_interfaces: 1024,
10531 },
10532 rx,
10533 tx.clone(),
10534 Box::new(cbs),
10535 );
10536
10537 let dest = [0xBB; 16];
10538 let (resp_tx, resp_rx) = mpsc::channel();
10539 tx.send(Event::Query(
10540 QueryRequest::NextHop { dest_hash: dest },
10541 resp_tx,
10542 ))
10543 .unwrap();
10544 tx.send(Event::Shutdown).unwrap();
10545 driver.run();
10546
10547 match resp_rx.recv().unwrap() {
10548 QueryResponse::NextHop(None) => {}
10549 _ => panic!("unexpected response"),
10550 }
10551 }
10552
10553 #[test]
10554 fn query_next_hop_if_name() {
10555 let (tx, rx) = event::channel();
10556 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10557 let mut driver = Driver::new(
10558 TransportConfig {
10559 transport_enabled: false,
10560 identity_hash: None,
10561 prefer_shorter_path: false,
10562 max_paths_per_destination: 1,
10563 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10564 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10565 max_path_destinations: usize::MAX,
10566 max_tunnel_destinations_total: usize::MAX,
10567 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10568 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10569 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10570 announce_sig_cache_enabled: true,
10571 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10572 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10573 announce_queue_max_entries: 256,
10574 announce_queue_max_interfaces: 1024,
10575 },
10576 rx,
10577 tx.clone(),
10578 Box::new(cbs),
10579 );
10580
10581 let dest = [0xCC; 16];
10582 let (resp_tx, resp_rx) = mpsc::channel();
10583 tx.send(Event::Query(
10584 QueryRequest::NextHopIfName { dest_hash: dest },
10585 resp_tx,
10586 ))
10587 .unwrap();
10588 tx.send(Event::Shutdown).unwrap();
10589 driver.run();
10590
10591 match resp_rx.recv().unwrap() {
10592 QueryResponse::NextHopIfName(None) => {}
10593 _ => panic!("unexpected response"),
10594 }
10595 }
10596
10597 #[test]
10598 fn query_drop_all_via() {
10599 let (tx, rx) = event::channel();
10600 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10601 let mut driver = Driver::new(
10602 TransportConfig {
10603 transport_enabled: false,
10604 identity_hash: None,
10605 prefer_shorter_path: false,
10606 max_paths_per_destination: 1,
10607 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10608 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10609 max_path_destinations: usize::MAX,
10610 max_tunnel_destinations_total: usize::MAX,
10611 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10612 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10613 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10614 announce_sig_cache_enabled: true,
10615 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10616 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10617 announce_queue_max_entries: 256,
10618 announce_queue_max_interfaces: 1024,
10619 },
10620 rx,
10621 tx.clone(),
10622 Box::new(cbs),
10623 );
10624
10625 let transport = [0xDD; 16];
10626 let (resp_tx, resp_rx) = mpsc::channel();
10627 tx.send(Event::Query(
10628 QueryRequest::DropAllVia {
10629 transport_hash: transport,
10630 },
10631 resp_tx,
10632 ))
10633 .unwrap();
10634 tx.send(Event::Shutdown).unwrap();
10635 driver.run();
10636
10637 match resp_rx.recv().unwrap() {
10638 QueryResponse::DropAllVia(count) => assert_eq!(count, 0),
10639 _ => panic!("unexpected response"),
10640 }
10641 }
10642
10643 #[test]
10644 fn query_drop_announce_queues() {
10645 let (tx, rx) = event::channel();
10646 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10647 let mut driver = Driver::new(
10648 TransportConfig {
10649 transport_enabled: false,
10650 identity_hash: None,
10651 prefer_shorter_path: false,
10652 max_paths_per_destination: 1,
10653 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10654 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10655 max_path_destinations: usize::MAX,
10656 max_tunnel_destinations_total: usize::MAX,
10657 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10658 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10659 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10660 announce_sig_cache_enabled: true,
10661 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10662 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10663 announce_queue_max_entries: 256,
10664 announce_queue_max_interfaces: 1024,
10665 },
10666 rx,
10667 tx.clone(),
10668 Box::new(cbs),
10669 );
10670
10671 let (resp_tx, resp_rx) = mpsc::channel();
10672 tx.send(Event::Query(QueryRequest::DropAnnounceQueues, resp_tx))
10673 .unwrap();
10674 tx.send(Event::Shutdown).unwrap();
10675 driver.run();
10676
10677 match resp_rx.recv().unwrap() {
10678 QueryResponse::DropAnnounceQueues => {}
10679 _ => panic!("unexpected response"),
10680 }
10681 }
10682
10683 #[test]
10688 fn register_link_dest_event() {
10689 let (tx, rx) = event::channel();
10690 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10691 let mut driver = Driver::new(
10692 TransportConfig {
10693 transport_enabled: false,
10694 identity_hash: None,
10695 prefer_shorter_path: false,
10696 max_paths_per_destination: 1,
10697 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10698 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10699 max_path_destinations: usize::MAX,
10700 max_tunnel_destinations_total: usize::MAX,
10701 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10702 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10703 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10704 announce_sig_cache_enabled: true,
10705 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10706 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10707 announce_queue_max_entries: 256,
10708 announce_queue_max_interfaces: 1024,
10709 },
10710 rx,
10711 tx.clone(),
10712 Box::new(cbs),
10713 );
10714 let info = make_interface_info(1);
10715 driver.engine.register_interface(info);
10716 let (writer, _sent) = MockWriter::new();
10717 driver
10718 .interfaces
10719 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10720
10721 let mut rng = OsRng;
10722 let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::generate(&mut rng);
10723 let sig_pub_bytes = sig_prv.public_key().public_bytes();
10724 let sig_prv_bytes = sig_prv.private_bytes();
10725 let dest_hash = [0xDD; 16];
10726
10727 tx.send(Event::RegisterLinkDestination {
10728 dest_hash,
10729 sig_prv_bytes,
10730 sig_pub_bytes,
10731 resource_strategy: 0,
10732 })
10733 .unwrap();
10734 tx.send(Event::Shutdown).unwrap();
10735 driver.run();
10736
10737 assert!(driver.link_manager.is_link_destination(&dest_hash));
10739 }
10740
10741 #[test]
10742 fn create_link_event() {
10743 let (tx, rx) = event::channel();
10744 let (cbs, _link_established, _, _) = MockCallbacks::with_link_tracking();
10745 let mut driver = Driver::new(
10746 TransportConfig {
10747 transport_enabled: false,
10748 identity_hash: None,
10749 prefer_shorter_path: false,
10750 max_paths_per_destination: 1,
10751 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10752 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10753 max_path_destinations: usize::MAX,
10754 max_tunnel_destinations_total: usize::MAX,
10755 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10756 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10757 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10758 announce_sig_cache_enabled: true,
10759 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10760 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10761 announce_queue_max_entries: 256,
10762 announce_queue_max_interfaces: 1024,
10763 },
10764 rx,
10765 tx.clone(),
10766 Box::new(cbs),
10767 );
10768 let info = make_interface_info(1);
10769 driver.engine.register_interface(info);
10770 let (writer, _sent) = MockWriter::new();
10771 driver
10772 .interfaces
10773 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10774
10775 let dest_hash = [0xDD; 16];
10776 let dummy_sig_pub = [0xAA; 32];
10777
10778 let (resp_tx, resp_rx) = mpsc::channel();
10779 tx.send(Event::CreateLink {
10780 dest_hash,
10781 dest_sig_pub_bytes: dummy_sig_pub,
10782 response_tx: resp_tx,
10783 })
10784 .unwrap();
10785 tx.send(Event::Shutdown).unwrap();
10786 driver.run();
10787
10788 let link_id = resp_rx.recv().unwrap();
10790 assert_ne!(link_id, [0u8; 16]);
10791
10792 assert_eq!(driver.link_manager.link_count(), 1);
10794
10795 }
10800
10801 #[test]
10802 fn deliver_local_routes_to_link_manager() {
10803 let (tx, rx) = event::channel();
10806 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10807 let mut driver = Driver::new(
10808 TransportConfig {
10809 transport_enabled: false,
10810 identity_hash: None,
10811 prefer_shorter_path: false,
10812 max_paths_per_destination: 1,
10813 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10814 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10815 max_path_destinations: usize::MAX,
10816 max_tunnel_destinations_total: usize::MAX,
10817 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10818 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10819 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10820 announce_sig_cache_enabled: true,
10821 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10822 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10823 announce_queue_max_entries: 256,
10824 announce_queue_max_interfaces: 1024,
10825 },
10826 rx,
10827 tx.clone(),
10828 Box::new(cbs),
10829 );
10830 let info = make_interface_info(1);
10831 driver.engine.register_interface(info);
10832 let (writer, _sent) = MockWriter::new();
10833 driver
10834 .interfaces
10835 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10836
10837 let mut rng = OsRng;
10839 let sig_prv = rns_crypto::ed25519::Ed25519PrivateKey::generate(&mut rng);
10840 let sig_pub_bytes = sig_prv.public_key().public_bytes();
10841 let dest_hash = [0xEE; 16];
10842 driver.link_manager.register_link_destination(
10843 dest_hash,
10844 sig_prv,
10845 sig_pub_bytes,
10846 crate::link_manager::ResourceStrategy::AcceptNone,
10847 );
10848
10849 assert!(driver.link_manager.is_link_destination(&dest_hash));
10853
10854 assert!(!driver.link_manager.is_link_destination(&[0xFF; 16]));
10856
10857 drop(tx);
10858 }
10859
10860 #[test]
10861 fn teardown_link_event() {
10862 let (tx, rx) = event::channel();
10863 let (cbs, _, link_closed, _) = MockCallbacks::with_link_tracking();
10864 let mut driver = Driver::new(
10865 TransportConfig {
10866 transport_enabled: false,
10867 identity_hash: None,
10868 prefer_shorter_path: false,
10869 max_paths_per_destination: 1,
10870 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10871 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10872 max_path_destinations: usize::MAX,
10873 max_tunnel_destinations_total: usize::MAX,
10874 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10875 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10876 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10877 announce_sig_cache_enabled: true,
10878 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10879 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10880 announce_queue_max_entries: 256,
10881 announce_queue_max_interfaces: 1024,
10882 },
10883 rx,
10884 tx.clone(),
10885 Box::new(cbs),
10886 );
10887 let info = make_interface_info(1);
10888 driver.engine.register_interface(info);
10889 let (writer, _sent) = MockWriter::new();
10890 driver
10891 .interfaces
10892 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10893
10894 let (resp_tx, resp_rx) = mpsc::channel();
10896 tx.send(Event::CreateLink {
10897 dest_hash: [0xDD; 16],
10898 dest_sig_pub_bytes: [0xAA; 32],
10899 response_tx: resp_tx,
10900 })
10901 .unwrap();
10902 tx.send(Event::Shutdown).unwrap();
10907 driver.run();
10908
10909 let link_id = resp_rx.recv().unwrap();
10910 assert_ne!(link_id, [0u8; 16]);
10911 assert_eq!(driver.link_manager.link_count(), 1);
10912
10913 let teardown_actions = driver.link_manager.teardown_link(&link_id);
10915 driver.dispatch_link_actions(teardown_actions);
10916
10917 assert_eq!(link_closed.lock().unwrap().len(), 1);
10919 assert_eq!(link_closed.lock().unwrap()[0], TypedLinkId(link_id));
10920 }
10921
10922 #[test]
10923 fn link_count_includes_link_manager() {
10924 let (tx, rx) = event::channel();
10925 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10926 let mut driver = Driver::new(
10927 TransportConfig {
10928 transport_enabled: false,
10929 identity_hash: None,
10930 prefer_shorter_path: false,
10931 max_paths_per_destination: 1,
10932 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10933 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10934 max_path_destinations: usize::MAX,
10935 max_tunnel_destinations_total: usize::MAX,
10936 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10937 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10938 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10939 announce_sig_cache_enabled: true,
10940 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10941 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
10942 announce_queue_max_entries: 256,
10943 announce_queue_max_interfaces: 1024,
10944 },
10945 rx,
10946 tx.clone(),
10947 Box::new(cbs),
10948 );
10949 let info = make_interface_info(1);
10950 driver.engine.register_interface(info);
10951 let (writer, _sent) = MockWriter::new();
10952 driver
10953 .interfaces
10954 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
10955
10956 let mut rng = OsRng;
10958 let dummy_sig = [0xAA; 32];
10959 driver.link_manager.create_link(
10960 &[0xDD; 16],
10961 &dummy_sig,
10962 1,
10963 constants::MTU as u32,
10964 &mut rng,
10965 );
10966
10967 let (resp_tx, resp_rx) = mpsc::channel();
10969 tx.send(Event::Query(QueryRequest::LinkCount, resp_tx))
10970 .unwrap();
10971 tx.send(Event::Shutdown).unwrap();
10972 driver.run();
10973
10974 match resp_rx.recv().unwrap() {
10975 QueryResponse::LinkCount(count) => assert_eq!(count, 1),
10976 _ => panic!("unexpected response"),
10977 }
10978 }
10979
10980 #[test]
10981 fn register_request_handler_event() {
10982 let (tx, rx) = event::channel();
10983 let (cbs, _, _, _, _, _) = MockCallbacks::new();
10984 let mut driver = Driver::new(
10985 TransportConfig {
10986 transport_enabled: false,
10987 identity_hash: None,
10988 prefer_shorter_path: false,
10989 max_paths_per_destination: 1,
10990 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
10991 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
10992 max_path_destinations: usize::MAX,
10993 max_tunnel_destinations_total: usize::MAX,
10994 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
10995 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
10996 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
10997 announce_sig_cache_enabled: true,
10998 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
10999 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11000 announce_queue_max_entries: 256,
11001 announce_queue_max_interfaces: 1024,
11002 },
11003 rx,
11004 tx.clone(),
11005 Box::new(cbs),
11006 );
11007
11008 tx.send(Event::RegisterRequestHandler {
11009 path: "/status".to_string(),
11010 allowed_list: None,
11011 handler: Box::new(|_link_id, _path, _data, _remote| Some(b"OK".to_vec())),
11012 })
11013 .unwrap();
11014 tx.send(Event::Shutdown).unwrap();
11015 driver.run();
11016
11017 }
11020
11021 #[test]
11024 fn management_announces_emitted_after_delay() {
11025 let (tx, rx) = event::channel();
11026 let (cbs, _announces, _, _, _, _) = MockCallbacks::new();
11027 let identity = Identity::new(&mut OsRng);
11028 let identity_hash = *identity.hash();
11029 let mut driver = Driver::new(
11030 TransportConfig {
11031 transport_enabled: true,
11032 identity_hash: Some(identity_hash),
11033 prefer_shorter_path: false,
11034 max_paths_per_destination: 1,
11035 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
11036 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
11037 max_path_destinations: usize::MAX,
11038 max_tunnel_destinations_total: usize::MAX,
11039 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
11040 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
11041 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
11042 announce_sig_cache_enabled: true,
11043 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
11044 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
11045 announce_queue_max_entries: 256,
11046 announce_queue_max_interfaces: 1024,
11047 },
11048 rx,
11049 tx.clone(),
11050 Box::new(cbs),
11051 );
11052
11053 let info = make_interface_info(1);
11055 driver.engine.register_interface(info.clone());
11056 let (writer, sent) = MockWriter::new();
11057 driver
11058 .interfaces
11059 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
11060
11061 driver.management_config.enable_remote_management = true;
11063 driver.transport_identity = Some(identity);
11064
11065 driver.started = time::now() - 10.0;
11067
11068 tx.send(Event::Tick).unwrap();
11070 tx.send(Event::Shutdown).unwrap();
11071 driver.run();
11072
11073 let sent_packets = sent.lock().unwrap();
11075 assert!(
11076 !sent_packets.is_empty(),
11077 "Management announce should be sent after startup delay"
11078 );
11079 }
11080
11081 #[test]
11082 fn runtime_config_list_contains_global_keys() {
11083 let driver = new_test_driver();
11084 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
11085 let QueryResponse::RuntimeConfigList(entries) = response else {
11086 panic!("expected runtime config list");
11087 };
11088 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
11089 assert!(keys.contains(&"global.tick_interval_ms".to_string()));
11090 assert!(keys.contains(&"global.known_destinations_ttl_secs".to_string()));
11091 assert!(keys.contains(&"global.rate_limiter_ttl_secs".to_string()));
11092 assert!(keys.contains(&"global.direct_connect_policy".to_string()));
11093 }
11094
11095 #[test]
11096 fn runtime_config_set_and_reset_tick_interval() {
11097 let mut driver = new_test_driver();
11098
11099 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11100 key: "global.tick_interval_ms".into(),
11101 value: RuntimeConfigValue::Int(250),
11102 });
11103 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11104 panic!("expected runtime config set success");
11105 };
11106 assert_eq!(entry.key, "global.tick_interval_ms");
11107 assert_eq!(entry.value, RuntimeConfigValue::Int(250));
11108 assert_eq!(driver.tick_interval_ms.load(Ordering::Relaxed), 250);
11109
11110 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
11111 key: "global.tick_interval_ms".into(),
11112 });
11113 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
11114 panic!("expected runtime config reset success");
11115 };
11116 assert_eq!(entry.value, RuntimeConfigValue::Int(1000));
11117 assert_eq!(driver.tick_interval_ms.load(Ordering::Relaxed), 1000);
11118 }
11119
11120 #[test]
11121 fn runtime_config_rejects_invalid_policy() {
11122 let mut driver = new_test_driver();
11123 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11124 key: "global.direct_connect_policy".into(),
11125 value: RuntimeConfigValue::String("bogus".into()),
11126 });
11127 let QueryResponse::RuntimeConfigSet(Err(err)) = response else {
11128 panic!("expected runtime config set failure");
11129 };
11130 assert_eq!(err.code, RuntimeConfigErrorCode::InvalidValue);
11131 }
11132
11133 #[test]
11134 fn runtime_config_set_and_reset_rate_limiter_ttl() {
11135 let mut driver = new_test_driver();
11136
11137 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11138 key: "global.rate_limiter_ttl_secs".into(),
11139 value: RuntimeConfigValue::Float(600.0),
11140 });
11141 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11142 panic!("expected runtime config set success");
11143 };
11144 assert_eq!(entry.value, RuntimeConfigValue::Float(600.0));
11145 assert_eq!(driver.rate_limiter_ttl_secs, 600.0);
11146
11147 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
11148 key: "global.rate_limiter_ttl_secs".into(),
11149 });
11150 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
11151 panic!("expected runtime config reset success");
11152 };
11153 assert_eq!(
11154 entry.value,
11155 RuntimeConfigValue::Float(DEFAULT_RATE_LIMITER_TTL_SECS)
11156 );
11157 assert_eq!(driver.rate_limiter_ttl_secs, DEFAULT_RATE_LIMITER_TTL_SECS);
11158 }
11159
11160 #[cfg(feature = "iface-backbone")]
11161 #[test]
11162 fn runtime_config_lists_backbone_keys() {
11163 let mut driver = new_test_driver();
11164 register_test_backbone(&mut driver, "public");
11165 register_test_backbone_client(&mut driver, "uplink");
11166 register_test_backbone_discovery(&mut driver, "public", false);
11167 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
11168 let QueryResponse::RuntimeConfigList(entries) = response else {
11169 panic!("expected runtime config list");
11170 };
11171 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
11172 assert!(keys.contains(&"backbone.public.idle_timeout_secs".to_string()));
11173 assert!(keys.contains(&"backbone.public.write_stall_timeout_secs".to_string()));
11174 assert!(keys.contains(&"backbone.public.max_connections".to_string()));
11175 assert!(keys.contains(&"backbone.public.discoverable".to_string()));
11176 assert!(keys.contains(&"backbone.public.discovery_name".to_string()));
11177 assert!(keys.contains(&"backbone.public.latitude".to_string()));
11178 assert!(keys.contains(&"backbone.public.longitude".to_string()));
11179 assert!(keys.contains(&"backbone.public.height".to_string()));
11180 assert!(keys.contains(&"backbone_client.uplink.connect_timeout_secs".to_string()));
11181 assert!(keys.contains(&"backbone_client.uplink.reconnect_wait_secs".to_string()));
11182 assert!(keys.contains(&"backbone_client.uplink.max_reconnect_tries".to_string()));
11183 }
11184
11185 #[cfg(feature = "iface-backbone")]
11186 #[test]
11187 fn runtime_config_sets_backbone_values() {
11188 let mut driver = new_test_driver();
11189 register_test_backbone(&mut driver, "public");
11190 register_test_backbone_discovery(&mut driver, "public", false);
11191 driver.transport_identity = Some(rns_crypto::identity::Identity::new(&mut OsRng));
11192
11193 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11194 key: "backbone.public.idle_timeout_secs".into(),
11195 value: RuntimeConfigValue::Float(2.5),
11196 });
11197 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11198 panic!("expected runtime config set success");
11199 };
11200 assert_eq!(entry.value, RuntimeConfigValue::Float(2.5));
11201
11202 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11203 key: "backbone.public.write_stall_timeout_secs".into(),
11204 value: RuntimeConfigValue::Float(15.0),
11205 });
11206 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11207 panic!("expected runtime config set success");
11208 };
11209 assert_eq!(entry.value, RuntimeConfigValue::Float(15.0));
11210
11211 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11212 key: "backbone.public.max_connections".into(),
11213 value: RuntimeConfigValue::Int(0),
11214 });
11215 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11216 panic!("expected runtime config set success");
11217 };
11218 assert_eq!(entry.value, RuntimeConfigValue::Int(0));
11219
11220 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
11221 key: "backbone.public.max_connections".into(),
11222 });
11223 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
11224 panic!("expected runtime config reset success");
11225 };
11226 assert_eq!(entry.value, RuntimeConfigValue::Int(8));
11227
11228 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
11229 key: "backbone.public.write_stall_timeout_secs".into(),
11230 });
11231 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
11232 panic!("expected runtime config reset success");
11233 };
11234 assert_eq!(entry.value, RuntimeConfigValue::Float(30.0));
11235
11236 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11237 key: "backbone.public.discoverable".into(),
11238 value: RuntimeConfigValue::Bool(true),
11239 });
11240 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11241 panic!("expected runtime config set success");
11242 };
11243 assert_eq!(entry.value, RuntimeConfigValue::Bool(true));
11244 assert!(driver
11245 .interface_announcer
11246 .as_ref()
11247 .map(|announcer| announcer.contains_interface("public"))
11248 .unwrap_or(false));
11249
11250 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11251 key: "backbone.public.discovery_name".into(),
11252 value: RuntimeConfigValue::String("Public Backbone".into()),
11253 });
11254 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11255 panic!("expected runtime config set success");
11256 };
11257 assert_eq!(
11258 entry.value,
11259 RuntimeConfigValue::String("Public Backbone".into())
11260 );
11261
11262 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11263 key: "backbone.public.latitude".into(),
11264 value: RuntimeConfigValue::Float(45.4642),
11265 });
11266 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11267 panic!("expected runtime config set success");
11268 };
11269 assert_eq!(entry.value, RuntimeConfigValue::Float(45.4642));
11270
11271 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11272 key: "backbone.public.longitude".into(),
11273 value: RuntimeConfigValue::Float(9.19),
11274 });
11275 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11276 panic!("expected runtime config set success");
11277 };
11278 assert_eq!(entry.value, RuntimeConfigValue::Float(9.19));
11279
11280 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11281 key: "backbone.public.height".into(),
11282 value: RuntimeConfigValue::Int(120),
11283 });
11284 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11285 panic!("expected runtime config set success");
11286 };
11287 assert_eq!(entry.value, RuntimeConfigValue::Float(120.0));
11288
11289 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
11290 key: "backbone.public.discoverable".into(),
11291 });
11292 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
11293 panic!("expected runtime config reset success");
11294 };
11295 assert_eq!(entry.value, RuntimeConfigValue::Bool(false));
11296 assert!(driver.interface_announcer.is_none());
11297
11298 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
11299 key: "backbone.public.latitude".into(),
11300 });
11301 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
11302 panic!("expected runtime config reset success");
11303 };
11304 assert_eq!(entry.value, RuntimeConfigValue::Null);
11305 }
11306
11307 #[cfg(feature = "iface-backbone")]
11308 #[test]
11309 fn runtime_config_sets_backbone_client_values() {
11310 let mut driver = new_test_driver();
11311 register_test_backbone_client(&mut driver, "uplink");
11312
11313 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11314 key: "backbone_client.uplink.connect_timeout_secs".into(),
11315 value: RuntimeConfigValue::Float(2.5),
11316 });
11317 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11318 panic!("expected runtime config set success");
11319 };
11320 assert_eq!(entry.value, RuntimeConfigValue::Float(2.5));
11321
11322 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11323 key: "backbone_client.uplink.max_reconnect_tries".into(),
11324 value: RuntimeConfigValue::Int(0),
11325 });
11326 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11327 panic!("expected runtime config set success");
11328 };
11329 assert_eq!(entry.value, RuntimeConfigValue::Int(0));
11330
11331 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
11332 key: "backbone_client.uplink.connect_timeout_secs".into(),
11333 });
11334 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
11335 panic!("expected runtime config reset success");
11336 };
11337 assert_eq!(entry.value, RuntimeConfigValue::Float(5.0));
11338 }
11339
11340 #[cfg(feature = "iface-backbone")]
11341 #[test]
11342 fn backbone_peer_state_query_lists_entries() {
11343 let mut driver = new_test_driver();
11344 register_test_backbone(&mut driver, "public");
11345 driver
11346 .backbone_peer_state
11347 .get("public")
11348 .unwrap()
11349 .peer_state
11350 .lock()
11351 .unwrap()
11352 .seed_entry(BackbonePeerStateEntry {
11353 interface_name: "public".into(),
11354 peer_ip: "203.0.113.10".parse().unwrap(),
11355 connected_count: 1,
11356 blacklisted_remaining_secs: Some(120.0),
11357 blacklist_reason: Some("repeated idle timeouts".into()),
11358 reject_count: 7,
11359 });
11360
11361 let response = driver.handle_query(QueryRequest::BackbonePeerState {
11362 interface_name: Some("public".into()),
11363 });
11364 let QueryResponse::BackbonePeerState(entries) = response else {
11365 panic!("expected backbone peer state list");
11366 };
11367 assert_eq!(entries.len(), 1);
11368 assert_eq!(entries[0].peer_ip.to_string(), "203.0.113.10");
11369 assert_eq!(entries[0].connected_count, 1);
11370 assert_eq!(entries[0].reject_count, 7);
11371 assert_eq!(
11372 entries[0].blacklist_reason.as_deref(),
11373 Some("repeated idle timeouts")
11374 );
11375 assert!(entries[0].blacklisted_remaining_secs.unwrap() > 0.0);
11376 }
11377
11378 #[cfg(feature = "iface-backbone")]
11379 #[test]
11380 fn backbone_peer_state_clear_removes_entry() {
11381 let mut driver = new_test_driver();
11382 register_test_backbone(&mut driver, "public");
11383 driver
11384 .backbone_peer_state
11385 .get("public")
11386 .unwrap()
11387 .peer_state
11388 .lock()
11389 .unwrap()
11390 .seed_entry(BackbonePeerStateEntry {
11391 interface_name: "public".into(),
11392 peer_ip: "203.0.113.11".parse().unwrap(),
11393 connected_count: 0,
11394 blacklisted_remaining_secs: None,
11395 blacklist_reason: None,
11396 reject_count: 0,
11397 });
11398
11399 let response = driver.handle_query_mut(QueryRequest::ClearBackbonePeerState {
11400 interface_name: "public".into(),
11401 peer_ip: "203.0.113.11".parse().unwrap(),
11402 });
11403 let QueryResponse::ClearBackbonePeerState(true) = response else {
11404 panic!("expected successful peer-state clear");
11405 };
11406
11407 let response = driver.handle_query(QueryRequest::BackbonePeerState {
11408 interface_name: Some("public".into()),
11409 });
11410 let QueryResponse::BackbonePeerState(entries) = response else {
11411 panic!("expected backbone peer state list");
11412 };
11413 assert!(entries.is_empty());
11414 }
11415
11416 #[cfg(feature = "iface-backbone")]
11417 #[test]
11418 fn backbone_peer_blacklist_sets_blacklist() {
11419 let mut driver = new_test_driver();
11420 register_test_backbone(&mut driver, "public");
11421 driver
11422 .backbone_peer_state
11423 .get("public")
11424 .unwrap()
11425 .peer_state
11426 .lock()
11427 .unwrap()
11428 .seed_entry(BackbonePeerStateEntry {
11429 interface_name: "public".into(),
11430 peer_ip: "203.0.113.50".parse().unwrap(),
11431 connected_count: 1,
11432 blacklisted_remaining_secs: None,
11433 blacklist_reason: None,
11434 reject_count: 0,
11435 });
11436
11437 let response = driver.handle_query_mut(QueryRequest::BlacklistBackbonePeer {
11438 interface_name: "public".into(),
11439 peer_ip: "203.0.113.50".parse().unwrap(),
11440 duration: Duration::from_secs(300),
11441 reason: "sentinel blacklist".into(),
11442 penalty_level: 2,
11443 });
11444 let QueryResponse::BlacklistBackbonePeer(true) = response else {
11445 panic!("expected successful blacklist");
11446 };
11447
11448 let response = driver.handle_query(QueryRequest::BackbonePeerState {
11450 interface_name: Some("public".into()),
11451 });
11452 let QueryResponse::BackbonePeerState(entries) = response else {
11453 panic!("expected backbone peer state list");
11454 };
11455 let entry = entries
11456 .iter()
11457 .find(|e| e.peer_ip == "203.0.113.50".parse::<std::net::IpAddr>().unwrap())
11458 .expect("expected entry for blacklisted peer");
11459 assert!(entry.blacklisted_remaining_secs.is_some());
11460 let remaining = entry.blacklisted_remaining_secs.unwrap();
11461 assert!(remaining > 290.0 && remaining <= 300.0);
11462 assert_eq!(
11463 entry.blacklist_reason.as_deref(),
11464 Some("sentinel blacklist")
11465 );
11466 }
11467
11468 #[cfg(feature = "iface-backbone")]
11469 #[test]
11470 fn backbone_peer_blacklist_unknown_interface_returns_false() {
11471 let mut driver = new_test_driver();
11472 let response = driver.handle_query_mut(QueryRequest::BlacklistBackbonePeer {
11473 interface_name: "nonexistent".into(),
11474 peer_ip: "203.0.113.50".parse().unwrap(),
11475 duration: Duration::from_secs(60),
11476 reason: "sentinel blacklist".into(),
11477 penalty_level: 1,
11478 });
11479 let QueryResponse::BlacklistBackbonePeer(false) = response else {
11480 panic!("expected false for unknown interface");
11481 };
11482 }
11483
11484 #[cfg(feature = "iface-backbone")]
11485 #[test]
11486 fn backbone_peer_blacklist_creates_entry_for_unknown_ip() {
11487 let mut driver = new_test_driver();
11488 register_test_backbone(&mut driver, "public");
11489
11490 let response = driver.handle_query_mut(QueryRequest::BlacklistBackbonePeer {
11492 interface_name: "public".into(),
11493 peer_ip: "198.51.100.1".parse().unwrap(),
11494 duration: Duration::from_secs(120),
11495 reason: "sentinel blacklist".into(),
11496 penalty_level: 1,
11497 });
11498 let QueryResponse::BlacklistBackbonePeer(true) = response else {
11499 panic!("expected successful blacklist for new IP");
11500 };
11501
11502 let response = driver.handle_query(QueryRequest::BackbonePeerState {
11503 interface_name: Some("public".into()),
11504 });
11505 let QueryResponse::BackbonePeerState(entries) = response else {
11506 panic!("expected backbone peer state list");
11507 };
11508 let entry = entries
11509 .iter()
11510 .find(|e| e.peer_ip == "198.51.100.1".parse::<std::net::IpAddr>().unwrap())
11511 .expect("expected entry for newly blacklisted IP");
11512 assert!(entry.blacklisted_remaining_secs.is_some());
11513 }
11514
11515 #[cfg(feature = "iface-tcp")]
11516 #[test]
11517 fn runtime_config_lists_tcp_server_keys() {
11518 let mut driver = new_test_driver();
11519 register_test_tcp_server(&mut driver, "public");
11520 register_test_tcp_server_discovery(&mut driver, "public", false);
11521 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
11522 let QueryResponse::RuntimeConfigList(entries) = response else {
11523 panic!("expected runtime config list");
11524 };
11525 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
11526 assert!(keys.contains(&"tcp_server.public.max_connections".to_string()));
11527 assert!(keys.contains(&"tcp_server.public.discoverable".to_string()));
11528 assert!(keys.contains(&"tcp_server.public.discovery_name".to_string()));
11529 }
11530
11531 #[cfg(feature = "iface-tcp")]
11532 #[test]
11533 fn runtime_config_sets_tcp_server_values() {
11534 let mut driver = new_test_driver();
11535 register_test_tcp_server(&mut driver, "public");
11536 register_test_tcp_server_discovery(&mut driver, "public", false);
11537 driver.transport_identity = Some(rns_crypto::identity::Identity::new(&mut OsRng));
11538
11539 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11540 key: "tcp_server.public.max_connections".into(),
11541 value: RuntimeConfigValue::Int(0),
11542 });
11543 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11544 panic!("expected runtime config set success");
11545 };
11546 assert_eq!(entry.value, RuntimeConfigValue::Int(0));
11547
11548 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
11549 key: "tcp_server.public.max_connections".into(),
11550 });
11551 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
11552 panic!("expected runtime config reset success");
11553 };
11554 assert_eq!(entry.value, RuntimeConfigValue::Int(4));
11555
11556 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11557 key: "tcp_server.public.discoverable".into(),
11558 value: RuntimeConfigValue::Bool(true),
11559 });
11560 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11561 panic!("expected runtime config set success");
11562 };
11563 assert_eq!(entry.value, RuntimeConfigValue::Bool(true));
11564
11565 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11566 key: "tcp_server.public.latitude".into(),
11567 value: RuntimeConfigValue::Float(41.9028),
11568 });
11569 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11570 panic!("expected runtime config set success");
11571 };
11572 assert_eq!(entry.value, RuntimeConfigValue::Float(41.9028));
11573
11574 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
11575 key: "tcp_server.public.latitude".into(),
11576 });
11577 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
11578 panic!("expected runtime config reset success");
11579 };
11580 assert_eq!(entry.value, RuntimeConfigValue::Null);
11581 }
11582
11583 #[cfg(feature = "iface-tcp")]
11584 #[test]
11585 fn runtime_config_lists_tcp_client_keys() {
11586 let mut driver = new_test_driver();
11587 register_test_tcp_client(&mut driver, "uplink");
11588 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
11589 let QueryResponse::RuntimeConfigList(entries) = response else {
11590 panic!("expected runtime config list");
11591 };
11592 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
11593 assert!(keys.contains(&"tcp_client.uplink.connect_timeout_secs".to_string()));
11594 assert!(keys.contains(&"tcp_client.uplink.reconnect_wait_secs".to_string()));
11595 assert!(keys.contains(&"tcp_client.uplink.max_reconnect_tries".to_string()));
11596 }
11597
11598 #[cfg(feature = "iface-tcp")]
11599 #[test]
11600 fn runtime_config_sets_tcp_client_values() {
11601 let mut driver = new_test_driver();
11602 register_test_tcp_client(&mut driver, "uplink");
11603
11604 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11605 key: "tcp_client.uplink.connect_timeout_secs".into(),
11606 value: RuntimeConfigValue::Float(2.5),
11607 });
11608 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11609 panic!("expected runtime config set success");
11610 };
11611 assert_eq!(entry.value, RuntimeConfigValue::Float(2.5));
11612
11613 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11614 key: "tcp_client.uplink.max_reconnect_tries".into(),
11615 value: RuntimeConfigValue::Int(0),
11616 });
11617 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11618 panic!("expected runtime config set success");
11619 };
11620 assert_eq!(entry.value, RuntimeConfigValue::Int(0));
11621
11622 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
11623 key: "tcp_client.uplink.connect_timeout_secs".into(),
11624 });
11625 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
11626 panic!("expected runtime config reset success");
11627 };
11628 assert_eq!(entry.value, RuntimeConfigValue::Float(5.0));
11629 }
11630
11631 #[cfg(feature = "iface-udp")]
11632 #[test]
11633 fn runtime_config_lists_udp_keys() {
11634 let mut driver = new_test_driver();
11635 register_test_udp(&mut driver, "lan");
11636 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
11637 let QueryResponse::RuntimeConfigList(entries) = response else {
11638 panic!("expected runtime config list");
11639 };
11640 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
11641 assert!(keys.contains(&"udp.lan.forward_ip".to_string()));
11642 assert!(keys.contains(&"udp.lan.forward_port".to_string()));
11643 }
11644
11645 #[cfg(feature = "iface-udp")]
11646 #[test]
11647 fn runtime_config_sets_udp_values() {
11648 let mut driver = new_test_driver();
11649 register_test_udp(&mut driver, "lan");
11650
11651 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11652 key: "udp.lan.forward_ip".into(),
11653 value: RuntimeConfigValue::String("192.168.1.10".into()),
11654 });
11655 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11656 panic!("expected set ok");
11657 };
11658 assert_eq!(
11659 entry.value,
11660 RuntimeConfigValue::String("192.168.1.10".into())
11661 );
11662
11663 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11664 key: "udp.lan.forward_port".into(),
11665 value: RuntimeConfigValue::Null,
11666 });
11667 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11668 panic!("expected set ok");
11669 };
11670 assert_eq!(entry.value, RuntimeConfigValue::Null);
11671
11672 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
11673 key: "udp.lan.forward_port".into(),
11674 });
11675 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
11676 panic!("expected reset ok");
11677 };
11678 assert_eq!(entry.value, RuntimeConfigValue::Int(4242));
11679 }
11680
11681 #[cfg(feature = "iface-auto")]
11682 #[test]
11683 fn runtime_config_lists_auto_keys() {
11684 let mut driver = new_test_driver();
11685 register_test_auto(&mut driver, "lan");
11686 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
11687 let QueryResponse::RuntimeConfigList(entries) = response else {
11688 panic!("expected runtime config list");
11689 };
11690 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
11691 assert!(keys.contains(&"auto.lan.announce_interval_secs".to_string()));
11692 assert!(keys.contains(&"auto.lan.peer_timeout_secs".to_string()));
11693 assert!(keys.contains(&"auto.lan.peer_job_interval_secs".to_string()));
11694 }
11695
11696 #[cfg(feature = "iface-auto")]
11697 #[test]
11698 fn runtime_config_sets_auto_values() {
11699 let mut driver = new_test_driver();
11700 register_test_auto(&mut driver, "lan");
11701
11702 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11703 key: "auto.lan.announce_interval_secs".into(),
11704 value: RuntimeConfigValue::Float(2.5),
11705 });
11706 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11707 panic!("expected set ok");
11708 };
11709 assert_eq!(entry.value, RuntimeConfigValue::Float(2.5));
11710
11711 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11712 key: "auto.lan.peer_timeout_secs".into(),
11713 value: RuntimeConfigValue::Float(30.0),
11714 });
11715 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11716 panic!("expected set ok");
11717 };
11718 assert_eq!(entry.value, RuntimeConfigValue::Float(30.0));
11719
11720 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
11721 key: "auto.lan.peer_job_interval_secs".into(),
11722 });
11723 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
11724 panic!("expected reset ok");
11725 };
11726 assert_eq!(entry.value, RuntimeConfigValue::Float(4.0));
11727 }
11728
11729 #[cfg(feature = "iface-i2p")]
11730 #[test]
11731 fn runtime_config_lists_i2p_keys() {
11732 let mut driver = new_test_driver();
11733 register_test_i2p(&mut driver, "anon");
11734 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
11735 let QueryResponse::RuntimeConfigList(entries) = response else {
11736 panic!("expected runtime config list");
11737 };
11738 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
11739 assert!(keys.contains(&"i2p.anon.reconnect_wait_secs".to_string()));
11740 }
11741
11742 #[cfg(feature = "iface-i2p")]
11743 #[test]
11744 fn runtime_config_sets_i2p_values() {
11745 let mut driver = new_test_driver();
11746 register_test_i2p(&mut driver, "anon");
11747
11748 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11749 key: "i2p.anon.reconnect_wait_secs".into(),
11750 value: RuntimeConfigValue::Float(3.5),
11751 });
11752 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11753 panic!("expected set ok");
11754 };
11755 assert_eq!(entry.value, RuntimeConfigValue::Float(3.5));
11756
11757 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
11758 key: "i2p.anon.reconnect_wait_secs".into(),
11759 });
11760 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
11761 panic!("expected reset ok");
11762 };
11763 assert_eq!(entry.value, RuntimeConfigValue::Float(15.0));
11764 }
11765
11766 #[cfg(feature = "iface-pipe")]
11767 #[test]
11768 fn runtime_config_lists_pipe_keys() {
11769 let mut driver = new_test_driver();
11770 register_test_pipe(&mut driver, "worker");
11771 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
11772 let QueryResponse::RuntimeConfigList(entries) = response else {
11773 panic!("expected runtime config list");
11774 };
11775 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
11776 assert!(keys.contains(&"pipe.worker.respawn_delay_secs".to_string()));
11777 }
11778
11779 #[cfg(feature = "iface-pipe")]
11780 #[test]
11781 fn runtime_config_sets_pipe_values() {
11782 let mut driver = new_test_driver();
11783 register_test_pipe(&mut driver, "worker");
11784
11785 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11786 key: "pipe.worker.respawn_delay_secs".into(),
11787 value: RuntimeConfigValue::Float(2.0),
11788 });
11789 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11790 panic!("expected set ok");
11791 };
11792 assert_eq!(entry.value, RuntimeConfigValue::Float(2.0));
11793
11794 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
11795 key: "pipe.worker.respawn_delay_secs".into(),
11796 });
11797 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
11798 panic!("expected reset ok");
11799 };
11800 assert_eq!(entry.value, RuntimeConfigValue::Float(5.0));
11801 }
11802
11803 #[cfg(feature = "iface-rnode")]
11804 #[test]
11805 fn runtime_config_lists_rnode_keys() {
11806 let mut driver = new_test_driver();
11807 register_test_rnode(&mut driver, "radio");
11808 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
11809 let QueryResponse::RuntimeConfigList(entries) = response else {
11810 panic!("expected runtime config list");
11811 };
11812 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
11813 assert!(keys.contains(&"rnode.radio.frequency_hz".to_string()));
11814 assert!(keys.contains(&"rnode.radio.bandwidth_hz".to_string()));
11815 assert!(keys.contains(&"rnode.radio.txpower_dbm".to_string()));
11816 assert!(keys.contains(&"rnode.radio.spreading_factor".to_string()));
11817 assert!(keys.contains(&"rnode.radio.coding_rate".to_string()));
11818 assert!(keys.contains(&"rnode.radio.st_alock_pct".to_string()));
11819 assert!(keys.contains(&"rnode.radio.lt_alock_pct".to_string()));
11820 }
11821
11822 #[cfg(feature = "iface-rnode")]
11823 #[test]
11824 fn runtime_config_sets_rnode_values() {
11825 let mut driver = new_test_driver();
11826 register_test_rnode(&mut driver, "radio");
11827
11828 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11829 key: "rnode.radio.frequency_hz".into(),
11830 value: RuntimeConfigValue::Int(915_000_000),
11831 });
11832 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11833 panic!("expected set ok");
11834 };
11835 assert_eq!(entry.value, RuntimeConfigValue::Int(915_000_000));
11836
11837 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11838 key: "rnode.radio.st_alock_pct".into(),
11839 value: RuntimeConfigValue::Float(12.5),
11840 });
11841 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11842 panic!("expected set ok");
11843 };
11844 assert_eq!(entry.value, RuntimeConfigValue::Float(12.5));
11845
11846 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
11847 key: "rnode.radio.frequency_hz".into(),
11848 });
11849 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
11850 panic!("expected reset ok");
11851 };
11852 assert_eq!(entry.value, RuntimeConfigValue::Int(868_000_000));
11853 }
11854
11855 #[test]
11856 fn runtime_config_lists_generic_interface_keys() {
11857 let mut driver = new_test_driver();
11858 register_test_generic_interface(&mut driver, 1, "public");
11859 let response = driver.handle_query(QueryRequest::ListRuntimeConfig);
11860 let QueryResponse::RuntimeConfigList(entries) = response else {
11861 panic!("expected runtime config list");
11862 };
11863 let keys: Vec<String> = entries.into_iter().map(|entry| entry.key).collect();
11864 assert!(keys.contains(&"interface.public.enabled".to_string()));
11865 assert!(keys.contains(&"interface.public.mode".to_string()));
11866 assert!(keys.contains(&"interface.public.announce_rate_target".to_string()));
11867 assert!(keys.contains(&"interface.public.announce_rate_grace".to_string()));
11868 assert!(keys.contains(&"interface.public.announce_rate_penalty".to_string()));
11869 assert!(keys.contains(&"interface.public.announce_cap".to_string()));
11870 assert!(keys.contains(&"interface.public.ingress_control".to_string()));
11871 assert!(keys.contains(&"interface.public.ic_max_held_announces".to_string()));
11872 assert!(keys.contains(&"interface.public.ic_burst_hold".to_string()));
11873 assert!(keys.contains(&"interface.public.ic_burst_freq_new".to_string()));
11874 assert!(keys.contains(&"interface.public.ic_burst_freq".to_string()));
11875 assert!(keys.contains(&"interface.public.ic_new_time".to_string()));
11876 assert!(keys.contains(&"interface.public.ic_burst_penalty".to_string()));
11877 assert!(keys.contains(&"interface.public.ic_held_release_interval".to_string()));
11878 assert!(keys.contains(&"interface.public.ifac_netname".to_string()));
11879 assert!(keys.contains(&"interface.public.ifac_passphrase".to_string()));
11880 assert!(keys.contains(&"interface.public.ifac_size_bytes".to_string()));
11881 }
11882
11883 #[test]
11884 fn runtime_config_sets_generic_interface_values() {
11885 let mut driver = new_test_driver();
11886 register_test_generic_interface(&mut driver, 1, "public");
11887
11888 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11889 key: "interface.public.enabled".into(),
11890 value: RuntimeConfigValue::Bool(false),
11891 });
11892 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11893 panic!("expected set ok");
11894 };
11895 assert_eq!(entry.value, RuntimeConfigValue::Bool(false));
11896 assert!(!driver.interfaces.get(&InterfaceId(1)).unwrap().enabled);
11897
11898 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11899 key: "interface.public.announce_cap".into(),
11900 value: RuntimeConfigValue::Float(0.15),
11901 });
11902 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11903 panic!("expected set ok");
11904 };
11905 assert_eq!(entry.value, RuntimeConfigValue::Float(0.15));
11906 assert_eq!(
11907 driver
11908 .engine
11909 .interface_info(&InterfaceId(1))
11910 .unwrap()
11911 .announce_cap,
11912 0.15
11913 );
11914
11915 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11916 key: "interface.public.mode".into(),
11917 value: RuntimeConfigValue::String("gateway".into()),
11918 });
11919 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11920 panic!("expected set ok");
11921 };
11922 assert_eq!(entry.value, RuntimeConfigValue::String("gateway".into()));
11923
11924 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
11925 key: "interface.public.mode".into(),
11926 });
11927 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
11928 panic!("expected reset ok");
11929 };
11930 assert_eq!(entry.value, RuntimeConfigValue::String("full".into()));
11931
11932 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11933 key: "interface.public.ic_max_held_announces".into(),
11934 value: RuntimeConfigValue::Int(17),
11935 });
11936 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11937 panic!("expected set ok");
11938 };
11939 assert_eq!(entry.value, RuntimeConfigValue::Int(17));
11940 assert_eq!(
11941 driver
11942 .engine
11943 .interface_info(&InterfaceId(1))
11944 .unwrap()
11945 .ingress_control
11946 .max_held_announces,
11947 17
11948 );
11949
11950 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11951 key: "interface.public.ic_burst_hold".into(),
11952 value: RuntimeConfigValue::Float(1.5),
11953 });
11954 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11955 panic!("expected set ok");
11956 };
11957 assert_eq!(entry.value, RuntimeConfigValue::Float(1.5));
11958
11959 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11960 key: "interface.public.ic_burst_freq_new".into(),
11961 value: RuntimeConfigValue::Float(2.5),
11962 });
11963 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11964 panic!("expected set ok");
11965 };
11966 assert_eq!(entry.value, RuntimeConfigValue::Float(2.5));
11967
11968 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11969 key: "interface.public.ic_burst_freq".into(),
11970 value: RuntimeConfigValue::Float(3.5),
11971 });
11972 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11973 panic!("expected set ok");
11974 };
11975 assert_eq!(entry.value, RuntimeConfigValue::Float(3.5));
11976
11977 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11978 key: "interface.public.ic_new_time".into(),
11979 value: RuntimeConfigValue::Float(4.5),
11980 });
11981 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11982 panic!("expected set ok");
11983 };
11984 assert_eq!(entry.value, RuntimeConfigValue::Float(4.5));
11985
11986 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11987 key: "interface.public.ic_burst_penalty".into(),
11988 value: RuntimeConfigValue::Float(5.5),
11989 });
11990 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
11991 panic!("expected set ok");
11992 };
11993 assert_eq!(entry.value, RuntimeConfigValue::Float(5.5));
11994
11995 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
11996 key: "interface.public.ic_held_release_interval".into(),
11997 value: RuntimeConfigValue::Float(6.5),
11998 });
11999 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12000 panic!("expected set ok");
12001 };
12002 assert_eq!(entry.value, RuntimeConfigValue::Float(6.5));
12003
12004 let ingress_control = driver
12005 .engine
12006 .interface_info(&InterfaceId(1))
12007 .unwrap()
12008 .ingress_control;
12009 assert_eq!(ingress_control.burst_hold, 1.5);
12010 assert_eq!(ingress_control.burst_freq_new, 2.5);
12011 assert_eq!(ingress_control.burst_freq, 3.5);
12012 assert_eq!(ingress_control.new_time, 4.5);
12013 assert_eq!(ingress_control.burst_penalty, 5.5);
12014 assert_eq!(ingress_control.held_release_interval, 6.5);
12015
12016 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12017 key: "interface.public.ic_max_held_announces".into(),
12018 });
12019 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12020 panic!("expected reset ok");
12021 };
12022 assert_eq!(
12023 entry.value,
12024 RuntimeConfigValue::Int(rns_core::constants::IC_MAX_HELD_ANNOUNCES as i64)
12025 );
12026
12027 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12028 key: "interface.public.enabled".into(),
12029 });
12030 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12031 panic!("expected reset ok");
12032 };
12033 assert_eq!(entry.value, RuntimeConfigValue::Bool(true));
12034 assert!(driver.interfaces.get(&InterfaceId(1)).unwrap().enabled);
12035
12036 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12037 key: "interface.public.ifac_netname".into(),
12038 value: RuntimeConfigValue::String("mesh".into()),
12039 });
12040 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12041 panic!("expected set ok");
12042 };
12043 assert_eq!(entry.value, RuntimeConfigValue::String("mesh".into()));
12044 assert_eq!(
12045 driver
12046 .interfaces
12047 .get(&InterfaceId(1))
12048 .unwrap()
12049 .ifac
12050 .as_ref()
12051 .unwrap()
12052 .size,
12053 16
12054 );
12055
12056 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12057 key: "interface.public.ifac_passphrase".into(),
12058 value: RuntimeConfigValue::String("secret".into()),
12059 });
12060 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12061 panic!("expected set ok");
12062 };
12063 assert_eq!(entry.value, RuntimeConfigValue::String("<redacted>".into()));
12064
12065 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12066 key: "interface.public.ifac_size_bytes".into(),
12067 value: RuntimeConfigValue::Int(24),
12068 });
12069 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12070 panic!("expected set ok");
12071 };
12072 assert_eq!(entry.value, RuntimeConfigValue::Int(24));
12073 let ifac = driver
12074 .interfaces
12075 .get(&InterfaceId(1))
12076 .unwrap()
12077 .ifac
12078 .as_ref()
12079 .unwrap();
12080 assert_eq!(ifac.size, 24);
12081
12082 let response = driver.handle_query(QueryRequest::GetRuntimeConfig {
12083 key: "interface.public.ifac_passphrase".into(),
12084 });
12085 let QueryResponse::RuntimeConfigEntry(Some(entry)) = response else {
12086 panic!("expected runtime config entry");
12087 };
12088 assert_eq!(entry.value, RuntimeConfigValue::String("<redacted>".into()));
12089
12090 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12091 key: "interface.public.ifac_netname".into(),
12092 });
12093 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12094 panic!("expected reset ok");
12095 };
12096 assert_eq!(entry.value, RuntimeConfigValue::Null);
12097 assert!(driver
12098 .interfaces
12099 .get(&InterfaceId(1))
12100 .unwrap()
12101 .ifac
12102 .is_some());
12103
12104 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12105 key: "interface.public.ifac_passphrase".into(),
12106 });
12107 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12108 panic!("expected reset ok");
12109 };
12110 assert_eq!(entry.value, RuntimeConfigValue::Null);
12111 assert!(driver
12112 .interfaces
12113 .get(&InterfaceId(1))
12114 .unwrap()
12115 .ifac
12116 .is_none());
12117 }
12118
12119 #[cfg(feature = "rns-hooks")]
12120 #[test]
12121 fn runtime_config_sets_provider_bridge_values() {
12122 let mut driver = new_test_driver();
12123
12124 let dir = tempfile::tempdir().unwrap();
12125 let socket_path = dir.path().join("provider.sock");
12126 let bridge = crate::provider_bridge::ProviderBridge::start(
12127 crate::provider_bridge::ProviderBridgeConfig {
12128 enabled: true,
12129 socket_path,
12130 queue_max_events: 1024,
12131 queue_max_bytes: 1024 * 1024,
12132 ..Default::default()
12133 },
12134 )
12135 .unwrap();
12136 driver.runtime_config_defaults.provider_queue_max_events = 1024;
12137 driver.runtime_config_defaults.provider_queue_max_bytes = 1024 * 1024;
12138 driver.provider_bridge = Some(bridge);
12139
12140 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12141 key: "provider.queue_max_events".into(),
12142 value: RuntimeConfigValue::Int(4096),
12143 });
12144 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12145 panic!("expected set ok");
12146 };
12147 assert_eq!(entry.value, RuntimeConfigValue::Int(4096));
12148 assert_eq!(entry.source, RuntimeConfigSource::RuntimeOverride,);
12149
12150 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12151 key: "provider.queue_max_bytes".into(),
12152 value: RuntimeConfigValue::Int(2 * 1024 * 1024),
12153 });
12154 let QueryResponse::RuntimeConfigSet(Ok(entry)) = response else {
12155 panic!("expected set ok");
12156 };
12157 assert_eq!(entry.value, RuntimeConfigValue::Int(2 * 1024 * 1024));
12158
12159 let response = driver.handle_query_mut(QueryRequest::SetRuntimeConfig {
12161 key: "provider.queue_max_events".into(),
12162 value: RuntimeConfigValue::Int(0),
12163 });
12164 assert!(matches!(response, QueryResponse::RuntimeConfigSet(Err(_))));
12165
12166 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12168 key: "provider.queue_max_events".into(),
12169 });
12170 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12171 panic!("expected reset ok");
12172 };
12173 assert_eq!(entry.value, RuntimeConfigValue::Int(1024));
12174 assert_eq!(entry.source, RuntimeConfigSource::Startup);
12175
12176 let response = driver.handle_query_mut(QueryRequest::ResetRuntimeConfig {
12177 key: "provider.queue_max_bytes".into(),
12178 });
12179 let QueryResponse::RuntimeConfigReset(Ok(entry)) = response else {
12180 panic!("expected reset ok");
12181 };
12182 assert_eq!(entry.value, RuntimeConfigValue::Int(1024 * 1024));
12183 }
12184
12185 #[test]
12186 fn disabled_interface_drops_ingress_and_egress() {
12187 let (tx, rx) = event::channel();
12188 let (cbs, _, _, _, _, _) = MockCallbacks::new();
12189 let mut driver = Driver::new(
12190 TransportConfig {
12191 transport_enabled: false,
12192 identity_hash: None,
12193 prefer_shorter_path: false,
12194 max_paths_per_destination: 1,
12195 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
12196 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
12197 max_path_destinations: usize::MAX,
12198 max_tunnel_destinations_total: usize::MAX,
12199 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
12200 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
12201 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
12202 announce_sig_cache_enabled: true,
12203 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
12204 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
12205 announce_queue_max_entries: 256,
12206 announce_queue_max_interfaces: 1024,
12207 },
12208 rx,
12209 tx.clone(),
12210 Box::new(cbs),
12211 );
12212 let info = make_interface_info(1);
12213 driver.register_interface_runtime_defaults(&info);
12214 driver.engine.register_interface(info.clone());
12215 let (writer, sent) = MockWriter::new();
12216 driver.interfaces.insert(
12217 InterfaceId(1),
12218 InterfaceEntry {
12219 id: InterfaceId(1),
12220 info,
12221 writer: Box::new(writer),
12222 async_writer_metrics: None,
12223 enabled: false,
12224 online: true,
12225 dynamic: false,
12226 ifac: None,
12227 stats: InterfaceStats::default(),
12228 interface_type: String::new(),
12229 send_retry_at: None,
12230 send_retry_backoff: Duration::ZERO,
12231 },
12232 );
12233
12234 driver.dispatch_all(vec![TransportAction::SendOnInterface {
12235 interface: InterfaceId(1),
12236 raw: vec![0x00, 0x01, 0x42],
12237 }]);
12238 assert!(sent.lock().unwrap().is_empty());
12239
12240 tx.send(Event::Frame {
12241 interface_id: InterfaceId(1),
12242 data: vec![0x00, 0x01, 0x42],
12243 })
12244 .unwrap();
12245 tx.send(Event::Shutdown).unwrap();
12246 driver.run();
12247
12248 let entry = driver.interfaces.get(&InterfaceId(1)).unwrap();
12249 assert_eq!(entry.stats.rxb, 0);
12250 assert_eq!(entry.stats.rx_packets, 0);
12251 }
12252
12253 #[test]
12254 fn management_announces_not_emitted_when_disabled() {
12255 let (tx, rx) = event::channel();
12256 let (cbs, _, _, _, _, _) = MockCallbacks::new();
12257 let identity = Identity::new(&mut OsRng);
12258 let identity_hash = *identity.hash();
12259 let mut driver = Driver::new(
12260 TransportConfig {
12261 transport_enabled: true,
12262 identity_hash: Some(identity_hash),
12263 prefer_shorter_path: false,
12264 max_paths_per_destination: 1,
12265 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
12266 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
12267 max_path_destinations: usize::MAX,
12268 max_tunnel_destinations_total: usize::MAX,
12269 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
12270 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
12271 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
12272 announce_sig_cache_enabled: true,
12273 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
12274 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
12275 announce_queue_max_entries: 256,
12276 announce_queue_max_interfaces: 1024,
12277 },
12278 rx,
12279 tx.clone(),
12280 Box::new(cbs),
12281 );
12282
12283 let info = make_interface_info(1);
12284 driver.engine.register_interface(info.clone());
12285 let (writer, sent) = MockWriter::new();
12286 driver
12287 .interfaces
12288 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
12289
12290 driver.transport_identity = Some(identity);
12292 driver.started = time::now() - 10.0;
12293
12294 tx.send(Event::Tick).unwrap();
12295 tx.send(Event::Shutdown).unwrap();
12296 driver.run();
12297
12298 let sent_packets = sent.lock().unwrap();
12300 assert!(
12301 sent_packets.is_empty(),
12302 "No announces should be sent when management is disabled"
12303 );
12304 }
12305
12306 #[test]
12307 fn management_announces_not_emitted_before_delay() {
12308 let (tx, rx) = event::channel();
12309 let (cbs, _, _, _, _, _) = MockCallbacks::new();
12310 let identity = Identity::new(&mut OsRng);
12311 let identity_hash = *identity.hash();
12312 let mut driver = Driver::new(
12313 TransportConfig {
12314 transport_enabled: true,
12315 identity_hash: Some(identity_hash),
12316 prefer_shorter_path: false,
12317 max_paths_per_destination: 1,
12318 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
12319 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
12320 max_path_destinations: usize::MAX,
12321 max_tunnel_destinations_total: usize::MAX,
12322 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
12323 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
12324 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
12325 announce_sig_cache_enabled: true,
12326 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
12327 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
12328 announce_queue_max_entries: 256,
12329 announce_queue_max_interfaces: 1024,
12330 },
12331 rx,
12332 tx.clone(),
12333 Box::new(cbs),
12334 );
12335
12336 let info = make_interface_info(1);
12337 driver.engine.register_interface(info.clone());
12338 let (writer, sent) = MockWriter::new();
12339 driver
12340 .interfaces
12341 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
12342
12343 driver.management_config.enable_remote_management = true;
12344 driver.transport_identity = Some(identity);
12345 driver.started = time::now();
12347
12348 tx.send(Event::Tick).unwrap();
12349 tx.send(Event::Shutdown).unwrap();
12350 driver.run();
12351
12352 let sent_packets = sent.lock().unwrap();
12353 assert!(sent_packets.is_empty(), "No announces before startup delay");
12354 }
12355
12356 #[test]
12361 fn announce_received_populates_known_destinations() {
12362 let (tx, rx) = event::channel();
12363 let (cbs, _, _, _, _, _) = MockCallbacks::new();
12364 let mut driver = Driver::new(
12365 TransportConfig {
12366 transport_enabled: false,
12367 identity_hash: None,
12368 prefer_shorter_path: false,
12369 max_paths_per_destination: 1,
12370 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
12371 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
12372 max_path_destinations: usize::MAX,
12373 max_tunnel_destinations_total: usize::MAX,
12374 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
12375 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
12376 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
12377 announce_sig_cache_enabled: true,
12378 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
12379 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
12380 announce_queue_max_entries: 256,
12381 announce_queue_max_interfaces: 1024,
12382 },
12383 rx,
12384 tx.clone(),
12385 Box::new(cbs),
12386 );
12387 let info = make_interface_info(1);
12388 driver.engine.register_interface(info);
12389 let (writer, _sent) = MockWriter::new();
12390 driver
12391 .interfaces
12392 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
12393
12394 let identity = Identity::new(&mut OsRng);
12395 let announce_raw = build_announce_packet(&identity);
12396
12397 let dest_hash =
12398 rns_core::destination::destination_hash("test", &["app"], Some(identity.hash()));
12399
12400 tx.send(Event::Frame {
12401 interface_id: InterfaceId(1),
12402 data: announce_raw,
12403 })
12404 .unwrap();
12405 tx.send(Event::Shutdown).unwrap();
12406 driver.run();
12407
12408 assert!(driver.known_destinations.contains_key(&dest_hash));
12410 let recalled = &driver.known_destinations[&dest_hash];
12411 assert_eq!(recalled.dest_hash.0, dest_hash);
12412 assert_eq!(recalled.identity_hash.0, *identity.hash());
12413 assert_eq!(&recalled.public_key, &identity.get_public_key().unwrap());
12414 assert_eq!(recalled.hops, 1);
12415 }
12416
12417 #[test]
12418 fn known_destinations_cleanup_respects_ttl() {
12419 let (tx, rx) = event::channel();
12420 let (cbs, _, _, _, _, _) = MockCallbacks::new();
12421 let mut driver = Driver::new(
12422 TransportConfig {
12423 transport_enabled: false,
12424 identity_hash: None,
12425 prefer_shorter_path: false,
12426 max_paths_per_destination: 1,
12427 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
12428 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
12429 max_path_destinations: usize::MAX,
12430 max_tunnel_destinations_total: usize::MAX,
12431 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
12432 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
12433 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
12434 announce_sig_cache_enabled: true,
12435 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
12436 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
12437 announce_queue_max_entries: 256,
12438 announce_queue_max_interfaces: 1024,
12439 },
12440 rx,
12441 tx.clone(),
12442 Box::new(cbs),
12443 );
12444
12445 driver.known_destinations_ttl = 10.0;
12446 driver.cache_cleanup_counter = 3599;
12447
12448 let stale_dest = [0x11; 16];
12449 let fresh_dest = [0x22; 16];
12450 driver.known_destinations.insert(
12451 stale_dest,
12452 crate::destination::AnnouncedIdentity {
12453 dest_hash: rns_core::types::DestHash(stale_dest),
12454 identity_hash: rns_core::types::IdentityHash([0x33; 16]),
12455 public_key: [0x44; 64],
12456 app_data: None,
12457 hops: 1,
12458 received_at: time::now() - 20.0,
12459 receiving_interface: InterfaceId(1),
12460 },
12461 );
12462 driver.known_destinations.insert(
12463 fresh_dest,
12464 crate::destination::AnnouncedIdentity {
12465 dest_hash: rns_core::types::DestHash(fresh_dest),
12466 identity_hash: rns_core::types::IdentityHash([0x55; 16]),
12467 public_key: [0x66; 64],
12468 app_data: None,
12469 hops: 1,
12470 received_at: time::now() - 5.0,
12471 receiving_interface: InterfaceId(1),
12472 },
12473 );
12474
12475 tx.send(Event::Tick).unwrap();
12476 tx.send(Event::Shutdown).unwrap();
12477 driver.run();
12478
12479 assert!(!driver.known_destinations.contains_key(&stale_dest));
12480 assert!(driver.known_destinations.contains_key(&fresh_dest));
12481 }
12482
12483 #[test]
12484 fn known_destinations_cap_prefers_evicting_oldest_non_active_non_local() {
12485 let mut driver = new_test_driver();
12486 driver.known_destinations_max_entries = 2;
12487 driver.engine.register_interface(make_interface_info(1));
12488
12489 let active_dest = [0x11; 16];
12490 let evictable_dest = [0x22; 16];
12491 let new_dest = [0x33; 16];
12492
12493 driver.engine.inject_path(
12494 active_dest,
12495 PathEntry {
12496 timestamp: 100.0,
12497 next_hop: [0x44; 16],
12498 hops: 1,
12499 expires: 1000.0,
12500 random_blobs: Vec::new(),
12501 receiving_interface: InterfaceId(1),
12502 packet_hash: [0x55; 32],
12503 announce_raw: None,
12504 },
12505 );
12506
12507 driver.upsert_known_destination(
12508 active_dest,
12509 make_announced_identity(active_dest, 10.0, InterfaceId(1)),
12510 );
12511 driver.upsert_known_destination(
12512 evictable_dest,
12513 make_announced_identity(evictable_dest, 20.0, InterfaceId(1)),
12514 );
12515 driver.upsert_known_destination(
12516 new_dest,
12517 make_announced_identity(new_dest, 30.0, InterfaceId(1)),
12518 );
12519
12520 assert!(driver.known_destinations.contains_key(&active_dest));
12521 assert!(!driver.known_destinations.contains_key(&evictable_dest));
12522 assert!(driver.known_destinations.contains_key(&new_dest));
12523 assert_eq!(driver.known_destinations_cap_evict_count, 1);
12524 }
12525
12526 #[test]
12527 fn known_destinations_cap_falls_back_to_oldest_overall_when_all_protected() {
12528 let mut driver = new_test_driver();
12529 driver.known_destinations_max_entries = 2;
12530
12531 let local_oldest = [0x41; 16];
12532 let local_newer = [0x42; 16];
12533 let new_dest = [0x43; 16];
12534 driver
12535 .local_destinations
12536 .insert(local_oldest, rns_core::constants::DESTINATION_SINGLE);
12537 driver
12538 .local_destinations
12539 .insert(local_newer, rns_core::constants::DESTINATION_SINGLE);
12540
12541 driver.upsert_known_destination(
12542 local_oldest,
12543 make_announced_identity(local_oldest, 10.0, InterfaceId(1)),
12544 );
12545 driver.upsert_known_destination(
12546 local_newer,
12547 make_announced_identity(local_newer, 20.0, InterfaceId(1)),
12548 );
12549 driver.upsert_known_destination(
12550 new_dest,
12551 make_announced_identity(new_dest, 30.0, InterfaceId(1)),
12552 );
12553
12554 assert!(!driver.known_destinations.contains_key(&local_oldest));
12555 assert!(driver.known_destinations.contains_key(&local_newer));
12556 assert!(driver.known_destinations.contains_key(&new_dest));
12557 assert_eq!(driver.known_destinations_cap_evict_count, 1);
12558 }
12559
12560 #[test]
12561 fn known_destinations_cap_update_existing_entry_does_not_evict() {
12562 let mut driver = new_test_driver();
12563 driver.known_destinations_max_entries = 1;
12564
12565 let dest = [0x61; 16];
12566 driver.upsert_known_destination(dest, make_announced_identity(dest, 10.0, InterfaceId(1)));
12567 driver.upsert_known_destination(dest, make_announced_identity(dest, 20.0, InterfaceId(2)));
12568
12569 assert_eq!(driver.known_destinations.len(), 1);
12570 assert_eq!(
12571 driver.known_destinations[&dest].receiving_interface,
12572 InterfaceId(2)
12573 );
12574 assert_eq!(driver.known_destinations_cap_evict_count, 0);
12575 }
12576
12577 #[test]
12578 fn known_destinations_cleanup_enforces_cap() {
12579 let (tx, rx) = event::channel();
12580 let (cbs, _, _, _, _, _) = MockCallbacks::new();
12581 let mut driver = Driver::new(
12582 TransportConfig {
12583 transport_enabled: false,
12584 identity_hash: None,
12585 prefer_shorter_path: false,
12586 max_paths_per_destination: 1,
12587 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
12588 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
12589 max_path_destinations: usize::MAX,
12590 max_tunnel_destinations_total: usize::MAX,
12591 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
12592 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
12593 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
12594 announce_sig_cache_enabled: true,
12595 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
12596 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
12597 announce_queue_max_entries: 256,
12598 announce_queue_max_interfaces: 1024,
12599 },
12600 rx,
12601 tx.clone(),
12602 Box::new(cbs),
12603 );
12604
12605 driver.known_destinations_ttl = 1000.0;
12606 driver.known_destinations_max_entries = 2;
12607 driver.cache_cleanup_counter = 3599;
12608 let now = time::now();
12609 driver.known_destinations.insert(
12610 [0x71; 16],
12611 make_announced_identity([0x71; 16], now - 30.0, InterfaceId(1)),
12612 );
12613 driver.known_destinations.insert(
12614 [0x72; 16],
12615 make_announced_identity([0x72; 16], now - 20.0, InterfaceId(1)),
12616 );
12617 driver.known_destinations.insert(
12618 [0x73; 16],
12619 make_announced_identity([0x73; 16], now - 10.0, InterfaceId(1)),
12620 );
12621
12622 tx.send(Event::Tick).unwrap();
12623 tx.send(Event::Shutdown).unwrap();
12624 driver.run();
12625
12626 assert_eq!(driver.known_destinations.len(), 2);
12627 assert!(!driver.known_destinations.contains_key(&[0x71; 16]));
12628 assert_eq!(driver.known_destinations_cap_evict_count, 1);
12629 }
12630
12631 #[test]
12632 fn query_has_path() {
12633 let (tx, rx) = event::channel();
12634 let (cbs, _, _, _, _, _) = MockCallbacks::new();
12635 let mut driver = Driver::new(
12636 TransportConfig {
12637 transport_enabled: false,
12638 identity_hash: None,
12639 prefer_shorter_path: false,
12640 max_paths_per_destination: 1,
12641 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
12642 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
12643 max_path_destinations: usize::MAX,
12644 max_tunnel_destinations_total: usize::MAX,
12645 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
12646 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
12647 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
12648 announce_sig_cache_enabled: true,
12649 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
12650 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
12651 announce_queue_max_entries: 256,
12652 announce_queue_max_interfaces: 1024,
12653 },
12654 rx,
12655 tx.clone(),
12656 Box::new(cbs),
12657 );
12658 let info = make_interface_info(1);
12659 driver.engine.register_interface(info);
12660 let (writer, _sent) = MockWriter::new();
12661 driver
12662 .interfaces
12663 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
12664
12665 let (resp_tx, resp_rx) = mpsc::channel();
12667 tx.send(Event::Query(
12668 QueryRequest::HasPath {
12669 dest_hash: [0xAA; 16],
12670 },
12671 resp_tx,
12672 ))
12673 .unwrap();
12674
12675 let identity = Identity::new(&mut OsRng);
12677 let announce_raw = build_announce_packet(&identity);
12678 let dest_hash =
12679 rns_core::destination::destination_hash("test", &["app"], Some(identity.hash()));
12680 tx.send(Event::Frame {
12681 interface_id: InterfaceId(1),
12682 data: announce_raw,
12683 })
12684 .unwrap();
12685
12686 let (resp_tx2, resp_rx2) = mpsc::channel();
12687 tx.send(Event::Query(QueryRequest::HasPath { dest_hash }, resp_tx2))
12688 .unwrap();
12689
12690 tx.send(Event::Shutdown).unwrap();
12691 driver.run();
12692
12693 match resp_rx.recv().unwrap() {
12695 QueryResponse::HasPath(false) => {}
12696 other => panic!("expected HasPath(false), got {:?}", other),
12697 }
12698
12699 match resp_rx2.recv().unwrap() {
12701 QueryResponse::HasPath(true) => {}
12702 other => panic!("expected HasPath(true), got {:?}", other),
12703 }
12704 }
12705
12706 #[test]
12707 fn query_hops_to() {
12708 let (tx, rx) = event::channel();
12709 let (cbs, _, _, _, _, _) = MockCallbacks::new();
12710 let mut driver = Driver::new(
12711 TransportConfig {
12712 transport_enabled: false,
12713 identity_hash: None,
12714 prefer_shorter_path: false,
12715 max_paths_per_destination: 1,
12716 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
12717 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
12718 max_path_destinations: usize::MAX,
12719 max_tunnel_destinations_total: usize::MAX,
12720 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
12721 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
12722 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
12723 announce_sig_cache_enabled: true,
12724 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
12725 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
12726 announce_queue_max_entries: 256,
12727 announce_queue_max_interfaces: 1024,
12728 },
12729 rx,
12730 tx.clone(),
12731 Box::new(cbs),
12732 );
12733 let info = make_interface_info(1);
12734 driver.engine.register_interface(info);
12735 let (writer, _sent) = MockWriter::new();
12736 driver
12737 .interfaces
12738 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
12739
12740 let identity = Identity::new(&mut OsRng);
12742 let announce_raw = build_announce_packet(&identity);
12743 let dest_hash =
12744 rns_core::destination::destination_hash("test", &["app"], Some(identity.hash()));
12745
12746 tx.send(Event::Frame {
12747 interface_id: InterfaceId(1),
12748 data: announce_raw,
12749 })
12750 .unwrap();
12751
12752 let (resp_tx, resp_rx) = mpsc::channel();
12753 tx.send(Event::Query(QueryRequest::HopsTo { dest_hash }, resp_tx))
12754 .unwrap();
12755 tx.send(Event::Shutdown).unwrap();
12756 driver.run();
12757
12758 match resp_rx.recv().unwrap() {
12759 QueryResponse::HopsTo(Some(1)) => {}
12760 other => panic!("expected HopsTo(Some(1)), got {:?}", other),
12761 }
12762 }
12763
12764 #[test]
12765 fn query_recall_identity() {
12766 let (tx, rx) = event::channel();
12767 let (cbs, _, _, _, _, _) = MockCallbacks::new();
12768 let mut driver = Driver::new(
12769 TransportConfig {
12770 transport_enabled: false,
12771 identity_hash: None,
12772 prefer_shorter_path: false,
12773 max_paths_per_destination: 1,
12774 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
12775 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
12776 max_path_destinations: usize::MAX,
12777 max_tunnel_destinations_total: usize::MAX,
12778 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
12779 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
12780 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
12781 announce_sig_cache_enabled: true,
12782 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
12783 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
12784 announce_queue_max_entries: 256,
12785 announce_queue_max_interfaces: 1024,
12786 },
12787 rx,
12788 tx.clone(),
12789 Box::new(cbs),
12790 );
12791 let info = make_interface_info(1);
12792 driver.engine.register_interface(info);
12793 let (writer, _sent) = MockWriter::new();
12794 driver
12795 .interfaces
12796 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
12797
12798 let identity = Identity::new(&mut OsRng);
12799 let announce_raw = build_announce_packet(&identity);
12800 let dest_hash =
12801 rns_core::destination::destination_hash("test", &["app"], Some(identity.hash()));
12802
12803 tx.send(Event::Frame {
12804 interface_id: InterfaceId(1),
12805 data: announce_raw,
12806 })
12807 .unwrap();
12808
12809 let (resp_tx, resp_rx) = mpsc::channel();
12811 tx.send(Event::Query(
12812 QueryRequest::RecallIdentity { dest_hash },
12813 resp_tx,
12814 ))
12815 .unwrap();
12816
12817 let (resp_tx2, resp_rx2) = mpsc::channel();
12819 tx.send(Event::Query(
12820 QueryRequest::RecallIdentity {
12821 dest_hash: [0xFF; 16],
12822 },
12823 resp_tx2,
12824 ))
12825 .unwrap();
12826
12827 tx.send(Event::Shutdown).unwrap();
12828 driver.run();
12829
12830 match resp_rx.recv().unwrap() {
12831 QueryResponse::RecallIdentity(Some(recalled)) => {
12832 assert_eq!(recalled.dest_hash.0, dest_hash);
12833 assert_eq!(recalled.identity_hash.0, *identity.hash());
12834 assert_eq!(recalled.public_key, identity.get_public_key().unwrap());
12835 assert_eq!(recalled.hops, 1);
12836 }
12837 other => panic!("expected RecallIdentity(Some(..)), got {:?}", other),
12838 }
12839
12840 match resp_rx2.recv().unwrap() {
12841 QueryResponse::RecallIdentity(None) => {}
12842 other => panic!("expected RecallIdentity(None), got {:?}", other),
12843 }
12844 }
12845
12846 #[test]
12847 fn request_path_sends_packet() {
12848 let (tx, rx) = event::channel();
12849 let (cbs, _, _, _, _, _) = MockCallbacks::new();
12850 let mut driver = Driver::new(
12851 TransportConfig {
12852 transport_enabled: false,
12853 identity_hash: None,
12854 prefer_shorter_path: false,
12855 max_paths_per_destination: 1,
12856 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
12857 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
12858 max_path_destinations: usize::MAX,
12859 max_tunnel_destinations_total: usize::MAX,
12860 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
12861 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
12862 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
12863 announce_sig_cache_enabled: true,
12864 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
12865 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
12866 announce_queue_max_entries: 256,
12867 announce_queue_max_interfaces: 1024,
12868 },
12869 rx,
12870 tx.clone(),
12871 Box::new(cbs),
12872 );
12873 let info = make_interface_info(1);
12874 driver.engine.register_interface(info);
12875 let (writer, sent) = MockWriter::new();
12876 driver
12877 .interfaces
12878 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
12879
12880 tx.send(Event::RequestPath {
12882 dest_hash: [0xAA; 16],
12883 })
12884 .unwrap();
12885 tx.send(Event::Shutdown).unwrap();
12886 driver.run();
12887
12888 let sent_packets = sent.lock().unwrap();
12890 assert!(
12891 !sent_packets.is_empty(),
12892 "Path request should be sent on wire"
12893 );
12894
12895 let raw = &sent_packets[0];
12897 let flags = rns_core::packet::PacketFlags::unpack(raw[0] & 0x7F);
12898 assert_eq!(flags.packet_type, constants::PACKET_TYPE_DATA);
12899 assert_eq!(flags.destination_type, constants::DESTINATION_PLAIN);
12900 assert_eq!(flags.transport_type, constants::TRANSPORT_BROADCAST);
12901 }
12902
12903 #[test]
12904 fn request_path_includes_transport_id() {
12905 let (tx, rx) = event::channel();
12906 let (cbs, _, _, _, _, _) = MockCallbacks::new();
12907 let mut driver = Driver::new(
12908 TransportConfig {
12909 transport_enabled: true,
12910 identity_hash: Some([0xBB; 16]),
12911 prefer_shorter_path: false,
12912 max_paths_per_destination: 1,
12913 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
12914 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
12915 max_path_destinations: usize::MAX,
12916 max_tunnel_destinations_total: usize::MAX,
12917 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
12918 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
12919 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
12920 announce_sig_cache_enabled: true,
12921 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
12922 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
12923 announce_queue_max_entries: 256,
12924 announce_queue_max_interfaces: 1024,
12925 },
12926 rx,
12927 tx.clone(),
12928 Box::new(cbs),
12929 );
12930 let info = make_interface_info(1);
12931 driver.engine.register_interface(info);
12932 let (writer, sent) = MockWriter::new();
12933 driver
12934 .interfaces
12935 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
12936
12937 tx.send(Event::RequestPath {
12938 dest_hash: [0xAA; 16],
12939 })
12940 .unwrap();
12941 tx.send(Event::Shutdown).unwrap();
12942 driver.run();
12943
12944 let sent_packets = sent.lock().unwrap();
12945 assert!(!sent_packets.is_empty());
12946
12947 let raw = &sent_packets[0];
12949 if let Ok(packet) = RawPacket::unpack(raw) {
12950 assert_eq!(
12952 packet.data.len(),
12953 48,
12954 "Path request data should be 48 bytes with transport_id"
12955 );
12956 assert_eq!(
12957 &packet.data[..16],
12958 &[0xAA; 16],
12959 "First 16 bytes should be dest_hash"
12960 );
12961 assert_eq!(
12962 &packet.data[16..32],
12963 &[0xBB; 16],
12964 "Next 16 bytes should be transport_id"
12965 );
12966 } else {
12967 panic!("Could not unpack sent packet");
12968 }
12969 }
12970
12971 #[test]
12972 fn path_request_dest_registered() {
12973 let (tx, rx) = event::channel();
12974 let (cbs, _, _, _, _, _) = MockCallbacks::new();
12975 let driver = Driver::new(
12976 TransportConfig {
12977 transport_enabled: false,
12978 identity_hash: None,
12979 prefer_shorter_path: false,
12980 max_paths_per_destination: 1,
12981 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
12982 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
12983 max_path_destinations: usize::MAX,
12984 max_tunnel_destinations_total: usize::MAX,
12985 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
12986 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
12987 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
12988 announce_sig_cache_enabled: true,
12989 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
12990 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
12991 announce_queue_max_entries: 256,
12992 announce_queue_max_interfaces: 1024,
12993 },
12994 rx,
12995 tx.clone(),
12996 Box::new(cbs),
12997 );
12998
12999 let expected_dest =
13001 rns_core::destination::destination_hash("rnstransport", &["path", "request"], None);
13002 assert_eq!(driver.path_request_dest, expected_dest);
13003
13004 drop(tx);
13005 }
13006
13007 #[test]
13012 fn register_proof_strategy_event() {
13013 let (tx, rx) = event::channel();
13014 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13015 let mut driver = Driver::new(
13016 TransportConfig {
13017 transport_enabled: false,
13018 identity_hash: None,
13019 prefer_shorter_path: false,
13020 max_paths_per_destination: 1,
13021 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13022 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13023 max_path_destinations: usize::MAX,
13024 max_tunnel_destinations_total: usize::MAX,
13025 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13026 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13027 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13028 announce_sig_cache_enabled: true,
13029 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13030 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13031 announce_queue_max_entries: 256,
13032 announce_queue_max_interfaces: 1024,
13033 },
13034 rx,
13035 tx.clone(),
13036 Box::new(cbs),
13037 );
13038
13039 let dest = [0xAA; 16];
13040 let identity = Identity::new(&mut OsRng);
13041 let prv_key = identity.get_private_key().unwrap();
13042
13043 tx.send(Event::RegisterProofStrategy {
13044 dest_hash: dest,
13045 strategy: rns_core::types::ProofStrategy::ProveAll,
13046 signing_key: Some(prv_key),
13047 })
13048 .unwrap();
13049 tx.send(Event::Shutdown).unwrap();
13050 driver.run();
13051
13052 assert!(driver.proof_strategies.contains_key(&dest));
13053 let (strategy, ref id_opt) = driver.proof_strategies[&dest];
13054 assert_eq!(strategy, rns_core::types::ProofStrategy::ProveAll);
13055 assert!(id_opt.is_some());
13056 }
13057
13058 #[test]
13059 fn register_proof_strategy_prove_none_no_identity() {
13060 let (tx, rx) = event::channel();
13061 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13062 let mut driver = Driver::new(
13063 TransportConfig {
13064 transport_enabled: false,
13065 identity_hash: None,
13066 prefer_shorter_path: false,
13067 max_paths_per_destination: 1,
13068 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13069 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13070 max_path_destinations: usize::MAX,
13071 max_tunnel_destinations_total: usize::MAX,
13072 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13073 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13074 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13075 announce_sig_cache_enabled: true,
13076 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13077 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13078 announce_queue_max_entries: 256,
13079 announce_queue_max_interfaces: 1024,
13080 },
13081 rx,
13082 tx.clone(),
13083 Box::new(cbs),
13084 );
13085
13086 let dest = [0xBB; 16];
13087 tx.send(Event::RegisterProofStrategy {
13088 dest_hash: dest,
13089 strategy: rns_core::types::ProofStrategy::ProveNone,
13090 signing_key: None,
13091 })
13092 .unwrap();
13093 tx.send(Event::Shutdown).unwrap();
13094 driver.run();
13095
13096 assert!(driver.proof_strategies.contains_key(&dest));
13097 let (strategy, ref id_opt) = driver.proof_strategies[&dest];
13098 assert_eq!(strategy, rns_core::types::ProofStrategy::ProveNone);
13099 assert!(id_opt.is_none());
13100 }
13101
13102 #[test]
13103 fn send_outbound_tracks_sent_packets() {
13104 let (tx, rx) = event::channel();
13105 let (cbs, _, _, _, _, _) = MockCallbacks::new();
13106 let mut driver = Driver::new(
13107 TransportConfig {
13108 transport_enabled: false,
13109 identity_hash: None,
13110 prefer_shorter_path: false,
13111 max_paths_per_destination: 1,
13112 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13113 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13114 max_path_destinations: usize::MAX,
13115 max_tunnel_destinations_total: usize::MAX,
13116 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13117 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13118 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13119 announce_sig_cache_enabled: true,
13120 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13121 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13122 announce_queue_max_entries: 256,
13123 announce_queue_max_interfaces: 1024,
13124 },
13125 rx,
13126 tx.clone(),
13127 Box::new(cbs),
13128 );
13129 let info = make_interface_info(1);
13130 driver.engine.register_interface(info);
13131 let (writer, _sent) = MockWriter::new();
13132 driver
13133 .interfaces
13134 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13135
13136 let dest = [0xCC; 16];
13138 let flags = PacketFlags {
13139 header_type: constants::HEADER_1,
13140 context_flag: constants::FLAG_UNSET,
13141 transport_type: constants::TRANSPORT_BROADCAST,
13142 destination_type: constants::DESTINATION_PLAIN,
13143 packet_type: constants::PACKET_TYPE_DATA,
13144 };
13145 let packet =
13146 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"test data").unwrap();
13147 let expected_hash = packet.packet_hash;
13148
13149 tx.send(Event::SendOutbound {
13150 raw: packet.raw,
13151 dest_type: constants::DESTINATION_PLAIN,
13152 attached_interface: None,
13153 })
13154 .unwrap();
13155 tx.send(Event::Shutdown).unwrap();
13156 driver.run();
13157
13158 assert!(driver.sent_packets.contains_key(&expected_hash));
13160 let (tracked_dest, _sent_time) = &driver.sent_packets[&expected_hash];
13161 assert_eq!(tracked_dest, &dest);
13162 }
13163
13164 #[test]
13165 fn prove_all_generates_proof_on_delivery() {
13166 let (tx, rx) = event::channel();
13167 let (cbs, _, _, deliveries, _, _) = MockCallbacks::new();
13168 let mut driver = Driver::new(
13169 TransportConfig {
13170 transport_enabled: false,
13171 identity_hash: None,
13172 prefer_shorter_path: false,
13173 max_paths_per_destination: 1,
13174 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13175 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13176 max_path_destinations: usize::MAX,
13177 max_tunnel_destinations_total: usize::MAX,
13178 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13179 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13180 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13181 announce_sig_cache_enabled: true,
13182 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13183 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13184 announce_queue_max_entries: 256,
13185 announce_queue_max_interfaces: 1024,
13186 },
13187 rx,
13188 tx.clone(),
13189 Box::new(cbs),
13190 );
13191 let info = make_interface_info(1);
13192 driver.engine.register_interface(info);
13193 let (writer, sent) = MockWriter::new();
13194 driver
13195 .interfaces
13196 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13197
13198 let dest = [0xDD; 16];
13200 let identity = Identity::new(&mut OsRng);
13201 let prv_key = identity.get_private_key().unwrap();
13202 driver
13203 .engine
13204 .register_destination(dest, constants::DESTINATION_SINGLE);
13205 driver.proof_strategies.insert(
13206 dest,
13207 (
13208 rns_core::types::ProofStrategy::ProveAll,
13209 Some(Identity::from_private_key(&prv_key)),
13210 ),
13211 );
13212
13213 let flags = PacketFlags {
13215 header_type: constants::HEADER_1,
13216 context_flag: constants::FLAG_UNSET,
13217 transport_type: constants::TRANSPORT_BROADCAST,
13218 destination_type: constants::DESTINATION_SINGLE,
13219 packet_type: constants::PACKET_TYPE_DATA,
13220 };
13221 let packet =
13222 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
13223
13224 tx.send(Event::Frame {
13225 interface_id: InterfaceId(1),
13226 data: packet.raw,
13227 })
13228 .unwrap();
13229 tx.send(Event::Shutdown).unwrap();
13230 driver.run();
13231
13232 assert_eq!(deliveries.lock().unwrap().len(), 1);
13234
13235 let sent_packets = sent.lock().unwrap();
13237 let has_proof = sent_packets.iter().any(|raw| {
13239 let flags = PacketFlags::unpack(raw[0] & 0x7F);
13240 flags.packet_type == constants::PACKET_TYPE_PROOF
13241 });
13242 assert!(
13243 has_proof,
13244 "ProveAll should generate a proof packet: sent {} packets",
13245 sent_packets.len()
13246 );
13247 }
13248
13249 #[test]
13250 fn prove_none_does_not_generate_proof() {
13251 let (tx, rx) = event::channel();
13252 let (cbs, _, _, deliveries, _, _) = MockCallbacks::new();
13253 let mut driver = Driver::new(
13254 TransportConfig {
13255 transport_enabled: false,
13256 identity_hash: None,
13257 prefer_shorter_path: false,
13258 max_paths_per_destination: 1,
13259 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13260 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13261 max_path_destinations: usize::MAX,
13262 max_tunnel_destinations_total: usize::MAX,
13263 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13264 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13265 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13266 announce_sig_cache_enabled: true,
13267 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13268 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13269 announce_queue_max_entries: 256,
13270 announce_queue_max_interfaces: 1024,
13271 },
13272 rx,
13273 tx.clone(),
13274 Box::new(cbs),
13275 );
13276 let info = make_interface_info(1);
13277 driver.engine.register_interface(info);
13278 let (writer, sent) = MockWriter::new();
13279 driver
13280 .interfaces
13281 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13282
13283 let dest = [0xDD; 16];
13285 driver
13286 .engine
13287 .register_destination(dest, constants::DESTINATION_SINGLE);
13288 driver
13289 .proof_strategies
13290 .insert(dest, (rns_core::types::ProofStrategy::ProveNone, None));
13291
13292 let flags = PacketFlags {
13294 header_type: constants::HEADER_1,
13295 context_flag: constants::FLAG_UNSET,
13296 transport_type: constants::TRANSPORT_BROADCAST,
13297 destination_type: constants::DESTINATION_SINGLE,
13298 packet_type: constants::PACKET_TYPE_DATA,
13299 };
13300 let packet =
13301 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
13302
13303 tx.send(Event::Frame {
13304 interface_id: InterfaceId(1),
13305 data: packet.raw,
13306 })
13307 .unwrap();
13308 tx.send(Event::Shutdown).unwrap();
13309 driver.run();
13310
13311 assert_eq!(deliveries.lock().unwrap().len(), 1);
13313
13314 let sent_packets = sent.lock().unwrap();
13316 let has_proof = sent_packets.iter().any(|raw| {
13317 let flags = PacketFlags::unpack(raw[0] & 0x7F);
13318 flags.packet_type == constants::PACKET_TYPE_PROOF
13319 });
13320 assert!(!has_proof, "ProveNone should not generate a proof packet");
13321 }
13322
13323 #[test]
13324 fn no_proof_strategy_does_not_generate_proof() {
13325 let (tx, rx) = event::channel();
13326 let (cbs, _, _, deliveries, _, _) = MockCallbacks::new();
13327 let mut driver = Driver::new(
13328 TransportConfig {
13329 transport_enabled: false,
13330 identity_hash: None,
13331 prefer_shorter_path: false,
13332 max_paths_per_destination: 1,
13333 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13334 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13335 max_path_destinations: usize::MAX,
13336 max_tunnel_destinations_total: usize::MAX,
13337 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13338 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13339 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13340 announce_sig_cache_enabled: true,
13341 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13342 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13343 announce_queue_max_entries: 256,
13344 announce_queue_max_interfaces: 1024,
13345 },
13346 rx,
13347 tx.clone(),
13348 Box::new(cbs),
13349 );
13350 let info = make_interface_info(1);
13351 driver.engine.register_interface(info);
13352 let (writer, sent) = MockWriter::new();
13353 driver
13354 .interfaces
13355 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13356
13357 let dest = [0xDD; 16];
13359 driver
13360 .engine
13361 .register_destination(dest, constants::DESTINATION_SINGLE);
13362
13363 let flags = PacketFlags {
13364 header_type: constants::HEADER_1,
13365 context_flag: constants::FLAG_UNSET,
13366 transport_type: constants::TRANSPORT_BROADCAST,
13367 destination_type: constants::DESTINATION_SINGLE,
13368 packet_type: constants::PACKET_TYPE_DATA,
13369 };
13370 let packet =
13371 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
13372
13373 tx.send(Event::Frame {
13374 interface_id: InterfaceId(1),
13375 data: packet.raw,
13376 })
13377 .unwrap();
13378 tx.send(Event::Shutdown).unwrap();
13379 driver.run();
13380
13381 assert_eq!(deliveries.lock().unwrap().len(), 1);
13382
13383 let sent_packets = sent.lock().unwrap();
13384 let has_proof = sent_packets.iter().any(|raw| {
13385 let flags = PacketFlags::unpack(raw[0] & 0x7F);
13386 flags.packet_type == constants::PACKET_TYPE_PROOF
13387 });
13388 assert!(!has_proof, "No proof strategy means no proof generated");
13389 }
13390
13391 #[test]
13392 fn prove_app_calls_callback() {
13393 let (tx, rx) = event::channel();
13394 let proof_requested = Arc::new(Mutex::new(Vec::new()));
13395 let deliveries = Arc::new(Mutex::new(Vec::new()));
13396 let cbs = MockCallbacks {
13397 announces: Arc::new(Mutex::new(Vec::new())),
13398 paths: Arc::new(Mutex::new(Vec::new())),
13399 deliveries: deliveries.clone(),
13400 iface_ups: Arc::new(Mutex::new(Vec::new())),
13401 iface_downs: Arc::new(Mutex::new(Vec::new())),
13402 link_established: Arc::new(Mutex::new(Vec::new())),
13403 link_closed: Arc::new(Mutex::new(Vec::new())),
13404 remote_identified: Arc::new(Mutex::new(Vec::new())),
13405 resources_received: Arc::new(Mutex::new(Vec::new())),
13406 resource_completed: Arc::new(Mutex::new(Vec::new())),
13407 resource_failed: Arc::new(Mutex::new(Vec::new())),
13408 channel_messages: Arc::new(Mutex::new(Vec::new())),
13409 link_data: Arc::new(Mutex::new(Vec::new())),
13410 responses: Arc::new(Mutex::new(Vec::new())),
13411 proofs: Arc::new(Mutex::new(Vec::new())),
13412 proof_requested: proof_requested.clone(),
13413 };
13414
13415 let mut driver = Driver::new(
13416 TransportConfig {
13417 transport_enabled: false,
13418 identity_hash: None,
13419 prefer_shorter_path: false,
13420 max_paths_per_destination: 1,
13421 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13422 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13423 max_path_destinations: usize::MAX,
13424 max_tunnel_destinations_total: usize::MAX,
13425 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13426 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13427 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13428 announce_sig_cache_enabled: true,
13429 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13430 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13431 announce_queue_max_entries: 256,
13432 announce_queue_max_interfaces: 1024,
13433 },
13434 rx,
13435 tx.clone(),
13436 Box::new(cbs),
13437 );
13438 let info = make_interface_info(1);
13439 driver.engine.register_interface(info);
13440 let (writer, sent) = MockWriter::new();
13441 driver
13442 .interfaces
13443 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13444
13445 let dest = [0xDD; 16];
13447 let identity = Identity::new(&mut OsRng);
13448 let prv_key = identity.get_private_key().unwrap();
13449 driver
13450 .engine
13451 .register_destination(dest, constants::DESTINATION_SINGLE);
13452 driver.proof_strategies.insert(
13453 dest,
13454 (
13455 rns_core::types::ProofStrategy::ProveApp,
13456 Some(Identity::from_private_key(&prv_key)),
13457 ),
13458 );
13459
13460 let flags = PacketFlags {
13461 header_type: constants::HEADER_1,
13462 context_flag: constants::FLAG_UNSET,
13463 transport_type: constants::TRANSPORT_BROADCAST,
13464 destination_type: constants::DESTINATION_SINGLE,
13465 packet_type: constants::PACKET_TYPE_DATA,
13466 };
13467 let packet =
13468 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"app test").unwrap();
13469
13470 tx.send(Event::Frame {
13471 interface_id: InterfaceId(1),
13472 data: packet.raw,
13473 })
13474 .unwrap();
13475 tx.send(Event::Shutdown).unwrap();
13476 driver.run();
13477
13478 let prs = proof_requested.lock().unwrap();
13480 assert_eq!(prs.len(), 1);
13481 assert_eq!(prs[0].0, DestHash(dest));
13482
13483 let sent_packets = sent.lock().unwrap();
13485 let has_proof = sent_packets.iter().any(|raw| {
13486 let flags = PacketFlags::unpack(raw[0] & 0x7F);
13487 flags.packet_type == constants::PACKET_TYPE_PROOF
13488 });
13489 assert!(
13490 has_proof,
13491 "ProveApp (callback returns true) should generate a proof"
13492 );
13493 }
13494
13495 #[test]
13496 fn inbound_proof_fires_callback() {
13497 let (tx, rx) = event::channel();
13498 let proofs = Arc::new(Mutex::new(Vec::new()));
13499 let cbs = MockCallbacks {
13500 announces: Arc::new(Mutex::new(Vec::new())),
13501 paths: Arc::new(Mutex::new(Vec::new())),
13502 deliveries: Arc::new(Mutex::new(Vec::new())),
13503 iface_ups: Arc::new(Mutex::new(Vec::new())),
13504 iface_downs: Arc::new(Mutex::new(Vec::new())),
13505 link_established: Arc::new(Mutex::new(Vec::new())),
13506 link_closed: Arc::new(Mutex::new(Vec::new())),
13507 remote_identified: Arc::new(Mutex::new(Vec::new())),
13508 resources_received: Arc::new(Mutex::new(Vec::new())),
13509 resource_completed: Arc::new(Mutex::new(Vec::new())),
13510 resource_failed: Arc::new(Mutex::new(Vec::new())),
13511 channel_messages: Arc::new(Mutex::new(Vec::new())),
13512 link_data: Arc::new(Mutex::new(Vec::new())),
13513 responses: Arc::new(Mutex::new(Vec::new())),
13514 proofs: proofs.clone(),
13515 proof_requested: Arc::new(Mutex::new(Vec::new())),
13516 };
13517
13518 let mut driver = Driver::new(
13519 TransportConfig {
13520 transport_enabled: false,
13521 identity_hash: None,
13522 prefer_shorter_path: false,
13523 max_paths_per_destination: 1,
13524 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13525 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13526 max_path_destinations: usize::MAX,
13527 max_tunnel_destinations_total: usize::MAX,
13528 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13529 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13530 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13531 announce_sig_cache_enabled: true,
13532 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13533 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13534 announce_queue_max_entries: 256,
13535 announce_queue_max_interfaces: 1024,
13536 },
13537 rx,
13538 tx.clone(),
13539 Box::new(cbs),
13540 );
13541 let info = make_interface_info(1);
13542 driver.engine.register_interface(info);
13543 let (writer, _sent) = MockWriter::new();
13544 driver
13545 .interfaces
13546 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13547
13548 let dest = [0xEE; 16];
13550 driver
13551 .engine
13552 .register_destination(dest, constants::DESTINATION_SINGLE);
13553
13554 let tracked_hash = [0x42u8; 32];
13556 let sent_time = time::now() - 0.5; driver.sent_packets.insert(tracked_hash, (dest, sent_time));
13558
13559 let mut proof_data = Vec::new();
13561 proof_data.extend_from_slice(&tracked_hash);
13562 proof_data.extend_from_slice(&[0xAA; 64]); let flags = PacketFlags {
13565 header_type: constants::HEADER_1,
13566 context_flag: constants::FLAG_UNSET,
13567 transport_type: constants::TRANSPORT_BROADCAST,
13568 destination_type: constants::DESTINATION_SINGLE,
13569 packet_type: constants::PACKET_TYPE_PROOF,
13570 };
13571 let packet =
13572 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, &proof_data).unwrap();
13573
13574 tx.send(Event::Frame {
13575 interface_id: InterfaceId(1),
13576 data: packet.raw,
13577 })
13578 .unwrap();
13579 tx.send(Event::Shutdown).unwrap();
13580 driver.run();
13581
13582 let proof_list = proofs.lock().unwrap();
13584 assert_eq!(proof_list.len(), 1);
13585 assert_eq!(proof_list[0].0, DestHash(dest));
13586 assert_eq!(proof_list[0].1, PacketHash(tracked_hash));
13587 assert!(
13588 proof_list[0].2 >= 0.4,
13589 "RTT should be approximately 0.5s, got {}",
13590 proof_list[0].2
13591 );
13592
13593 assert!(!driver.sent_packets.contains_key(&tracked_hash));
13595 }
13596
13597 #[test]
13598 fn inbound_proof_for_unknown_packet_is_ignored() {
13599 let (tx, rx) = event::channel();
13600 let proofs = Arc::new(Mutex::new(Vec::new()));
13601 let cbs = MockCallbacks {
13602 announces: Arc::new(Mutex::new(Vec::new())),
13603 paths: Arc::new(Mutex::new(Vec::new())),
13604 deliveries: Arc::new(Mutex::new(Vec::new())),
13605 iface_ups: Arc::new(Mutex::new(Vec::new())),
13606 iface_downs: Arc::new(Mutex::new(Vec::new())),
13607 link_established: Arc::new(Mutex::new(Vec::new())),
13608 link_closed: Arc::new(Mutex::new(Vec::new())),
13609 remote_identified: Arc::new(Mutex::new(Vec::new())),
13610 resources_received: Arc::new(Mutex::new(Vec::new())),
13611 resource_completed: Arc::new(Mutex::new(Vec::new())),
13612 resource_failed: Arc::new(Mutex::new(Vec::new())),
13613 channel_messages: Arc::new(Mutex::new(Vec::new())),
13614 link_data: Arc::new(Mutex::new(Vec::new())),
13615 responses: Arc::new(Mutex::new(Vec::new())),
13616 proofs: proofs.clone(),
13617 proof_requested: Arc::new(Mutex::new(Vec::new())),
13618 };
13619
13620 let mut driver = Driver::new(
13621 TransportConfig {
13622 transport_enabled: false,
13623 identity_hash: None,
13624 prefer_shorter_path: false,
13625 max_paths_per_destination: 1,
13626 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13627 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13628 max_path_destinations: usize::MAX,
13629 max_tunnel_destinations_total: usize::MAX,
13630 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13631 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13632 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13633 announce_sig_cache_enabled: true,
13634 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13635 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13636 announce_queue_max_entries: 256,
13637 announce_queue_max_interfaces: 1024,
13638 },
13639 rx,
13640 tx.clone(),
13641 Box::new(cbs),
13642 );
13643 let info = make_interface_info(1);
13644 driver.engine.register_interface(info);
13645 let (writer, _sent) = MockWriter::new();
13646 driver
13647 .interfaces
13648 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13649
13650 let dest = [0xEE; 16];
13651 driver
13652 .engine
13653 .register_destination(dest, constants::DESTINATION_SINGLE);
13654
13655 let unknown_hash = [0xFF; 32];
13657 let mut proof_data = Vec::new();
13658 proof_data.extend_from_slice(&unknown_hash);
13659 proof_data.extend_from_slice(&[0xAA; 64]);
13660
13661 let flags = PacketFlags {
13662 header_type: constants::HEADER_1,
13663 context_flag: constants::FLAG_UNSET,
13664 transport_type: constants::TRANSPORT_BROADCAST,
13665 destination_type: constants::DESTINATION_SINGLE,
13666 packet_type: constants::PACKET_TYPE_PROOF,
13667 };
13668 let packet =
13669 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, &proof_data).unwrap();
13670
13671 tx.send(Event::Frame {
13672 interface_id: InterfaceId(1),
13673 data: packet.raw,
13674 })
13675 .unwrap();
13676 tx.send(Event::Shutdown).unwrap();
13677 driver.run();
13678
13679 assert!(proofs.lock().unwrap().is_empty());
13681 }
13682
13683 #[test]
13684 fn inbound_implicit_proof_matches_truncated_destination() {
13685 let (tx, rx) = event::channel();
13686 let proofs = Arc::new(Mutex::new(Vec::new()));
13687 let cbs = MockCallbacks {
13688 announces: Arc::new(Mutex::new(Vec::new())),
13689 paths: Arc::new(Mutex::new(Vec::new())),
13690 deliveries: Arc::new(Mutex::new(Vec::new())),
13691 iface_ups: Arc::new(Mutex::new(Vec::new())),
13692 iface_downs: Arc::new(Mutex::new(Vec::new())),
13693 link_established: Arc::new(Mutex::new(Vec::new())),
13694 link_closed: Arc::new(Mutex::new(Vec::new())),
13695 remote_identified: Arc::new(Mutex::new(Vec::new())),
13696 resources_received: Arc::new(Mutex::new(Vec::new())),
13697 resource_completed: Arc::new(Mutex::new(Vec::new())),
13698 resource_failed: Arc::new(Mutex::new(Vec::new())),
13699 channel_messages: Arc::new(Mutex::new(Vec::new())),
13700 link_data: Arc::new(Mutex::new(Vec::new())),
13701 responses: Arc::new(Mutex::new(Vec::new())),
13702 proofs: proofs.clone(),
13703 proof_requested: Arc::new(Mutex::new(Vec::new())),
13704 };
13705
13706 let mut driver = Driver::new(
13707 TransportConfig {
13708 transport_enabled: false,
13709 identity_hash: None,
13710 prefer_shorter_path: false,
13711 max_paths_per_destination: 1,
13712 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13713 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13714 max_path_destinations: usize::MAX,
13715 max_tunnel_destinations_total: usize::MAX,
13716 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13717 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13718 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13719 announce_sig_cache_enabled: true,
13720 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13721 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13722 announce_queue_max_entries: 256,
13723 announce_queue_max_interfaces: 1024,
13724 },
13725 rx,
13726 tx.clone(),
13727 Box::new(cbs),
13728 );
13729 let info = make_interface_info(1);
13730 driver.engine.register_interface(info);
13731 let (writer, _sent) = MockWriter::new();
13732 driver
13733 .interfaces
13734 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13735
13736 let tracked_hash = [0x3Cu8; 32];
13737 let sent_time = time::now() - 0.25;
13738 driver
13739 .sent_packets
13740 .insert(tracked_hash, ([0xEE; 16], sent_time));
13741
13742 let mut proof_dest = [0u8; 16];
13743 proof_dest.copy_from_slice(&tracked_hash[..16]);
13744 driver
13745 .engine
13746 .register_destination(proof_dest, constants::DESTINATION_SINGLE);
13747
13748 let proof_data = vec![0xAA; 64];
13750 let flags = PacketFlags {
13751 header_type: constants::HEADER_1,
13752 context_flag: constants::FLAG_UNSET,
13753 transport_type: constants::TRANSPORT_BROADCAST,
13754 destination_type: constants::DESTINATION_SINGLE,
13755 packet_type: constants::PACKET_TYPE_PROOF,
13756 };
13757 let packet =
13758 RawPacket::pack(flags, 0, &proof_dest, None, constants::CONTEXT_NONE, &proof_data)
13759 .unwrap();
13760
13761 tx.send(Event::Frame {
13762 interface_id: InterfaceId(1),
13763 data: packet.raw,
13764 })
13765 .unwrap();
13766 tx.send(Event::Shutdown).unwrap();
13767 driver.run();
13768
13769 let proof_list = proofs.lock().unwrap();
13770 assert_eq!(proof_list.len(), 1);
13771 assert_eq!(proof_list[0].0, DestHash([0xEE; 16]));
13772 assert_eq!(proof_list[0].1, PacketHash(tracked_hash));
13773 assert!(!driver.sent_packets.contains_key(&tracked_hash));
13774 }
13775
13776 #[test]
13777 fn link_manager_data_send_is_tracked_for_proofs() {
13778 let mut driver = new_test_driver();
13779 let (writer, _sent) = MockWriter::new();
13780 driver
13781 .interfaces
13782 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13783
13784 let flags = PacketFlags {
13785 header_type: constants::HEADER_1,
13786 context_flag: constants::FLAG_UNSET,
13787 transport_type: constants::TRANSPORT_BROADCAST,
13788 destination_type: constants::DESTINATION_LINK,
13789 packet_type: constants::PACKET_TYPE_DATA,
13790 };
13791 let packet = RawPacket::pack(
13792 flags,
13793 0,
13794 &[0x77; 16],
13795 None,
13796 constants::CONTEXT_NONE,
13797 b"track me",
13798 )
13799 .unwrap();
13800 let packet_hash = packet.packet_hash;
13801 let destination_hash = packet.destination_hash;
13802
13803 driver.dispatch_link_actions(vec![LinkManagerAction::SendPacket {
13804 raw: packet.raw,
13805 dest_type: constants::DESTINATION_LINK,
13806 attached_interface: Some(InterfaceId(1)),
13807 }]);
13808
13809 assert_eq!(
13810 driver
13811 .sent_packets
13812 .get(&packet_hash)
13813 .map(|(dest, _)| *dest),
13814 Some(destination_hash)
13815 );
13816 }
13817
13818 #[test]
13819 fn inbound_proof_with_valid_signature_fires_callback() {
13820 let (tx, rx) = event::channel();
13822 let proofs = Arc::new(Mutex::new(Vec::new()));
13823 let cbs = MockCallbacks {
13824 announces: Arc::new(Mutex::new(Vec::new())),
13825 paths: Arc::new(Mutex::new(Vec::new())),
13826 deliveries: Arc::new(Mutex::new(Vec::new())),
13827 iface_ups: Arc::new(Mutex::new(Vec::new())),
13828 iface_downs: Arc::new(Mutex::new(Vec::new())),
13829 link_established: Arc::new(Mutex::new(Vec::new())),
13830 link_closed: Arc::new(Mutex::new(Vec::new())),
13831 remote_identified: Arc::new(Mutex::new(Vec::new())),
13832 resources_received: Arc::new(Mutex::new(Vec::new())),
13833 resource_completed: Arc::new(Mutex::new(Vec::new())),
13834 resource_failed: Arc::new(Mutex::new(Vec::new())),
13835 channel_messages: Arc::new(Mutex::new(Vec::new())),
13836 link_data: Arc::new(Mutex::new(Vec::new())),
13837 responses: Arc::new(Mutex::new(Vec::new())),
13838 proofs: proofs.clone(),
13839 proof_requested: Arc::new(Mutex::new(Vec::new())),
13840 };
13841
13842 let mut driver = Driver::new(
13843 TransportConfig {
13844 transport_enabled: false,
13845 identity_hash: None,
13846 prefer_shorter_path: false,
13847 max_paths_per_destination: 1,
13848 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13849 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13850 max_path_destinations: usize::MAX,
13851 max_tunnel_destinations_total: usize::MAX,
13852 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13853 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13854 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13855 announce_sig_cache_enabled: true,
13856 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13857 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13858 announce_queue_max_entries: 256,
13859 announce_queue_max_interfaces: 1024,
13860 },
13861 rx,
13862 tx.clone(),
13863 Box::new(cbs),
13864 );
13865 let info = make_interface_info(1);
13866 driver.engine.register_interface(info);
13867 let (writer, _sent) = MockWriter::new();
13868 driver
13869 .interfaces
13870 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13871
13872 let dest = [0xEE; 16];
13873 driver
13874 .engine
13875 .register_destination(dest, constants::DESTINATION_SINGLE);
13876
13877 let identity = Identity::new(&mut OsRng);
13879 let pub_key = identity.get_public_key();
13880 driver.known_destinations.insert(
13881 dest,
13882 crate::destination::AnnouncedIdentity {
13883 dest_hash: DestHash(dest),
13884 identity_hash: IdentityHash(*identity.hash()),
13885 public_key: pub_key.unwrap(),
13886 app_data: None,
13887 hops: 0,
13888 received_at: time::now(),
13889 receiving_interface: InterfaceId(0),
13890 },
13891 );
13892
13893 let tracked_hash = [0x42u8; 32];
13895 let sent_time = time::now() - 0.5;
13896 driver.sent_packets.insert(tracked_hash, (dest, sent_time));
13897
13898 let signature = identity.sign(&tracked_hash).unwrap();
13899 let mut proof_data = Vec::new();
13900 proof_data.extend_from_slice(&tracked_hash);
13901 proof_data.extend_from_slice(&signature);
13902
13903 let flags = PacketFlags {
13904 header_type: constants::HEADER_1,
13905 context_flag: constants::FLAG_UNSET,
13906 transport_type: constants::TRANSPORT_BROADCAST,
13907 destination_type: constants::DESTINATION_SINGLE,
13908 packet_type: constants::PACKET_TYPE_PROOF,
13909 };
13910 let packet =
13911 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, &proof_data).unwrap();
13912
13913 tx.send(Event::Frame {
13914 interface_id: InterfaceId(1),
13915 data: packet.raw,
13916 })
13917 .unwrap();
13918 tx.send(Event::Shutdown).unwrap();
13919 driver.run();
13920
13921 let proof_list = proofs.lock().unwrap();
13923 assert_eq!(proof_list.len(), 1);
13924 assert_eq!(proof_list[0].0, DestHash(dest));
13925 assert_eq!(proof_list[0].1, PacketHash(tracked_hash));
13926 }
13927
13928 #[test]
13929 fn inbound_proof_with_invalid_signature_rejected() {
13930 let (tx, rx) = event::channel();
13932 let proofs = Arc::new(Mutex::new(Vec::new()));
13933 let cbs = MockCallbacks {
13934 announces: Arc::new(Mutex::new(Vec::new())),
13935 paths: Arc::new(Mutex::new(Vec::new())),
13936 deliveries: Arc::new(Mutex::new(Vec::new())),
13937 iface_ups: Arc::new(Mutex::new(Vec::new())),
13938 iface_downs: Arc::new(Mutex::new(Vec::new())),
13939 link_established: Arc::new(Mutex::new(Vec::new())),
13940 link_closed: Arc::new(Mutex::new(Vec::new())),
13941 remote_identified: Arc::new(Mutex::new(Vec::new())),
13942 resources_received: Arc::new(Mutex::new(Vec::new())),
13943 resource_completed: Arc::new(Mutex::new(Vec::new())),
13944 resource_failed: Arc::new(Mutex::new(Vec::new())),
13945 channel_messages: Arc::new(Mutex::new(Vec::new())),
13946 link_data: Arc::new(Mutex::new(Vec::new())),
13947 responses: Arc::new(Mutex::new(Vec::new())),
13948 proofs: proofs.clone(),
13949 proof_requested: Arc::new(Mutex::new(Vec::new())),
13950 };
13951
13952 let mut driver = Driver::new(
13953 TransportConfig {
13954 transport_enabled: false,
13955 identity_hash: None,
13956 prefer_shorter_path: false,
13957 max_paths_per_destination: 1,
13958 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
13959 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
13960 max_path_destinations: usize::MAX,
13961 max_tunnel_destinations_total: usize::MAX,
13962 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
13963 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
13964 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
13965 announce_sig_cache_enabled: true,
13966 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
13967 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
13968 announce_queue_max_entries: 256,
13969 announce_queue_max_interfaces: 1024,
13970 },
13971 rx,
13972 tx.clone(),
13973 Box::new(cbs),
13974 );
13975 let info = make_interface_info(1);
13976 driver.engine.register_interface(info);
13977 let (writer, _sent) = MockWriter::new();
13978 driver
13979 .interfaces
13980 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
13981
13982 let dest = [0xEE; 16];
13983 driver
13984 .engine
13985 .register_destination(dest, constants::DESTINATION_SINGLE);
13986
13987 let identity = Identity::new(&mut OsRng);
13989 let pub_key = identity.get_public_key();
13990 driver.known_destinations.insert(
13991 dest,
13992 crate::destination::AnnouncedIdentity {
13993 dest_hash: DestHash(dest),
13994 identity_hash: IdentityHash(*identity.hash()),
13995 public_key: pub_key.unwrap(),
13996 app_data: None,
13997 hops: 0,
13998 received_at: time::now(),
13999 receiving_interface: InterfaceId(0),
14000 },
14001 );
14002
14003 let tracked_hash = [0x42u8; 32];
14005 let sent_time = time::now() - 0.5;
14006 driver.sent_packets.insert(tracked_hash, (dest, sent_time));
14007
14008 let mut proof_data = Vec::new();
14010 proof_data.extend_from_slice(&tracked_hash);
14011 proof_data.extend_from_slice(&[0xAA; 64]);
14012
14013 let flags = PacketFlags {
14014 header_type: constants::HEADER_1,
14015 context_flag: constants::FLAG_UNSET,
14016 transport_type: constants::TRANSPORT_BROADCAST,
14017 destination_type: constants::DESTINATION_SINGLE,
14018 packet_type: constants::PACKET_TYPE_PROOF,
14019 };
14020 let packet =
14021 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, &proof_data).unwrap();
14022
14023 tx.send(Event::Frame {
14024 interface_id: InterfaceId(1),
14025 data: packet.raw,
14026 })
14027 .unwrap();
14028 tx.send(Event::Shutdown).unwrap();
14029 driver.run();
14030
14031 assert!(proofs.lock().unwrap().is_empty());
14033 }
14034
14035 #[test]
14036 fn proof_data_is_valid_explicit_proof() {
14037 let (tx, rx) = event::channel();
14039 let (cbs, _, _, _, _, _) = MockCallbacks::new();
14040 let mut driver = Driver::new(
14041 TransportConfig {
14042 transport_enabled: false,
14043 identity_hash: None,
14044 prefer_shorter_path: false,
14045 max_paths_per_destination: 1,
14046 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14047 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14048 max_path_destinations: usize::MAX,
14049 max_tunnel_destinations_total: usize::MAX,
14050 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14051 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14052 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14053 announce_sig_cache_enabled: true,
14054 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14055 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14056 announce_queue_max_entries: 256,
14057 announce_queue_max_interfaces: 1024,
14058 },
14059 rx,
14060 tx.clone(),
14061 Box::new(cbs),
14062 );
14063 let info = make_interface_info(1);
14064 driver.engine.register_interface(info);
14065 let (writer, sent) = MockWriter::new();
14066 driver
14067 .interfaces
14068 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14069
14070 let dest = [0xDD; 16];
14071 let identity = Identity::new(&mut OsRng);
14072 let prv_key = identity.get_private_key().unwrap();
14073 driver
14074 .engine
14075 .register_destination(dest, constants::DESTINATION_SINGLE);
14076 driver.proof_strategies.insert(
14077 dest,
14078 (
14079 rns_core::types::ProofStrategy::ProveAll,
14080 Some(Identity::from_private_key(&prv_key)),
14081 ),
14082 );
14083
14084 let flags = PacketFlags {
14085 header_type: constants::HEADER_1,
14086 context_flag: constants::FLAG_UNSET,
14087 transport_type: constants::TRANSPORT_BROADCAST,
14088 destination_type: constants::DESTINATION_SINGLE,
14089 packet_type: constants::PACKET_TYPE_DATA,
14090 };
14091 let data_packet =
14092 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"verify me").unwrap();
14093 let data_packet_hash = data_packet.packet_hash;
14094
14095 tx.send(Event::Frame {
14096 interface_id: InterfaceId(1),
14097 data: data_packet.raw,
14098 })
14099 .unwrap();
14100 tx.send(Event::Shutdown).unwrap();
14101 driver.run();
14102
14103 let sent_packets = sent.lock().unwrap();
14105 let proof_raw = sent_packets.iter().find(|raw| {
14106 let f = PacketFlags::unpack(raw[0] & 0x7F);
14107 f.packet_type == constants::PACKET_TYPE_PROOF
14108 });
14109 assert!(proof_raw.is_some(), "Should have sent a proof");
14110
14111 let proof_packet = RawPacket::unpack(proof_raw.unwrap()).unwrap();
14112 assert_eq!(
14114 proof_packet.data.len(),
14115 96,
14116 "Explicit proof should be 96 bytes"
14117 );
14118
14119 let result = rns_core::receipt::validate_proof(
14121 &proof_packet.data,
14122 &data_packet_hash,
14123 &Identity::from_private_key(&prv_key), );
14125 assert_eq!(result, rns_core::receipt::ProofResult::Valid);
14126 }
14127
14128 #[test]
14129 fn query_local_destinations_empty() {
14130 let (tx, rx) = event::channel();
14131 let (cbs, _, _, _, _, _) = MockCallbacks::new();
14132 let driver_config = TransportConfig {
14133 transport_enabled: false,
14134 identity_hash: None,
14135 prefer_shorter_path: false,
14136 max_paths_per_destination: 1,
14137 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14138 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14139 max_path_destinations: usize::MAX,
14140 max_tunnel_destinations_total: usize::MAX,
14141 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14142 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14143 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14144 announce_sig_cache_enabled: true,
14145 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14146 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14147 announce_queue_max_entries: 256,
14148 announce_queue_max_interfaces: 1024,
14149 };
14150 let mut driver = Driver::new(driver_config, rx, tx.clone(), Box::new(cbs));
14151
14152 let (resp_tx, resp_rx) = mpsc::channel();
14153 tx.send(Event::Query(QueryRequest::LocalDestinations, resp_tx))
14154 .unwrap();
14155 tx.send(Event::Shutdown).unwrap();
14156 driver.run();
14157
14158 match resp_rx.recv().unwrap() {
14159 QueryResponse::LocalDestinations(entries) => {
14160 assert_eq!(entries.len(), 2);
14162 for entry in &entries {
14163 assert_eq!(entry.dest_type, rns_core::constants::DESTINATION_PLAIN);
14164 }
14165 }
14166 other => panic!("expected LocalDestinations, got {:?}", other),
14167 }
14168 }
14169
14170 #[test]
14171 fn query_local_destinations_with_registered() {
14172 let (tx, rx) = event::channel();
14173 let (cbs, _, _, _, _, _) = MockCallbacks::new();
14174 let driver_config = TransportConfig {
14175 transport_enabled: false,
14176 identity_hash: None,
14177 prefer_shorter_path: false,
14178 max_paths_per_destination: 1,
14179 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14180 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14181 max_path_destinations: usize::MAX,
14182 max_tunnel_destinations_total: usize::MAX,
14183 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14184 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14185 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14186 announce_sig_cache_enabled: true,
14187 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14188 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14189 announce_queue_max_entries: 256,
14190 announce_queue_max_interfaces: 1024,
14191 };
14192 let mut driver = Driver::new(driver_config, rx, tx.clone(), Box::new(cbs));
14193
14194 let dest_hash = [0xAA; 16];
14195 tx.send(Event::RegisterDestination {
14196 dest_hash,
14197 dest_type: rns_core::constants::DESTINATION_SINGLE,
14198 })
14199 .unwrap();
14200
14201 let (resp_tx, resp_rx) = mpsc::channel();
14202 tx.send(Event::Query(QueryRequest::LocalDestinations, resp_tx))
14203 .unwrap();
14204 tx.send(Event::Shutdown).unwrap();
14205 driver.run();
14206
14207 match resp_rx.recv().unwrap() {
14208 QueryResponse::LocalDestinations(entries) => {
14209 assert_eq!(entries.len(), 3);
14211 assert!(entries.iter().any(|e| e.hash == dest_hash
14212 && e.dest_type == rns_core::constants::DESTINATION_SINGLE));
14213 }
14214 other => panic!("expected LocalDestinations, got {:?}", other),
14215 }
14216 }
14217
14218 #[test]
14219 fn query_local_destinations_tracks_link_dest() {
14220 let (tx, rx) = event::channel();
14221 let (cbs, _, _, _, _, _) = MockCallbacks::new();
14222 let driver_config = TransportConfig {
14223 transport_enabled: false,
14224 identity_hash: None,
14225 prefer_shorter_path: false,
14226 max_paths_per_destination: 1,
14227 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14228 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14229 max_path_destinations: usize::MAX,
14230 max_tunnel_destinations_total: usize::MAX,
14231 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14232 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14233 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14234 announce_sig_cache_enabled: true,
14235 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14236 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14237 announce_queue_max_entries: 256,
14238 announce_queue_max_interfaces: 1024,
14239 };
14240 let mut driver = Driver::new(driver_config, rx, tx.clone(), Box::new(cbs));
14241
14242 let dest_hash = [0xBB; 16];
14243 tx.send(Event::RegisterLinkDestination {
14244 dest_hash,
14245 sig_prv_bytes: [0x11; 32],
14246 sig_pub_bytes: [0x22; 32],
14247 resource_strategy: 0,
14248 })
14249 .unwrap();
14250
14251 let (resp_tx, resp_rx) = mpsc::channel();
14252 tx.send(Event::Query(QueryRequest::LocalDestinations, resp_tx))
14253 .unwrap();
14254 tx.send(Event::Shutdown).unwrap();
14255 driver.run();
14256
14257 match resp_rx.recv().unwrap() {
14258 QueryResponse::LocalDestinations(entries) => {
14259 assert_eq!(entries.len(), 3);
14261 assert!(entries.iter().any(|e| e.hash == dest_hash
14262 && e.dest_type == rns_core::constants::DESTINATION_SINGLE));
14263 }
14264 other => panic!("expected LocalDestinations, got {:?}", other),
14265 }
14266 }
14267
14268 #[test]
14269 fn query_links_empty() {
14270 let (tx, rx) = event::channel();
14271 let (cbs, _, _, _, _, _) = MockCallbacks::new();
14272 let driver_config = TransportConfig {
14273 transport_enabled: false,
14274 identity_hash: None,
14275 prefer_shorter_path: false,
14276 max_paths_per_destination: 1,
14277 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14278 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14279 max_path_destinations: usize::MAX,
14280 max_tunnel_destinations_total: usize::MAX,
14281 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14282 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14283 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14284 announce_sig_cache_enabled: true,
14285 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14286 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14287 announce_queue_max_entries: 256,
14288 announce_queue_max_interfaces: 1024,
14289 };
14290 let mut driver = Driver::new(driver_config, rx, tx.clone(), Box::new(cbs));
14291
14292 let (resp_tx, resp_rx) = mpsc::channel();
14293 tx.send(Event::Query(QueryRequest::Links, resp_tx)).unwrap();
14294 tx.send(Event::Shutdown).unwrap();
14295 driver.run();
14296
14297 match resp_rx.recv().unwrap() {
14298 QueryResponse::Links(entries) => {
14299 assert!(entries.is_empty());
14300 }
14301 other => panic!("expected Links, got {:?}", other),
14302 }
14303 }
14304
14305 #[test]
14306 fn query_resources_empty() {
14307 let (tx, rx) = event::channel();
14308 let (cbs, _, _, _, _, _) = MockCallbacks::new();
14309 let driver_config = TransportConfig {
14310 transport_enabled: false,
14311 identity_hash: None,
14312 prefer_shorter_path: false,
14313 max_paths_per_destination: 1,
14314 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14315 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14316 max_path_destinations: usize::MAX,
14317 max_tunnel_destinations_total: usize::MAX,
14318 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14319 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14320 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14321 announce_sig_cache_enabled: true,
14322 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14323 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14324 announce_queue_max_entries: 256,
14325 announce_queue_max_interfaces: 1024,
14326 };
14327 let mut driver = Driver::new(driver_config, rx, tx.clone(), Box::new(cbs));
14328
14329 let (resp_tx, resp_rx) = mpsc::channel();
14330 tx.send(Event::Query(QueryRequest::Resources, resp_tx))
14331 .unwrap();
14332 tx.send(Event::Shutdown).unwrap();
14333 driver.run();
14334
14335 match resp_rx.recv().unwrap() {
14336 QueryResponse::Resources(entries) => {
14337 assert!(entries.is_empty());
14338 }
14339 other => panic!("expected Resources, got {:?}", other),
14340 }
14341 }
14342
14343 #[test]
14344 fn infer_interface_type_from_name() {
14345 assert_eq!(
14346 super::infer_interface_type("TCPServerInterface/Client-1234"),
14347 "TCPServerClientInterface"
14348 );
14349 assert_eq!(
14350 super::infer_interface_type("BackboneInterface/5"),
14351 "BackboneInterface"
14352 );
14353 assert_eq!(
14354 super::infer_interface_type("LocalInterface"),
14355 "LocalServerClientInterface"
14356 );
14357 assert_eq!(
14358 super::infer_interface_type("MyAutoGroup:fe80::1"),
14359 "AutoInterface"
14360 );
14361 }
14362
14363 #[test]
14366 fn test_extract_dest_hash_empty() {
14367 assert_eq!(super::extract_dest_hash(&[]), [0u8; 16]);
14368 }
14369
14370 #[test]
14375 fn send_probe_unknown_dest_returns_none() {
14376 let (tx, rx) = event::channel();
14377 let (cbs, _, _, _, _, _) = MockCallbacks::new();
14378 let mut driver = Driver::new(
14379 TransportConfig {
14380 transport_enabled: false,
14381 identity_hash: None,
14382 prefer_shorter_path: false,
14383 max_paths_per_destination: 1,
14384 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14385 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14386 max_path_destinations: usize::MAX,
14387 max_tunnel_destinations_total: usize::MAX,
14388 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14389 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14390 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14391 announce_sig_cache_enabled: true,
14392 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14393 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14394 announce_queue_max_entries: 256,
14395 announce_queue_max_interfaces: 1024,
14396 },
14397 rx,
14398 tx.clone(),
14399 Box::new(cbs),
14400 );
14401 let info = make_interface_info(1);
14402 driver.engine.register_interface(info);
14403 let (writer, _sent) = MockWriter::new();
14404 driver
14405 .interfaces
14406 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14407
14408 let (resp_tx, resp_rx) = mpsc::channel();
14410 tx.send(Event::Query(
14411 QueryRequest::SendProbe {
14412 dest_hash: [0xAA; 16],
14413 payload_size: 16,
14414 },
14415 resp_tx,
14416 ))
14417 .unwrap();
14418 tx.send(Event::Shutdown).unwrap();
14419 driver.run();
14420
14421 match resp_rx.recv().unwrap() {
14422 QueryResponse::SendProbe(None) => {}
14423 other => panic!("expected SendProbe(None), got {:?}", other),
14424 }
14425 }
14426
14427 #[test]
14428 fn send_probe_known_dest_returns_packet_hash() {
14429 let (tx, rx) = event::channel();
14430 let (cbs, _, _, _, _, _) = MockCallbacks::new();
14431 let mut driver = Driver::new(
14432 TransportConfig {
14433 transport_enabled: false,
14434 identity_hash: None,
14435 prefer_shorter_path: false,
14436 max_paths_per_destination: 1,
14437 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14438 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14439 max_path_destinations: usize::MAX,
14440 max_tunnel_destinations_total: usize::MAX,
14441 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14442 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14443 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14444 announce_sig_cache_enabled: true,
14445 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14446 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14447 announce_queue_max_entries: 256,
14448 announce_queue_max_interfaces: 1024,
14449 },
14450 rx,
14451 tx.clone(),
14452 Box::new(cbs),
14453 );
14454 let info = make_interface_info(1);
14455 driver.engine.register_interface(info);
14456 let (writer, sent) = MockWriter::new();
14457 driver
14458 .interfaces
14459 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14460
14461 let remote_identity = Identity::new(&mut OsRng);
14463 let dest_hash = rns_core::destination::destination_hash(
14464 "rnstransport",
14465 &["probe"],
14466 Some(remote_identity.hash()),
14467 );
14468
14469 let (inject_tx, inject_rx) = mpsc::channel();
14471 tx.send(Event::Query(
14472 QueryRequest::InjectIdentity {
14473 dest_hash,
14474 identity_hash: *remote_identity.hash(),
14475 public_key: remote_identity.get_public_key().unwrap(),
14476 app_data: None,
14477 hops: 1,
14478 received_at: 0.0,
14479 },
14480 inject_tx,
14481 ))
14482 .unwrap();
14483
14484 let (resp_tx, resp_rx) = mpsc::channel();
14486 tx.send(Event::Query(
14487 QueryRequest::SendProbe {
14488 dest_hash,
14489 payload_size: 16,
14490 },
14491 resp_tx,
14492 ))
14493 .unwrap();
14494 tx.send(Event::Shutdown).unwrap();
14495 driver.run();
14496
14497 match inject_rx.recv().unwrap() {
14499 QueryResponse::InjectIdentity(true) => {}
14500 other => panic!("expected InjectIdentity(true), got {:?}", other),
14501 }
14502
14503 match resp_rx.recv().unwrap() {
14505 QueryResponse::SendProbe(Some((packet_hash, _hops))) => {
14506 assert_ne!(packet_hash, [0u8; 32]);
14508 assert!(driver.sent_packets.contains_key(&packet_hash));
14510 let sent_data = sent.lock().unwrap();
14512 assert!(!sent_data.is_empty(), "Probe packet should be sent on wire");
14513 let raw = &sent_data[0];
14515 let flags = PacketFlags::unpack(raw[0] & 0x7F);
14516 assert_eq!(flags.packet_type, constants::PACKET_TYPE_DATA);
14517 assert_eq!(flags.destination_type, constants::DESTINATION_SINGLE);
14518 }
14519 other => panic!("expected SendProbe(Some(..)), got {:?}", other),
14520 }
14521 }
14522
14523 #[test]
14524 fn check_proof_not_found_returns_none() {
14525 let (tx, rx) = event::channel();
14526 let (cbs, _, _, _, _, _) = MockCallbacks::new();
14527 let mut driver = Driver::new(
14528 TransportConfig {
14529 transport_enabled: false,
14530 identity_hash: None,
14531 prefer_shorter_path: false,
14532 max_paths_per_destination: 1,
14533 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14534 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14535 max_path_destinations: usize::MAX,
14536 max_tunnel_destinations_total: usize::MAX,
14537 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14538 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14539 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14540 announce_sig_cache_enabled: true,
14541 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14542 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14543 announce_queue_max_entries: 256,
14544 announce_queue_max_interfaces: 1024,
14545 },
14546 rx,
14547 tx.clone(),
14548 Box::new(cbs),
14549 );
14550
14551 let (resp_tx, resp_rx) = mpsc::channel();
14552 tx.send(Event::Query(
14553 QueryRequest::CheckProof {
14554 packet_hash: [0xBB; 32],
14555 },
14556 resp_tx,
14557 ))
14558 .unwrap();
14559 tx.send(Event::Shutdown).unwrap();
14560 driver.run();
14561
14562 match resp_rx.recv().unwrap() {
14563 QueryResponse::CheckProof(None) => {}
14564 other => panic!("expected CheckProof(None), got {:?}", other),
14565 }
14566 }
14567
14568 #[test]
14569 fn check_proof_found_returns_rtt() {
14570 let (tx, rx) = event::channel();
14571 let (cbs, _, _, _, _, _) = MockCallbacks::new();
14572 let mut driver = Driver::new(
14573 TransportConfig {
14574 transport_enabled: false,
14575 identity_hash: None,
14576 prefer_shorter_path: false,
14577 max_paths_per_destination: 1,
14578 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14579 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14580 max_path_destinations: usize::MAX,
14581 max_tunnel_destinations_total: usize::MAX,
14582 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14583 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14584 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14585 announce_sig_cache_enabled: true,
14586 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14587 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14588 announce_queue_max_entries: 256,
14589 announce_queue_max_interfaces: 1024,
14590 },
14591 rx,
14592 tx.clone(),
14593 Box::new(cbs),
14594 );
14595
14596 let packet_hash = [0xCC; 32];
14598 driver
14599 .completed_proofs
14600 .insert(packet_hash, (0.123, time::now()));
14601
14602 let (resp_tx, resp_rx) = mpsc::channel();
14603 tx.send(Event::Query(
14604 QueryRequest::CheckProof { packet_hash },
14605 resp_tx,
14606 ))
14607 .unwrap();
14608 tx.send(Event::Shutdown).unwrap();
14609 driver.run();
14610
14611 match resp_rx.recv().unwrap() {
14612 QueryResponse::CheckProof(Some(rtt)) => {
14613 assert!(
14614 (rtt - 0.123).abs() < 0.001,
14615 "RTT should be ~0.123, got {}",
14616 rtt
14617 );
14618 }
14619 other => panic!("expected CheckProof(Some(..)), got {:?}", other),
14620 }
14621 assert!(!driver.completed_proofs.contains_key(&packet_hash));
14623 }
14624
14625 #[test]
14626 fn inbound_proof_populates_completed_proofs() {
14627 let (tx, rx) = event::channel();
14628 let proofs = Arc::new(Mutex::new(Vec::new()));
14629 let cbs = MockCallbacks {
14630 announces: Arc::new(Mutex::new(Vec::new())),
14631 paths: Arc::new(Mutex::new(Vec::new())),
14632 deliveries: Arc::new(Mutex::new(Vec::new())),
14633 iface_ups: Arc::new(Mutex::new(Vec::new())),
14634 iface_downs: Arc::new(Mutex::new(Vec::new())),
14635 link_established: Arc::new(Mutex::new(Vec::new())),
14636 link_closed: Arc::new(Mutex::new(Vec::new())),
14637 remote_identified: Arc::new(Mutex::new(Vec::new())),
14638 resources_received: Arc::new(Mutex::new(Vec::new())),
14639 resource_completed: Arc::new(Mutex::new(Vec::new())),
14640 resource_failed: Arc::new(Mutex::new(Vec::new())),
14641 channel_messages: Arc::new(Mutex::new(Vec::new())),
14642 link_data: Arc::new(Mutex::new(Vec::new())),
14643 responses: Arc::new(Mutex::new(Vec::new())),
14644 proofs: proofs.clone(),
14645 proof_requested: Arc::new(Mutex::new(Vec::new())),
14646 };
14647
14648 let mut driver = Driver::new(
14649 TransportConfig {
14650 transport_enabled: false,
14651 identity_hash: None,
14652 prefer_shorter_path: false,
14653 max_paths_per_destination: 1,
14654 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14655 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14656 max_path_destinations: usize::MAX,
14657 max_tunnel_destinations_total: usize::MAX,
14658 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14659 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14660 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14661 announce_sig_cache_enabled: true,
14662 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14663 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14664 announce_queue_max_entries: 256,
14665 announce_queue_max_interfaces: 1024,
14666 },
14667 rx,
14668 tx.clone(),
14669 Box::new(cbs),
14670 );
14671 let info = make_interface_info(1);
14672 driver.engine.register_interface(info);
14673 let (writer, sent) = MockWriter::new();
14674 driver
14675 .interfaces
14676 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14677
14678 let dest = [0xDD; 16];
14680 let identity = Identity::new(&mut OsRng);
14681 let prv_key = identity.get_private_key().unwrap();
14682 driver
14683 .engine
14684 .register_destination(dest, constants::DESTINATION_SINGLE);
14685 driver.proof_strategies.insert(
14686 dest,
14687 (
14688 rns_core::types::ProofStrategy::ProveAll,
14689 Some(Identity::from_private_key(&prv_key)),
14690 ),
14691 );
14692
14693 let flags = PacketFlags {
14695 header_type: constants::HEADER_1,
14696 context_flag: constants::FLAG_UNSET,
14697 transport_type: constants::TRANSPORT_BROADCAST,
14698 destination_type: constants::DESTINATION_SINGLE,
14699 packet_type: constants::PACKET_TYPE_DATA,
14700 };
14701 let data_packet = RawPacket::pack(
14702 flags,
14703 0,
14704 &dest,
14705 None,
14706 constants::CONTEXT_NONE,
14707 b"probe data",
14708 )
14709 .unwrap();
14710 let data_packet_hash = data_packet.packet_hash;
14711
14712 driver
14714 .sent_packets
14715 .insert(data_packet_hash, (dest, time::now()));
14716
14717 tx.send(Event::Frame {
14719 interface_id: InterfaceId(1),
14720 data: data_packet.raw,
14721 })
14722 .unwrap();
14723 tx.send(Event::Shutdown).unwrap();
14724 driver.run();
14725
14726 let sent_packets = sent.lock().unwrap();
14728 let proof_packets: Vec<_> = sent_packets
14729 .iter()
14730 .filter(|raw| {
14731 let flags = PacketFlags::unpack(raw[0] & 0x7F);
14732 flags.packet_type == constants::PACKET_TYPE_PROOF
14733 })
14734 .collect();
14735 assert!(!proof_packets.is_empty(), "Should have sent a proof packet");
14736
14737 let proof_raw = proof_packets[0].clone();
14748 drop(sent_packets); let (tx2, rx2) = event::channel();
14752 let proofs2 = Arc::new(Mutex::new(Vec::new()));
14753 let cbs2 = MockCallbacks {
14754 announces: Arc::new(Mutex::new(Vec::new())),
14755 paths: Arc::new(Mutex::new(Vec::new())),
14756 deliveries: Arc::new(Mutex::new(Vec::new())),
14757 iface_ups: Arc::new(Mutex::new(Vec::new())),
14758 iface_downs: Arc::new(Mutex::new(Vec::new())),
14759 link_established: Arc::new(Mutex::new(Vec::new())),
14760 link_closed: Arc::new(Mutex::new(Vec::new())),
14761 remote_identified: Arc::new(Mutex::new(Vec::new())),
14762 resources_received: Arc::new(Mutex::new(Vec::new())),
14763 resource_completed: Arc::new(Mutex::new(Vec::new())),
14764 resource_failed: Arc::new(Mutex::new(Vec::new())),
14765 channel_messages: Arc::new(Mutex::new(Vec::new())),
14766 link_data: Arc::new(Mutex::new(Vec::new())),
14767 responses: Arc::new(Mutex::new(Vec::new())),
14768 proofs: proofs2.clone(),
14769 proof_requested: Arc::new(Mutex::new(Vec::new())),
14770 };
14771 let mut driver2 = Driver::new(
14772 TransportConfig {
14773 transport_enabled: false,
14774 identity_hash: None,
14775 prefer_shorter_path: false,
14776 max_paths_per_destination: 1,
14777 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14778 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14779 max_path_destinations: usize::MAX,
14780 max_tunnel_destinations_total: usize::MAX,
14781 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14782 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14783 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14784 announce_sig_cache_enabled: true,
14785 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14786 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14787 announce_queue_max_entries: 256,
14788 announce_queue_max_interfaces: 1024,
14789 },
14790 rx2,
14791 tx2.clone(),
14792 Box::new(cbs2),
14793 );
14794 let info2 = make_interface_info(1);
14795 driver2.engine.register_interface(info2);
14796 let (writer2, _sent2) = MockWriter::new();
14797 driver2
14798 .interfaces
14799 .insert(InterfaceId(1), make_entry(1, Box::new(writer2), true));
14800
14801 driver2
14803 .sent_packets
14804 .insert(data_packet_hash, (dest, time::now()));
14805
14806 tx2.send(Event::Frame {
14808 interface_id: InterfaceId(1),
14809 data: proof_raw,
14810 })
14811 .unwrap();
14812 tx2.send(Event::Shutdown).unwrap();
14813 driver2.run();
14814
14815 let proof_events = proofs2.lock().unwrap();
14817 assert_eq!(proof_events.len(), 1, "on_proof callback should fire once");
14818 assert_eq!(
14819 proof_events[0].1 .0, data_packet_hash,
14820 "proof should match original packet hash"
14821 );
14822 assert!(proof_events[0].2 >= 0.0, "RTT should be non-negative");
14823
14824 assert!(
14826 driver2.completed_proofs.contains_key(&data_packet_hash),
14827 "completed_proofs should contain the packet hash"
14828 );
14829 let (rtt, _received) = driver2.completed_proofs[&data_packet_hash];
14830 assert!(rtt >= 0.0, "RTT should be non-negative");
14831 }
14832
14833 #[test]
14834 fn interface_stats_includes_probe_responder() {
14835 let (tx, rx) = event::channel();
14836 let (cbs, _, _, _, _, _) = MockCallbacks::new();
14837 let mut driver = Driver::new(
14838 TransportConfig {
14839 transport_enabled: true,
14840 identity_hash: Some([0x42; 16]),
14841 prefer_shorter_path: false,
14842 max_paths_per_destination: 1,
14843 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14844 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14845 max_path_destinations: usize::MAX,
14846 max_tunnel_destinations_total: usize::MAX,
14847 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14848 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14849 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14850 announce_sig_cache_enabled: true,
14851 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14852 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14853 announce_queue_max_entries: 256,
14854 announce_queue_max_interfaces: 1024,
14855 },
14856 rx,
14857 tx.clone(),
14858 Box::new(cbs),
14859 );
14860 let (writer, _sent) = MockWriter::new();
14861 driver
14862 .interfaces
14863 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14864
14865 driver.probe_responder_hash = Some([0xEE; 16]);
14867
14868 let (resp_tx, resp_rx) = mpsc::channel();
14869 tx.send(Event::Query(QueryRequest::InterfaceStats, resp_tx))
14870 .unwrap();
14871 tx.send(Event::Shutdown).unwrap();
14872 driver.run();
14873
14874 match resp_rx.recv().unwrap() {
14875 QueryResponse::InterfaceStats(stats) => {
14876 assert_eq!(stats.probe_responder, Some([0xEE; 16]));
14877 }
14878 other => panic!("expected InterfaceStats, got {:?}", other),
14879 }
14880 }
14881
14882 #[test]
14883 fn interface_stats_probe_responder_none_when_disabled() {
14884 let (tx, rx) = event::channel();
14885 let (cbs, _, _, _, _, _) = MockCallbacks::new();
14886 let mut driver = Driver::new(
14887 TransportConfig {
14888 transport_enabled: false,
14889 identity_hash: None,
14890 prefer_shorter_path: false,
14891 max_paths_per_destination: 1,
14892 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14893 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14894 max_path_destinations: usize::MAX,
14895 max_tunnel_destinations_total: usize::MAX,
14896 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14897 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14898 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14899 announce_sig_cache_enabled: true,
14900 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14901 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14902 announce_queue_max_entries: 256,
14903 announce_queue_max_interfaces: 1024,
14904 },
14905 rx,
14906 tx.clone(),
14907 Box::new(cbs),
14908 );
14909 let (writer, _sent) = MockWriter::new();
14910 driver
14911 .interfaces
14912 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14913
14914 let (resp_tx, resp_rx) = mpsc::channel();
14915 tx.send(Event::Query(QueryRequest::InterfaceStats, resp_tx))
14916 .unwrap();
14917 tx.send(Event::Shutdown).unwrap();
14918 driver.run();
14919
14920 match resp_rx.recv().unwrap() {
14921 QueryResponse::InterfaceStats(stats) => {
14922 assert_eq!(stats.probe_responder, None);
14923 }
14924 other => panic!("expected InterfaceStats, got {:?}", other),
14925 }
14926 }
14927
14928 #[test]
14929 fn test_extract_dest_hash_too_short() {
14930 assert_eq!(super::extract_dest_hash(&[0x00, 0x00, 0xAA]), [0u8; 16]);
14932 }
14933
14934 #[test]
14935 fn test_extract_dest_hash_header1() {
14936 let mut raw = vec![0x00, 0x00]; let dest = [0x11; 16];
14939 raw.extend_from_slice(&dest);
14940 raw.extend_from_slice(&[0xFF; 10]); assert_eq!(super::extract_dest_hash(&raw), dest);
14942 }
14943
14944 #[test]
14945 fn test_extract_dest_hash_header2() {
14946 let mut raw = vec![0x40, 0x00]; raw.extend_from_slice(&[0xAA; 16]); let dest = [0x22; 16];
14950 raw.extend_from_slice(&dest); raw.extend_from_slice(&[0xFF; 10]); assert_eq!(super::extract_dest_hash(&raw), dest);
14953 }
14954
14955 #[test]
14956 fn test_extract_dest_hash_header2_too_short() {
14957 let mut raw = vec![0x40, 0x00];
14959 raw.extend_from_slice(&[0xAA; 16]); assert_eq!(super::extract_dest_hash(&raw), [0u8; 16]);
14961 }
14962
14963 #[test]
14964 fn announce_stores_receiving_interface_in_known_destinations() {
14965 let (tx, rx) = event::channel();
14968 let (cbs, _, _, _, _, _) = MockCallbacks::new();
14969 let mut driver = Driver::new(
14970 TransportConfig {
14971 transport_enabled: false,
14972 identity_hash: None,
14973 prefer_shorter_path: false,
14974 max_paths_per_destination: 1,
14975 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
14976 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
14977 max_path_destinations: usize::MAX,
14978 max_tunnel_destinations_total: usize::MAX,
14979 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
14980 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
14981 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
14982 announce_sig_cache_enabled: true,
14983 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
14984 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
14985 announce_queue_max_entries: 256,
14986 announce_queue_max_interfaces: 1024,
14987 },
14988 rx,
14989 tx.clone(),
14990 Box::new(cbs),
14991 );
14992 let info = make_interface_info(1);
14993 driver.engine.register_interface(info);
14994 let (writer, _sent) = MockWriter::new();
14995 driver
14996 .interfaces
14997 .insert(InterfaceId(1), make_entry(1, Box::new(writer), true));
14998
14999 let identity = Identity::new(&mut OsRng);
15000 let announce_raw = build_announce_packet(&identity);
15001
15002 tx.send(Event::Frame {
15003 interface_id: InterfaceId(1),
15004 data: announce_raw,
15005 })
15006 .unwrap();
15007 tx.send(Event::Shutdown).unwrap();
15008 driver.run();
15009
15010 assert_eq!(driver.known_destinations.len(), 1);
15012 let (_, announced) = driver.known_destinations.iter().next().unwrap();
15013 assert_eq!(
15014 announced.receiving_interface,
15015 InterfaceId(1),
15016 "receiving_interface should match the interface the announce arrived on"
15017 );
15018 }
15019
15020 #[test]
15021 fn announce_on_different_interfaces_stores_correct_id() {
15022 let (tx, rx) = event::channel();
15024 let (cbs, _, _, _, _, _) = MockCallbacks::new();
15025 let mut driver = Driver::new(
15026 TransportConfig {
15027 transport_enabled: false,
15028 identity_hash: None,
15029 prefer_shorter_path: false,
15030 max_paths_per_destination: 1,
15031 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15032 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15033 max_path_destinations: usize::MAX,
15034 max_tunnel_destinations_total: usize::MAX,
15035 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15036 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15037 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15038 announce_sig_cache_enabled: true,
15039 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15040 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15041 announce_queue_max_entries: 256,
15042 announce_queue_max_interfaces: 1024,
15043 },
15044 rx,
15045 tx.clone(),
15046 Box::new(cbs),
15047 );
15048 for id in [1, 2] {
15050 driver.engine.register_interface(make_interface_info(id));
15051 let (writer, _) = MockWriter::new();
15052 driver
15053 .interfaces
15054 .insert(InterfaceId(id), make_entry(id, Box::new(writer), true));
15055 }
15056
15057 let identity = Identity::new(&mut OsRng);
15058 let announce_raw = build_announce_packet(&identity);
15059
15060 tx.send(Event::Frame {
15062 interface_id: InterfaceId(2),
15063 data: announce_raw,
15064 })
15065 .unwrap();
15066 tx.send(Event::Shutdown).unwrap();
15067 driver.run();
15068
15069 assert_eq!(driver.known_destinations.len(), 1);
15070 let (_, announced) = driver.known_destinations.iter().next().unwrap();
15071 assert_eq!(announced.receiving_interface, InterfaceId(2));
15072 }
15073
15074 #[test]
15075 fn inject_identity_stores_sentinel_interface() {
15076 let (tx, rx) = event::channel();
15079 let (cbs, _, _, _, _, _) = MockCallbacks::new();
15080 let mut driver = Driver::new(
15081 TransportConfig {
15082 transport_enabled: false,
15083 identity_hash: None,
15084 prefer_shorter_path: false,
15085 max_paths_per_destination: 1,
15086 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15087 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15088 max_path_destinations: usize::MAX,
15089 max_tunnel_destinations_total: usize::MAX,
15090 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15091 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15092 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15093 announce_sig_cache_enabled: true,
15094 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15095 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15096 announce_queue_max_entries: 256,
15097 announce_queue_max_interfaces: 1024,
15098 },
15099 rx,
15100 tx.clone(),
15101 Box::new(cbs),
15102 );
15103
15104 let identity = Identity::new(&mut OsRng);
15105 let dest_hash =
15106 rns_core::destination::destination_hash("test", &["app"], Some(identity.hash()));
15107
15108 let (resp_tx, resp_rx) = mpsc::channel();
15109 tx.send(Event::Query(
15110 QueryRequest::InjectIdentity {
15111 dest_hash,
15112 identity_hash: *identity.hash(),
15113 public_key: identity.get_public_key().unwrap(),
15114 app_data: Some(b"restored".to_vec()),
15115 hops: 2,
15116 received_at: 99.0,
15117 },
15118 resp_tx,
15119 ))
15120 .unwrap();
15121 tx.send(Event::Shutdown).unwrap();
15122 driver.run();
15123
15124 match resp_rx.recv().unwrap() {
15125 QueryResponse::InjectIdentity(true) => {}
15126 other => panic!("expected InjectIdentity(true), got {:?}", other),
15127 }
15128
15129 let announced = driver
15130 .known_destinations
15131 .get(&dest_hash)
15132 .expect("identity should be cached");
15133 assert_eq!(
15134 announced.receiving_interface,
15135 InterfaceId(0),
15136 "injected identity should have sentinel InterfaceId(0)"
15137 );
15138 assert_eq!(announced.dest_hash.0, dest_hash);
15139 assert_eq!(announced.identity_hash.0, *identity.hash());
15140 assert_eq!(announced.public_key, identity.get_public_key().unwrap());
15141 assert_eq!(announced.app_data, Some(b"restored".to_vec()));
15142 assert_eq!(announced.hops, 2);
15143 assert_eq!(announced.received_at, 99.0);
15144 }
15145
15146 #[test]
15147 fn inject_identity_overwrites_previous_entry() {
15148 let (tx, rx) = event::channel();
15150 let (cbs, _, _, _, _, _) = MockCallbacks::new();
15151 let mut driver = Driver::new(
15152 TransportConfig {
15153 transport_enabled: false,
15154 identity_hash: None,
15155 prefer_shorter_path: false,
15156 max_paths_per_destination: 1,
15157 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15158 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15159 max_path_destinations: usize::MAX,
15160 max_tunnel_destinations_total: usize::MAX,
15161 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15162 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15163 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15164 announce_sig_cache_enabled: true,
15165 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15166 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15167 announce_queue_max_entries: 256,
15168 announce_queue_max_interfaces: 1024,
15169 },
15170 rx,
15171 tx.clone(),
15172 Box::new(cbs),
15173 );
15174
15175 let identity = Identity::new(&mut OsRng);
15176 let dest_hash =
15177 rns_core::destination::destination_hash("test", &["app"], Some(identity.hash()));
15178
15179 let (resp_tx1, resp_rx1) = mpsc::channel();
15181 tx.send(Event::Query(
15182 QueryRequest::InjectIdentity {
15183 dest_hash,
15184 identity_hash: *identity.hash(),
15185 public_key: identity.get_public_key().unwrap(),
15186 app_data: Some(b"first".to_vec()),
15187 hops: 1,
15188 received_at: 10.0,
15189 },
15190 resp_tx1,
15191 ))
15192 .unwrap();
15193
15194 let (resp_tx2, resp_rx2) = mpsc::channel();
15196 tx.send(Event::Query(
15197 QueryRequest::InjectIdentity {
15198 dest_hash,
15199 identity_hash: *identity.hash(),
15200 public_key: identity.get_public_key().unwrap(),
15201 app_data: Some(b"second".to_vec()),
15202 hops: 3,
15203 received_at: 20.0,
15204 },
15205 resp_tx2,
15206 ))
15207 .unwrap();
15208
15209 tx.send(Event::Shutdown).unwrap();
15210 driver.run();
15211
15212 assert!(matches!(
15213 resp_rx1.recv().unwrap(),
15214 QueryResponse::InjectIdentity(true)
15215 ));
15216 assert!(matches!(
15217 resp_rx2.recv().unwrap(),
15218 QueryResponse::InjectIdentity(true)
15219 ));
15220
15221 let announced = driver.known_destinations.get(&dest_hash).unwrap();
15223 assert_eq!(announced.app_data, Some(b"second".to_vec()));
15224 assert_eq!(announced.hops, 3);
15225 assert_eq!(announced.received_at, 20.0);
15226 }
15227
15228 #[test]
15229 fn re_announce_updates_receiving_interface() {
15230 let (tx, rx) = event::channel();
15233 let (cbs, _, _, _, _, _) = MockCallbacks::new();
15234 let mut driver = Driver::new(
15235 TransportConfig {
15236 transport_enabled: false,
15237 identity_hash: None,
15238 prefer_shorter_path: false,
15239 max_paths_per_destination: 1,
15240 packet_hashlist_max_entries: rns_core::constants::HASHLIST_MAXSIZE,
15241 max_discovery_pr_tags: rns_core::constants::MAX_PR_TAGS,
15242 max_path_destinations: usize::MAX,
15243 max_tunnel_destinations_total: usize::MAX,
15244 destination_timeout_secs: rns_core::constants::DESTINATION_TIMEOUT,
15245 announce_table_ttl_secs: rns_core::constants::ANNOUNCE_TABLE_TTL,
15246 announce_table_max_bytes: rns_core::constants::ANNOUNCE_TABLE_MAX_BYTES,
15247 announce_sig_cache_enabled: true,
15248 announce_sig_cache_max_entries: rns_core::constants::ANNOUNCE_SIG_CACHE_MAXSIZE,
15249 announce_sig_cache_ttl_secs: rns_core::constants::ANNOUNCE_SIG_CACHE_TTL,
15250 announce_queue_max_entries: 256,
15251 announce_queue_max_interfaces: 1024,
15252 },
15253 rx,
15254 tx.clone(),
15255 Box::new(cbs),
15256 );
15257 for id in [1, 2] {
15258 driver.engine.register_interface(make_interface_info(id));
15259 let (writer, _) = MockWriter::new();
15260 driver
15261 .interfaces
15262 .insert(InterfaceId(id), make_entry(id, Box::new(writer), true));
15263 }
15264
15265 let identity = Identity::new(&mut OsRng);
15266 let announce_raw = build_announce_packet(&identity);
15267
15268 tx.send(Event::Frame {
15270 interface_id: InterfaceId(1),
15271 data: announce_raw.clone(),
15272 })
15273 .unwrap();
15274 let identity2 = Identity::new(&mut OsRng);
15278 let announce_raw2 = build_announce_packet(&identity2);
15279 tx.send(Event::Frame {
15280 interface_id: InterfaceId(2),
15281 data: announce_raw2,
15282 })
15283 .unwrap();
15284 tx.send(Event::Shutdown).unwrap();
15285 driver.run();
15286
15287 assert_eq!(driver.known_destinations.len(), 2);
15289 for (_, announced) in &driver.known_destinations {
15290 assert!(
15292 announced.receiving_interface == InterfaceId(1)
15293 || announced.receiving_interface == InterfaceId(2)
15294 );
15295 }
15296 let ifaces: Vec<_> = driver
15298 .known_destinations
15299 .values()
15300 .map(|a| a.receiving_interface)
15301 .collect();
15302 assert!(ifaces.contains(&InterfaceId(1)));
15303 assert!(ifaces.contains(&InterfaceId(2)));
15304 }
15305
15306 #[test]
15307 fn test_extract_dest_hash_other_flags_preserved() {
15308 let mut raw = vec![0x3F, 0x00];
15311 let dest = [0x33; 16];
15312 raw.extend_from_slice(&dest);
15313 raw.extend_from_slice(&[0xFF; 10]);
15314 assert_eq!(super::extract_dest_hash(&raw), dest);
15315
15316 let mut raw2 = vec![0xFF, 0x00];
15318 raw2.extend_from_slice(&[0xBB; 16]); let dest2 = [0x44; 16];
15320 raw2.extend_from_slice(&dest2);
15321 raw2.extend_from_slice(&[0xFF; 10]);
15322 assert_eq!(super::extract_dest_hash(&raw2), dest2);
15323 }
15324}