1use std::collections::{HashMap, HashSet};
4use std::io;
5use std::sync::atomic::{AtomicU64, Ordering};
6use std::sync::{Arc, Mutex};
7use std::time::{Duration, Instant};
8
9type MemoryRollup = (f64, f64, f64, f64, f64, f64, f64, f64);
10#[cfg(feature = "iface-backbone")]
11type BackboneCandidateOrder = (
12 std::cmp::Reverse<u8>,
13 u8,
14 u8,
15 u8,
16 std::cmp::Reverse<u32>,
17 std::cmp::Reverse<u64>,
18 [u8; 32],
19);
20
21use rns_core::packet::RawPacket;
22use rns_core::transport::announce_verify_queue::{AnnounceVerifyQueue, OverflowPolicy};
23use rns_core::transport::tables::PathEntry;
24use rns_core::transport::types::{InterfaceId, TransportAction, TransportConfig};
25use rns_core::transport::TransportEngine;
26use rns_crypto::{OsRng, Rng};
27
28#[cfg(feature = "hooks")]
29use crate::provider_bridge::ProviderBridge;
30#[cfg(feature = "hooks")]
31use rns_hooks::{create_hook_slots, EngineAccess, HookContext, HookManager, HookPoint, HookSlot};
32
33#[cfg(feature = "hooks")]
34use crate::event::BackbonePeerHookEvent;
35use crate::event::{
36 BackbonePeerPoolMemberStatus, BackbonePeerPoolStatus, BackbonePeerStateEntry, BlackholeInfo,
37 DrainStatus, Event, EventReceiver, InterfaceStatsResponse, KnownDestinationEntry,
38 LifecycleState, LocalDestinationEntry, NextHopResponse, PathTableEntry, QueryRequest,
39 QueryResponse, RateTableEntry, RuntimeConfigApplyMode, RuntimeConfigEntry, RuntimeConfigError,
40 RuntimeConfigErrorCode, RuntimeConfigSource, RuntimeConfigValue, SingleInterfaceStat,
41 TrafficDetail,
42};
43use crate::holepunch::orchestrator::{HolePunchManager, HolePunchManagerAction};
44use crate::ifac;
45#[cfg(all(feature = "iface-auto", test))]
46use crate::interface::auto::AutoRuntime;
47#[cfg(feature = "iface-auto")]
48use crate::interface::auto::AutoRuntimeConfigHandle;
49#[cfg(feature = "iface-backbone")]
50use crate::interface::backbone::{
51 start_client_with_ifac, BackboneClientConfig, BackboneClientRuntime,
52 BackboneClientRuntimeConfigHandle, BackbonePeerStateHandle, BackboneRuntimeConfigHandle,
53};
54#[cfg(all(feature = "iface-backbone", target_os = "linux", test))]
55use crate::interface::backbone::{BackboneAbuseConfig, BackboneServerRuntime};
56#[cfg(all(feature = "iface-i2p", test))]
57use crate::interface::i2p::I2pRuntime;
58#[cfg(feature = "iface-i2p")]
59use crate::interface::i2p::I2pRuntimeConfigHandle;
60#[cfg(all(feature = "iface-pipe", test))]
61use crate::interface::pipe::PipeRuntime;
62#[cfg(feature = "iface-pipe")]
63use crate::interface::pipe::PipeRuntimeConfigHandle;
64#[cfg(feature = "iface-rnode")]
65use crate::interface::rnode::{
66 validate_sub_config, RNodeRuntime, RNodeRuntimeConfigHandle, RNodeSubConfig,
67};
68#[cfg(feature = "iface-tcp")]
69use crate::interface::tcp::TcpClientRuntimeConfigHandle;
70#[cfg(all(feature = "iface-tcp", test))]
71use crate::interface::tcp_server::TcpServerRuntime;
72#[cfg(feature = "iface-tcp")]
73use crate::interface::tcp_server::TcpServerRuntimeConfigHandle;
74#[cfg(all(feature = "iface-udp", test))]
75use crate::interface::udp::UdpRuntime;
76#[cfg(feature = "iface-udp")]
77use crate::interface::udp::UdpRuntimeConfigHandle;
78use crate::interface::{InterfaceEntry, InterfaceStats};
79use crate::link_manager::{LinkManager, LinkManagerAction};
80use crate::time;
81
82const DEFAULT_KNOWN_DESTINATIONS_TTL: f64 = 48.0 * 60.0 * 60.0;
83const DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES: usize = 8192;
84const DEFAULT_RATE_LIMITER_TTL_SECS: f64 = 48.0 * 60.0 * 60.0;
85const DEFAULT_TICK_INTERVAL_MS: u64 = 1000;
86const DEFAULT_KNOWN_DESTINATIONS_CLEANUP_INTERVAL_TICKS: u32 = 3600;
87const KNOWN_DESTINATION_CLEANUP_BATCH_SIZE: usize = 64;
88const DEFAULT_ANNOUNCE_CACHE_CLEANUP_INTERVAL_TICKS: u32 = 3600;
89const DEFAULT_ANNOUNCE_CACHE_CLEANUP_BATCH_SIZE: usize = 10_000;
90const DEFAULT_DISCOVERY_CLEANUP_INTERVAL_TICKS: u32 = 3600;
91const DEFAULT_MANAGEMENT_ANNOUNCE_INTERVAL_SECS: f64 = 3600.0;
92const DEFAULT_LINK_TEARDOWN_FLUSH: Duration = Duration::from_millis(150);
93const SEND_RETRY_BACKOFF_MIN: Duration = Duration::from_millis(25);
94const SEND_RETRY_BACKOFF_MAX: Duration = Duration::from_millis(1000);
95
96#[derive(Debug, Clone, Copy, PartialEq)]
98pub struct AnnounceRateDefaults {
99 pub target: Option<f64>,
100 pub penalty: f64,
101 pub grace: u32,
102}
103
104impl Default for AnnounceRateDefaults {
105 fn default() -> Self {
106 Self {
107 target: Some(3600.0),
108 penalty: 0.0,
109 grace: 5,
110 }
111 }
112}
113
114mod dispatch;
115mod events;
116mod interface_management;
117mod lifecycle;
118mod queries;
119mod runtime_config;
120
121#[cfg(test)]
122mod tests;
123
124fn inject_transport_header(raw: &[u8], next_hop: &[u8; 16]) -> Vec<u8> {
125 if raw.len() < 18 {
126 return raw.to_vec();
127 }
128
129 let new_flags = (rns_core::constants::HEADER_2 << 6)
130 | (rns_core::constants::TRANSPORT_TRANSPORT << 4)
131 | (raw[0] & 0x0F);
132
133 let mut new_raw = Vec::with_capacity(raw.len() + 16);
134 new_raw.push(new_flags);
135 new_raw.push(raw[1]);
136 new_raw.extend_from_slice(next_hop);
137 new_raw.extend_from_slice(&raw[2..]);
138 new_raw
139}
140
141fn recover_mutex_guard<'a, T>(mutex: &'a Mutex<T>, label: &str) -> std::sync::MutexGuard<'a, T> {
142 match mutex.lock() {
143 Ok(guard) => guard,
144 Err(poisoned) => {
145 log::error!("recovering from poisoned mutex: {}", label);
146 poisoned.into_inner()
147 }
148 }
149}
150
151#[derive(Debug, Clone, Copy)]
152pub(crate) struct RuntimeConfigDefaults {
153 pub(crate) tick_interval_ms: u64,
154 pub(crate) known_destinations_ttl: f64,
155 pub(crate) rate_limiter_ttl_secs: f64,
156 pub(crate) known_destinations_cleanup_interval_ticks: u32,
157 pub(crate) announce_cache_cleanup_interval_ticks: u32,
158 pub(crate) announce_cache_cleanup_batch_size: usize,
159 pub(crate) discovery_cleanup_interval_ticks: u32,
160 pub(crate) management_announce_interval_secs: f64,
161 pub(crate) direct_connect_policy: crate::event::HolePunchPolicy,
162 #[cfg(feature = "hooks")]
163 pub(crate) provider_queue_max_events: usize,
164 #[cfg(feature = "hooks")]
165 pub(crate) provider_queue_max_bytes: usize,
166}
167
168#[cfg(feature = "iface-backbone")]
169#[derive(Debug, Clone)]
170pub(crate) struct BackboneDiscoveryRuntime {
171 pub(crate) discoverable: bool,
172 pub(crate) config: crate::discovery::DiscoveryConfig,
173 pub(crate) transport_enabled: bool,
174 pub(crate) ifac_netname: Option<String>,
175 pub(crate) ifac_netkey: Option<String>,
176}
177
178#[cfg(feature = "iface-backbone")]
179#[derive(Debug, Clone)]
180pub(crate) struct BackboneDiscoveryRuntimeHandle {
181 pub(crate) interface_name: String,
182 pub(crate) current: BackboneDiscoveryRuntime,
183 pub(crate) startup: BackboneDiscoveryRuntime,
184}
185
186#[cfg(feature = "iface-tcp")]
187#[derive(Debug, Clone)]
188pub(crate) struct TcpServerDiscoveryRuntime {
189 pub(crate) discoverable: bool,
190 pub(crate) config: crate::discovery::DiscoveryConfig,
191 pub(crate) transport_enabled: bool,
192 pub(crate) ifac_netname: Option<String>,
193 pub(crate) ifac_netkey: Option<String>,
194}
195
196#[cfg(feature = "iface-tcp")]
197#[derive(Debug, Clone)]
198pub(crate) struct TcpServerDiscoveryRuntimeHandle {
199 pub(crate) interface_name: String,
200 pub(crate) current: TcpServerDiscoveryRuntime,
201 pub(crate) startup: TcpServerDiscoveryRuntime,
202}
203
204#[derive(Debug, Clone, PartialEq, Eq)]
205pub(crate) struct IfacRuntimeConfig {
206 pub(crate) netname: Option<String>,
207 pub(crate) netkey: Option<String>,
208 pub(crate) size: usize,
209}
210
211#[cfg(feature = "iface-backbone")]
212impl BackboneDiscoveryRuntimeHandle {
213 pub(crate) fn from_parts(
214 interface_name: String,
215 startup_config: crate::discovery::DiscoveryConfig,
216 transport_enabled: bool,
217 ifac_netname: Option<String>,
218 ifac_netkey: Option<String>,
219 discoverable: bool,
220 ) -> Self {
221 let startup = BackboneDiscoveryRuntime {
222 discoverable,
223 config: startup_config,
224 transport_enabled,
225 ifac_netname,
226 ifac_netkey,
227 };
228 Self {
229 interface_name,
230 current: startup.clone(),
231 startup,
232 }
233 }
234}
235
236#[cfg(feature = "iface-tcp")]
237impl TcpServerDiscoveryRuntimeHandle {
238 pub(crate) fn from_parts(
239 interface_name: String,
240 startup_config: crate::discovery::DiscoveryConfig,
241 transport_enabled: bool,
242 ifac_netname: Option<String>,
243 ifac_netkey: Option<String>,
244 discoverable: bool,
245 ) -> Self {
246 let startup = TcpServerDiscoveryRuntime {
247 discoverable,
248 config: startup_config,
249 transport_enabled,
250 ifac_netname,
251 ifac_netkey,
252 };
253 Self {
254 interface_name,
255 current: startup.clone(),
256 startup,
257 }
258 }
259}
260
261impl IfacRuntimeConfig {
262 pub(crate) fn from_parts(netname: Option<String>, netkey: Option<String>, size: usize) -> Self {
263 Self {
264 netname,
265 netkey,
266 size,
267 }
268 }
269}
270
271#[cfg(feature = "iface-backbone")]
272#[derive(Debug, Clone)]
273pub struct BackbonePeerPoolSettings {
274 pub max_connected: usize,
275 pub failure_threshold: usize,
276 pub failure_window: Duration,
277 pub cooldown: Duration,
278}
279
280#[cfg(feature = "iface-backbone")]
281pub(crate) const BACKBONE_PEER_POOL_CONFIGURED_DEFAULT_PRIORITY: u8 = 60;
282#[cfg(feature = "iface-backbone")]
283pub(crate) const BACKBONE_PEER_POOL_DISCOVERED_PRIORITY: u8 = 40;
284
285#[cfg(feature = "iface-backbone")]
286pub(crate) struct BackbonePeerPoolCandidateConfig {
287 pub(crate) client: BackboneClientConfig,
288 pub(crate) mode: u8,
289 pub(crate) gravity: i64,
290 pub(crate) recursive_prs: bool,
291 pub(crate) announces_from_internal: bool,
292 pub(crate) announces_to_internal: Option<bool>,
293 pub(crate) ingress_control: rns_core::transport::types::IngressControlConfig,
294 pub(crate) ifac_runtime: IfacRuntimeConfig,
295 pub(crate) ifac_enabled: bool,
296 pub(crate) interface_type_name: String,
297 pub(crate) source: BackbonePeerPoolCandidateSource,
298 pub(crate) priority: u8,
299 pub(crate) discovery: Option<BackbonePeerPoolDiscoveryCandidate>,
300}
301
302#[cfg(feature = "iface-backbone")]
303#[derive(Debug, Clone, Copy, PartialEq, Eq)]
304pub(crate) enum BackbonePeerPoolCandidateSource {
305 Configured,
306 Discovered,
307}
308
309#[cfg(feature = "iface-backbone")]
310impl BackbonePeerPoolCandidateSource {
311 fn as_str(self) -> &'static str {
312 match self {
313 Self::Configured => "configured",
314 Self::Discovered => "discovered",
315 }
316 }
317}
318
319#[cfg(feature = "iface-backbone")]
320#[derive(Debug, Clone)]
321pub(crate) struct BackbonePeerPoolDiscoveryCandidate {
322 pub(crate) discovery_hash: [u8; 32],
323 pub(crate) status: crate::discovery::DiscoveredStatus,
324 pub(crate) hops: u8,
325 pub(crate) stamp_value: u32,
326 pub(crate) last_heard: f64,
327}
328
329#[cfg(feature = "iface-backbone")]
330struct BackbonePeerPool {
331 settings: BackbonePeerPoolSettings,
332 candidates: Vec<BackbonePeerPoolCandidate>,
333}
334
335#[cfg(feature = "iface-backbone")]
336struct BackbonePeerPoolCandidate {
337 config: BackbonePeerPoolCandidateConfig,
338 active_id: Option<InterfaceId>,
339 failures: Vec<f64>,
340 retry_after: Option<f64>,
341 cooldown_until: Option<f64>,
342 last_error: Option<String>,
343}
344
345#[cfg(feature = "hooks")]
347struct EngineRef<'a> {
348 engine: &'a TransportEngine,
349 interfaces: &'a HashMap<InterfaceId, InterfaceEntry>,
350 link_manager: &'a LinkManager,
351 now: f64,
352}
353
354#[cfg(feature = "hooks")]
355impl<'a> EngineAccess for EngineRef<'a> {
356 fn has_path(&self, dest: &[u8; 16]) -> bool {
357 self.engine.has_path(dest)
358 }
359 fn hops_to(&self, dest: &[u8; 16]) -> Option<u8> {
360 self.engine.hops_to(dest)
361 }
362 fn next_hop(&self, dest: &[u8; 16]) -> Option<[u8; 16]> {
363 self.engine.next_hop(dest)
364 }
365 fn is_blackholed(&self, identity: &[u8; 16]) -> bool {
366 self.engine.is_blackholed(identity, self.now)
367 }
368 fn interface_name(&self, id: u64) -> Option<String> {
369 self.interfaces
370 .get(&InterfaceId(id))
371 .map(|e| e.info.name.clone())
372 }
373 fn interface_mode(&self, id: u64) -> Option<u8> {
374 self.interfaces.get(&InterfaceId(id)).map(|e| e.info.mode)
375 }
376 fn identity_hash(&self) -> Option<[u8; 16]> {
377 self.engine.identity_hash().copied()
378 }
379 fn announce_rate(&self, id: u64) -> Option<i32> {
380 self.interfaces
381 .get(&InterfaceId(id))
382 .map(|e| (e.stats.outgoing_announce_freq() * 1000.0) as i32)
383 }
384 fn link_state(&self, link_hash: &[u8; 16]) -> Option<u8> {
385 use rns_core::link::types::LinkState;
386 self.link_manager.link_state(link_hash).map(|s| match s {
387 LinkState::Pending => 0,
388 LinkState::Handshake => 1,
389 LinkState::Active => 2,
390 LinkState::Stale => 3,
391 LinkState::Closed => 4,
392 })
393 }
394}
395
396#[cfg(any(test, feature = "hooks"))]
401fn extract_dest_hash(raw: &[u8]) -> [u8; 16] {
402 let mut dest = [0u8; 16];
403 if raw.is_empty() {
404 return dest;
405 }
406 let is_header2 = raw[0] & 0x40 != 0;
407 let start = if is_header2 { 18 } else { 2 };
408 let end = start + 16;
409 if raw.len() >= end {
410 dest.copy_from_slice(&raw[start..end]);
411 }
412 dest
413}
414
415#[cfg(feature = "hooks")]
417fn run_hook_inner(
418 programs: &mut [rns_hooks::LoadedProgram],
419 hook_manager: &Option<HookManager>,
420 engine_access: &dyn EngineAccess,
421 ctx: &HookContext,
422 now: f64,
423 provider_events_enabled: bool,
424) -> Option<rns_hooks::ExecuteResult> {
425 if programs.is_empty() {
426 return None;
427 }
428 let mgr = hook_manager.as_ref()?;
429 mgr.run_chain_with_provider_events(programs, ctx, engine_access, now, provider_events_enabled)
430}
431
432#[cfg(feature = "hooks")]
433fn backbone_peer_hook_context(event: &BackbonePeerHookEvent) -> HookContext<'_> {
434 HookContext::BackbonePeer {
435 server_interface_id: event.server_interface_id.0,
436 peer_interface_id: event.peer_interface_id.map(|id| id.0),
437 peer_ip: event.peer_ip,
438 peer_port: event.peer_port,
439 connected_for: event.connected_for,
440 had_received_data: event.had_received_data,
441 penalty_level: event.penalty_level,
442 blacklist_for: event.blacklist_for,
443 }
444}
445
446#[cfg(feature = "hooks")]
448fn convert_injected_actions(actions: Vec<rns_hooks::ActionWire>) -> Vec<TransportAction> {
449 actions
450 .into_iter()
451 .map(|a| {
452 use rns_hooks::ActionWire;
453 match a {
454 ActionWire::SendOnInterface { interface, raw } => {
455 TransportAction::SendOnInterface {
456 interface: InterfaceId(interface),
457 raw: raw.into(),
458 }
459 }
460 ActionWire::BroadcastOnAllInterfaces {
461 raw,
462 exclude,
463 has_exclude,
464 } => TransportAction::BroadcastOnAllInterfaces {
465 raw: raw.into(),
466 exclude: if has_exclude != 0 {
467 Some(InterfaceId(exclude))
468 } else {
469 None
470 },
471 },
472 ActionWire::DeliverLocal {
473 destination_hash,
474 raw,
475 packet_hash,
476 receiving_interface,
477 } => TransportAction::DeliverLocal {
478 destination_hash,
479 raw: raw.into(),
480 packet_hash,
481 receiving_interface: InterfaceId(receiving_interface),
482 },
483 ActionWire::PathUpdated {
484 destination_hash,
485 hops,
486 next_hop,
487 interface,
488 } => TransportAction::PathUpdated {
489 destination_hash,
490 hops,
491 next_hop,
492 interface: InterfaceId(interface),
493 },
494 ActionWire::CacheAnnounce { packet_hash, raw } => TransportAction::CacheAnnounce {
495 packet_hash,
496 raw: raw.into(),
497 },
498 ActionWire::TunnelEstablished {
499 tunnel_id,
500 interface,
501 } => TransportAction::TunnelEstablished {
502 tunnel_id,
503 interface: InterfaceId(interface),
504 },
505 ActionWire::TunnelSynthesize {
506 interface,
507 data,
508 dest_hash,
509 } => TransportAction::TunnelSynthesize {
510 interface: InterfaceId(interface),
511 data,
512 dest_hash,
513 },
514 ActionWire::ForwardToLocalClients {
515 raw,
516 exclude,
517 has_exclude,
518 } => TransportAction::ForwardToLocalClients {
519 raw: raw.into(),
520 exclude: if has_exclude != 0 {
521 Some(InterfaceId(exclude))
522 } else {
523 None
524 },
525 },
526 ActionWire::ForwardPlainBroadcast {
527 raw,
528 to_local,
529 exclude,
530 has_exclude,
531 } => TransportAction::ForwardPlainBroadcast {
532 raw: raw.into(),
533 to_local: to_local != 0,
534 exclude: if has_exclude != 0 {
535 Some(InterfaceId(exclude))
536 } else {
537 None
538 },
539 },
540 ActionWire::AnnounceReceived {
541 destination_hash,
542 identity_hash,
543 public_key,
544 name_hash,
545 random_hash,
546 app_data,
547 hops,
548 receiving_interface,
549 } => TransportAction::AnnounceReceived {
550 destination_hash,
551 identity_hash,
552 public_key,
553 name_hash,
554 random_hash,
555 ratchet: None,
556 app_data,
557 hops,
558 receiving_interface: InterfaceId(receiving_interface),
559 rx: rns_core::transport::RxMetadata::default(),
560 },
561 }
562 })
563 .collect()
564}
565
566fn infer_interface_type(name: &str) -> String {
570 if name.starts_with("TCPServerInterface") {
571 "TCPServerClientInterface".to_string()
572 } else if name.starts_with("BackboneInterface") {
573 "BackboneInterface".to_string()
574 } else if name.starts_with("LocalInterface") {
575 "LocalServerClientInterface".to_string()
576 } else if name.starts_with("WeaveInterfacePeer[") {
577 "WeaveInterfacePeer".to_string()
578 } else {
579 "AutoInterface".to_string()
582 }
583}
584
585pub use crate::common::callbacks::Callbacks;
586
587#[derive(Clone)]
588struct SharedAnnounceRecord {
589 name_hash: [u8; 10],
590 identity_prv_key: [u8; 64],
591 app_data: Option<Vec<u8>>,
592}
593
594#[derive(Debug, Clone)]
595pub(crate) struct KnownDestinationState {
596 announced: crate::destination::AnnouncedIdentity,
597 was_used: bool,
598 last_used_at: Option<f64>,
599 retained: bool,
600}
601
602struct KnownDestinationCleanup {
603 candidates: Vec<[u8; 16]>,
604 cursor: usize,
605 started_at: Instant,
606 removed: usize,
607}
608
609#[derive(Debug, Clone, Copy)]
610pub(crate) struct TrafficSample {
611 sampled_at: f64,
612 rxb: u64,
613 txb: u64,
614 rx_packets: u64,
615 tx_packets: u64,
616 arxb: u64,
617 atxb: u64,
618 prxb: u64,
619 ptxb: u64,
620}
621
622impl TrafficSample {
623 fn from_stats(sampled_at: f64, stats: &crate::interface::InterfaceStats) -> Self {
624 Self {
625 sampled_at,
626 rxb: stats.rxb,
627 txb: stats.txb,
628 rx_packets: stats.rx_packets,
629 tx_packets: stats.tx_packets,
630 arxb: stats.arxb,
631 atxb: stats.atxb,
632 prxb: stats.prxb,
633 ptxb: stats.ptxb,
634 }
635 }
636}
637
638pub(crate) struct ManagedInterface {
640 pub(crate) parent_id: InterfaceId,
641 pub(crate) type_name: String,
642 pub(crate) control: Option<crate::interface::ListenerControl>,
643 pub(crate) static_ids: Vec<InterfaceId>,
644}
645
646pub struct Driver {
647 pub(crate) tracked_link_send: Option<([u8; 32], crate::link_send::Completion)>,
648 pub(crate) pending_link_frames: std::collections::VecDeque<PendingLinkFrame>,
649 pub(crate) engine: TransportEngine,
650 pub(crate) interfaces: HashMap<InterfaceId, InterfaceEntry>,
651 pub(crate) dynamic_interface_parents: HashMap<InterfaceId, InterfaceId>,
653 pub(crate) traffic_samples: HashMap<InterfaceId, TrafficSample>,
655 pub(crate) rng: OsRng,
656 pub(crate) rx: EventReceiver,
657 pub(crate) callbacks: Box<dyn Callbacks>,
658 pub(crate) started: f64,
659 pub(crate) lifecycle_state: LifecycleState,
660 pub(crate) drain_started_at: Option<Instant>,
661 pub(crate) drain_deadline: Option<Instant>,
662 pub(crate) listener_controls: Vec<crate::interface::ListenerControl>,
663 pub(crate) interface_management: Option<crate::node::InterfaceManagementConfig>,
664 pub(crate) managed_interfaces: HashMap<String, ManagedInterface>,
665 pub(crate) retired_interface_parents: HashSet<InterfaceId>,
666 pub(crate) announce_cache: Option<crate::announce_cache::AnnounceCache>,
667 pub(crate) tunnel_synth_dest: [u8; 16],
669 pub(crate) transport_identity: Option<rns_crypto::identity::Identity>,
671 pub(crate) link_manager: LinkManager,
673 pub(crate) management_config: crate::management::ManagementConfig,
675 pub(crate) last_management_announce: f64,
677 pub(crate) initial_announce_sent: bool,
679 pub(crate) known_destinations: HashMap<[u8; 16], KnownDestinationState>,
681 pub(crate) ratchet_store: Option<Arc<dyn crate::storage::RatchetStore>>,
683 pub(crate) ratchet_expiry_secs: f64,
685 pub(crate) known_destinations_ttl: f64,
687 pub(crate) known_destinations_max_entries: usize,
689 pub(crate) rate_limiter_ttl_secs: f64,
691 pub(crate) path_request_dest: [u8; 16],
693 pub(crate) proof_strategies: HashMap<
696 [u8; 16],
697 (
698 rns_core::types::ProofStrategy,
699 Option<rns_crypto::identity::Identity>,
700 ),
701 >,
702 pub(crate) sent_packets: HashMap<[u8; 32], ([u8; 16], f64)>,
704 pub(crate) completed_proofs: HashMap<[u8; 32], (f64, f64)>,
706 pub(crate) local_destinations: HashMap<[u8; 16], u8>,
708 shared_announces: HashMap<[u8; 16], SharedAnnounceRecord>,
710 shared_reconnect_pending: HashMap<InterfaceId, bool>,
712 pub(crate) holepunch_manager: HolePunchManager,
714 pub(crate) event_tx: crate::event::EventSender,
716 pub(crate) interface_writer_queue_capacity: usize,
718 pub(crate) underlay_mark: Option<u32>,
720 pub(crate) announce_rate_defaults: AnnounceRateDefaults,
722 pub(crate) ingress_control_defaults: rns_core::transport::types::IngressControlConfig,
724 pub(crate) tick_interval_ms: Arc<AtomicU64>,
726 #[cfg(feature = "iface-backbone")]
728 pub(crate) backbone_runtime: HashMap<String, BackboneRuntimeConfigHandle>,
729 #[cfg(feature = "iface-backbone")]
731 pub(crate) backbone_peer_state: HashMap<String, BackbonePeerStateHandle>,
732 #[cfg(feature = "iface-backbone")]
734 pub(crate) backbone_client_runtime: HashMap<String, BackboneClientRuntimeConfigHandle>,
735 #[cfg(feature = "iface-backbone")]
737 pub(crate) backbone_discovery_runtime: HashMap<String, BackboneDiscoveryRuntimeHandle>,
738 #[cfg(feature = "iface-backbone")]
740 backbone_peer_pool: Option<BackbonePeerPool>,
741 pub(crate) next_dynamic_interface_id: Arc<AtomicU64>,
743 #[cfg(feature = "iface-tcp")]
745 pub(crate) tcp_server_runtime: HashMap<String, TcpServerRuntimeConfigHandle>,
746 #[cfg(feature = "iface-tcp")]
748 pub(crate) tcp_client_runtime: HashMap<String, TcpClientRuntimeConfigHandle>,
749 #[cfg(feature = "iface-tcp")]
751 pub(crate) tcp_server_discovery_runtime: HashMap<String, TcpServerDiscoveryRuntimeHandle>,
752 #[cfg(feature = "iface-udp")]
754 pub(crate) udp_runtime: HashMap<String, UdpRuntimeConfigHandle>,
755 #[cfg(feature = "iface-auto")]
757 pub(crate) auto_runtime: HashMap<String, AutoRuntimeConfigHandle>,
758 #[cfg(feature = "iface-i2p")]
760 pub(crate) i2p_runtime: HashMap<String, I2pRuntimeConfigHandle>,
761 #[cfg(feature = "iface-pipe")]
763 pub(crate) pipe_runtime: HashMap<String, PipeRuntimeConfigHandle>,
764 #[cfg(feature = "iface-rnode")]
766 pub(crate) rnode_runtime: HashMap<String, RNodeRuntimeConfigHandle>,
767 pub(crate) interface_runtime_defaults:
769 HashMap<String, rns_core::transport::types::InterfaceInfo>,
770 pub(crate) interface_ifac_runtime: HashMap<String, IfacRuntimeConfig>,
772 pub(crate) interface_ifac_runtime_defaults: HashMap<String, IfacRuntimeConfig>,
774 pub(crate) discovered_interfaces: crate::discovery::DiscoveredInterfaceStorage,
776 pub(crate) discovery_required_value: u8,
778 pub(crate) discovery_stamp_cache: crate::discovery::DiscoveryStampCache,
780 pub(crate) discovery_name_hash: [u8; 10],
782 pub(crate) probe_responder_hash: Option<[u8; 16]>,
784 pub(crate) discover_interfaces: bool,
786 pub(crate) autoconnect_interface_mode: Option<u8>,
788 pub(crate) autoconnect_interface_gravity: i64,
790 pub(crate) autoconnect_announces_to_internal: Option<bool>,
792 pub(crate) autoconnect_unverified_implementations: bool,
794 pub(crate) interface_announcer: Option<crate::discovery::InterfaceAnnouncer>,
796 pub(crate) announce_verify_queue: Arc<Mutex<AnnounceVerifyQueue>>,
798 pub(crate) async_announce_verification: bool,
800 pub(crate) discovery_cleanup_counter: u32,
802 pub(crate) discovery_cleanup_interval_ticks: u32,
804 pub(crate) memory_stats_counter: u32,
806 pub(crate) cache_cleanup_counter: u32,
808 pub(crate) announce_cache_cleanup_counter: u32,
810 pub(crate) known_destinations_cleanup_interval_ticks: u32,
812 known_destination_cleanup: Option<KnownDestinationCleanup>,
814 ratchet_cleanup_handle: Option<std::thread::JoinHandle<()>>,
816 pub(crate) known_destinations_cap_evict_count: usize,
818 pub(crate) announce_cache_cleanup_interval_ticks: u32,
820 pub(crate) cache_cleanup_active_hashes: Option<Vec<[u8; 32]>>,
822 pub(crate) cache_cleanup_entries: Option<std::fs::ReadDir>,
824 pub(crate) cache_cleanup_removed: usize,
826 pub(crate) announce_cache_cleanup_batch_size: usize,
828 pub(crate) management_announce_interval_secs: f64,
830 pub(crate) runtime_config_defaults: RuntimeConfigDefaults,
832 #[cfg(feature = "hooks")]
834 pub(crate) hook_slots: [HookSlot; HookPoint::COUNT],
835 #[cfg(feature = "hooks")]
837 pub(crate) hook_manager: Option<HookManager>,
838 #[cfg(feature = "hooks")]
839 pub(crate) provider_bridge: Option<ProviderBridge>,
840}
841
842impl Driver {
843 pub fn new(
845 config: TransportConfig,
846 rx: EventReceiver,
847 tx: crate::event::EventSender,
848 callbacks: Box<dyn Callbacks>,
849 ) -> Self {
850 let announce_queue_max_entries = config.announce_queue_max_entries;
851 let tunnel_synth_dest = rns_core::destination::destination_hash(
852 "rnstransport",
853 &["tunnel", "synthesize"],
854 None,
855 );
856 let path_request_dest =
857 rns_core::destination::destination_hash("rnstransport", &["path", "request"], None);
858 let discovery_name_hash = crate::discovery::discovery_name_hash();
859 let mut engine = TransportEngine::new(config);
860 engine.register_destination(tunnel_synth_dest, rns_core::constants::DESTINATION_PLAIN);
861 engine.register_destination(path_request_dest, rns_core::constants::DESTINATION_PLAIN);
863 let mut local_destinations = HashMap::new();
866 local_destinations.insert(tunnel_synth_dest, rns_core::constants::DESTINATION_PLAIN);
867 local_destinations.insert(path_request_dest, rns_core::constants::DESTINATION_PLAIN);
868 let runtime_config_defaults = RuntimeConfigDefaults {
869 tick_interval_ms: DEFAULT_TICK_INTERVAL_MS,
870 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
871 rate_limiter_ttl_secs: DEFAULT_RATE_LIMITER_TTL_SECS,
872 known_destinations_cleanup_interval_ticks:
873 DEFAULT_KNOWN_DESTINATIONS_CLEANUP_INTERVAL_TICKS,
874 announce_cache_cleanup_interval_ticks: DEFAULT_ANNOUNCE_CACHE_CLEANUP_INTERVAL_TICKS,
875 announce_cache_cleanup_batch_size: DEFAULT_ANNOUNCE_CACHE_CLEANUP_BATCH_SIZE,
876 discovery_cleanup_interval_ticks: DEFAULT_DISCOVERY_CLEANUP_INTERVAL_TICKS,
877 management_announce_interval_secs: DEFAULT_MANAGEMENT_ANNOUNCE_INTERVAL_SECS,
878 direct_connect_policy: crate::event::HolePunchPolicy::default(),
879 #[cfg(feature = "hooks")]
880 provider_queue_max_events: crate::provider_bridge::ProviderBridgeConfig::default()
881 .queue_max_events,
882 #[cfg(feature = "hooks")]
883 provider_queue_max_bytes: crate::provider_bridge::ProviderBridgeConfig::default()
884 .queue_max_bytes,
885 };
886 Driver {
887 tracked_link_send: None,
888 pending_link_frames: std::collections::VecDeque::new(),
889 engine,
890 interfaces: HashMap::new(),
891 dynamic_interface_parents: HashMap::new(),
892 traffic_samples: HashMap::new(),
893 rng: OsRng,
894 rx,
895 callbacks,
896 started: time::now(),
897 lifecycle_state: LifecycleState::Active,
898 drain_started_at: None,
899 drain_deadline: None,
900 listener_controls: Vec::new(),
901 interface_management: None,
902 managed_interfaces: HashMap::new(),
903 retired_interface_parents: HashSet::new(),
904 announce_cache: None,
905 tunnel_synth_dest,
906 transport_identity: None,
907 link_manager: LinkManager::new(),
908 management_config: Default::default(),
909 last_management_announce: 0.0,
910 initial_announce_sent: false,
911 known_destinations: HashMap::new(),
912 ratchet_store: None,
913 ratchet_expiry_secs: rns_core::constants::RATCHET_EXPIRY as f64,
914 known_destinations_ttl: DEFAULT_KNOWN_DESTINATIONS_TTL,
915 known_destinations_max_entries: DEFAULT_KNOWN_DESTINATIONS_MAX_ENTRIES,
916 rate_limiter_ttl_secs: DEFAULT_RATE_LIMITER_TTL_SECS,
917 path_request_dest,
918 proof_strategies: HashMap::new(),
919 sent_packets: HashMap::new(),
920 completed_proofs: HashMap::new(),
921 local_destinations,
922 shared_announces: HashMap::new(),
923 shared_reconnect_pending: HashMap::new(),
924 holepunch_manager: HolePunchManager::new(
925 vec![],
926 rns_core::holepunch::ProbeProtocol::Rnsp,
927 None,
928 ),
929 event_tx: tx,
930 interface_writer_queue_capacity: crate::interface::DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY,
931 underlay_mark: None,
932 announce_rate_defaults: AnnounceRateDefaults::default(),
933 ingress_control_defaults: rns_core::transport::types::IngressControlConfig::enabled(),
934 tick_interval_ms: Arc::new(AtomicU64::new(DEFAULT_TICK_INTERVAL_MS)),
935 #[cfg(feature = "iface-backbone")]
936 backbone_runtime: HashMap::new(),
937 #[cfg(feature = "iface-backbone")]
938 backbone_peer_state: HashMap::new(),
939 #[cfg(feature = "iface-backbone")]
940 backbone_client_runtime: HashMap::new(),
941 #[cfg(feature = "iface-backbone")]
942 backbone_discovery_runtime: HashMap::new(),
943 #[cfg(feature = "iface-backbone")]
944 backbone_peer_pool: None,
945 next_dynamic_interface_id: Arc::new(AtomicU64::new(10000)),
946 #[cfg(feature = "iface-tcp")]
947 tcp_server_runtime: HashMap::new(),
948 #[cfg(feature = "iface-tcp")]
949 tcp_client_runtime: HashMap::new(),
950 #[cfg(feature = "iface-tcp")]
951 tcp_server_discovery_runtime: HashMap::new(),
952 #[cfg(feature = "iface-udp")]
953 udp_runtime: HashMap::new(),
954 #[cfg(feature = "iface-auto")]
955 auto_runtime: HashMap::new(),
956 #[cfg(feature = "iface-i2p")]
957 i2p_runtime: HashMap::new(),
958 #[cfg(feature = "iface-pipe")]
959 pipe_runtime: HashMap::new(),
960 #[cfg(feature = "iface-rnode")]
961 rnode_runtime: HashMap::new(),
962 interface_runtime_defaults: HashMap::new(),
963 interface_ifac_runtime: HashMap::new(),
964 interface_ifac_runtime_defaults: HashMap::new(),
965 discovered_interfaces: crate::discovery::DiscoveredInterfaceStorage::new(
966 std::env::temp_dir().join("rns-discovered-interfaces"),
967 ),
968 discovery_required_value: crate::discovery::DEFAULT_STAMP_VALUE,
969 discovery_stamp_cache: crate::discovery::DiscoveryStampCache::default(),
970 discovery_name_hash,
971 probe_responder_hash: None,
972 discover_interfaces: false,
973 autoconnect_interface_mode: None,
974 autoconnect_interface_gravity: 0,
975 autoconnect_announces_to_internal: None,
976 autoconnect_unverified_implementations: false,
977 interface_announcer: None,
978 announce_verify_queue: Arc::new(Mutex::new(AnnounceVerifyQueue::new(
979 announce_queue_max_entries,
980 ))),
981 async_announce_verification: false,
982 discovery_cleanup_counter: 0,
983 discovery_cleanup_interval_ticks: runtime_config_defaults
984 .discovery_cleanup_interval_ticks,
985 memory_stats_counter: 0,
986 cache_cleanup_counter: 0,
987 announce_cache_cleanup_counter: 0,
988 known_destinations_cleanup_interval_ticks: runtime_config_defaults
989 .known_destinations_cleanup_interval_ticks,
990 known_destination_cleanup: None,
991 ratchet_cleanup_handle: None,
992 known_destinations_cap_evict_count: 0,
993 announce_cache_cleanup_interval_ticks: runtime_config_defaults
994 .announce_cache_cleanup_interval_ticks,
995 cache_cleanup_active_hashes: None,
996 cache_cleanup_entries: None,
997 cache_cleanup_removed: 0,
998 announce_cache_cleanup_batch_size: runtime_config_defaults
999 .announce_cache_cleanup_batch_size,
1000 management_announce_interval_secs: runtime_config_defaults
1001 .management_announce_interval_secs,
1002 runtime_config_defaults,
1003 #[cfg(feature = "hooks")]
1004 hook_slots: create_hook_slots(),
1005 #[cfg(feature = "hooks")]
1006 hook_manager: HookManager::new().ok(),
1007 #[cfg(feature = "hooks")]
1008 provider_bridge: None,
1009 }
1010 }
1011
1012 pub fn set_announce_verify_queue_config(
1013 &mut self,
1014 max_entries: usize,
1015 max_bytes: usize,
1016 max_stale_secs: f64,
1017 overflow_policy: OverflowPolicy,
1018 ) {
1019 self.announce_verify_queue = Arc::new(Mutex::new(AnnounceVerifyQueue::with_limits(
1020 max_entries,
1021 max_bytes,
1022 max_stale_secs,
1023 overflow_policy,
1024 )));
1025 }
1026
1027 pub fn set_announce_rate_defaults(&mut self, defaults: AnnounceRateDefaults) {
1028 self.announce_rate_defaults = defaults;
1029 }
1030
1031 pub fn set_ingress_control_defaults(
1032 &mut self,
1033 defaults: rns_core::transport::types::IngressControlConfig,
1034 ) {
1035 self.ingress_control_defaults = defaults;
1036 }
1037
1038 pub(crate) fn apply_announce_rate_defaults(
1039 &self,
1040 info: &mut rns_core::transport::types::InterfaceInfo,
1041 ) {
1042 if !self.engine.transport_enabled() || info.announce_rate_target.is_some() {
1043 return;
1044 }
1045
1046 let Some(target) = self.announce_rate_defaults.target else {
1047 return;
1048 };
1049
1050 info.announce_rate_target = Some(target);
1051 if info.announce_rate_grace == 0 {
1052 info.announce_rate_grace = self.announce_rate_defaults.grace;
1053 }
1054 if info.announce_rate_penalty == 0.0 {
1055 info.announce_rate_penalty = self.announce_rate_defaults.penalty;
1056 }
1057 }
1058
1059 pub(crate) fn apply_ingress_control_defaults(
1060 &self,
1061 info: &mut rns_core::transport::types::InterfaceInfo,
1062 ) {
1063 if !self.engine.transport_enabled() {
1064 return;
1065 }
1066
1067 let baseline = if info.ingress_control.enabled {
1068 rns_core::transport::types::IngressControlConfig::enabled()
1069 } else {
1070 rns_core::transport::types::IngressControlConfig::disabled()
1071 };
1072 let defaults = self.ingress_control_defaults;
1073 let ic = &mut info.ingress_control;
1074
1075 if ic.max_held_announces == baseline.max_held_announces {
1076 ic.max_held_announces = defaults.max_held_announces;
1077 }
1078 if ic.burst_hold == baseline.burst_hold {
1079 ic.burst_hold = defaults.burst_hold;
1080 }
1081 if ic.burst_freq_new == baseline.burst_freq_new {
1082 ic.burst_freq_new = defaults.burst_freq_new;
1083 }
1084 if ic.burst_freq == baseline.burst_freq {
1085 ic.burst_freq = defaults.burst_freq;
1086 }
1087 if ic.pr_burst_freq_new == baseline.pr_burst_freq_new {
1088 ic.pr_burst_freq_new = defaults.pr_burst_freq_new;
1089 }
1090 if ic.pr_burst_freq == baseline.pr_burst_freq {
1091 ic.pr_burst_freq = defaults.pr_burst_freq;
1092 }
1093 if ic.new_time == baseline.new_time {
1094 ic.new_time = defaults.new_time;
1095 }
1096 if ic.burst_penalty == baseline.burst_penalty {
1097 ic.burst_penalty = defaults.burst_penalty;
1098 }
1099 if ic.held_release_interval == baseline.held_release_interval {
1100 ic.held_release_interval = defaults.held_release_interval;
1101 }
1102 if ic.egress_enabled == baseline.egress_enabled {
1103 ic.egress_enabled = defaults.egress_enabled;
1104 }
1105 if ic.egress_pr_freq == baseline.egress_pr_freq {
1106 ic.egress_pr_freq = defaults.egress_pr_freq;
1107 }
1108 }
1109
1110 fn wrap_interface_writer(
1111 &self,
1112 interface_id: InterfaceId,
1113 interface_name: &str,
1114 writer: Box<dyn crate::interface::Writer>,
1115 ) -> (
1116 Box<dyn crate::interface::Writer>,
1117 crate::interface::AsyncWriterMetrics,
1118 ) {
1119 crate::interface::wrap_async_writer(
1120 writer,
1121 interface_id,
1122 interface_name,
1123 self.event_tx.clone(),
1124 self.interface_writer_queue_capacity,
1125 )
1126 }
1127
1128 #[cfg(feature = "hooks")]
1129 fn provider_events_enabled(&self) -> bool {
1130 self.provider_bridge.is_some()
1131 }
1132
1133 #[cfg(feature = "hooks")]
1134 fn run_backbone_peer_hook(
1135 &mut self,
1136 attach_point: &str,
1137 point: HookPoint,
1138 event: &BackbonePeerHookEvent,
1139 ) {
1140 let ctx = backbone_peer_hook_context(event);
1141 let now = time::now();
1142 let engine_ref = EngineRef {
1143 engine: &self.engine,
1144 interfaces: &self.interfaces,
1145 link_manager: &self.link_manager,
1146 now,
1147 };
1148 let provider_events_enabled = self.provider_events_enabled();
1149 if let Some(ref e) = run_hook_inner(
1150 &mut self.hook_slots[point as usize].programs,
1151 &self.hook_manager,
1152 &engine_ref,
1153 &ctx,
1154 now,
1155 provider_events_enabled,
1156 ) {
1157 self.forward_hook_side_effects(attach_point, e);
1158 }
1159 }
1160
1161 #[cfg(feature = "iface-backbone")]
1162 fn make_discoverable_interface(
1163 runtime: &BackboneDiscoveryRuntimeHandle,
1164 ) -> crate::discovery::DiscoverableInterface {
1165 crate::discovery::DiscoverableInterface {
1166 interface_name: runtime.interface_name.clone(),
1167 config: runtime.current.config.clone(),
1168 transport_enabled: runtime.current.transport_enabled,
1169 ifac_netname: runtime.current.ifac_netname.clone(),
1170 ifac_netkey: runtime.current.ifac_netkey.clone(),
1171 }
1172 }
1173
1174 #[cfg(feature = "iface-backbone")]
1175 fn sync_backbone_discovery_runtime(
1176 &mut self,
1177 interface_name: &str,
1178 ) -> Result<(), RuntimeConfigError> {
1179 let handle = self
1180 .backbone_discovery_runtime
1181 .get(interface_name)
1182 .ok_or(RuntimeConfigError {
1183 code: RuntimeConfigErrorCode::NotFound,
1184 message: format!("backbone interface '{}' not found", interface_name),
1185 })?
1186 .clone();
1187
1188 if handle.current.discoverable {
1189 let iface = Self::make_discoverable_interface(&handle);
1190 if let Some(announcer) = self.interface_announcer.as_mut() {
1191 announcer.upsert_interface(iface);
1192 } else if let Some(identity) = self.transport_identity.as_ref() {
1193 self.interface_announcer = Some(crate::discovery::InterfaceAnnouncer::new(
1194 *identity.hash(),
1195 vec![iface],
1196 ));
1197 }
1198 } else if let Some(announcer) = self.interface_announcer.as_mut() {
1199 announcer.remove_interface(interface_name);
1200 if announcer.is_empty() {
1201 self.interface_announcer = None;
1202 }
1203 }
1204
1205 Ok(())
1206 }
1207
1208 #[cfg(feature = "iface-tcp")]
1209 fn make_tcp_server_discoverable_interface(
1210 runtime: &TcpServerDiscoveryRuntimeHandle,
1211 ) -> crate::discovery::DiscoverableInterface {
1212 crate::discovery::DiscoverableInterface {
1213 interface_name: runtime.interface_name.clone(),
1214 config: runtime.current.config.clone(),
1215 transport_enabled: runtime.current.transport_enabled,
1216 ifac_netname: runtime.current.ifac_netname.clone(),
1217 ifac_netkey: runtime.current.ifac_netkey.clone(),
1218 }
1219 }
1220
1221 #[cfg(feature = "iface-tcp")]
1222 fn sync_tcp_server_discovery_runtime(
1223 &mut self,
1224 interface_name: &str,
1225 ) -> Result<(), RuntimeConfigError> {
1226 let handle = self
1227 .tcp_server_discovery_runtime
1228 .get(interface_name)
1229 .ok_or(RuntimeConfigError {
1230 code: RuntimeConfigErrorCode::NotFound,
1231 message: format!("tcp server interface '{}' not found", interface_name),
1232 })?
1233 .clone();
1234
1235 if handle.current.discoverable {
1236 let iface = Self::make_tcp_server_discoverable_interface(&handle);
1237 if let Some(announcer) = self.interface_announcer.as_mut() {
1238 announcer.upsert_interface(iface);
1239 } else if let Some(identity) = self.transport_identity.as_ref() {
1240 self.interface_announcer = Some(crate::discovery::InterfaceAnnouncer::new(
1241 *identity.hash(),
1242 vec![iface],
1243 ));
1244 }
1245 } else if let Some(announcer) = self.interface_announcer.as_mut() {
1246 announcer.remove_interface(interface_name);
1247 if announcer.is_empty() {
1248 self.interface_announcer = None;
1249 }
1250 }
1251
1252 Ok(())
1253 }
1254
1255 #[cfg(feature = "hooks")]
1256 fn update_hook_program<F>(
1257 &mut self,
1258 name: &str,
1259 attach_point: &str,
1260 mut update: F,
1261 ) -> Result<(), String>
1262 where
1263 F: FnMut(&mut rns_hooks::LoadedProgram),
1264 {
1265 let point_idx = crate::config::parse_hook_point(attach_point)
1266 .ok_or_else(|| format!("unknown hook point '{}'", attach_point))?;
1267 let program = self.hook_slots[point_idx]
1268 .programs
1269 .iter_mut()
1270 .find(|program| program.name == name)
1271 .ok_or_else(|| format!("hook '{}' not found at point '{}'", name, attach_point))?;
1272 update(program);
1273 Ok(())
1274 }
1275
1276 pub(crate) fn set_tick_interval_handle(&mut self, tick_interval_ms: Arc<AtomicU64>) {
1277 self.tick_interval_ms = tick_interval_ms;
1278 }
1279
1280 #[allow(dead_code)]
1281 pub(crate) fn set_packet_hashlist_max_entries(&mut self, max_entries: usize) {
1282 self.engine.set_packet_hashlist_max_entries(max_entries);
1283 }
1284
1285 #[cfg(feature = "hooks")]
1286 fn forward_hook_side_effects(&mut self, attach_point: &str, exec: &rns_hooks::ExecuteResult) {
1287 if !exec.injected_actions.is_empty() {
1288 self.dispatch_all(convert_injected_actions(exec.injected_actions.clone()));
1289 }
1290 if let Some(ref bridge) = self.provider_bridge {
1291 for event in &exec.provider_events {
1292 bridge.emit_event(
1293 attach_point,
1294 event.hook_name.clone(),
1295 event.payload_type.clone(),
1296 event.payload.clone(),
1297 );
1298 }
1299 }
1300 }
1301
1302 #[cfg(feature = "hooks")]
1303 fn collect_hook_side_effects(
1304 &mut self,
1305 attach_point: &str,
1306 exec: &rns_hooks::ExecuteResult,
1307 out: &mut Vec<TransportAction>,
1308 ) {
1309 if !exec.injected_actions.is_empty() {
1310 out.extend(convert_injected_actions(exec.injected_actions.clone()));
1311 }
1312 if let Some(ref bridge) = self.provider_bridge {
1313 for event in &exec.provider_events {
1314 bridge.emit_event(
1315 attach_point,
1316 event.hook_name.clone(),
1317 event.payload_type.clone(),
1318 event.payload.clone(),
1319 );
1320 }
1321 }
1322 }
1323
1324 pub fn set_probe_config(
1326 &mut self,
1327 addrs: Vec<std::net::SocketAddr>,
1328 protocol: rns_core::holepunch::ProbeProtocol,
1329 device: Option<String>,
1330 ) {
1331 self.holepunch_manager = HolePunchManager::new(addrs, protocol, device);
1332 self.holepunch_manager.set_underlay_mark(self.underlay_mark);
1333 }
1334}
1335
1336pub(crate) struct PendingLinkFrame {
1337 interface: InterfaceId,
1338 data: Vec<u8>,
1339 is_announce: bool,
1340 completion: Option<crate::link_send::Completion>,
1341}