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