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