Skip to main content

rns_net/interface/
mod.rs

1//! Network interface abstractions.
2
3pub mod tcp;
4pub mod tcp_server;
5pub mod udp;
6pub mod local;
7pub mod serial_iface;
8pub mod kiss_iface;
9pub mod pipe;
10pub mod rnode;
11pub mod backbone;
12pub mod auto;
13pub mod i2p;
14
15use std::io;
16
17use rns_core::transport::types::{InterfaceId, InterfaceInfo};
18use crate::ifac::IfacState;
19
20/// Writable end of an interface. Held by the driver.
21///
22/// Each implementation wraps a socket + framing.
23pub trait Writer: Send {
24    fn send_frame(&mut self, data: &[u8]) -> io::Result<()>;
25}
26
27/// Maximum number of announce timestamps to keep per direction.
28const ANNOUNCE_SAMPLE_MAX: usize = 6;
29
30/// Traffic statistics for an interface.
31#[derive(Debug, Clone, Default)]
32pub struct InterfaceStats {
33    pub rxb: u64,
34    pub txb: u64,
35    pub rx_packets: u64,
36    pub tx_packets: u64,
37    pub started: f64,
38    /// Recent incoming announce timestamps (bounded).
39    pub ia_timestamps: Vec<f64>,
40    /// Recent outgoing announce timestamps (bounded).
41    pub oa_timestamps: Vec<f64>,
42}
43
44impl InterfaceStats {
45    /// Record an incoming announce timestamp.
46    pub fn record_incoming_announce(&mut self, now: f64) {
47        self.ia_timestamps.push(now);
48        if self.ia_timestamps.len() > ANNOUNCE_SAMPLE_MAX {
49            self.ia_timestamps.remove(0);
50        }
51    }
52
53    /// Record an outgoing announce timestamp.
54    pub fn record_outgoing_announce(&mut self, now: f64) {
55        self.oa_timestamps.push(now);
56        if self.oa_timestamps.len() > ANNOUNCE_SAMPLE_MAX {
57            self.oa_timestamps.remove(0);
58        }
59    }
60
61    /// Compute announce frequency (per second) from timestamps.
62    fn compute_frequency(timestamps: &[f64]) -> f64 {
63        if timestamps.len() < 2 {
64            return 0.0;
65        }
66        let span = timestamps[timestamps.len() - 1] - timestamps[0];
67        if span <= 0.0 {
68            return 0.0;
69        }
70        (timestamps.len() - 1) as f64 / span
71    }
72
73    /// Incoming announce frequency (per second).
74    pub fn incoming_announce_freq(&self) -> f64 {
75        Self::compute_frequency(&self.ia_timestamps)
76    }
77
78    /// Outgoing announce frequency (per second).
79    pub fn outgoing_announce_freq(&self) -> f64 {
80        Self::compute_frequency(&self.oa_timestamps)
81    }
82}
83
84/// Everything the driver tracks per interface.
85pub struct InterfaceEntry {
86    pub id: InterfaceId,
87    pub info: InterfaceInfo,
88    pub writer: Box<dyn Writer>,
89    pub online: bool,
90    /// True for dynamically spawned interfaces (e.g. TCP server clients).
91    /// These are fully removed on InterfaceDown rather than just marked offline.
92    pub dynamic: bool,
93    /// IFAC state for this interface, if access codes are enabled.
94    pub ifac: Option<IfacState>,
95    /// Traffic statistics.
96    pub stats: InterfaceStats,
97    /// Human-readable interface type string (e.g. "TCPClientInterface").
98    pub interface_type: String,
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104    use rns_core::constants;
105
106    struct MockWriter {
107        sent: Vec<Vec<u8>>,
108    }
109
110    impl MockWriter {
111        fn new() -> Self {
112            MockWriter { sent: Vec::new() }
113        }
114    }
115
116    impl Writer for MockWriter {
117        fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
118            self.sent.push(data.to_vec());
119            Ok(())
120        }
121    }
122
123    #[test]
124    fn interface_entry_construction() {
125        let entry = InterfaceEntry {
126            id: InterfaceId(1),
127            info: InterfaceInfo {
128                id: InterfaceId(1),
129                name: String::new(),
130                mode: constants::MODE_FULL,
131                out_capable: true,
132                in_capable: true,
133                bitrate: None,
134                announce_rate_target: None,
135                announce_rate_grace: 0,
136                announce_rate_penalty: 0.0,
137                announce_cap: constants::ANNOUNCE_CAP,
138                is_local_client: false,
139                wants_tunnel: false,
140                tunnel_id: None,
141            },
142            writer: Box::new(MockWriter::new()),
143            online: false,
144            dynamic: false,
145            ifac: None,
146            stats: InterfaceStats::default(),
147            interface_type: String::new(),
148        };
149        assert_eq!(entry.id, InterfaceId(1));
150        assert!(!entry.online);
151        assert!(!entry.dynamic);
152    }
153
154    #[test]
155    fn mock_writer_captures_bytes() {
156        let mut writer = MockWriter::new();
157        writer.send_frame(b"hello").unwrap();
158        writer.send_frame(b"world").unwrap();
159        assert_eq!(writer.sent.len(), 2);
160        assert_eq!(writer.sent[0], b"hello");
161        assert_eq!(writer.sent[1], b"world");
162    }
163
164    #[test]
165    fn writer_send_frame_produces_output() {
166        let mut writer = MockWriter::new();
167        let data = vec![0x01, 0x02, 0x03];
168        writer.send_frame(&data).unwrap();
169        assert_eq!(writer.sent[0], data);
170    }
171}