1#[cfg(feature = "iface-tcp")]
4pub mod tcp;
5#[cfg(feature = "iface-tcp")]
6pub mod tcp_server;
7#[cfg(feature = "iface-udp")]
8pub mod udp;
9#[cfg(feature = "iface-local")]
10pub mod local;
11#[cfg(feature = "iface-serial")]
12pub mod serial_iface;
13#[cfg(feature = "iface-kiss")]
14pub mod kiss_iface;
15#[cfg(feature = "iface-pipe")]
16pub mod pipe;
17#[cfg(feature = "iface-rnode")]
18pub mod rnode;
19#[cfg(feature = "iface-backbone")]
20pub mod backbone;
21#[cfg(feature = "iface-auto")]
22pub mod auto;
23#[cfg(feature = "iface-i2p")]
24pub mod i2p;
25pub mod registry;
26
27use std::any::Any;
28use std::collections::HashMap;
29use std::io;
30use std::sync::atomic::AtomicU64;
31use std::sync::Arc;
32
33use rns_core::transport::types::{InterfaceId, InterfaceInfo};
34use crate::event::EventSender;
35use crate::ifac::IfacState;
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 { 16 }
140
141 fn parse_config(
143 &self,
144 name: &str,
145 id: InterfaceId,
146 params: &HashMap<String, String>,
147 ) -> Result<Box<dyn InterfaceConfigData>, String>;
148
149 fn start(
151 &self,
152 config: Box<dyn InterfaceConfigData>,
153 ctx: StartContext,
154 ) -> io::Result<StartResult>;
155}
156
157impl InterfaceStatusView for InterfaceEntry {
158 fn id(&self) -> InterfaceId { self.id }
159 fn info(&self) -> &InterfaceInfo { &self.info }
160 fn online(&self) -> bool { self.online }
161 fn stats(&self) -> &InterfaceStats { &self.stats }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use rns_core::constants;
168
169 struct MockWriter {
170 sent: Vec<Vec<u8>>,
171 }
172
173 impl MockWriter {
174 fn new() -> Self {
175 MockWriter { sent: Vec::new() }
176 }
177 }
178
179 impl Writer for MockWriter {
180 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
181 self.sent.push(data.to_vec());
182 Ok(())
183 }
184 }
185
186 #[test]
187 fn interface_entry_construction() {
188 let entry = InterfaceEntry {
189 id: InterfaceId(1),
190 info: InterfaceInfo {
191 id: InterfaceId(1),
192 name: String::new(),
193 mode: constants::MODE_FULL,
194 out_capable: true,
195 in_capable: true,
196 bitrate: None,
197 announce_rate_target: None,
198 announce_rate_grace: 0,
199 announce_rate_penalty: 0.0,
200 announce_cap: constants::ANNOUNCE_CAP,
201 is_local_client: false,
202 wants_tunnel: false,
203 tunnel_id: None,
204 mtu: constants::MTU as u32,
205 ia_freq: 0.0,
206 started: 0.0,
207 ingress_control: false,
208 },
209 writer: Box::new(MockWriter::new()),
210 online: false,
211 dynamic: false,
212 ifac: None,
213 stats: InterfaceStats::default(),
214 interface_type: String::new(),
215 };
216 assert_eq!(entry.id, InterfaceId(1));
217 assert!(!entry.online);
218 assert!(!entry.dynamic);
219 }
220
221 #[test]
222 fn mock_writer_captures_bytes() {
223 let mut writer = MockWriter::new();
224 writer.send_frame(b"hello").unwrap();
225 writer.send_frame(b"world").unwrap();
226 assert_eq!(writer.sent.len(), 2);
227 assert_eq!(writer.sent[0], b"hello");
228 assert_eq!(writer.sent[1], b"world");
229 }
230
231 #[test]
232 fn writer_send_frame_produces_output() {
233 let mut writer = MockWriter::new();
234 let data = vec![0x01, 0x02, 0x03];
235 writer.send_frame(&data).unwrap();
236 assert_eq!(writer.sent[0], data);
237 }
238}