Skip to main content

phantom_protocol/transport/
udp_transport.rs

1//! Low-level UDP socket / pacing helper.
2//!
3//! Zero-copy `AesSession` AES-256-GCM with in-place encryption over a
4//! `tokio::net::UdpSocket`. (`send_batch` is now a plain per-packet loop — the
5//! old `sendmmsg(2)`/GSO batch path was removed; see the unsafe note below.)
6//! This is NOT the production `SessionTransport`-level UDP transport — that lives
7//! in `api/udp_transport.rs` (`UdpClientTransport` / `UdpServerTransport`). This
8//! module is the socket helper plus the `PacedSender` rate-limit wrapper.
9//!
10//! ## Paced Sending
11//!
12//! `PacedSender` wraps `UdpTransport` + `Pacer` to enforce a rate limit
13//! set by the `BandwidthEstimator`. Prevents burst-induced congestion.
14//! On Linux, `set_pacing_rate` can additionally offload pacing to the kernel
15//! via `SO_MAX_PACING_RATE` (with the `fq` qdisc).
16
17// This module opts back in to `unsafe` (denied at the crate root in lib.rs).
18// The single remaining `unsafe` block is the `libc::setsockopt` call in
19// `set_pacing_rate` (Linux `SO_MAX_PACING_RATE`). The previous dead
20// `sendmmsg(2)` GSO-batch path — the only user of `libc::sendmmsg` /
21// `libc::mmsghdr` / `MaybeUninit::zeroed` — was removed in the unsafe
22// hygiene pass (it had no callers; UNSAFE-2). Every block must carry a
23// `// SAFETY:` comment.
24#![allow(unsafe_code)]
25
26use super::buffer_pool::BufferPool;
27use super::pacer::Pacer;
28use crate::crypto::aes_session::AesSession;
29use crate::transport::bandwidth_estimator;
30use crate::transport::handshake::{ClientHello, HandshakeResponse, HandshakeServer, ServerReply};
31use std::net::SocketAddr;
32use std::sync::Arc;
33use tokio::io::{self, Result as IoResult};
34use tokio::net::UdpSocket;
35
36/// High-performance UDP transport with batching and encryption
37pub struct UdpTransport {
38    socket: Arc<UdpSocket>,
39    peer_addr: SocketAddr,
40    session: Arc<AesSession>,
41    buffer_pool: Arc<BufferPool>,
42}
43
44impl UdpTransport {
45    /// Create a new UDP transport
46    pub async fn bind(local_addr: &str) -> IoResult<Self> {
47        let socket = UdpSocket::bind(local_addr).await?;
48        socket.set_broadcast(false)?;
49
50        let peer_addr = "0.0.0.0:0"
51            .parse()
52            .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
53
54        let session = AesSession::from_shared_secret(&[0u8; 32]).map_err(io::Error::other)?;
55
56        Ok(Self {
57            socket: Arc::new(socket),
58            peer_addr,
59            session: Arc::new(session),
60            buffer_pool: Arc::new(BufferPool::new(65536, 16, 256)),
61        })
62    }
63
64    /// Connect to a peer
65    pub async fn connect(&mut self, peer_addr: SocketAddr, session: AesSession) {
66        self.peer_addr = peer_addr;
67        self.session = Arc::new(session);
68    }
69
70    /// Send encrypted data
71    #[inline]
72    pub async fn send(&self, data: &[u8]) -> IoResult<usize> {
73        let encrypted = self.session.encrypt(&[], data).map_err(io::Error::other)?;
74        self.socket.send_to(&encrypted, self.peer_addr).await
75    }
76
77    /// Send encrypted data with in-place encryption (zero-copy)
78    #[inline]
79    pub async fn send_zero_copy(&self, data: &[u8]) -> IoResult<usize> {
80        let mut buf = Vec::with_capacity(data.len() + 16);
81        buf.extend_from_slice(data);
82        self.session
83            .encrypt_in_place(&[], &mut buf)
84            .map_err(io::Error::other)?;
85        self.socket.send_to(&buf, self.peer_addr).await
86    }
87
88    /// Receive and decrypt data
89    #[inline]
90    pub async fn recv(&self) -> IoResult<(Vec<u8>, SocketAddr)> {
91        let mut buf = self.buffer_pool.acquire();
92        buf.resize(65536, 0);
93
94        let (len, addr) = self.socket.recv_from(&mut buf).await?;
95
96        let decrypted = self
97            .session
98            .decrypt(&[], &buf[..len])
99            .map_err(io::Error::other)?;
100
101        Ok((decrypted, addr))
102    }
103
104    /// Batch send multiple packets (simple loop fallback).
105    #[inline]
106    pub async fn send_batch(&self, packets: &[&[u8]]) -> IoResult<usize> {
107        let mut total = 0;
108        for packet in packets {
109            total += self.send(packet).await?;
110        }
111        Ok(total)
112    }
113
114    /// Get a reference to the underlying socket (for raw operations).
115    pub fn socket(&self) -> &Arc<UdpSocket> {
116        &self.socket
117    }
118
119    /// Set the kernel-level pacing rate for this UDP socket (Linux only).
120    ///
121    /// Uses `SO_MAX_PACING_RATE` via `setsockopt(2)`. When the kernel's
122    /// `net.core.default_qdisc` is `fq` (Fair Queuing), the kernel will
123    /// enforce the pacing rate without any per-packet user-space timer,
124    /// eliminating microbursts caused by tokio sleep granularity (~1 ms).
125    ///
126    /// On non-Linux platforms this is a no-op and always returns `Ok`.
127    ///
128    /// # Arguments
129    /// * `rate_bps` — desired pacing rate in **bytes per second**
130    pub fn set_pacing_rate(&self, rate_bps: u64) -> IoResult<()> {
131        // Pacing offload (`SO_MAX_PACING_RATE`) is Linux-only; the rate is
132        // unused on other platforms.
133        #[cfg(not(target_os = "linux"))]
134        let _ = rate_bps;
135        #[cfg(target_os = "linux")]
136        {
137            use std::os::unix::io::AsRawFd;
138            // SO_MAX_PACING_RATE takes a u32 on older kernels (Linux 3.13+)
139            // and a u64 on 4.20+. We use u32 for maximum compatibility.
140            let rate_u32 = rate_bps.min(u32::MAX as u64) as u32;
141            let fd = self.socket.as_ref().as_raw_fd();
142            // SAFETY: fd is valid, &rate_u32 is a valid u32 pointer,
143            // size_of::<u32>() is correct. SO_MAX_PACING_RATE = 47.
144            let ret = unsafe {
145                libc::setsockopt(
146                    fd,
147                    libc::SOL_SOCKET,
148                    47, // SO_MAX_PACING_RATE
149                    &rate_u32 as *const u32 as *const libc::c_void,
150                    std::mem::size_of::<u32>() as libc::socklen_t,
151                )
152            };
153            if ret != 0 {
154                return Err(io::Error::last_os_error());
155            }
156        }
157        Ok(())
158    }
159
160    /// Get buffer pool stats
161    pub fn buffer_stats(&self) -> super::buffer_pool::PoolStats {
162        self.buffer_pool.stats()
163    }
164}
165
166/// Raw UDP listener for handling new handshakes (Phase 3: Paranoia Funnel)
167pub struct UdpHandshakeListener {
168    socket: Arc<UdpSocket>,
169    buffer_pool: Arc<BufferPool>,
170}
171
172impl UdpHandshakeListener {
173    pub async fn bind(local_addr: &str) -> IoResult<Self> {
174        let socket = UdpSocket::bind(local_addr).await?;
175        socket.set_broadcast(false)?;
176
177        Ok(Self {
178            socket: Arc::new(socket),
179            buffer_pool: Arc::new(BufferPool::new(65536, 16, 256)),
180        })
181    }
182
183    /// Read raw packets from socket, dropping small ones instantly (anti-amplification)
184    pub async fn accept_handshake(&self, server: &HandshakeServer, difficulty: u8) -> IoResult<()> {
185        let mut buf = self.buffer_pool.acquire();
186        buf.resize(65536, 0);
187
188        loop {
189            let (len, addr) = self.socket.recv_from(&mut buf).await?;
190
191            // 1. Padding Check / Anti-amplification
192            // Any ClientHello packet smaller than 1200 bytes is dropped instantly without generating errors.
193            if len < 1200 {
194                continue;
195            }
196
197            // Decode the ClientHello. An unknown / future layout surfaces as
198            // a borsh parse error and is dropped silently. The unified server
199            // path handles cookie/PoW, resume, and best-effort 0-RTT
200            // early-data; this is a demonstration listener that simply replies.
201            let client_hello = match borsh::from_slice::<ClientHello>(&buf[..len]) {
202                Ok(ch) => ch,
203                Err(_) => {
204                    // Not a valid ClientHello, drop silently
205                    continue;
206                }
207            };
208
209            // Process ClientHello
210            match server.process_client_hello(&client_hello, difficulty, addr.ip()) {
211                HandshakeResponse::Retry(retry_req) => {
212                    // Server demands PoW or Cookie, send Retry (stateless) and forget.
213                    // T4.4: frame with the ServerReply discriminant the client dispatches on.
214                    if let Ok(encoded) = ServerReply::Retry(retry_req).to_wire() {
215                        let _ = self.socket.send_to(&encoded, addr).await;
216                    }
217                }
218                HandshakeResponse::Success(server_hello, _session, _early_data) => {
219                    // Handshake Success, send the discriminant-framed ServerHello.
220                    if let Ok(encoded) = ServerReply::Hello(server_hello).to_wire() {
221                        let _ = self.socket.send_to(&encoded, addr).await;
222                    }
223                    // Transition connection to established state...
224                }
225                HandshakeResponse::Reject(reject) => {
226                    // Unsupported version (H9): send the typed reject so the
227                    // client gets an actionable signal, then forget.
228                    if let Ok(encoded) = ServerReply::Reject(reject).to_wire() {
229                        let _ = self.socket.send_to(&encoded, addr).await;
230                    }
231                }
232                HandshakeResponse::Fail(_) => {
233                    // Handshake error, drop silently
234                }
235            }
236
237            // For now, this is a demonstration of the listener loop
238            break;
239        }
240
241        Ok(())
242    }
243}
244
245// ─── Paced Sender ───────────────────────────────────────────────────────────
246
247/// Rate-limited UDP sender — integrates `Pacer` + `UdpTransport`.
248///
249/// Call `try_send()` instead of `UdpTransport::send()`. If the pacer
250/// has no tokens, the send is delayed via `tokio::time::sleep`.
251pub struct PacedSender {
252    transport: Arc<UdpTransport>,
253    pacer: Arc<Pacer>,
254    estimator: Arc<parking_lot::Mutex<bandwidth_estimator::BandwidthEstimator>>,
255}
256
257impl PacedSender {
258    /// Create a new paced sender.
259    pub fn new(
260        transport: Arc<UdpTransport>,
261        pacer: Arc<Pacer>,
262        estimator: Arc<parking_lot::Mutex<bandwidth_estimator::BandwidthEstimator>>,
263    ) -> Self {
264        Self {
265            transport,
266            pacer,
267            estimator,
268        }
269    }
270
271    /// Send data respecting the pacing rate.
272    /// If tokens aren't available, yields to tokio until they are.
273    pub async fn send(&self, data: &[u8]) -> IoResult<usize> {
274        let bytes = data.len() as u64;
275
276        // Wait for pacing tokens
277        loop {
278            if self.pacer.try_consume(bytes) {
279                break;
280            }
281            let wait = self.pacer.time_until_available(bytes);
282            if wait.is_zero() {
283                break;
284            }
285            tokio::time::sleep(wait).await;
286        }
287
288        // Notify estimator of outgoing data
289        self.estimator.lock().on_send(bytes);
290
291        self.transport.send(data).await
292    }
293
294    /// Send without pacing (bypass for control packets).
295    pub async fn send_unpaced(&self, data: &[u8]) -> IoResult<usize> {
296        self.transport.send(data).await
297    }
298
299    /// Process an ACK and update everything.
300    pub fn on_ack(&self, sample: bandwidth_estimator::DeliverySample) {
301        let mut est = self.estimator.lock();
302        est.on_ack(sample);
303        let new_rate = est.pacing_rate();
304        self.pacer.set_rate(new_rate);
305    }
306
307    /// Update the pacing rate (explicit override).
308    pub fn set_rate(&self, rate_bps: u64) {
309        self.pacer.set_rate(rate_bps);
310    }
311
312    /// Get current pacing rate.
313    pub fn rate(&self) -> u64 {
314        self.pacer.rate()
315    }
316}
317
318impl std::fmt::Debug for PacedSender {
319    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320        f.debug_struct("PacedSender")
321            .field("rate_bps", &self.pacer.rate())
322            .field("pacer_enabled", &self.pacer.is_enabled())
323            .finish()
324    }
325}
326
327/// Ultra-fast datagram sender (for benchmarks)
328pub struct FastSender {
329    socket: Arc<UdpSocket>,
330    session: Arc<AesSession>,
331    peer_addr: SocketAddr,
332}
333
334impl FastSender {
335    pub fn new(socket: Arc<UdpSocket>, session: Arc<AesSession>, peer_addr: SocketAddr) -> Self {
336        Self {
337            socket,
338            session,
339            peer_addr,
340        }
341    }
342
343    /// Send with in-place encryption
344    #[inline]
345    pub async fn send(&self, data: &[u8]) -> IoResult<usize> {
346        let mut buf = Vec::with_capacity(data.len() + 16);
347        buf.extend_from_slice(data);
348        self.session
349            .encrypt_in_place(&[], &mut buf)
350            .map_err(io::Error::other)?;
351        self.socket.send_to(&buf, self.peer_addr).await
352    }
353}
354
355#[cfg(test)]
356mod tests {
357    use super::*;
358
359    #[tokio::test]
360    async fn test_udp_transport_create() {
361        let transport = UdpTransport::bind("127.0.0.1:0").await.unwrap();
362        assert_eq!(transport.buffer_stats().pool_size, 16);
363    }
364
365    #[tokio::test]
366    async fn test_paced_sender_creation() {
367        let transport = Arc::new(UdpTransport::bind("127.0.0.1:0").await.unwrap());
368        let pacer = Arc::new(Pacer::new(1_000_000)); // 1 MB/s
369        let estimator = Arc::new(parking_lot::Mutex::new(
370            bandwidth_estimator::BandwidthEstimator::new(),
371        ));
372        let sender = PacedSender::new(transport, pacer, estimator);
373
374        assert_eq!(sender.rate(), 1_000_000);
375        sender.set_rate(2_000_000);
376        assert_eq!(sender.rate(), 2_000_000);
377    }
378}