1use std::io;
8use std::net::{SocketAddr, UdpSocket};
9use std::sync::atomic::{AtomicBool, Ordering};
10use std::sync::Arc;
11use std::thread;
12use std::time::Duration;
13
14use rns_core::transport::types::{InterfaceId, InterfaceInfo};
15
16use crate::event::{Event, EventSender, LoopContext, LoopSource, SourceStatus};
17use crate::interface::Writer;
18
19use super::puncher;
20
21const KEEPALIVE_INTERVAL: Duration = Duration::from_secs(30);
23
24const INACTIVITY_TIMEOUT: Duration = Duration::from_secs(120);
26
27pub struct UdpDirectWriter {
31 socket: Arc<UdpSocket>,
32 peer_addr: SocketAddr,
33 running: Arc<AtomicBool>,
34 inline: bool,
37}
38
39impl Writer for UdpDirectWriter {
40 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
41 match self.socket.send_to(data, self.peer_addr) {
42 Err(e) if self.inline && e.kind() == io::ErrorKind::WouldBlock => Ok(()),
45 result => result.map(|_| ()),
46 }
47 }
48
49 fn send_frame_confirmed(&mut self, data: &[u8]) -> io::Result<()> {
50 self.socket.send_to(data, self.peer_addr).map(|_| ())
51 }
52
53 fn enqueue_confirmed(
54 &mut self,
55 data: &[u8],
56 completion: &mut Option<crate::link_send::Completion>,
57 ) -> io::Result<()> {
58 self.socket.send_to(data, self.peer_addr)?;
61 if let Some(completion) = completion.take() {
62 completion.finish(Ok(()));
63 }
64 Ok(())
65 }
66
67 fn sends_inline(&self) -> bool {
68 self.inline
69 }
70}
71
72impl Drop for UdpDirectWriter {
73 fn drop(&mut self) {
74 self.running.store(false, Ordering::Relaxed);
75 }
76}
77
78pub fn start_direct_interface(
84 socket: UdpSocket,
85 peer_addr: SocketAddr,
86 interface_id: InterfaceId,
87 session_id: [u8; 16],
88 punch_token: [u8; 32],
89 tx: EventSender,
90) -> io::Result<(Box<dyn Writer>, InterfaceInfo)> {
91 let socket = Arc::new(socket);
92 let inline = crate::engine::FOLD_IO;
93 if inline {
94 socket.set_nonblocking(true)?;
95 } else {
96 socket.set_read_timeout(Some(Duration::from_secs(1)))?;
97 }
98
99 let running = Arc::new(AtomicBool::new(true));
100
101 let writer = UdpDirectWriter {
102 socket: socket.clone(),
103 peer_addr,
104 running: running.clone(),
105 inline,
106 };
107
108 let name = format!(
109 "DirectPeer/{:02x}{:02x}{:02x}{:02x}",
110 session_id[0], session_id[1], session_id[2], session_id[3]
111 );
112
113 let info = InterfaceInfo {
114 stamp_costs: Default::default(),
115 id: interface_id,
116 name: name.clone(),
117 mode: rns_core::constants::MODE_FULL,
118 gravity: 0,
119 recursive_prs: false,
120 announces_from_internal: true,
121 announces_to_internal: None,
122 out_capable: true,
123 in_capable: true,
124 bitrate: None,
125 airtime_profile: None,
126 announce_rate_target: None,
127 announce_rate_grace: 0,
128 announce_rate_penalty: 0.0,
129 announce_cap: 1.0,
130 is_local_client: false,
131 wants_tunnel: false,
132 tunnel_id: None,
133 mtu: 1400,
134 ia_freq: 0.0,
135 ip_freq: 0.0,
136 op_freq: 0.0,
137 op_samples: 0,
138 started: 0.0,
139 ingress_control: rns_core::transport::types::IngressControlConfig::disabled(),
140 };
141
142 if inline {
143 let now = std::time::Instant::now();
144 tx.register_source(
145 crate::interface::loop_writer::read_key(interface_id),
146 Box::new(DirectSource {
147 socket,
148 peer_addr,
149 interface_id,
150 keepalive: puncher::build_keepalive_packet(&session_id, &punch_token),
151 last_inbound: now,
152 last_keepalive: now,
153 tx: tx.clone(),
154 running,
155 }),
156 )?;
157 return Ok((Box::new(writer), info));
158 }
159
160 let running_clone = running.clone();
161
162 thread::Builder::new()
164 .name(format!("direct-udp-{}", &name))
165 .spawn(move || {
166 run_reader(
167 socket,
168 peer_addr,
169 interface_id,
170 session_id,
171 punch_token,
172 tx,
173 running_clone,
174 );
175 })?;
176
177 Ok((Box::new(writer), info))
178}
179
180fn run_reader(
181 socket: Arc<UdpSocket>,
182 peer_addr: SocketAddr,
183 interface_id: InterfaceId,
184 session_id: [u8; 16],
185 punch_token: [u8; 32],
186 tx: EventSender,
187 running: Arc<AtomicBool>,
188) {
189 let mut buf = [0u8; 2048];
190 let mut last_inbound = std::time::Instant::now();
191 let mut last_keepalive = std::time::Instant::now();
192 let keepalive_pkt = puncher::build_keepalive_packet(&session_id, &punch_token);
193
194 while running.load(Ordering::Relaxed) {
195 if last_keepalive.elapsed() >= KEEPALIVE_INTERVAL {
197 let _ = socket.send_to(&keepalive_pkt, peer_addr);
198 last_keepalive = std::time::Instant::now();
199 }
200
201 if last_inbound.elapsed() >= INACTIVITY_TIMEOUT {
203 log::info!("[{}] Direct UDP interface timed out", interface_id.0);
204 let _ = tx.send(Event::InterfaceDown(interface_id));
205 break;
206 }
207
208 match socket.recv_from(&mut buf) {
209 Ok((len, src)) => {
210 if src != peer_addr {
212 continue;
213 }
214 last_inbound = std::time::Instant::now();
215
216 if len >= 4 && (buf[..4] == *b"RNSH" || buf[..4] == *b"RNSA") {
218 continue;
219 }
220
221 if len > 0 {
223 let _ = tx.send(Event::Frame {
224 interface_id,
225 data: buf[..len].into(),
226 rssi: None,
227 snr: None,
228 });
229 }
230 }
231 Err(ref e)
232 if e.kind() == io::ErrorKind::WouldBlock || e.kind() == io::ErrorKind::TimedOut =>
233 {
234 continue;
235 }
236 Err(e) => {
237 log::warn!("[{}] Direct UDP recv error: {}", interface_id.0, e);
238 let _ = tx.send(Event::InterfaceDown(interface_id));
239 break;
240 }
241 }
242 }
243}
244
245const READ_BUDGET: usize = 64;
247
248struct DirectSource {
251 socket: Arc<UdpSocket>,
252 peer_addr: SocketAddr,
253 interface_id: InterfaceId,
254 keepalive: Vec<u8>,
255 last_inbound: std::time::Instant,
256 last_keepalive: std::time::Instant,
257 tx: EventSender,
258 running: Arc<AtomicBool>,
260}
261
262impl LoopSource for DirectSource {
263 fn fd(&self) -> std::os::fd::RawFd {
264 use std::os::fd::AsRawFd;
265 self.socket.as_raw_fd()
266 }
267
268 fn stopped(&self) -> bool {
269 !self.running.load(Ordering::Relaxed)
270 }
271
272 fn on_ready(&mut self, cx: &mut LoopContext<'_>) -> SourceStatus {
273 if self.stopped() {
274 return SourceStatus::Remove;
275 }
276 if !self.tx.inbound_data_has_room() {
277 return SourceStatus::Suspend;
278 }
279 let mut frames = Vec::new();
280 for _ in 0..READ_BUDGET {
281 let (len, src) = match self.socket.recv_from(cx.buf) {
282 Ok(received) => received,
283 Err(e) if e.kind() == io::ErrorKind::WouldBlock => break,
284 Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
285 Err(e) => {
286 log::warn!("[{}] Direct UDP recv error: {}", self.interface_id.0, e);
287 let _ = self.tx.push_from_loop(frames);
288 let _ = self
289 .tx
290 .push_from_loop([Event::InterfaceDown(self.interface_id)]);
291 return SourceStatus::Remove;
292 }
293 };
294 if src != self.peer_addr {
296 continue;
297 }
298 self.last_inbound = std::time::Instant::now();
299 if len >= 4 && (cx.buf[..4] == *b"RNSH" || cx.buf[..4] == *b"RNSA") {
301 continue;
302 }
303 if len > 0 {
304 frames.push(Event::Frame {
305 interface_id: self.interface_id,
306 data: cx.buf[..len].into(),
307 rssi: None,
308 snr: None,
309 });
310 }
311 }
312 match self.tx.push_from_loop(frames) {
313 Ok(true) => SourceStatus::Rearm,
314 Ok(false) => SourceStatus::Suspend,
315 Err(_) => SourceStatus::Remove,
316 }
317 }
318
319 fn deadline(&self) -> Option<std::time::Instant> {
320 Some((self.last_keepalive + KEEPALIVE_INTERVAL).min(self.last_inbound + INACTIVITY_TIMEOUT))
321 }
322
323 fn on_timer(&mut self, _cx: &mut LoopContext<'_>) -> SourceStatus {
324 if self.stopped() {
325 return SourceStatus::Remove;
326 }
327 let now = std::time::Instant::now();
328 if now >= self.last_inbound + INACTIVITY_TIMEOUT {
329 log::info!("[{}] Direct UDP interface timed out", self.interface_id.0);
330 let _ = self
331 .tx
332 .push_from_loop([Event::InterfaceDown(self.interface_id)]);
333 return SourceStatus::Remove;
334 }
335 if now >= self.last_keepalive + KEEPALIVE_INTERVAL {
336 let _ = self.socket.send_to(&self.keepalive, self.peer_addr);
337 self.last_keepalive = now;
338 }
339 SourceStatus::Rearm
340 }
341}
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346
347 #[test]
348 fn test_udp_direct_writer() {
349 let sock_a = UdpSocket::bind("127.0.0.1:0").unwrap();
351 let sock_b = UdpSocket::bind("127.0.0.1:0").unwrap();
352 let addr_b = sock_b.local_addr().unwrap();
353 sock_b
354 .set_read_timeout(Some(Duration::from_secs(1)))
355 .unwrap();
356
357 let mut writer = UdpDirectWriter {
358 socket: Arc::new(sock_a),
359 peer_addr: addr_b,
360 running: Arc::new(AtomicBool::new(true)),
361 inline: false,
362 };
363
364 let data = b"hello direct peer";
366 writer.send_frame(data).unwrap();
367
368 let mut buf = [0u8; 64];
370 let (len, _src) = sock_b.recv_from(&mut buf).unwrap();
371 assert_eq!(&buf[..len], data);
372 }
373
374 #[test]
375 fn folded_direct_link_runs_on_the_driver_loop() {
376 if !crate::engine::FOLD_IO {
377 return;
378 }
379 let local = UdpSocket::bind("127.0.0.1:0").unwrap();
380 let peer = UdpSocket::bind("127.0.0.1:0").unwrap();
381 let peer_addr = peer.local_addr().unwrap();
382 let local_addr = local.local_addr().unwrap();
383 let (tx, rx) = crate::event::channel();
384 let (writer, _info) = start_direct_interface(
385 local,
386 peer_addr,
387 InterfaceId(910),
388 [1; 16],
389 [2; 32],
390 tx.clone(),
391 )
392 .unwrap();
393 assert!(writer.sends_inline());
394 assert!(!std::fs::read_dir("/proc/self/task").unwrap().any(|task| {
396 std::fs::read_to_string(task.unwrap().path().join("comm"))
397 .is_ok_and(|comm| comm.starts_with("direct-udp-"))
398 }));
399 let (mut writer, _) =
400 crate::interface::wrap_async_writer(writer, InterfaceId(910), "direct", tx, 16);
401
402 let stranger = UdpSocket::bind("127.0.0.1:0").unwrap();
404 stranger.send_to(b"not from the peer", local_addr).unwrap();
405 peer.send_to(b"RNSH keepalive", local_addr).unwrap();
406 peer.send_to(b"from the peer", local_addr).unwrap();
407 match rx.recv_timeout(Duration::from_secs(2)).unwrap() {
408 Event::Frame {
409 interface_id, data, ..
410 } => {
411 assert_eq!(interface_id, InterfaceId(910));
412 assert_eq!(&*data, b"from the peer");
413 }
414 other => panic!("expected Frame, got {other:?}"),
415 }
416 assert!(rx.recv_timeout(Duration::from_millis(100)).is_err());
417
418 writer.send_frame(b"to the peer").unwrap();
420 let mut buf = [0u8; 64];
421 peer.set_read_timeout(Some(Duration::from_secs(1))).unwrap();
422 let (n, _) = peer.recv_from(&mut buf).unwrap();
423 assert_eq!(&buf[..n], b"to the peer");
424 }
425}