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, LoopContext, LoopSource, SourceStatus};
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
72const LOOP_READ_BUDGET: usize = 64;
75
76const MAX_DATAGRAM: usize = 2048;
78
79struct UdpWriter {
81 socket: UdpSocket,
82 runtime: Arc<Mutex<UdpRuntime>>,
83 inline: bool,
85}
86
87impl UdpWriter {
88 fn target(&self) -> io::Result<SocketAddr> {
89 let runtime = lock_or_recover(&self.runtime, "udp runtime");
90 match (runtime.forward_ip.as_deref(), runtime.forward_port) {
91 (Some(ip), Some(port)) => ip
92 .parse::<std::net::IpAddr>()
93 .map(|ip| SocketAddr::new(ip, port))
94 .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e)),
95 _ => Err(io::Error::other(
96 "UDP interface has no forward target configured",
97 )),
98 }
99 }
100
101 fn send(&mut self, data: &[u8]) -> io::Result<()> {
102 let target = self.target()?;
103 self.socket.send_to(data, target)?;
104 Ok(())
105 }
106}
107
108impl Writer for UdpWriter {
109 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
110 match self.send(data) {
111 Err(e) if self.inline && e.kind() == io::ErrorKind::WouldBlock => {
114 log::debug!("UDP send buffer full, datagram dropped");
115 Ok(())
116 }
117 result => result,
118 }
119 }
120
121 fn send_frame_confirmed(&mut self, data: &[u8]) -> io::Result<()> {
122 self.send(data)
123 }
124
125 fn enqueue_confirmed(
126 &mut self,
127 data: &[u8],
128 completion: &mut Option<crate::link_send::Completion>,
129 ) -> io::Result<()> {
130 self.send(data)?;
133 if let Some(completion) = completion.take() {
134 completion.finish(Ok(()));
135 }
136 Ok(())
137 }
138
139 fn sends_inline(&self) -> bool {
140 self.inline
141 }
142}
143
144pub fn start(config: UdpConfig, tx: EventSender) -> io::Result<Option<Box<dyn Writer>>> {
147 start_with_mark(config, tx, None).map(|(writer, _)| writer)
148}
149
150fn start_with_mark(
151 config: UdpConfig,
152 tx: EventSender,
153 underlay_mark: Option<u32>,
154) -> io::Result<(Option<Box<dyn Writer>>, ListenerControl)> {
155 let id = config.interface_id;
156 let control = ListenerControl::new();
157 {
158 let startup = UdpRuntime::from_config(&config);
159 *lock_or_recover(&config.runtime, "udp runtime") = startup;
160 }
161 let inline = crate::engine::FOLD_IO;
162 let send_socket = UdpSocket::bind("0.0.0.0:0")?;
163 super::apply_underlay_mark(send_socket.as_raw_fd(), underlay_mark)?;
164 send_socket.set_broadcast(true)?;
165 send_socket.set_nonblocking(inline)?;
166 let writer: Option<Box<dyn Writer>> = Some(Box::new(UdpWriter {
167 socket: send_socket,
168 runtime: Arc::clone(&config.runtime),
169 inline,
170 }));
171
172 if let (Some(ref bind_ip), Some(bind_port)) = (&config.listen_ip, config.listen_port) {
174 let bind_addr = format!("{}:{}", bind_ip, bind_port);
175 let recv_socket = UdpSocket::bind(&bind_addr)?;
176 super::apply_underlay_mark(recv_socket.as_raw_fd(), underlay_mark)?;
177 log::info!("[{}] UDP listening on {}", config.name, bind_addr);
178
179 let _ = tx.send(Event::InterfaceUp(id, None, None));
181
182 if inline {
183 recv_socket.set_nonblocking(true)?;
184 let source = UdpSource {
185 socket: recv_socket,
186 id,
187 name: config.name.clone(),
188 tx: tx.clone(),
189 control: control.clone(),
190 };
191 tx.register_source(
192 crate::interface::loop_writer::read_key(id),
193 Box::new(source),
194 )?;
195 return Ok((writer, control));
196 }
197 recv_socket.set_read_timeout(Some(Duration::from_millis(200)))?;
198
199 let name = config.name.clone();
200 let reader_control = control.clone();
201 thread::Builder::new()
202 .name(format!("udp-reader-{}", id.0))
203 .spawn(move || {
204 udp_reader_loop(recv_socket, id, name, tx, reader_control);
205 })?;
206 }
207
208 Ok((writer, control))
209}
210
211fn udp_reader_loop(
213 socket: UdpSocket,
214 id: InterfaceId,
215 name: String,
216 tx: EventSender,
217 control: ListenerControl,
218) {
219 let mut buf = [0u8; 2048];
220
221 loop {
222 if control.should_stop() {
223 return;
224 }
225 match socket.recv_from(&mut buf) {
226 Ok((n, _src)) => {
227 if n == 0 {
228 continue;
229 }
230 if tx
231 .send(Event::Frame {
232 interface_id: id,
233 data: buf[..n].into(),
234 rssi: None,
235 snr: None,
236 })
237 .is_err()
238 {
239 return;
241 }
242 }
243 Err(e)
244 if matches!(
245 e.kind(),
246 io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut
247 ) =>
248 {
249 continue;
250 }
251 Err(e) => {
252 if control.should_stop() {
253 return;
254 }
255 log::warn!("[{}] recv error: {}", name, e);
256 let _ = tx.send(Event::InterfaceDown(id));
257 return;
258 }
259 }
260 }
261}
262
263struct UdpSource {
266 socket: UdpSocket,
267 id: InterfaceId,
268 name: String,
269 tx: EventSender,
270 control: ListenerControl,
271}
272
273impl LoopSource for UdpSource {
274 fn fd(&self) -> std::os::fd::RawFd {
275 self.socket.as_raw_fd()
276 }
277
278 fn stopped(&self) -> bool {
279 self.control.should_stop()
280 }
281
282 fn on_ready(&mut self, cx: &mut LoopContext<'_>) -> SourceStatus {
283 if self.control.should_stop() {
284 return SourceStatus::Remove;
285 }
286 if !self.tx.inbound_data_has_room() {
287 return SourceStatus::Suspend;
288 }
289 let buf = &mut cx.buf[..MAX_DATAGRAM];
290 let mut frames = Vec::new();
291 let mut status = SourceStatus::Rearm;
292 for _ in 0..LOOP_READ_BUDGET {
293 match self.socket.recv_from(buf) {
294 Ok((0, _)) => {}
295 Ok((n, _)) => frames.push(Event::Frame {
296 interface_id: self.id,
297 data: buf[..n].into(),
298 rssi: None,
299 snr: None,
300 }),
301 Err(e) if e.kind() == io::ErrorKind::WouldBlock => break,
302 Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
303 Err(e) => {
304 log::warn!("[{}] recv error: {}", self.name, e);
305 frames.push(Event::InterfaceDown(self.id));
306 status = SourceStatus::Remove;
307 break;
308 }
309 }
310 }
311 match self.tx.push_from_loop(frames) {
312 Ok(true) => status,
313 Ok(false) if status == SourceStatus::Rearm => SourceStatus::Suspend,
314 Ok(false) => status,
315 Err(_) => SourceStatus::Remove,
316 }
317 }
318}
319
320use super::{InterfaceConfigData, InterfaceFactory, StartContext, StartResult};
323use rns_core::transport::types::InterfaceInfo;
324use std::collections::HashMap;
325
326pub struct UdpFactory;
328
329impl InterfaceFactory for UdpFactory {
330 fn type_name(&self) -> &str {
331 "UDPInterface"
332 }
333
334 fn parse_config(
335 &self,
336 name: &str,
337 id: InterfaceId,
338 params: &HashMap<String, String>,
339 ) -> Result<Box<dyn InterfaceConfigData>, String> {
340 let listen_ip = params.get("listen_ip").cloned();
341
342 let port_shorthand: Option<u16> = params.get("port").and_then(|v| v.parse().ok());
344
345 let listen_port: Option<u16> = params
346 .get("listen_port")
347 .and_then(|v| v.parse().ok())
348 .or(port_shorthand);
349
350 let forward_ip = params.get("forward_ip").cloned();
351
352 let forward_port: Option<u16> = params
353 .get("forward_port")
354 .and_then(|v| v.parse().ok())
355 .or(port_shorthand);
356
357 let mut config = UdpConfig {
358 name: name.to_string(),
359 listen_ip,
360 listen_port,
361 forward_ip,
362 forward_port,
363 interface_id: id,
364 runtime: Arc::new(Mutex::new(UdpRuntime {
365 forward_ip: None,
366 forward_port: None,
367 })),
368 };
369 let startup = UdpRuntime::from_config(&config);
370 config.runtime = Arc::new(Mutex::new(startup));
371 Ok(Box::new(config))
372 }
373
374 fn start(
375 &self,
376 config: Box<dyn InterfaceConfigData>,
377 ctx: StartContext,
378 ) -> io::Result<StartResult> {
379 let udp_config = *config
380 .into_any()
381 .downcast::<UdpConfig>()
382 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "wrong config type"))?;
383
384 let id = udp_config.interface_id;
385 let name = udp_config.name.clone();
386 let out_capable = udp_config.forward_ip.is_some();
387 let in_capable = udp_config.listen_ip.is_some();
388
389 let info = InterfaceInfo {
390 stamp_costs: Default::default(),
391 id,
392 name,
393 mode: ctx.mode,
394 gravity: ctx.gravity,
395 recursive_prs: ctx.recursive_prs,
396 announces_from_internal: ctx.announces_from_internal,
397 announces_to_internal: ctx.announces_to_internal,
398 out_capable,
399 in_capable,
400 bitrate: Some(10_000_000),
401 airtime_profile: None,
402 announce_rate_target: None,
403 announce_rate_grace: 0,
404 announce_rate_penalty: 0.0,
405 announce_cap: rns_core::constants::ANNOUNCE_CAP,
406 is_local_client: false,
407 wants_tunnel: false,
408 tunnel_id: None,
409 mtu: 1400,
410 ingress_control: rns_core::transport::types::IngressControlConfig::enabled(),
411 ia_freq: 0.0,
412 ip_freq: 0.0,
413 op_freq: 0.0,
414 op_samples: 0,
415 started: crate::time::now(),
416 };
417
418 let (maybe_writer, control) = start_with_mark(udp_config, ctx.tx, ctx.underlay_mark)?;
419
420 let writer: Box<dyn Writer> = maybe_writer
421 .ok_or_else(|| io::Error::other("UDPInterface did not provide a writer"))?;
422
423 Ok(StartResult::Simple {
424 control: Some(control),
425 id,
426 info,
427 writer,
428 interface_type_name: "UDPInterface".to_string(),
429 })
430 }
431}
432
433pub(crate) fn udp_runtime_handle_from_config(config: &UdpConfig) -> UdpRuntimeConfigHandle {
434 UdpRuntimeConfigHandle {
435 interface_name: config.name.clone(),
436 runtime: Arc::clone(&config.runtime),
437 startup: UdpRuntime::from_config(config),
438 }
439}
440
441#[cfg(test)]
442mod tests {
443 use super::*;
444
445 #[test]
446 fn detach_stops_idle_udp_listener() {
447 let socket = UdpSocket::bind("127.0.0.1:0").unwrap();
448 socket
449 .set_read_timeout(Some(Duration::from_millis(200)))
450 .unwrap();
451 let (tx, rx) = crate::event::channel();
452 let control = ListenerControl::new();
453 let reader_control = control.clone();
454
455 let reader = thread::spawn(move || {
456 udp_reader_loop(
457 socket,
458 InterfaceId(76),
459 "detach-test".into(),
460 tx,
461 reader_control,
462 )
463 });
464 thread::sleep(Duration::from_millis(50));
465 control.request_stop();
466 reader.join().unwrap();
467 assert!(rx.try_recv().is_err());
468 }
469 use std::net::UdpSocket;
470 use std::time::Duration;
471
472 fn find_free_port() -> u16 {
473 crate::test_support::port()
474 }
475
476 #[test]
477 fn bind_and_receive() {
478 let port = find_free_port();
479 let (tx, rx) = crate::event::channel();
480
481 let config = UdpConfig {
482 name: "test-udp".into(),
483 listen_ip: Some("127.0.0.1".into()),
484 listen_port: Some(port),
485 forward_ip: None,
486 forward_port: None,
487 interface_id: InterfaceId(10),
488 ..UdpConfig::default()
489 };
490
491 let _writer = start(config, tx).unwrap();
492
493 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
495
496 let sender = UdpSocket::bind("127.0.0.1:0").unwrap();
498 let payload = b"hello udp";
499 sender
500 .send_to(payload, format!("127.0.0.1:{}", port))
501 .unwrap();
502
503 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
505 match event {
506 Event::Frame {
507 interface_id,
508 data,
509 rssi: _,
510 snr: _,
511 } => {
512 assert_eq!(interface_id, InterfaceId(10));
513 assert_eq!(&*data, &payload[..]);
514 }
515 other => panic!("expected Frame, got {:?}", other),
516 }
517 }
518
519 #[test]
520 fn send_broadcast() {
521 let recv_port = find_free_port();
522 let (tx, _rx) = crate::event::channel();
523
524 let config = UdpConfig {
525 name: "test-udp-send".into(),
526 listen_ip: None,
527 listen_port: None,
528 forward_ip: Some("127.0.0.1".into()),
529 forward_port: Some(recv_port),
530 interface_id: InterfaceId(11),
531 ..UdpConfig::default()
532 };
533
534 let writer = start(config, tx).unwrap();
535 let mut writer = writer.unwrap();
536
537 let receiver = UdpSocket::bind(format!("127.0.0.1:{}", recv_port)).unwrap();
539 receiver
540 .set_read_timeout(Some(Duration::from_secs(2)))
541 .unwrap();
542
543 let payload = b"broadcast data";
545 writer.send_frame(payload).unwrap();
546
547 let mut buf = [0u8; 256];
549 let (n, _) = receiver.recv_from(&mut buf).unwrap();
550 assert_eq!(&buf[..n], payload);
551 }
552
553 #[test]
554 fn round_trip() {
555 let listen_port = find_free_port();
556 let forward_port = find_free_port();
557 let (tx, rx) = crate::event::channel();
558
559 let config = UdpConfig {
560 name: "test-udp-rt".into(),
561 listen_ip: Some("127.0.0.1".into()),
562 listen_port: Some(listen_port),
563 forward_ip: Some("127.0.0.1".into()),
564 forward_port: Some(forward_port),
565 interface_id: InterfaceId(12),
566 ..UdpConfig::default()
567 };
568
569 let writer = start(config, tx).unwrap();
570 assert!(writer.is_some());
571
572 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
574
575 let sender = UdpSocket::bind("127.0.0.1:0").unwrap();
577 sender
578 .send_to(b"ping", format!("127.0.0.1:{}", listen_port))
579 .unwrap();
580
581 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
582 match event {
583 Event::Frame { data, .. } => assert_eq!(&*data, b"ping"),
584 other => panic!("expected Frame, got {:?}", other),
585 }
586 }
587
588 #[test]
589 fn multiple_datagrams() {
590 let port = find_free_port();
591 let (tx, rx) = crate::event::channel();
592
593 let config = UdpConfig {
594 name: "test-udp-multi".into(),
595 listen_ip: Some("127.0.0.1".into()),
596 listen_port: Some(port),
597 forward_ip: None,
598 forward_port: None,
599 interface_id: InterfaceId(13),
600 ..UdpConfig::default()
601 };
602
603 let _writer = start(config, tx).unwrap();
604
605 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
607
608 let sender = UdpSocket::bind("127.0.0.1:0").unwrap();
609 for i in 0..5u8 {
610 sender.send_to(&[i], format!("127.0.0.1:{}", port)).unwrap();
611 }
612
613 for i in 0..5u8 {
614 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
615 match event {
616 Event::Frame { data, .. } => assert_eq!(data, vec![i].into()),
617 other => panic!("expected Frame, got {:?}", other),
618 }
619 }
620 }
621
622 #[test]
623 fn empty_datagram_is_ignored_before_transport_ingress() {
624 let port = find_free_port();
625 let (tx, rx) = crate::event::channel();
626 let config = UdpConfig {
627 name: "test-udp-empty".into(),
628 listen_ip: Some("127.0.0.1".into()),
629 listen_port: Some(port),
630 interface_id: InterfaceId(14),
631 ..UdpConfig::default()
632 };
633 let _writer = start(config, tx).unwrap();
634 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
635
636 let sender = UdpSocket::bind("127.0.0.1:0").unwrap();
637 let target = format!("127.0.0.1:{port}");
638 sender.send_to(&[], &target).unwrap();
639 sender.send_to(b"after-empty", &target).unwrap();
640
641 match rx.recv_timeout(Duration::from_secs(2)).unwrap() {
642 Event::Frame { data, .. } => assert_eq!(&*data, b"after-empty"),
643 other => panic!("expected non-empty Frame, got {other:?}"),
644 }
645 }
646
647 #[test]
648 fn writer_send_to() {
649 let recv_port = find_free_port();
650
651 let receiver = UdpSocket::bind(format!("127.0.0.1:{}", recv_port)).unwrap();
653 receiver
654 .set_read_timeout(Some(Duration::from_secs(2)))
655 .unwrap();
656
657 let send_socket = UdpSocket::bind("127.0.0.1:0").unwrap();
659 send_socket.set_broadcast(true).unwrap();
660 let mut writer = UdpWriter {
661 socket: send_socket,
662 runtime: Arc::new(Mutex::new(UdpRuntime {
663 forward_ip: Some("127.0.0.1".into()),
664 forward_port: Some(recv_port),
665 })),
666 inline: false,
667 };
668
669 let payload = vec![0xAA, 0xBB, 0xCC];
670 writer.send_frame(&payload).unwrap();
671
672 let mut buf = [0u8; 256];
673 let (n, _) = receiver.recv_from(&mut buf).unwrap();
674 assert_eq!(&buf[..n], &payload);
675 }
676
677 fn thread_named(name: &str) -> bool {
678 std::fs::read_dir("/proc/self/task").unwrap().any(|task| {
679 let comm = task.unwrap().path().join("comm");
680 std::fs::read_to_string(comm).is_ok_and(|comm| comm.trim() == name)
681 })
682 }
683
684 #[test]
685 fn folded_udp_interface_runs_on_the_driver_loop() {
686 if !crate::engine::FOLD_IO {
687 return;
688 }
689 let listen_port = find_free_port();
690 let receiver = UdpSocket::bind("127.0.0.1:0").unwrap();
691 receiver
692 .set_read_timeout(Some(Duration::from_secs(2)))
693 .unwrap();
694 let (tx, rx) = crate::event::channel();
695 let config = UdpConfig {
696 name: "test-udp-folded".into(),
697 listen_ip: Some("127.0.0.1".into()),
698 listen_port: Some(listen_port),
699 forward_ip: Some("127.0.0.1".into()),
700 forward_port: Some(receiver.local_addr().unwrap().port()),
701 interface_id: InterfaceId(77),
702 ..UdpConfig::default()
703 };
704 let writer = start(config, tx.clone()).unwrap().unwrap();
705 assert!(writer.sends_inline());
706 let (mut writer, _) =
707 crate::interface::wrap_async_writer(writer, InterfaceId(77), "folded", tx, 16);
708 assert!(!thread_named("udp-reader-77"));
709 assert!(!thread_named("iface-writer-77"));
710
711 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
713 let sender = UdpSocket::bind("127.0.0.1:0").unwrap();
714 sender
715 .send_to(b"in", format!("127.0.0.1:{listen_port}"))
716 .unwrap();
717 match rx.recv_timeout(Duration::from_secs(2)).unwrap() {
718 Event::Frame { data, .. } => assert_eq!(&*data, b"in"),
719 other => panic!("expected Frame, got {other:?}"),
720 }
721
722 let mut buf = [0u8; 64];
724 writer.send_frame(b"plain").unwrap();
725 let (n, _) = receiver.recv_from(&mut buf).unwrap();
726 assert_eq!(&buf[..n], b"plain");
727
728 let pool = crate::link_send::SendPool::new(1);
729 let (completion, receipt) = crate::link_send::Completion::new(pool.try_acquire().unwrap());
730 let mut completion = Some(completion);
731 writer
732 .enqueue_confirmed(b"confirmed", &mut completion)
733 .unwrap();
734 assert!(completion.is_none());
735 receipt.wait().unwrap();
736 let (n, _) = receiver.recv_from(&mut buf).unwrap();
737 assert_eq!(&buf[..n], b"confirmed");
738 }
739
740 #[test]
741 fn writer_sends_to_an_ipv6_target() {
742 let receiver = UdpSocket::bind("[::1]:0").unwrap();
743 receiver
744 .set_read_timeout(Some(Duration::from_secs(2)))
745 .unwrap();
746 let mut writer = UdpWriter {
747 socket: UdpSocket::bind("[::1]:0").unwrap(),
748 runtime: Arc::new(Mutex::new(UdpRuntime {
749 forward_ip: Some("::1".into()),
750 forward_port: Some(receiver.local_addr().unwrap().port()),
751 })),
752 inline: false,
753 };
754 writer.send_frame(b"v6").unwrap();
755 let mut buf = [0u8; 16];
756 let (n, _) = receiver.recv_from(&mut buf).unwrap();
757 assert_eq!(&buf[..n], b"v6");
758 }
759}