rns_net/holepunch/
udp_direct.rs1use std::io;
8use std::net::{SocketAddr, UdpSocket};
9use std::sync::atomic::{AtomicBool, Ordering};
10use std::sync::Arc;
11use std::thread;
12use std::time::Duration;
13
14use rns_core::transport::types::{InterfaceId, InterfaceInfo};
15
16use crate::event::{Event, EventSender};
17use crate::interface::Writer;
18
19use super::puncher;
20
21const KEEPALIVE_INTERVAL: Duration = Duration::from_secs(30);
23
24const INACTIVITY_TIMEOUT: Duration = Duration::from_secs(120);
26
27pub struct UdpDirectWriter {
31 socket: Arc<UdpSocket>,
32 peer_addr: SocketAddr,
33 running: Arc<AtomicBool>,
34}
35
36impl Writer for UdpDirectWriter {
37 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
38 self.socket.send_to(data, self.peer_addr)?;
39 Ok(())
40 }
41}
42
43impl Drop for UdpDirectWriter {
44 fn drop(&mut self) {
45 self.running.store(false, Ordering::Relaxed);
46 }
47}
48
49pub fn start_direct_interface(
55 socket: UdpSocket,
56 peer_addr: SocketAddr,
57 interface_id: InterfaceId,
58 session_id: [u8; 16],
59 punch_token: [u8; 32],
60 tx: EventSender,
61) -> io::Result<(Box<dyn Writer>, InterfaceInfo)> {
62 let socket = Arc::new(socket);
63 socket.set_read_timeout(Some(Duration::from_secs(1)))?;
64
65 let running = Arc::new(AtomicBool::new(true));
66
67 let writer = UdpDirectWriter {
68 socket: socket.clone(),
69 peer_addr,
70 running: running.clone(),
71 };
72
73 let name = format!(
74 "DirectPeer/{:02x}{:02x}{:02x}{:02x}",
75 session_id[0], session_id[1], session_id[2], session_id[3]
76 );
77
78 let info = InterfaceInfo {
79 id: interface_id,
80 name: name.clone(),
81 mode: rns_core::constants::MODE_FULL,
82 gravity: 0,
83 recursive_prs: false,
84 announces_from_internal: true,
85 announces_to_internal: None,
86 out_capable: true,
87 in_capable: true,
88 bitrate: None,
89 airtime_profile: None,
90 announce_rate_target: None,
91 announce_rate_grace: 0,
92 announce_rate_penalty: 0.0,
93 announce_cap: 1.0,
94 is_local_client: false,
95 wants_tunnel: false,
96 tunnel_id: None,
97 mtu: 1400,
98 ia_freq: 0.0,
99 ip_freq: 0.0,
100 op_freq: 0.0,
101 op_samples: 0,
102 started: 0.0,
103 ingress_control: rns_core::transport::types::IngressControlConfig::disabled(),
104 };
105
106 let running_clone = running.clone();
107
108 thread::Builder::new()
110 .name(format!("direct-udp-{}", &name))
111 .spawn(move || {
112 run_reader(
113 socket,
114 peer_addr,
115 interface_id,
116 session_id,
117 punch_token,
118 tx,
119 running_clone,
120 );
121 })?;
122
123 Ok((Box::new(writer), info))
124}
125
126fn run_reader(
127 socket: Arc<UdpSocket>,
128 peer_addr: SocketAddr,
129 interface_id: InterfaceId,
130 session_id: [u8; 16],
131 punch_token: [u8; 32],
132 tx: EventSender,
133 running: Arc<AtomicBool>,
134) {
135 let mut buf = [0u8; 2048];
136 let mut last_inbound = std::time::Instant::now();
137 let mut last_keepalive = std::time::Instant::now();
138 let keepalive_pkt = puncher::build_keepalive_packet(&session_id, &punch_token);
139
140 while running.load(Ordering::Relaxed) {
141 if last_keepalive.elapsed() >= KEEPALIVE_INTERVAL {
143 let _ = socket.send_to(&keepalive_pkt, peer_addr);
144 last_keepalive = std::time::Instant::now();
145 }
146
147 if last_inbound.elapsed() >= INACTIVITY_TIMEOUT {
149 log::info!("[{}] Direct UDP interface timed out", interface_id.0);
150 let _ = tx.send(Event::InterfaceDown(interface_id));
151 break;
152 }
153
154 match socket.recv_from(&mut buf) {
155 Ok((len, src)) => {
156 if src != peer_addr {
158 continue;
159 }
160 last_inbound = std::time::Instant::now();
161
162 if len >= 4 && (buf[..4] == *b"RNSH" || buf[..4] == *b"RNSA") {
164 continue;
165 }
166
167 if len > 0 {
169 let _ = tx.send(Event::Frame {
170 interface_id,
171 data: buf[..len].to_vec(),
172 rssi: None,
173 snr: None,
174 });
175 }
176 }
177 Err(ref e)
178 if e.kind() == io::ErrorKind::WouldBlock || e.kind() == io::ErrorKind::TimedOut =>
179 {
180 continue;
181 }
182 Err(e) => {
183 log::warn!("[{}] Direct UDP recv error: {}", interface_id.0, e);
184 let _ = tx.send(Event::InterfaceDown(interface_id));
185 break;
186 }
187 }
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn test_udp_direct_writer() {
197 let sock_a = UdpSocket::bind("127.0.0.1:0").unwrap();
199 let sock_b = UdpSocket::bind("127.0.0.1:0").unwrap();
200 let addr_b = sock_b.local_addr().unwrap();
201 sock_b
202 .set_read_timeout(Some(Duration::from_secs(1)))
203 .unwrap();
204
205 let mut writer = UdpDirectWriter {
206 socket: Arc::new(sock_a),
207 peer_addr: addr_b,
208 running: Arc::new(AtomicBool::new(true)),
209 };
210
211 let data = b"hello direct peer";
213 writer.send_frame(data).unwrap();
214
215 let mut buf = [0u8; 64];
217 let (len, _src) = sock_b.recv_from(&mut buf).unwrap();
218 assert_eq!(&buf[..len], data);
219 }
220}