1#[cfg(feature = "iface-auto")]
4pub mod auto;
5#[cfg(feature = "iface-backbone")]
6pub mod backbone;
7#[cfg(feature = "iface-i2p")]
8pub mod i2p;
9#[cfg(feature = "iface-kiss")]
10pub mod kiss_iface;
11#[cfg(feature = "iface-local")]
12pub mod local;
13#[cfg(feature = "iface-pipe")]
14pub mod pipe;
15pub mod registry;
16#[cfg(feature = "iface-rnode")]
17pub mod rnode;
18#[cfg(feature = "iface-serial")]
19pub mod serial_iface;
20#[cfg(feature = "iface-tcp")]
21pub mod tcp;
22#[cfg(feature = "iface-tcp")]
23pub mod tcp_server;
24#[cfg(feature = "iface-udp")]
25pub mod udp;
26
27use std::any::Any;
28use std::collections::HashMap;
29use std::io;
30use std::sync::atomic::AtomicU64;
31use std::sync::Arc;
32
33use crate::event::EventSender;
34use crate::ifac::IfacState;
35use rns_core::transport::types::{InterfaceId, InterfaceInfo};
36
37#[cfg(target_os = "linux")]
41pub fn bind_to_device(fd: std::os::unix::io::RawFd, device: &str) -> io::Result<()> {
42 let dev_bytes = device.as_bytes();
43 if dev_bytes.len() >= libc::IFNAMSIZ {
44 return Err(io::Error::new(
45 io::ErrorKind::InvalidInput,
46 format!("device name too long: {}", device),
47 ));
48 }
49 let ret = unsafe {
50 libc::setsockopt(
51 fd,
52 libc::SOL_SOCKET,
53 libc::SO_BINDTODEVICE,
54 dev_bytes.as_ptr() as *const libc::c_void,
55 dev_bytes.len() as libc::socklen_t,
56 )
57 };
58 if ret != 0 {
59 return Err(io::Error::last_os_error());
60 }
61 Ok(())
62}
63
64pub trait Writer: Send {
68 fn send_frame(&mut self, data: &[u8]) -> io::Result<()>;
69}
70
71pub use crate::common::interface_stats::{InterfaceStats, ANNOUNCE_SAMPLE_MAX};
72
73use crate::common::management::InterfaceStatusView;
74
75pub struct InterfaceEntry {
77 pub id: InterfaceId,
78 pub info: InterfaceInfo,
79 pub writer: Box<dyn Writer>,
80 pub enabled: bool,
82 pub online: bool,
83 pub dynamic: bool,
86 pub ifac: Option<IfacState>,
88 pub stats: InterfaceStats,
90 pub interface_type: String,
92}
93
94pub enum StartResult {
96 Simple {
98 id: InterfaceId,
99 info: InterfaceInfo,
100 writer: Box<dyn Writer>,
101 interface_type_name: String,
102 },
103 Listener,
105 Multi(Vec<SubInterface>),
107}
108
109pub struct SubInterface {
111 pub id: InterfaceId,
112 pub info: InterfaceInfo,
113 pub writer: Box<dyn Writer>,
114 pub interface_type_name: String,
115}
116
117pub struct StartContext {
119 pub tx: EventSender,
120 pub next_dynamic_id: Arc<AtomicU64>,
121 pub mode: u8,
122}
123
124pub trait InterfaceConfigData: Send + Any {
126 fn as_any(&self) -> &dyn Any;
127 fn into_any(self: Box<Self>) -> Box<dyn Any>;
128}
129
130impl<T: Send + 'static> InterfaceConfigData for T {
131 fn as_any(&self) -> &dyn Any {
132 self
133 }
134
135 fn into_any(self: Box<Self>) -> Box<dyn Any> {
136 self
137 }
138}
139
140pub trait InterfaceFactory: Send + Sync {
142 fn type_name(&self) -> &str;
144
145 fn default_ifac_size(&self) -> usize {
147 16
148 }
149
150 fn parse_config(
152 &self,
153 name: &str,
154 id: InterfaceId,
155 params: &HashMap<String, String>,
156 ) -> Result<Box<dyn InterfaceConfigData>, String>;
157
158 fn start(
160 &self,
161 config: Box<dyn InterfaceConfigData>,
162 ctx: StartContext,
163 ) -> io::Result<StartResult>;
164}
165
166impl InterfaceStatusView for InterfaceEntry {
167 fn id(&self) -> InterfaceId {
168 self.id
169 }
170 fn info(&self) -> &InterfaceInfo {
171 &self.info
172 }
173 fn online(&self) -> bool {
174 self.online
175 }
176 fn stats(&self) -> &InterfaceStats {
177 &self.stats
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use rns_core::constants;
185
186 struct MockWriter {
187 sent: Vec<Vec<u8>>,
188 }
189
190 impl MockWriter {
191 fn new() -> Self {
192 MockWriter { sent: Vec::new() }
193 }
194 }
195
196 impl Writer for MockWriter {
197 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
198 self.sent.push(data.to_vec());
199 Ok(())
200 }
201 }
202
203 #[test]
204 fn interface_entry_construction() {
205 let entry = InterfaceEntry {
206 id: InterfaceId(1),
207 info: InterfaceInfo {
208 id: InterfaceId(1),
209 name: String::new(),
210 mode: constants::MODE_FULL,
211 out_capable: true,
212 in_capable: true,
213 bitrate: None,
214 announce_rate_target: None,
215 announce_rate_grace: 0,
216 announce_rate_penalty: 0.0,
217 announce_cap: constants::ANNOUNCE_CAP,
218 is_local_client: false,
219 wants_tunnel: false,
220 tunnel_id: None,
221 mtu: constants::MTU as u32,
222 ia_freq: 0.0,
223 started: 0.0,
224 ingress_control: false,
225 },
226 writer: Box::new(MockWriter::new()),
227 enabled: true,
228 online: false,
229 dynamic: false,
230 ifac: None,
231 stats: InterfaceStats::default(),
232 interface_type: String::new(),
233 };
234 assert_eq!(entry.id, InterfaceId(1));
235 assert!(!entry.online);
236 assert!(!entry.dynamic);
237 }
238
239 #[test]
240 fn mock_writer_captures_bytes() {
241 let mut writer = MockWriter::new();
242 writer.send_frame(b"hello").unwrap();
243 writer.send_frame(b"world").unwrap();
244 assert_eq!(writer.sent.len(), 2);
245 assert_eq!(writer.sent[0], b"hello");
246 assert_eq!(writer.sent[1], b"world");
247 }
248
249 #[test]
250 fn writer_send_frame_produces_output() {
251 let mut writer = MockWriter::new();
252 let data = vec![0x01, 0x02, 0x03];
253 writer.send_frame(&data).unwrap();
254 assert_eq!(writer.sent[0], data);
255 }
256}