1use std::{
16 future::Future,
17 io,
18 net::SocketAddr,
19 pin::Pin,
20 sync::{
21 Arc, Mutex,
22 atomic::{AtomicU64, Ordering},
23 },
24 time::{Duration, Instant},
25};
26
27use ana_gotatun::{
28 noise::{Tunn, TunnResult, errors::WireGuardError, rate_limiter::RateLimiter},
29 packet::{Packet, PacketBufPool, WgKind},
30 x25519::{self},
31};
32use bytes::{Bytes, BytesMut};
33use scion_sdk_utils::backoff::ExponentialBackoff;
34use tokio::{select, task::JoinHandle, time::Interval};
35use tracing::instrument;
36use zerocopy::IntoBytes as _;
37
38use super::{PACKET_BUF_POOL_SIZE, TunnelGuard};
39use crate::udp_batch::{QueuePacketError, RecvBatchError, UdpBatchReceiver, UdpBatchSender};
40
41const HANDSHAKE_RATE_LIMIT: u64 = 20;
42const RECEIVE_BATCH_SIZE: usize = 64;
43
44#[derive(Debug, thiserror::Error)]
46pub enum SnapTunnelDriverError {
47 #[error("send i/o error: {0}")]
49 SendIoError(#[from] std::io::Error),
50 #[error("receive i/o error: {0}")]
52 ReceiveIoError(std::io::Error),
53 #[error("receive queue closed")]
55 ReceiveQueueClosed,
56 #[error("connection expired")]
58 ConnectionExpired,
59 #[error("error receiving a Wireguard packet: {0:?}")]
62 WireguardError(WireGuardError),
63}
64
65impl SnapTunnelDriverError {
66 #[must_use]
71 pub fn is_transient(&self) -> bool {
72 match self {
73 Self::SendIoError(_) | Self::ReceiveIoError(_) => true,
76 Self::ConnectionExpired => true,
79 Self::ReceiveQueueClosed => false,
81 Self::WireguardError(error) => {
91 match error {
92 WireGuardError::NoCurrentSession
93 | WireGuardError::WrongIndex
94 | WireGuardError::UnexpectedPacket
95 | WireGuardError::WrongPacketType
96 | WireGuardError::IncorrectPacketLength
97 | WireGuardError::InvalidPacket
98 | WireGuardError::InvalidCounter
99 | WireGuardError::DuplicateCounter
100 | WireGuardError::InvalidMac
101 | WireGuardError::InvalidAeadTag
102 | WireGuardError::InvalidTai64nTimestamp
103 | WireGuardError::WrongTai64nTimestamp
104 | WireGuardError::ConnectionExpired => true,
105 WireGuardError::WrongKey
106 | WireGuardError::LockFailed
107 | WireGuardError::DestinationBufferTooSmall => false,
108 }
109 }
110 }
111 }
112}
113
114struct SnapTunnelDriver {
115 pub tunn: Arc<Mutex<Tunn>>,
116 pub static_private: x25519::StaticSecret,
117 pub peer_public: x25519::PublicKey,
118 pub underlay_socket: Arc<tokio::net::UdpSocket>,
119 pub dataplane_address: SocketAddr,
120 pub persistent_keepalive_seconds: Option<u16>,
121 pub update_timers_interval: Interval,
122 pub packet_sender: async_channel::Sender<BytesMut>,
123 pub local_sockaddr: Option<SocketAddr>,
124 pub pool: PacketBufPool<PACKET_BUF_POOL_SIZE>,
125 pub receiver: UdpBatchReceiver<RECEIVE_BATCH_SIZE, PACKET_BUF_POOL_SIZE>,
126 pub sender: UdpBatchSender<RECEIVE_BATCH_SIZE, PACKET_BUF_POOL_SIZE>,
127 pub discarded_datagrams: Arc<AtomicU64>,
129}
130
131impl SnapTunnelDriver {
132 fn new(
133 static_private: x25519::StaticSecret,
134 peer_public: x25519::PublicKey,
135 underlay_socket: Arc<tokio::net::UdpSocket>,
136 dataplane_address: SocketAddr,
137 persistent_keepalive_seconds: Option<u16>,
138 packet_sender: async_channel::Sender<BytesMut>,
139 pool: PacketBufPool<PACKET_BUF_POOL_SIZE>,
140 ) -> io::Result<Self> {
141 let update_timers_interval = tokio::time::interval_at(
142 tokio::time::Instant::now() + Duration::from_millis(250),
143 Duration::from_millis(250),
144 );
145 let receiver = UdpBatchReceiver::<RECEIVE_BATCH_SIZE, PACKET_BUF_POOL_SIZE>::new(
146 underlay_socket.as_ref(),
147 &pool,
148 )?;
149 let sender = UdpBatchSender::<RECEIVE_BATCH_SIZE, PACKET_BUF_POOL_SIZE>::new(
150 underlay_socket.as_ref(),
151 )?;
152 Ok(Self {
153 tunn: Arc::new(Mutex::new(Self::create_tunn(
154 static_private.clone(),
155 peer_public,
156 dataplane_address,
157 persistent_keepalive_seconds,
158 ))),
159 static_private,
160 peer_public,
161 underlay_socket,
162 dataplane_address,
163 persistent_keepalive_seconds,
164 update_timers_interval,
165 packet_sender,
166 local_sockaddr: None,
167 receiver,
168 sender,
169 pool,
170 discarded_datagrams: Arc::new(AtomicU64::new(0)),
171 })
172 }
173
174 fn try_flush(
182 socket: &tokio::net::UdpSocket,
183 sender: &mut UdpBatchSender<RECEIVE_BATCH_SIZE, PACKET_BUF_POOL_SIZE>,
184 discarded_datagrams: &AtomicU64,
185 ) {
186 let _ = sender.try_flush_best_effort(socket);
187 Self::account_discarded_datagrams(sender, discarded_datagrams);
188 }
189
190 async fn flush(
192 socket: &tokio::net::UdpSocket,
193 sender: &mut UdpBatchSender<RECEIVE_BATCH_SIZE, PACKET_BUF_POOL_SIZE>,
194 discarded_datagrams: &AtomicU64,
195 ) -> io::Result<()> {
196 let result = sender.flush(socket).await;
197 Self::account_discarded_datagrams(sender, discarded_datagrams);
198 result
199 }
200
201 fn account_discarded_datagrams(
206 sender: &mut UdpBatchSender<RECEIVE_BATCH_SIZE, PACKET_BUF_POOL_SIZE>,
207 discarded_datagrams: &AtomicU64,
208 ) {
209 let discarded = sender.take_discarded_datagrams();
210 if discarded > 0 {
211 discarded_datagrams.fetch_add(discarded, Ordering::Relaxed);
212 }
213 }
214
215 #[instrument(name = "st-client", skip(self), fields(socket_addr= ?self.local_sockaddr))]
216 async fn initiate_connection(&mut self) -> Result<SocketAddr, SnapTunnelDriverError> {
217 let handshake_init = self.tunn.lock().unwrap().format_handshake_initiation(false);
218 if let Some(wg_init) = handshake_init
219 && let Err(e) = self
220 .underlay_socket
221 .send_to(
222 to_bytes(WgKind::HandshakeInit(wg_init)).as_bytes(),
223 self.dataplane_address,
224 )
225 .await
226 {
227 return Err(SnapTunnelDriverError::SendIoError(e));
228 }
229 loop {
231 self.drive_once().await?;
232 if let Some(sockaddr) = self.tunn.lock().unwrap().get_initiator_remote_sockaddr() {
233 if self.local_sockaddr.is_none() {
234 self.local_sockaddr = Some(sockaddr);
235 }
236 tracing::debug!(local_addr=?sockaddr, "handshake completed, local address assigned");
237 return Ok(sockaddr);
238 }
239 }
240 }
241
242 #[instrument(name = "st-client", skip(self), fields(socket_addr= ?self.local_sockaddr))]
243 async fn main_loop(mut self) {
244 let local_sockaddr = self
245 .local_sockaddr
246 .expect("local address must be set before main_loop()");
247 loop {
248 match self.drive_once().await {
249 Err(SnapTunnelDriverError::ReceiveQueueClosed) => {
250 tracing::info!("receive queue closed, snap tunnel driver shutting down");
251 return;
252 }
253 Err(SnapTunnelDriverError::ConnectionExpired) => {
254 loop {
255 let mut backoff = BackoffState::new();
256 *self.tunn.lock().expect("poison") = Self::create_tunn(
258 self.static_private.clone(),
259 self.peer_public,
260 self.dataplane_address,
261 self.persistent_keepalive_seconds,
262 );
263 match self.initiate_connection().await {
264 Ok(addr) if addr == local_sockaddr => break,
265 Ok(addr) => {
266 tracing::error!(expected_addr=?local_sockaddr, new_addr=?addr, "local socket address changed");
267 }
268 Err(err) => {
269 tracing::error!(?err, "error driving tunnel");
270 }
271 }
272 backoff.backoff().await;
273 }
274 }
275 Err(ref e) => tracing::error!(err=?e, "error driving tunnel"),
276 _ => {}
277 }
278 }
279 }
280
281 async fn drive_once(&mut self) -> Result<(), SnapTunnelDriverError> {
285 select! {
286 biased;
288 _ = self.update_timers_interval.tick() => {
289 let p = match self.tunn.lock().unwrap().update_timers() {
290 Ok(Some(wg)) => { Some(wg) },
291 Ok(None) => None,
292 Err(WireGuardError::ConnectionExpired) => {
293 return Err(SnapTunnelDriverError::ConnectionExpired);
294 }
295 Err(e) => {
296 tracing::error!(err=?e, "unexpected error updating timers on tunnel");
299 None
300 }
301 };
302 if let Some(wg) = p && let Err(e) = self.underlay_socket.send_to(to_bytes(wg).as_bytes(), self.dataplane_address).await {
303 return Err(SnapTunnelDriverError::SendIoError(e));
304 }
305 },
306 recv = self.receiver.recv_batch(&self.underlay_socket, &self.pool, |buf, sender_addr| {
307 if sender_addr != self.dataplane_address {
308 return Ok(());
309 }
310 let Ok(wg) = buf.try_into_wg() else {
311 tracing::debug!("received packet that is not a valid WireGuard packet, ignoring");
312 return Ok(());
313 };
314 let result = self.tunn.lock().unwrap().handle_incoming_packet(wg);
315 match result {
316 TunnResult::Done => {}
317 TunnResult::Err(e) => {
318 return Err(SnapTunnelDriverError::WireguardError(e));
319 }
320 TunnResult::WriteToNetwork(p) => {
321 if let Err(error) = self
322 .sender
323 .try_queue_packet(to_bytes(p), self.dataplane_address)
324 {
325 match error {
326 QueuePacketError::Full { packet, target } => {
327 Self::try_flush(
328 &self.underlay_socket,
329 &mut self.sender,
330 &self.discarded_datagrams,
331 );
332 if self.sender.try_queue_packet(packet, target).is_err() {
333 tracing::debug!(?target, "dropping outbound packet because batched sender remains full");
334 }
335 }
336 QueuePacketError::PacketTooLarge {
337 packet_len,
338 max_packet_size,
339 ..
340 } => {
341 return Err(SnapTunnelDriverError::SendIoError(io::Error::new(
342 io::ErrorKind::InvalidInput,
343 format!(
344 "outbound packet length {packet_len} exceeds batched sender max of {max_packet_size}"
345 ),
346 )));
347 }
348 }
349 }
350 for queued in self.tunn.lock().unwrap().get_queued_packets() {
351 if let Err(error) = self
352 .sender
353 .try_queue_packet(to_bytes(queued), self.dataplane_address)
354 {
355 match error {
356 QueuePacketError::Full { packet, target } => {
357 Self::try_flush(
358 &self.underlay_socket,
359 &mut self.sender,
360 &self.discarded_datagrams,
361 );
362 if self.sender.try_queue_packet(packet, target).is_err() {
363 tracing::debug!(?target, "dropping queued outbound packet because batched sender remains full");
364 }
365 }
366 QueuePacketError::PacketTooLarge {
367 packet_len,
368 max_packet_size,
369 ..
370 } => {
371 return Err(SnapTunnelDriverError::SendIoError(io::Error::new(
372 io::ErrorKind::InvalidInput,
373 format!(
374 "queued outbound packet length {packet_len} exceeds batched sender max of {max_packet_size}"
375 ),
376 )));
377 }
378 }
379 }
380 }
381 }
382 TunnResult::WriteToTunnel(mut p) => {
383 let buf = p.buf_mut().to_owned();
384 if !buf.is_empty() {
385 match self.packet_sender.try_send(buf) {
386 Ok(()) => {}
387 Err(async_channel::TrySendError::Full(_)) => {
388 tracing::debug!("receive channel is full, dropping packet");
389 }
390 Err(_) => {
391 return Err(SnapTunnelDriverError::ReceiveQueueClosed);
392 }
393 }
394 }
395 }
396 }
397 Ok(())
398 }) => {
399 match recv {
400 Ok(()) => {
401 Self::flush(
402 &self.underlay_socket,
403 &mut self.sender,
404 &self.discarded_datagrams,
405 )
406 .await?;
407 }
408 Err(RecvBatchError::Io(e)) => {
409 return Err(SnapTunnelDriverError::ReceiveIoError(e));
410 }
411 Err(RecvBatchError::Handler(e)) => {
412 return Err(e);
413 }
414 }
415 }
416 }
417 Ok(())
418 }
419
420 fn create_tunn(
421 static_private: x25519::StaticSecret,
422 peer_public: x25519::PublicKey,
423 dataplane_address: SocketAddr,
424 persistent_keepalive_seconds: Option<u16>,
425 ) -> Tunn {
426 let local_public = x25519::PublicKey::from(&static_private);
427 Tunn::new(
428 static_private,
429 peer_public,
430 None,
431 persistent_keepalive_seconds,
432 0,
433 Arc::new(RateLimiter::new(&local_public, HANDSHAKE_RATE_LIMIT)),
434 dataplane_address,
435 )
436 }
437}
438
439#[derive(Debug, thiserror::Error)]
441pub enum SnapTunnelReceiveError {
442 #[error("receive queue closed")]
444 ReceiveQueueClosed,
445}
446
447type RecvFuture = Pin<Box<dyn Future<Output = Result<BytesMut, async_channel::RecvError>> + Send>>;
448
449pub struct SnapTunnel {
451 _guard: TunnelGuard,
452 tunn: Arc<Mutex<Tunn>>,
453 underlay_socket: Arc<tokio::net::UdpSocket>,
454 dataplane_address: SocketAddr,
455 local_sockaddr: SocketAddr,
456 receive_queue: async_channel::Receiver<BytesMut>,
457 recv_future: Mutex<Option<RecvFuture>>,
459 driver_task: JoinHandle<()>,
462 discarded_datagrams: Arc<AtomicU64>,
463}
464
465impl Drop for SnapTunnel {
466 fn drop(&mut self) {
467 self.driver_task.abort();
468 }
469}
470
471impl SnapTunnel {
472 pub(super) async fn new(
483 guard: TunnelGuard,
484 static_private: x25519::StaticSecret,
485 peer_public: x25519::PublicKey,
486 underlay_socket: Arc<tokio::net::UdpSocket>,
487 dataplane_address: SocketAddr,
488 receive_queue_capacity: usize,
489 persistent_keepalive_seconds: Option<u16>,
490 pool: PacketBufPool<PACKET_BUF_POOL_SIZE>,
491 ) -> Result<Self, SnapTunnelDriverError> {
492 let (packet_sender, packet_receiver) = async_channel::bounded(receive_queue_capacity);
493 let mut driver = SnapTunnelDriver::new(
494 static_private,
495 peer_public,
496 underlay_socket.clone(),
497 dataplane_address,
498 persistent_keepalive_seconds,
499 packet_sender,
500 pool.clone(),
501 )?;
502 let socket_addr = driver.initiate_connection().await?;
503 Ok(Self {
504 _guard: guard,
505 tunn: driver.tunn.clone(),
506 discarded_datagrams: driver.discarded_datagrams.clone(),
507 underlay_socket,
508 dataplane_address,
509 local_sockaddr: socket_addr,
510 receive_queue: packet_receiver,
511 recv_future: Mutex::new(None),
512 driver_task: tokio::spawn(driver.main_loop()),
513 })
514 }
515
516 #[instrument(name = "st-client", skip_all, fields(socket_addr= ?self.local_sockaddr, payload_len= packet.len()))]
519 pub async fn send(&self, packet: Packet) -> io::Result<()> {
520 let encapsulated_packet = self.tunn.lock().unwrap().handle_outgoing_packet(packet);
521 match encapsulated_packet {
522 Some(wg) => {
523 let bytes = match wg {
524 WgKind::HandshakeInit(p) => p.into_bytes(),
525 WgKind::HandshakeResp(p) => p.into_bytes(),
526 WgKind::CookieReply(p) => p.into_bytes(),
527 WgKind::Data(p) => p.into_bytes(),
528 };
529 tracing::trace!(dataplane_address=?self.dataplane_address, "sending packet");
530 self.underlay_socket
531 .send_to(bytes.as_bytes(), self.dataplane_address)
532 .await?;
533 Ok(())
534 }
535 None => {
536 tracing::trace!("handshake ongoing, queueing packet");
540 Ok(())
541 }
542 }
543 }
544
545 #[instrument(name = "st-client", skip_all, fields(socket_addr= ?self.local_sockaddr, payload_len= packet.len()))]
547 pub fn try_send(&self, packet: Packet) -> io::Result<()> {
548 match self.tunn.lock().unwrap().handle_outgoing_packet(packet) {
549 Some(wg) => {
550 let bytes = match wg {
551 WgKind::HandshakeInit(p) => p.into_bytes(),
552 WgKind::HandshakeResp(p) => p.into_bytes(),
553 WgKind::CookieReply(p) => p.into_bytes(),
554 WgKind::Data(p) => p.into_bytes(),
555 };
556 tracing::trace!(dataplane_address=?self.dataplane_address, "trying to send packet");
557 self.underlay_socket
558 .try_send_to(bytes.as_bytes(), self.dataplane_address)?;
559 Ok(())
560 }
561 None => {
562 Ok(())
566 }
567 }
568 }
569
570 pub async fn recv(&self) -> Result<Bytes, SnapTunnelReceiveError> {
572 match self.receive_queue.recv().await {
573 Ok(packet) => Ok(packet.into()),
574 Err(_) => Err(SnapTunnelReceiveError::ReceiveQueueClosed),
575 }
576 }
577
578 pub fn try_recv(&self) -> Result<Option<Bytes>, SnapTunnelReceiveError> {
582 match self.receive_queue.try_recv() {
583 Ok(packet) => Ok(Some(packet.into())),
584 Err(async_channel::TryRecvError::Empty) => Ok(None),
585 Err(async_channel::TryRecvError::Closed) => {
586 Err(SnapTunnelReceiveError::ReceiveQueueClosed)
587 }
588 }
589 }
590
591 pub fn poll_recv(
593 &self,
594 cx: &mut std::task::Context<'_>,
595 ) -> std::task::Poll<Result<Bytes, SnapTunnelReceiveError>> {
596 let mut fut_guard = self.recv_future.lock().expect("lock poisoned");
597
598 if fut_guard.is_none() {
600 let receiver = self.receive_queue.clone();
602 *fut_guard = Some(Box::pin(async move { receiver.recv().await }));
603 }
604
605 let fut = fut_guard.as_mut().expect("future cannot be none");
607 match fut.as_mut().poll(cx) {
608 std::task::Poll::Ready(Ok(packet)) => {
609 *fut_guard = None;
611 std::task::Poll::Ready(Ok(packet.into()))
612 }
613 std::task::Poll::Ready(Err(_)) => {
614 tracing::trace!("receive queue closed, returning error");
615 *fut_guard = None;
616 std::task::Poll::Ready(Err(SnapTunnelReceiveError::ReceiveQueueClosed))
617 }
618 std::task::Poll::Pending => std::task::Poll::Pending,
619 }
620 }
621
622 pub fn local_addr(&self) -> SocketAddr {
624 self.local_sockaddr
625 }
626
627 pub async fn writable(&self) -> io::Result<()> {
629 self.underlay_socket.writable().await
630 }
631
632 pub fn data_plane_address(&self) -> SocketAddr {
634 self.dataplane_address
635 }
636
637 pub fn discarded_datagrams(&self) -> u64 {
645 self.discarded_datagrams.load(Ordering::Relaxed)
646 }
647}
648
649struct BackoffState {
650 last: Instant,
651 exp_backoff: ExponentialBackoff,
652 attempt: usize,
653}
654
655impl BackoffState {
656 fn new() -> Self {
657 Self {
658 last: Instant::now(),
659 exp_backoff: ExponentialBackoff::new(
660 5.0, 180.0, 1.3, 0.5,
662 ),
663 attempt: 0,
664 }
665 }
666
667 fn backoff(&mut self) -> impl Future<Output = ()> {
668 let now = Instant::now();
669 let until_next = (self.last + self.exp_backoff.duration(self.attempt as u32))
670 .checked_duration_since(now);
671 self.attempt += 1;
672 self.last = now;
673
674 async move {
675 if let Some(d) = until_next {
676 tokio::time::sleep(d).await;
677 }
678 }
679 }
680}
681
682fn to_bytes(wg: WgKind) -> Packet<[u8]> {
683 match wg {
684 WgKind::HandshakeInit(p) => p.into_bytes(),
685 WgKind::HandshakeResp(p) => p.into_bytes(),
686 WgKind::CookieReply(p) => p.into_bytes(),
687 WgKind::Data(p) => p.into_bytes(),
688 }
689}
690
691#[cfg(test)]
692mod tests {
693 use super::*;
694
695 #[test]
696 fn driver_error_transient_classification() {
697 assert!(SnapTunnelDriverError::SendIoError(io::Error::other("boom")).is_transient());
699 assert!(SnapTunnelDriverError::ReceiveIoError(io::Error::other("boom")).is_transient());
700 assert!(SnapTunnelDriverError::ConnectionExpired.is_transient());
701 for error in [
704 WireGuardError::NoCurrentSession,
705 WireGuardError::WrongIndex,
706 WireGuardError::UnexpectedPacket,
707 WireGuardError::InvalidCounter,
708 WireGuardError::DuplicateCounter,
709 ] {
710 assert!(
711 SnapTunnelDriverError::WireguardError(error).is_transient(),
712 "a handshake-timing condition was reported as permanent"
713 );
714 }
715 assert!(!SnapTunnelDriverError::WireguardError(WireGuardError::WrongKey).is_transient());
718 assert!(!SnapTunnelDriverError::ReceiveQueueClosed.is_transient());
719 }
720}