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