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 online: bool,
81 pub dynamic: bool,
84 pub ifac: Option<IfacState>,
86 pub stats: InterfaceStats,
88 pub interface_type: String,
90}
91
92pub enum StartResult {
94 Simple {
96 id: InterfaceId,
97 info: InterfaceInfo,
98 writer: Box<dyn Writer>,
99 interface_type_name: String,
100 },
101 Listener,
103 Multi(Vec<SubInterface>),
105}
106
107pub struct SubInterface {
109 pub id: InterfaceId,
110 pub info: InterfaceInfo,
111 pub writer: Box<dyn Writer>,
112 pub interface_type_name: String,
113}
114
115pub struct StartContext {
117 pub tx: EventSender,
118 pub next_dynamic_id: Arc<AtomicU64>,
119 pub mode: u8,
120}
121
122pub trait InterfaceConfigData: Send + Any {
124 fn into_any(self: Box<Self>) -> Box<dyn Any>;
125}
126
127impl<T: Send + 'static> InterfaceConfigData for T {
128 fn into_any(self: Box<Self>) -> Box<dyn Any> {
129 self
130 }
131}
132
133pub trait InterfaceFactory: Send + Sync {
135 fn type_name(&self) -> &str;
137
138 fn default_ifac_size(&self) -> usize {
140 16
141 }
142
143 fn parse_config(
145 &self,
146 name: &str,
147 id: InterfaceId,
148 params: &HashMap<String, String>,
149 ) -> Result<Box<dyn InterfaceConfigData>, String>;
150
151 fn start(
153 &self,
154 config: Box<dyn InterfaceConfigData>,
155 ctx: StartContext,
156 ) -> io::Result<StartResult>;
157}
158
159impl InterfaceStatusView for InterfaceEntry {
160 fn id(&self) -> InterfaceId {
161 self.id
162 }
163 fn info(&self) -> &InterfaceInfo {
164 &self.info
165 }
166 fn online(&self) -> bool {
167 self.online
168 }
169 fn stats(&self) -> &InterfaceStats {
170 &self.stats
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177 use rns_core::constants;
178
179 struct MockWriter {
180 sent: Vec<Vec<u8>>,
181 }
182
183 impl MockWriter {
184 fn new() -> Self {
185 MockWriter { sent: Vec::new() }
186 }
187 }
188
189 impl Writer for MockWriter {
190 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
191 self.sent.push(data.to_vec());
192 Ok(())
193 }
194 }
195
196 #[test]
197 fn interface_entry_construction() {
198 let entry = InterfaceEntry {
199 id: InterfaceId(1),
200 info: InterfaceInfo {
201 id: InterfaceId(1),
202 name: String::new(),
203 mode: constants::MODE_FULL,
204 out_capable: true,
205 in_capable: true,
206 bitrate: None,
207 announce_rate_target: None,
208 announce_rate_grace: 0,
209 announce_rate_penalty: 0.0,
210 announce_cap: constants::ANNOUNCE_CAP,
211 is_local_client: false,
212 wants_tunnel: false,
213 tunnel_id: None,
214 mtu: constants::MTU as u32,
215 ia_freq: 0.0,
216 started: 0.0,
217 ingress_control: false,
218 },
219 writer: Box::new(MockWriter::new()),
220 online: false,
221 dynamic: false,
222 ifac: None,
223 stats: InterfaceStats::default(),
224 interface_type: String::new(),
225 };
226 assert_eq!(entry.id, InterfaceId(1));
227 assert!(!entry.online);
228 assert!(!entry.dynamic);
229 }
230
231 #[test]
232 fn mock_writer_captures_bytes() {
233 let mut writer = MockWriter::new();
234 writer.send_frame(b"hello").unwrap();
235 writer.send_frame(b"world").unwrap();
236 assert_eq!(writer.sent.len(), 2);
237 assert_eq!(writer.sent[0], b"hello");
238 assert_eq!(writer.sent[1], b"world");
239 }
240
241 #[test]
242 fn writer_send_frame_produces_output() {
243 let mut writer = MockWriter::new();
244 let data = vec![0x01, 0x02, 0x03];
245 writer.send_frame(&data).unwrap();
246 assert_eq!(writer.sent[0], data);
247 }
248}