Skip to main content

rns_net/
event.rs

1//! Event types for the driver loop.
2
3use std::fmt;
4use std::sync::mpsc;
5
6use rns_core::transport::types::{InterfaceId, InterfaceInfo};
7
8use crate::interface::Writer;
9
10/// Events sent to the driver thread.
11pub enum Event {
12    /// A decoded frame arrived from an interface.
13    Frame { interface_id: InterfaceId, data: Vec<u8> },
14    /// An interface came online after (re)connecting.
15    /// Carries a new writer if the connection was re-established.
16    /// Carries InterfaceInfo if this is a new dynamic interface (e.g. TCP server client).
17    InterfaceUp(InterfaceId, Option<Box<dyn Writer>>, Option<InterfaceInfo>),
18    /// An interface went offline (socket closed, error).
19    InterfaceDown(InterfaceId),
20    /// Periodic maintenance tick (1s interval).
21    Tick,
22    /// Shut down the driver loop.
23    Shutdown,
24    /// Send an outbound packet.
25    SendOutbound {
26        raw: Vec<u8>,
27        dest_type: u8,
28        attached_interface: Option<InterfaceId>,
29    },
30    /// Register a local destination.
31    RegisterDestination {
32        dest_hash: [u8; 16],
33        dest_type: u8,
34    },
35    /// Deregister a local destination.
36    DeregisterDestination {
37        dest_hash: [u8; 16],
38    },
39    /// Deregister a link destination (stop accepting incoming links).
40    DeregisterLinkDestination {
41        dest_hash: [u8; 16],
42    },
43    /// Query driver state. Response is sent via the provided channel.
44    Query(QueryRequest, mpsc::Sender<QueryResponse>),
45    /// Register a link destination (accepts incoming LINKREQUEST).
46    RegisterLinkDestination {
47        dest_hash: [u8; 16],
48        sig_prv_bytes: [u8; 32],
49        sig_pub_bytes: [u8; 32],
50    },
51    /// Register a request handler for a path on established links.
52    RegisterRequestHandler {
53        path: String,
54        allowed_list: Option<Vec<[u8; 16]>>,
55        handler: Box<dyn Fn([u8; 16], &str, &[u8], Option<&([u8; 16], [u8; 64])>) -> Option<Vec<u8>> + Send>,
56    },
57    /// Create an outbound link. Response sends (link_id) back.
58    CreateLink {
59        dest_hash: [u8; 16],
60        dest_sig_pub_bytes: [u8; 32],
61        response_tx: mpsc::Sender<[u8; 16]>,
62    },
63    /// Send a request on an established link.
64    SendRequest {
65        link_id: [u8; 16],
66        path: String,
67        data: Vec<u8>,
68    },
69    /// Identify on a link (send identity to remote peer).
70    IdentifyOnLink {
71        link_id: [u8; 16],
72        identity_prv_key: [u8; 64],
73    },
74    /// Tear down a link.
75    TeardownLink {
76        link_id: [u8; 16],
77    },
78    /// Send a resource on a link.
79    SendResource {
80        link_id: [u8; 16],
81        data: Vec<u8>,
82        metadata: Option<Vec<u8>>,
83    },
84    /// Set the resource acceptance strategy for a link.
85    SetResourceStrategy {
86        link_id: [u8; 16],
87        strategy: u8,
88    },
89    /// Accept or reject a pending resource (for AcceptApp strategy).
90    AcceptResource {
91        link_id: [u8; 16],
92        resource_hash: Vec<u8>,
93        accept: bool,
94    },
95    /// Send a channel message on a link.
96    SendChannelMessage {
97        link_id: [u8; 16],
98        msgtype: u16,
99        payload: Vec<u8>,
100    },
101    /// Send generic data on a link with a given context.
102    SendOnLink {
103        link_id: [u8; 16],
104        data: Vec<u8>,
105        context: u8,
106    },
107    /// Request a path to a destination from the network.
108    RequestPath {
109        dest_hash: [u8; 16],
110    },
111    /// Register a proof strategy for a destination.
112    RegisterProofStrategy {
113        dest_hash: [u8; 16],
114        strategy: rns_core::types::ProofStrategy,
115        /// Full identity private key (64 bytes) for signing proofs.
116        signing_key: Option<[u8; 64]>,
117    },
118    /// Propose a direct connection to a peer via hole punching.
119    ProposeDirectConnect {
120        link_id: [u8; 16],
121    },
122    /// Set the direct-connect policy.
123    SetDirectConnectPolicy {
124        policy: crate::holepunch::orchestrator::HolePunchPolicy,
125    },
126    /// (Internal) Probe result arrived from a worker thread.
127    HolePunchProbeResult {
128        link_id: [u8; 16],
129        session_id: [u8; 16],
130        observed_addr: std::net::SocketAddr,
131        socket: std::net::UdpSocket,
132    },
133    /// (Internal) Probe failed.
134    HolePunchProbeFailed {
135        link_id: [u8; 16],
136        session_id: [u8; 16],
137    },
138    /// An interface's configuration changed (placeholder for future use).
139    InterfaceConfigChanged(InterfaceId),
140    /// Load a WASM hook at runtime.
141    LoadHook {
142        name: String,
143        wasm_bytes: Vec<u8>,
144        attach_point: String,
145        priority: i32,
146        response_tx: mpsc::Sender<Result<(), String>>,
147    },
148    /// Unload a WASM hook at runtime.
149    UnloadHook {
150        name: String,
151        attach_point: String,
152        response_tx: mpsc::Sender<Result<(), String>>,
153    },
154    /// Reload a WASM hook at runtime (detach + recompile + reattach with same priority).
155    ReloadHook {
156        name: String,
157        attach_point: String,
158        wasm_bytes: Vec<u8>,
159        response_tx: mpsc::Sender<Result<(), String>>,
160    },
161    /// List all loaded hooks.
162    ListHooks {
163        response_tx: mpsc::Sender<Vec<HookInfo>>,
164    },
165}
166
167/// Information about a loaded hook program.
168#[derive(Debug, Clone)]
169pub struct HookInfo {
170    pub name: String,
171    pub attach_point: String,
172    pub priority: i32,
173    pub enabled: bool,
174    pub consecutive_traps: u32,
175}
176
177/// Queries that can be sent to the driver.
178#[derive(Debug)]
179pub enum QueryRequest {
180    /// Get interface statistics and transport info.
181    InterfaceStats,
182    /// Get path table entries, optionally filtered by max hops.
183    PathTable { max_hops: Option<u8> },
184    /// Get rate table entries.
185    RateTable,
186    /// Look up the next hop for a destination.
187    NextHop { dest_hash: [u8; 16] },
188    /// Look up the next hop interface name for a destination.
189    NextHopIfName { dest_hash: [u8; 16] },
190    /// Get link table entry count.
191    LinkCount,
192    /// Drop a specific path.
193    DropPath { dest_hash: [u8; 16] },
194    /// Drop all paths that route via a given transport hash.
195    DropAllVia { transport_hash: [u8; 16] },
196    /// Drop all announce queues.
197    DropAnnounceQueues,
198    /// Get the transport identity hash.
199    TransportIdentity,
200    /// Get all blackholed identities.
201    GetBlackholed,
202    /// Add an identity to the blackhole list.
203    BlackholeIdentity {
204        identity_hash: [u8; 16],
205        duration_hours: Option<f64>,
206        reason: Option<String>,
207    },
208    /// Remove an identity from the blackhole list.
209    UnblackholeIdentity {
210        identity_hash: [u8; 16],
211    },
212    /// Check if a path exists to a destination.
213    HasPath { dest_hash: [u8; 16] },
214    /// Get hop count to a destination.
215    HopsTo { dest_hash: [u8; 16] },
216    /// Recall identity info for a destination.
217    RecallIdentity { dest_hash: [u8; 16] },
218    /// Get locally registered destinations.
219    LocalDestinations,
220    /// Get active links.
221    Links,
222    /// Get active resource transfers.
223    Resources,
224    /// Inject a path entry into the path table.
225    InjectPath {
226        dest_hash: [u8; 16],
227        next_hop: [u8; 16],
228        hops: u8,
229        expires: f64,
230        interface_name: String,
231        packet_hash: [u8; 32],
232    },
233    /// Inject an identity into the known destinations cache.
234    InjectIdentity {
235        dest_hash: [u8; 16],
236        identity_hash: [u8; 16],
237        public_key: [u8; 64],
238        app_data: Option<Vec<u8>>,
239        hops: u8,
240        received_at: f64,
241    },
242    /// Get discovered interfaces.
243    DiscoveredInterfaces {
244        only_available: bool,
245        only_transport: bool,
246    },
247}
248
249/// Responses to queries.
250#[derive(Debug)]
251pub enum QueryResponse {
252    InterfaceStats(InterfaceStatsResponse),
253    PathTable(Vec<PathTableEntry>),
254    RateTable(Vec<RateTableEntry>),
255    NextHop(Option<NextHopResponse>),
256    NextHopIfName(Option<String>),
257    LinkCount(usize),
258    DropPath(bool),
259    DropAllVia(usize),
260    DropAnnounceQueues,
261    TransportIdentity(Option<[u8; 16]>),
262    Blackholed(Vec<BlackholeInfo>),
263    BlackholeResult(bool),
264    UnblackholeResult(bool),
265    HasPath(bool),
266    HopsTo(Option<u8>),
267    RecallIdentity(Option<crate::destination::AnnouncedIdentity>),
268    LocalDestinations(Vec<LocalDestinationEntry>),
269    Links(Vec<LinkInfoEntry>),
270    Resources(Vec<ResourceInfoEntry>),
271    InjectPath(bool),
272    InjectIdentity(bool),
273    /// List of discovered interfaces.
274    DiscoveredInterfaces(Vec<crate::discovery::DiscoveredInterface>),
275}
276
277/// Interface statistics response.
278#[derive(Debug, Clone)]
279pub struct InterfaceStatsResponse {
280    pub interfaces: Vec<SingleInterfaceStat>,
281    pub transport_id: Option<[u8; 16]>,
282    pub transport_enabled: bool,
283    pub transport_uptime: f64,
284    /// Total received bytes across all interfaces.
285    pub total_rxb: u64,
286    /// Total transmitted bytes across all interfaces.
287    pub total_txb: u64,
288}
289
290/// Statistics for a single interface.
291#[derive(Debug, Clone)]
292pub struct SingleInterfaceStat {
293    pub name: String,
294    pub status: bool,
295    pub mode: u8,
296    pub rxb: u64,
297    pub txb: u64,
298    pub rx_packets: u64,
299    pub tx_packets: u64,
300    pub bitrate: Option<u64>,
301    pub ifac_size: Option<usize>,
302    pub started: f64,
303    /// Incoming announce frequency (per second).
304    pub ia_freq: f64,
305    /// Outgoing announce frequency (per second).
306    pub oa_freq: f64,
307    /// Human-readable interface type string (e.g. "TCPClientInterface").
308    pub interface_type: String,
309}
310
311/// A locally registered destination.
312#[derive(Debug, Clone)]
313pub struct LocalDestinationEntry {
314    pub hash: [u8; 16],
315    pub dest_type: u8,
316}
317
318/// Information about an active link.
319#[derive(Debug, Clone)]
320pub struct LinkInfoEntry {
321    pub link_id: [u8; 16],
322    pub state: String,
323    pub is_initiator: bool,
324    pub dest_hash: [u8; 16],
325    pub remote_identity: Option<[u8; 16]>,
326    pub rtt: Option<f64>,
327}
328
329/// Information about an active resource transfer.
330#[derive(Debug, Clone)]
331pub struct ResourceInfoEntry {
332    pub link_id: [u8; 16],
333    pub direction: String,
334    pub total_parts: usize,
335    pub transferred_parts: usize,
336    pub complete: bool,
337}
338
339/// A single path table entry for query responses.
340#[derive(Debug, Clone)]
341pub struct PathTableEntry {
342    pub hash: [u8; 16],
343    pub timestamp: f64,
344    pub via: [u8; 16],
345    pub hops: u8,
346    pub expires: f64,
347    pub interface: InterfaceId,
348    pub interface_name: String,
349}
350
351/// A single rate table entry for query responses.
352#[derive(Debug, Clone)]
353pub struct RateTableEntry {
354    pub hash: [u8; 16],
355    pub last: f64,
356    pub rate_violations: u32,
357    pub blocked_until: f64,
358    pub timestamps: Vec<f64>,
359}
360
361/// A blackholed identity for query responses.
362#[derive(Debug, Clone)]
363pub struct BlackholeInfo {
364    pub identity_hash: [u8; 16],
365    pub created: f64,
366    pub expires: f64,
367    pub reason: Option<String>,
368}
369
370/// Next hop lookup result.
371#[derive(Debug, Clone)]
372pub struct NextHopResponse {
373    pub next_hop: [u8; 16],
374    pub hops: u8,
375    pub interface: InterfaceId,
376}
377
378impl fmt::Debug for Event {
379    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380        match self {
381            Event::Frame { interface_id, data } => {
382                f.debug_struct("Frame")
383                    .field("interface_id", interface_id)
384                    .field("data_len", &data.len())
385                    .finish()
386            }
387            Event::InterfaceUp(id, writer, info) => {
388                f.debug_tuple("InterfaceUp")
389                    .field(id)
390                    .field(&writer.is_some())
391                    .field(&info.is_some())
392                    .finish()
393            }
394            Event::InterfaceDown(id) => f.debug_tuple("InterfaceDown").field(id).finish(),
395            Event::Tick => write!(f, "Tick"),
396            Event::Shutdown => write!(f, "Shutdown"),
397            Event::SendOutbound { raw, dest_type, .. } => {
398                f.debug_struct("SendOutbound")
399                    .field("raw_len", &raw.len())
400                    .field("dest_type", dest_type)
401                    .finish()
402            }
403            Event::RegisterDestination { dest_hash, dest_type } => {
404                f.debug_struct("RegisterDestination")
405                    .field("dest_hash", dest_hash)
406                    .field("dest_type", dest_type)
407                    .finish()
408            }
409            Event::DeregisterDestination { dest_hash } => {
410                f.debug_struct("DeregisterDestination")
411                    .field("dest_hash", dest_hash)
412                    .finish()
413            }
414            Event::DeregisterLinkDestination { dest_hash } => {
415                f.debug_struct("DeregisterLinkDestination")
416                    .field("dest_hash", dest_hash)
417                    .finish()
418            }
419            Event::Query(req, _) => {
420                f.debug_tuple("Query")
421                    .field(req)
422                    .finish()
423            }
424            Event::RegisterLinkDestination { dest_hash, .. } => {
425                f.debug_struct("RegisterLinkDestination")
426                    .field("dest_hash", dest_hash)
427                    .finish()
428            }
429            Event::RegisterRequestHandler { path, .. } => {
430                f.debug_struct("RegisterRequestHandler")
431                    .field("path", path)
432                    .finish()
433            }
434            Event::CreateLink { dest_hash, .. } => {
435                f.debug_struct("CreateLink")
436                    .field("dest_hash", dest_hash)
437                    .finish()
438            }
439            Event::SendRequest { link_id, path, .. } => {
440                f.debug_struct("SendRequest")
441                    .field("link_id", link_id)
442                    .field("path", path)
443                    .finish()
444            }
445            Event::IdentifyOnLink { link_id, .. } => {
446                f.debug_struct("IdentifyOnLink")
447                    .field("link_id", link_id)
448                    .finish()
449            }
450            Event::TeardownLink { link_id } => {
451                f.debug_struct("TeardownLink")
452                    .field("link_id", link_id)
453                    .finish()
454            }
455            Event::SendResource { link_id, data, .. } => {
456                f.debug_struct("SendResource")
457                    .field("link_id", link_id)
458                    .field("data_len", &data.len())
459                    .finish()
460            }
461            Event::SetResourceStrategy { link_id, strategy } => {
462                f.debug_struct("SetResourceStrategy")
463                    .field("link_id", link_id)
464                    .field("strategy", strategy)
465                    .finish()
466            }
467            Event::AcceptResource { link_id, accept, .. } => {
468                f.debug_struct("AcceptResource")
469                    .field("link_id", link_id)
470                    .field("accept", accept)
471                    .finish()
472            }
473            Event::SendChannelMessage { link_id, msgtype, payload } => {
474                f.debug_struct("SendChannelMessage")
475                    .field("link_id", link_id)
476                    .field("msgtype", msgtype)
477                    .field("payload_len", &payload.len())
478                    .finish()
479            }
480            Event::SendOnLink { link_id, data, context } => {
481                f.debug_struct("SendOnLink")
482                    .field("link_id", link_id)
483                    .field("data_len", &data.len())
484                    .field("context", context)
485                    .finish()
486            }
487            Event::RequestPath { dest_hash } => {
488                f.debug_struct("RequestPath")
489                    .field("dest_hash", dest_hash)
490                    .finish()
491            }
492            Event::RegisterProofStrategy { dest_hash, strategy, .. } => {
493                f.debug_struct("RegisterProofStrategy")
494                    .field("dest_hash", dest_hash)
495                    .field("strategy", strategy)
496                    .finish()
497            }
498            Event::ProposeDirectConnect { link_id } => {
499                f.debug_struct("ProposeDirectConnect")
500                    .field("link_id", link_id)
501                    .finish()
502            }
503            Event::SetDirectConnectPolicy { .. } => {
504                write!(f, "SetDirectConnectPolicy")
505            }
506            Event::HolePunchProbeResult { link_id, session_id, observed_addr, .. } => {
507                f.debug_struct("HolePunchProbeResult")
508                    .field("link_id", link_id)
509                    .field("session_id", session_id)
510                    .field("observed_addr", observed_addr)
511                    .finish()
512            }
513            Event::HolePunchProbeFailed { link_id, session_id } => {
514                f.debug_struct("HolePunchProbeFailed")
515                    .field("link_id", link_id)
516                    .field("session_id", session_id)
517                    .finish()
518            }
519            Event::InterfaceConfigChanged(id) => {
520                f.debug_tuple("InterfaceConfigChanged").field(id).finish()
521            }
522            Event::LoadHook { name, attach_point, priority, .. } => {
523                f.debug_struct("LoadHook")
524                    .field("name", name)
525                    .field("attach_point", attach_point)
526                    .field("priority", priority)
527                    .finish()
528            }
529            Event::UnloadHook { name, attach_point, .. } => {
530                f.debug_struct("UnloadHook")
531                    .field("name", name)
532                    .field("attach_point", attach_point)
533                    .finish()
534            }
535            Event::ReloadHook { name, attach_point, .. } => {
536                f.debug_struct("ReloadHook")
537                    .field("name", name)
538                    .field("attach_point", attach_point)
539                    .finish()
540            }
541            Event::ListHooks { .. } => write!(f, "ListHooks"),
542        }
543    }
544}
545
546pub type EventSender = mpsc::Sender<Event>;
547pub type EventReceiver = mpsc::Receiver<Event>;
548
549pub fn channel() -> (EventSender, EventReceiver) {
550    mpsc::channel()
551}