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 recursive_prs: ctx.recursive_prs,
234 announces_from_internal: ctx.announces_from_internal,
235 out_capable,
236 in_capable,
237 bitrate: Some(10_000_000),
238 airtime_profile: None,
239 announce_rate_target: None,
240 announce_rate_grace: 0,
241 announce_rate_penalty: 0.0,
242 announce_cap: rns_core::constants::ANNOUNCE_CAP,
243 is_local_client: false,
244 wants_tunnel: false,
245 tunnel_id: None,
246 mtu: 1400,
247 ingress_control: rns_core::transport::types::IngressControlConfig::enabled(),
248 ia_freq: 0.0,
249 ip_freq: 0.0,
250 op_freq: 0.0,
251 op_samples: 0,
252 started: crate::time::now(),
253 };
254
255 let maybe_writer = start(udp_config, ctx.tx)?;
256
257 let writer: Box<dyn Writer> = maybe_writer
258 .ok_or_else(|| io::Error::other("UDPInterface did not provide a writer"))?;
259
260 Ok(StartResult::Simple {
261 id,
262 info,
263 writer,
264 interface_type_name: "UDPInterface".to_string(),
265 })
266 }
267}
268
269pub(crate) fn udp_runtime_handle_from_config(config: &UdpConfig) -> UdpRuntimeConfigHandle {
270 UdpRuntimeConfigHandle {
271 interface_name: config.name.clone(),
272 runtime: Arc::clone(&config.runtime),
273 startup: UdpRuntime::from_config(config),
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280 use std::net::UdpSocket;
281 use std::time::Duration;
282
283 fn find_free_port() -> u16 {
284 UdpSocket::bind("127.0.0.1:0")
285 .unwrap()
286 .local_addr()
287 .unwrap()
288 .port()
289 }
290
291 #[test]
292 fn bind_and_receive() {
293 let port = find_free_port();
294 let (tx, rx) = crate::event::channel();
295
296 let config = UdpConfig {
297 name: "test-udp".into(),
298 listen_ip: Some("127.0.0.1".into()),
299 listen_port: Some(port),
300 forward_ip: None,
301 forward_port: None,
302 interface_id: InterfaceId(10),
303 ..UdpConfig::default()
304 };
305
306 let _writer = start(config, tx).unwrap();
307
308 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
310
311 let sender = UdpSocket::bind("127.0.0.1:0").unwrap();
313 let payload = b"hello udp";
314 sender
315 .send_to(payload, format!("127.0.0.1:{}", port))
316 .unwrap();
317
318 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
320 match event {
321 Event::Frame {
322 interface_id,
323 data,
324 rssi: _,
325 snr: _,
326 } => {
327 assert_eq!(interface_id, InterfaceId(10));
328 assert_eq!(data, payload);
329 }
330 other => panic!("expected Frame, got {:?}", other),
331 }
332 }
333
334 #[test]
335 fn send_broadcast() {
336 let recv_port = find_free_port();
337 let (tx, _rx) = crate::event::channel();
338
339 let config = UdpConfig {
340 name: "test-udp-send".into(),
341 listen_ip: None,
342 listen_port: None,
343 forward_ip: Some("127.0.0.1".into()),
344 forward_port: Some(recv_port),
345 interface_id: InterfaceId(11),
346 ..UdpConfig::default()
347 };
348
349 let writer = start(config, tx).unwrap();
350 let mut writer = writer.unwrap();
351
352 let receiver = UdpSocket::bind(format!("127.0.0.1:{}", recv_port)).unwrap();
354 receiver
355 .set_read_timeout(Some(Duration::from_secs(2)))
356 .unwrap();
357
358 let payload = b"broadcast data";
360 writer.send_frame(payload).unwrap();
361
362 let mut buf = [0u8; 256];
364 let (n, _) = receiver.recv_from(&mut buf).unwrap();
365 assert_eq!(&buf[..n], payload);
366 }
367
368 #[test]
369 fn round_trip() {
370 let listen_port = find_free_port();
371 let forward_port = find_free_port();
372 let (tx, rx) = crate::event::channel();
373
374 let config = UdpConfig {
375 name: "test-udp-rt".into(),
376 listen_ip: Some("127.0.0.1".into()),
377 listen_port: Some(listen_port),
378 forward_ip: Some("127.0.0.1".into()),
379 forward_port: Some(forward_port),
380 interface_id: InterfaceId(12),
381 ..UdpConfig::default()
382 };
383
384 let writer = start(config, tx).unwrap();
385 assert!(writer.is_some());
386
387 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
389
390 let sender = UdpSocket::bind("127.0.0.1:0").unwrap();
392 sender
393 .send_to(b"ping", format!("127.0.0.1:{}", listen_port))
394 .unwrap();
395
396 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
397 match event {
398 Event::Frame { data, .. } => assert_eq!(data, b"ping"),
399 other => panic!("expected Frame, got {:?}", other),
400 }
401 }
402
403 #[test]
404 fn multiple_datagrams() {
405 let port = find_free_port();
406 let (tx, rx) = crate::event::channel();
407
408 let config = UdpConfig {
409 name: "test-udp-multi".into(),
410 listen_ip: Some("127.0.0.1".into()),
411 listen_port: Some(port),
412 forward_ip: None,
413 forward_port: None,
414 interface_id: InterfaceId(13),
415 ..UdpConfig::default()
416 };
417
418 let _writer = start(config, tx).unwrap();
419
420 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
422
423 let sender = UdpSocket::bind("127.0.0.1:0").unwrap();
424 for i in 0..5u8 {
425 sender.send_to(&[i], format!("127.0.0.1:{}", port)).unwrap();
426 }
427
428 for i in 0..5u8 {
429 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
430 match event {
431 Event::Frame { data, .. } => assert_eq!(data, vec![i]),
432 other => panic!("expected Frame, got {:?}", other),
433 }
434 }
435 }
436
437 #[test]
438 fn writer_send_to() {
439 let recv_port = find_free_port();
440
441 let receiver = UdpSocket::bind(format!("127.0.0.1:{}", recv_port)).unwrap();
443 receiver
444 .set_read_timeout(Some(Duration::from_secs(2)))
445 .unwrap();
446
447 let send_socket = UdpSocket::bind("127.0.0.1:0").unwrap();
449 send_socket.set_broadcast(true).unwrap();
450 let mut writer = UdpWriter {
451 socket: send_socket,
452 runtime: Arc::new(Mutex::new(UdpRuntime {
453 forward_ip: Some("127.0.0.1".into()),
454 forward_port: Some(recv_port),
455 })),
456 };
457
458 let payload = vec![0xAA, 0xBB, 0xCC];
459 writer.send_frame(&payload).unwrap();
460
461 let mut buf = [0u8; 256];
462 let (n, _) = receiver.recv_from(&mut buf).unwrap();
463 assert_eq!(&buf[..n], &payload);
464 }
465}