1use std::io::{self};
7use std::net::{SocketAddr, UdpSocket};
8use std::os::unix::io::AsRawFd;
9use std::sync::{Arc, Mutex};
10use std::thread;
11use std::time::Duration;
12
13use rns_core::transport::types::InterfaceId;
14
15use crate::event::{Event, EventSender};
16use crate::interface::{lock_or_recover, ListenerControl, Writer};
17
18#[derive(Debug, Clone)]
20pub struct UdpConfig {
21 pub name: String,
22 pub listen_ip: Option<String>,
23 pub listen_port: Option<u16>,
24 pub forward_ip: Option<String>,
25 pub forward_port: Option<u16>,
26 pub interface_id: InterfaceId,
27 pub runtime: Arc<Mutex<UdpRuntime>>,
28}
29
30#[derive(Debug, Clone)]
31pub struct UdpRuntime {
32 pub forward_ip: Option<String>,
33 pub forward_port: Option<u16>,
34}
35
36impl UdpRuntime {
37 pub fn from_config(config: &UdpConfig) -> Self {
38 Self {
39 forward_ip: config.forward_ip.clone(),
40 forward_port: config.forward_port,
41 }
42 }
43}
44
45#[derive(Debug, Clone)]
46pub struct UdpRuntimeConfigHandle {
47 pub interface_name: String,
48 pub runtime: Arc<Mutex<UdpRuntime>>,
49 pub startup: UdpRuntime,
50}
51
52impl Default for UdpConfig {
53 fn default() -> Self {
54 let mut config = UdpConfig {
55 name: String::new(),
56 listen_ip: None,
57 listen_port: None,
58 forward_ip: None,
59 forward_port: None,
60 interface_id: InterfaceId(0),
61 runtime: Arc::new(Mutex::new(UdpRuntime {
62 forward_ip: None,
63 forward_port: None,
64 })),
65 };
66 let startup = UdpRuntime::from_config(&config);
67 config.runtime = Arc::new(Mutex::new(startup));
68 config
69 }
70}
71
72struct UdpWriter {
74 socket: UdpSocket,
75 runtime: Arc<Mutex<UdpRuntime>>,
76}
77
78impl Writer for UdpWriter {
79 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
80 let runtime = lock_or_recover(&self.runtime, "udp runtime").clone();
81 let target = match (runtime.forward_ip, runtime.forward_port) {
82 (Some(ip), Some(port)) => format!("{}:{}", ip, port)
83 .parse::<SocketAddr>()
84 .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?,
85 _ => {
86 return Err(io::Error::other(
87 "UDP interface has no forward target configured",
88 ))
89 }
90 };
91 self.socket.send_to(data, target)?;
92 Ok(())
93 }
94}
95
96pub fn start(config: UdpConfig, tx: EventSender) -> io::Result<Option<Box<dyn Writer>>> {
99 start_with_mark(config, tx, None).map(|(writer, _)| writer)
100}
101
102fn start_with_mark(
103 config: UdpConfig,
104 tx: EventSender,
105 underlay_mark: Option<u32>,
106) -> io::Result<(Option<Box<dyn Writer>>, ListenerControl)> {
107 let id = config.interface_id;
108 let control = ListenerControl::new();
109 {
110 let startup = UdpRuntime::from_config(&config);
111 *lock_or_recover(&config.runtime, "udp runtime") = startup;
112 }
113 let send_socket = UdpSocket::bind("0.0.0.0:0")?;
114 super::apply_underlay_mark(send_socket.as_raw_fd(), underlay_mark)?;
115 send_socket.set_broadcast(true)?;
116 let writer: Option<Box<dyn Writer>> = Some(Box::new(UdpWriter {
117 socket: send_socket,
118 runtime: Arc::clone(&config.runtime),
119 }));
120
121 if let (Some(ref bind_ip), Some(bind_port)) = (&config.listen_ip, config.listen_port) {
123 let bind_addr = format!("{}:{}", bind_ip, bind_port);
124 let recv_socket = UdpSocket::bind(&bind_addr)?;
125 super::apply_underlay_mark(recv_socket.as_raw_fd(), underlay_mark)?;
126 recv_socket.set_read_timeout(Some(Duration::from_millis(200)))?;
127
128 log::info!("[{}] UDP listening on {}", config.name, bind_addr);
129
130 let _ = tx.send(Event::InterfaceUp(id, None, None));
132
133 let name = config.name.clone();
134 let reader_control = control.clone();
135 thread::Builder::new()
136 .name(format!("udp-reader-{}", id.0))
137 .spawn(move || {
138 udp_reader_loop(recv_socket, id, name, tx, reader_control);
139 })?;
140 }
141
142 Ok((writer, control))
143}
144
145fn udp_reader_loop(
147 socket: UdpSocket,
148 id: InterfaceId,
149 name: String,
150 tx: EventSender,
151 control: ListenerControl,
152) {
153 let mut buf = [0u8; 2048];
154
155 loop {
156 if control.should_stop() {
157 return;
158 }
159 match socket.recv_from(&mut buf) {
160 Ok((n, _src)) => {
161 if n == 0 {
162 continue;
163 }
164 if tx
165 .send(Event::Frame {
166 interface_id: id,
167 data: buf[..n].to_vec(),
168 rssi: None,
169 snr: None,
170 })
171 .is_err()
172 {
173 return;
175 }
176 }
177 Err(e)
178 if matches!(
179 e.kind(),
180 io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut
181 ) =>
182 {
183 continue;
184 }
185 Err(e) => {
186 if control.should_stop() {
187 return;
188 }
189 log::warn!("[{}] recv error: {}", name, e);
190 let _ = tx.send(Event::InterfaceDown(id));
191 return;
192 }
193 }
194 }
195}
196
197use super::{InterfaceConfigData, InterfaceFactory, StartContext, StartResult};
200use rns_core::transport::types::InterfaceInfo;
201use std::collections::HashMap;
202
203pub struct UdpFactory;
205
206impl InterfaceFactory for UdpFactory {
207 fn type_name(&self) -> &str {
208 "UDPInterface"
209 }
210
211 fn parse_config(
212 &self,
213 name: &str,
214 id: InterfaceId,
215 params: &HashMap<String, String>,
216 ) -> Result<Box<dyn InterfaceConfigData>, String> {
217 let listen_ip = params.get("listen_ip").cloned();
218
219 let port_shorthand: Option<u16> = params.get("port").and_then(|v| v.parse().ok());
221
222 let listen_port: Option<u16> = params
223 .get("listen_port")
224 .and_then(|v| v.parse().ok())
225 .or(port_shorthand);
226
227 let forward_ip = params.get("forward_ip").cloned();
228
229 let forward_port: Option<u16> = params
230 .get("forward_port")
231 .and_then(|v| v.parse().ok())
232 .or(port_shorthand);
233
234 let mut config = UdpConfig {
235 name: name.to_string(),
236 listen_ip,
237 listen_port,
238 forward_ip,
239 forward_port,
240 interface_id: id,
241 runtime: Arc::new(Mutex::new(UdpRuntime {
242 forward_ip: None,
243 forward_port: None,
244 })),
245 };
246 let startup = UdpRuntime::from_config(&config);
247 config.runtime = Arc::new(Mutex::new(startup));
248 Ok(Box::new(config))
249 }
250
251 fn start(
252 &self,
253 config: Box<dyn InterfaceConfigData>,
254 ctx: StartContext,
255 ) -> io::Result<StartResult> {
256 let udp_config = *config
257 .into_any()
258 .downcast::<UdpConfig>()
259 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "wrong config type"))?;
260
261 let id = udp_config.interface_id;
262 let name = udp_config.name.clone();
263 let out_capable = udp_config.forward_ip.is_some();
264 let in_capable = udp_config.listen_ip.is_some();
265
266 let info = InterfaceInfo {
267 id,
268 name,
269 mode: ctx.mode,
270 gravity: ctx.gravity,
271 recursive_prs: ctx.recursive_prs,
272 announces_from_internal: ctx.announces_from_internal,
273 announces_to_internal: ctx.announces_to_internal,
274 out_capable,
275 in_capable,
276 bitrate: Some(10_000_000),
277 airtime_profile: None,
278 announce_rate_target: None,
279 announce_rate_grace: 0,
280 announce_rate_penalty: 0.0,
281 announce_cap: rns_core::constants::ANNOUNCE_CAP,
282 is_local_client: false,
283 wants_tunnel: false,
284 tunnel_id: None,
285 mtu: 1400,
286 ingress_control: rns_core::transport::types::IngressControlConfig::enabled(),
287 ia_freq: 0.0,
288 ip_freq: 0.0,
289 op_freq: 0.0,
290 op_samples: 0,
291 started: crate::time::now(),
292 };
293
294 let (maybe_writer, control) = start_with_mark(udp_config, ctx.tx, ctx.underlay_mark)?;
295
296 let writer: Box<dyn Writer> = maybe_writer
297 .ok_or_else(|| io::Error::other("UDPInterface did not provide a writer"))?;
298
299 Ok(StartResult::Simple {
300 control: Some(control),
301 id,
302 info,
303 writer,
304 interface_type_name: "UDPInterface".to_string(),
305 })
306 }
307}
308
309pub(crate) fn udp_runtime_handle_from_config(config: &UdpConfig) -> UdpRuntimeConfigHandle {
310 UdpRuntimeConfigHandle {
311 interface_name: config.name.clone(),
312 runtime: Arc::clone(&config.runtime),
313 startup: UdpRuntime::from_config(config),
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320
321 #[test]
322 fn detach_stops_idle_udp_listener() {
323 let socket = UdpSocket::bind("127.0.0.1:0").unwrap();
324 socket
325 .set_read_timeout(Some(Duration::from_millis(200)))
326 .unwrap();
327 let (tx, rx) = crate::event::channel();
328 let control = ListenerControl::new();
329 let reader_control = control.clone();
330
331 let reader = thread::spawn(move || {
332 udp_reader_loop(
333 socket,
334 InterfaceId(76),
335 "detach-test".into(),
336 tx,
337 reader_control,
338 )
339 });
340 thread::sleep(Duration::from_millis(50));
341 control.request_stop();
342 reader.join().unwrap();
343 assert!(rx.try_recv().is_err());
344 }
345 use std::net::UdpSocket;
346 use std::time::Duration;
347
348 fn find_free_port() -> u16 {
349 crate::test_support::port()
350 }
351
352 #[test]
353 fn bind_and_receive() {
354 let port = find_free_port();
355 let (tx, rx) = crate::event::channel();
356
357 let config = UdpConfig {
358 name: "test-udp".into(),
359 listen_ip: Some("127.0.0.1".into()),
360 listen_port: Some(port),
361 forward_ip: None,
362 forward_port: None,
363 interface_id: InterfaceId(10),
364 ..UdpConfig::default()
365 };
366
367 let _writer = start(config, tx).unwrap();
368
369 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
371
372 let sender = UdpSocket::bind("127.0.0.1:0").unwrap();
374 let payload = b"hello udp";
375 sender
376 .send_to(payload, format!("127.0.0.1:{}", port))
377 .unwrap();
378
379 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
381 match event {
382 Event::Frame {
383 interface_id,
384 data,
385 rssi: _,
386 snr: _,
387 } => {
388 assert_eq!(interface_id, InterfaceId(10));
389 assert_eq!(data, payload);
390 }
391 other => panic!("expected Frame, got {:?}", other),
392 }
393 }
394
395 #[test]
396 fn send_broadcast() {
397 let recv_port = find_free_port();
398 let (tx, _rx) = crate::event::channel();
399
400 let config = UdpConfig {
401 name: "test-udp-send".into(),
402 listen_ip: None,
403 listen_port: None,
404 forward_ip: Some("127.0.0.1".into()),
405 forward_port: Some(recv_port),
406 interface_id: InterfaceId(11),
407 ..UdpConfig::default()
408 };
409
410 let writer = start(config, tx).unwrap();
411 let mut writer = writer.unwrap();
412
413 let receiver = UdpSocket::bind(format!("127.0.0.1:{}", recv_port)).unwrap();
415 receiver
416 .set_read_timeout(Some(Duration::from_secs(2)))
417 .unwrap();
418
419 let payload = b"broadcast data";
421 writer.send_frame(payload).unwrap();
422
423 let mut buf = [0u8; 256];
425 let (n, _) = receiver.recv_from(&mut buf).unwrap();
426 assert_eq!(&buf[..n], payload);
427 }
428
429 #[test]
430 fn round_trip() {
431 let listen_port = find_free_port();
432 let forward_port = find_free_port();
433 let (tx, rx) = crate::event::channel();
434
435 let config = UdpConfig {
436 name: "test-udp-rt".into(),
437 listen_ip: Some("127.0.0.1".into()),
438 listen_port: Some(listen_port),
439 forward_ip: Some("127.0.0.1".into()),
440 forward_port: Some(forward_port),
441 interface_id: InterfaceId(12),
442 ..UdpConfig::default()
443 };
444
445 let writer = start(config, tx).unwrap();
446 assert!(writer.is_some());
447
448 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
450
451 let sender = UdpSocket::bind("127.0.0.1:0").unwrap();
453 sender
454 .send_to(b"ping", format!("127.0.0.1:{}", listen_port))
455 .unwrap();
456
457 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
458 match event {
459 Event::Frame { data, .. } => assert_eq!(data, b"ping"),
460 other => panic!("expected Frame, got {:?}", other),
461 }
462 }
463
464 #[test]
465 fn multiple_datagrams() {
466 let port = find_free_port();
467 let (tx, rx) = crate::event::channel();
468
469 let config = UdpConfig {
470 name: "test-udp-multi".into(),
471 listen_ip: Some("127.0.0.1".into()),
472 listen_port: Some(port),
473 forward_ip: None,
474 forward_port: None,
475 interface_id: InterfaceId(13),
476 ..UdpConfig::default()
477 };
478
479 let _writer = start(config, tx).unwrap();
480
481 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
483
484 let sender = UdpSocket::bind("127.0.0.1:0").unwrap();
485 for i in 0..5u8 {
486 sender.send_to(&[i], format!("127.0.0.1:{}", port)).unwrap();
487 }
488
489 for i in 0..5u8 {
490 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
491 match event {
492 Event::Frame { data, .. } => assert_eq!(data, vec![i]),
493 other => panic!("expected Frame, got {:?}", other),
494 }
495 }
496 }
497
498 #[test]
499 fn empty_datagram_is_ignored_before_transport_ingress() {
500 let port = find_free_port();
501 let (tx, rx) = crate::event::channel();
502 let config = UdpConfig {
503 name: "test-udp-empty".into(),
504 listen_ip: Some("127.0.0.1".into()),
505 listen_port: Some(port),
506 interface_id: InterfaceId(14),
507 ..UdpConfig::default()
508 };
509 let _writer = start(config, tx).unwrap();
510 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
511
512 let sender = UdpSocket::bind("127.0.0.1:0").unwrap();
513 let target = format!("127.0.0.1:{port}");
514 sender.send_to(&[], &target).unwrap();
515 sender.send_to(b"after-empty", &target).unwrap();
516
517 match rx.recv_timeout(Duration::from_secs(2)).unwrap() {
518 Event::Frame { data, .. } => assert_eq!(data, b"after-empty"),
519 other => panic!("expected non-empty Frame, got {other:?}"),
520 }
521 }
522
523 #[test]
524 fn writer_send_to() {
525 let recv_port = find_free_port();
526
527 let receiver = UdpSocket::bind(format!("127.0.0.1:{}", recv_port)).unwrap();
529 receiver
530 .set_read_timeout(Some(Duration::from_secs(2)))
531 .unwrap();
532
533 let send_socket = UdpSocket::bind("127.0.0.1:0").unwrap();
535 send_socket.set_broadcast(true).unwrap();
536 let mut writer = UdpWriter {
537 socket: send_socket,
538 runtime: Arc::new(Mutex::new(UdpRuntime {
539 forward_ip: Some("127.0.0.1".into()),
540 forward_port: Some(recv_port),
541 })),
542 };
543
544 let payload = vec![0xAA, 0xBB, 0xCC];
545 writer.send_frame(&payload).unwrap();
546
547 let mut buf = [0u8; 256];
548 let (n, _) = receiver.recv_from(&mut buf).unwrap();
549 assert_eq!(&buf[..n], &payload);
550 }
551}