1use std::{
16 future::Future,
17 io,
18 net::SocketAddr,
19 pin::Pin,
20 sync::{
21 Arc, Mutex,
22 atomic::{AtomicBool, 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
44const REHANDSHAKE_BUDGET: Duration = Duration::from_secs(600);
46
47#[derive(Debug, thiserror::Error)]
49pub enum SnapTunnelDriverError {
50 #[error("send i/o error: {0}")]
52 SendIoError(#[from] std::io::Error),
53 #[error("receive i/o error: {0}")]
55 ReceiveIoError(std::io::Error),
56 #[error("receive queue closed")]
58 ReceiveQueueClosed,
59 #[error("connection expired")]
61 ConnectionExpired,
62 #[error("error receiving a Wireguard packet: {0:?}")]
65 WireguardError(WireGuardError),
66}
67
68impl SnapTunnelDriverError {
69 #[must_use]
74 pub fn is_transient(&self) -> bool {
75 match self {
76 Self::SendIoError(_) | Self::ReceiveIoError(_) => true,
79 Self::ConnectionExpired => true,
82 Self::ReceiveQueueClosed => false,
84 Self::WireguardError(error) => {
94 match error {
95 WireGuardError::NoCurrentSession
96 | WireGuardError::WrongIndex
97 | WireGuardError::UnexpectedPacket
98 | WireGuardError::WrongPacketType
99 | WireGuardError::IncorrectPacketLength
100 | WireGuardError::InvalidPacket
101 | WireGuardError::InvalidCounter
102 | WireGuardError::DuplicateCounter
103 | WireGuardError::InvalidMac
104 | WireGuardError::InvalidAeadTag
105 | WireGuardError::InvalidTai64nTimestamp
106 | WireGuardError::WrongTai64nTimestamp
107 | WireGuardError::ConnectionExpired => true,
108 WireGuardError::WrongKey
109 | WireGuardError::LockFailed
110 | WireGuardError::DestinationBufferTooSmall => false,
111 }
112 }
113 }
114 }
115}
116
117struct SnapTunnelDriver {
118 pub tunn: Arc<Mutex<Tunn>>,
119 pub static_private: x25519::StaticSecret,
120 pub peer_public: x25519::PublicKey,
121 pub underlay_socket: Arc<tokio::net::UdpSocket>,
122 pub dataplane_address: SocketAddr,
123 pub persistent_keepalive_seconds: Option<u16>,
124 pub update_timers_interval: Interval,
125 pub packet_sender: async_channel::Sender<BytesMut>,
126 pub local_sockaddr: Option<SocketAddr>,
127 pub pool: PacketBufPool<PACKET_BUF_POOL_SIZE>,
128 pub receiver: UdpBatchReceiver<RECEIVE_BATCH_SIZE, PACKET_BUF_POOL_SIZE>,
129 pub sender: UdpBatchSender<RECEIVE_BATCH_SIZE, PACKET_BUF_POOL_SIZE>,
130 pub discarded_datagrams: Arc<AtomicU64>,
132 pub closed: Arc<AtomicBool>,
134}
135
136impl SnapTunnelDriver {
137 fn new(
138 static_private: x25519::StaticSecret,
139 peer_public: x25519::PublicKey,
140 underlay_socket: Arc<tokio::net::UdpSocket>,
141 dataplane_address: SocketAddr,
142 persistent_keepalive_seconds: Option<u16>,
143 packet_sender: async_channel::Sender<BytesMut>,
144 pool: PacketBufPool<PACKET_BUF_POOL_SIZE>,
145 ) -> io::Result<Self> {
146 let update_timers_interval = tokio::time::interval_at(
147 tokio::time::Instant::now() + Duration::from_millis(250),
148 Duration::from_millis(250),
149 );
150 let receiver = UdpBatchReceiver::<RECEIVE_BATCH_SIZE, PACKET_BUF_POOL_SIZE>::new(
151 underlay_socket.as_ref(),
152 &pool,
153 )?;
154 let sender = UdpBatchSender::<RECEIVE_BATCH_SIZE, PACKET_BUF_POOL_SIZE>::new(
155 underlay_socket.as_ref(),
156 )?;
157 Ok(Self {
158 tunn: Arc::new(Mutex::new(Self::create_tunn(
159 static_private.clone(),
160 peer_public,
161 dataplane_address,
162 persistent_keepalive_seconds,
163 ))),
164 static_private,
165 peer_public,
166 underlay_socket,
167 dataplane_address,
168 persistent_keepalive_seconds,
169 update_timers_interval,
170 packet_sender,
171 local_sockaddr: None,
172 receiver,
173 sender,
174 pool,
175 discarded_datagrams: Arc::new(AtomicU64::new(0)),
176 closed: Arc::new(AtomicBool::new(false)),
177 })
178 }
179
180 fn try_flush(
188 socket: &tokio::net::UdpSocket,
189 sender: &mut UdpBatchSender<RECEIVE_BATCH_SIZE, PACKET_BUF_POOL_SIZE>,
190 discarded_datagrams: &AtomicU64,
191 ) {
192 let _ = sender.try_flush_best_effort(socket);
193 Self::account_discarded_datagrams(sender, discarded_datagrams);
194 }
195
196 async fn flush(
198 socket: &tokio::net::UdpSocket,
199 sender: &mut UdpBatchSender<RECEIVE_BATCH_SIZE, PACKET_BUF_POOL_SIZE>,
200 discarded_datagrams: &AtomicU64,
201 ) -> io::Result<()> {
202 let result = sender.flush(socket).await;
203 Self::account_discarded_datagrams(sender, discarded_datagrams);
204 result
205 }
206
207 fn account_discarded_datagrams(
212 sender: &mut UdpBatchSender<RECEIVE_BATCH_SIZE, PACKET_BUF_POOL_SIZE>,
213 discarded_datagrams: &AtomicU64,
214 ) {
215 let discarded = sender.take_discarded_datagrams();
216 if discarded > 0 {
217 discarded_datagrams.fetch_add(discarded, Ordering::Relaxed);
218 }
219 }
220
221 #[instrument(name = "st-client", skip(self), fields(socket_addr= ?self.local_sockaddr))]
222 async fn initiate_connection(&mut self) -> Result<SocketAddr, SnapTunnelDriverError> {
223 let handshake_init = self.tunn.lock().unwrap().format_handshake_initiation(false);
224 if let Some(wg_init) = handshake_init
225 && let Err(e) = self
226 .underlay_socket
227 .send_to(
228 to_bytes(WgKind::HandshakeInit(wg_init)).as_bytes(),
229 self.dataplane_address,
230 )
231 .await
232 {
233 return Err(SnapTunnelDriverError::SendIoError(e));
234 }
235 loop {
237 self.drive_once().await?;
238 if let Some(sockaddr) = self.tunn.lock().unwrap().get_initiator_remote_sockaddr() {
239 if self.local_sockaddr.is_none() {
240 self.local_sockaddr = Some(sockaddr);
241 }
242 tracing::debug!(local_addr=?sockaddr, "handshake completed, local address assigned");
243 return Ok(sockaddr);
244 }
245 }
246 }
247
248 #[instrument(name = "st-client", skip(self), fields(socket_addr= ?self.local_sockaddr))]
250 async fn main_loop(mut self) {
251 self.run().await;
252 self.closed.store(true, Ordering::Release);
253 tracing::info!("snap tunnel driver stopped");
254 }
255
256 async fn run(&mut self) {
259 let local_sockaddr = self
260 .local_sockaddr
261 .expect("local address must be set before main_loop()");
262 loop {
263 match self.drive_once().await {
264 Err(SnapTunnelDriverError::ReceiveQueueClosed) => {
265 tracing::info!("receive queue closed, snap tunnel driver shutting down");
266 return;
267 }
268 Err(SnapTunnelDriverError::ConnectionExpired) => {
269 if !self.rehandshake(local_sockaddr).await {
270 return;
271 }
272 }
273 Err(ref e) => tracing::error!(err=?e, "error driving tunnel"),
274 _ => {}
275 }
276 }
277 }
278
279 async fn rehandshake(&mut self, local_sockaddr: SocketAddr) -> bool {
285 let started = Instant::now();
286 let mut backoff = BackoffState::new();
287 loop {
288 *self.tunn.lock().expect("poison") = Self::create_tunn(
289 self.static_private.clone(),
290 self.peer_public,
291 self.dataplane_address,
292 self.persistent_keepalive_seconds,
293 );
294 let result = self.initiate_connection().await;
295 match rehandshake_outcome(local_sockaddr, &result, started.elapsed()) {
296 Recovery::Recovered => return true,
297 Recovery::Retry => {
298 tracing::warn!(?result, "re-handshake failed, retrying");
299 }
300 Recovery::Stop(reason) => {
301 tracing::error!(
302 ?reason,
303 ?result,
304 "cannot re-establish snap tunnel, stopping driver"
305 );
306 return false;
307 }
308 }
309 backoff.backoff().await;
310 }
311 }
312
313 async fn drive_once(&mut self) -> Result<(), SnapTunnelDriverError> {
317 select! {
318 biased;
320 _ = self.update_timers_interval.tick() => {
321 let p = match self.tunn.lock().unwrap().update_timers() {
322 Ok(Some(wg)) => { Some(wg) },
323 Ok(None) => None,
324 Err(WireGuardError::ConnectionExpired) => {
325 return Err(SnapTunnelDriverError::ConnectionExpired);
326 }
327 Err(e) => {
328 tracing::error!(err=?e, "unexpected error updating timers on tunnel");
331 None
332 }
333 };
334 if let Some(wg) = p && let Err(e) = self.underlay_socket.send_to(to_bytes(wg).as_bytes(), self.dataplane_address).await {
335 return Err(SnapTunnelDriverError::SendIoError(e));
336 }
337 },
338 recv = self.receiver.recv_batch(&self.underlay_socket, &self.pool, |buf, sender_addr| {
339 if sender_addr != self.dataplane_address {
340 return Ok(());
341 }
342 let Ok(wg) = buf.try_into_wg() else {
343 tracing::debug!("received packet that is not a valid WireGuard packet, ignoring");
344 return Ok(());
345 };
346 let result = self.tunn.lock().unwrap().handle_incoming_packet(wg);
347 match result {
348 TunnResult::Done => {}
349 TunnResult::Err(e) => {
350 return Err(SnapTunnelDriverError::WireguardError(e));
351 }
352 TunnResult::WriteToNetwork(p) => {
353 if let Err(error) = self
354 .sender
355 .try_queue_packet(to_bytes(p), self.dataplane_address)
356 {
357 match error {
358 QueuePacketError::Full { packet, target } => {
359 Self::try_flush(
360 &self.underlay_socket,
361 &mut self.sender,
362 &self.discarded_datagrams,
363 );
364 if self.sender.try_queue_packet(packet, target).is_err() {
365 tracing::debug!(?target, "dropping outbound packet because batched sender remains full");
366 }
367 }
368 QueuePacketError::PacketTooLarge {
369 packet_len,
370 max_packet_size,
371 ..
372 } => {
373 return Err(SnapTunnelDriverError::SendIoError(io::Error::new(
374 io::ErrorKind::InvalidInput,
375 format!(
376 "outbound packet length {packet_len} exceeds batched sender max of {max_packet_size}"
377 ),
378 )));
379 }
380 }
381 }
382 for queued in self.tunn.lock().unwrap().get_queued_packets() {
383 if let Err(error) = self
384 .sender
385 .try_queue_packet(to_bytes(queued), self.dataplane_address)
386 {
387 match error {
388 QueuePacketError::Full { packet, target } => {
389 Self::try_flush(
390 &self.underlay_socket,
391 &mut self.sender,
392 &self.discarded_datagrams,
393 );
394 if self.sender.try_queue_packet(packet, target).is_err() {
395 tracing::debug!(?target, "dropping queued outbound packet because batched sender remains full");
396 }
397 }
398 QueuePacketError::PacketTooLarge {
399 packet_len,
400 max_packet_size,
401 ..
402 } => {
403 return Err(SnapTunnelDriverError::SendIoError(io::Error::new(
404 io::ErrorKind::InvalidInput,
405 format!(
406 "queued outbound packet length {packet_len} exceeds batched sender max of {max_packet_size}"
407 ),
408 )));
409 }
410 }
411 }
412 }
413 }
414 TunnResult::WriteToTunnel(mut p) => {
415 let buf = p.buf_mut().to_owned();
416 if !buf.is_empty() {
417 match self.packet_sender.try_send(buf) {
418 Ok(()) => {}
419 Err(async_channel::TrySendError::Full(_)) => {
420 tracing::debug!("receive channel is full, dropping packet");
421 }
422 Err(_) => {
423 return Err(SnapTunnelDriverError::ReceiveQueueClosed);
424 }
425 }
426 }
427 }
428 }
429 Ok(())
430 }) => {
431 match recv {
432 Ok(()) => {
433 Self::flush(
434 &self.underlay_socket,
435 &mut self.sender,
436 &self.discarded_datagrams,
437 )
438 .await?;
439 }
440 Err(RecvBatchError::Io(e)) => {
441 return Err(SnapTunnelDriverError::ReceiveIoError(e));
442 }
443 Err(RecvBatchError::Handler(e)) => {
444 return Err(e);
445 }
446 }
447 }
448 }
449 Ok(())
450 }
451
452 fn create_tunn(
453 static_private: x25519::StaticSecret,
454 peer_public: x25519::PublicKey,
455 dataplane_address: SocketAddr,
456 persistent_keepalive_seconds: Option<u16>,
457 ) -> Tunn {
458 let local_public = x25519::PublicKey::from(&static_private);
459 Tunn::new(
460 static_private,
461 peer_public,
462 None,
463 persistent_keepalive_seconds,
464 0,
465 Arc::new(RateLimiter::new(&local_public, HANDSHAKE_RATE_LIMIT)),
466 dataplane_address,
467 )
468 }
469}
470
471#[derive(Debug, PartialEq, Eq)]
473enum Recovery {
474 Recovered,
476 Retry,
478 Stop(StopReason),
480}
481
482#[derive(Debug, PartialEq, Eq)]
484enum StopReason {
485 AddressChanged { new_addr: SocketAddr },
489 PermanentError,
491 BudgetExhausted,
493}
494
495fn rehandshake_outcome(
498 expected_addr: SocketAddr,
499 result: &Result<SocketAddr, SnapTunnelDriverError>,
500 elapsed: Duration,
501) -> Recovery {
502 match result {
503 Ok(addr) if *addr == expected_addr => Recovery::Recovered,
504 Ok(addr) => Recovery::Stop(StopReason::AddressChanged { new_addr: *addr }),
505 Err(e) if !e.is_transient() => Recovery::Stop(StopReason::PermanentError),
506 Err(_) if elapsed >= REHANDSHAKE_BUDGET => Recovery::Stop(StopReason::BudgetExhausted),
507 Err(_) => Recovery::Retry,
508 }
509}
510
511#[derive(Debug, thiserror::Error)]
513pub enum SnapTunnelReceiveError {
514 #[error("receive queue closed")]
516 ReceiveQueueClosed,
517}
518
519type RecvFuture = Pin<Box<dyn Future<Output = Result<BytesMut, async_channel::RecvError>> + Send>>;
520
521pub struct SnapTunnel {
528 _guard: TunnelGuard,
529 tunn: Arc<Mutex<Tunn>>,
530 underlay_socket: Arc<tokio::net::UdpSocket>,
531 dataplane_address: SocketAddr,
532 local_sockaddr: SocketAddr,
533 receive_queue: async_channel::Receiver<BytesMut>,
534 recv_future: Mutex<Option<RecvFuture>>,
536 driver_task: JoinHandle<()>,
539 discarded_datagrams: Arc<AtomicU64>,
540 closed: Arc<AtomicBool>,
541}
542
543impl Drop for SnapTunnel {
544 fn drop(&mut self) {
545 self.driver_task.abort();
546 }
547}
548
549impl SnapTunnel {
550 pub(super) async fn new(
561 guard: TunnelGuard,
562 static_private: x25519::StaticSecret,
563 peer_public: x25519::PublicKey,
564 underlay_socket: Arc<tokio::net::UdpSocket>,
565 dataplane_address: SocketAddr,
566 receive_queue_capacity: usize,
567 persistent_keepalive_seconds: Option<u16>,
568 pool: PacketBufPool<PACKET_BUF_POOL_SIZE>,
569 ) -> Result<Self, SnapTunnelDriverError> {
570 let (packet_sender, packet_receiver) = async_channel::bounded(receive_queue_capacity);
571 let mut driver = SnapTunnelDriver::new(
572 static_private,
573 peer_public,
574 underlay_socket.clone(),
575 dataplane_address,
576 persistent_keepalive_seconds,
577 packet_sender,
578 pool.clone(),
579 )?;
580 let socket_addr = driver.initiate_connection().await?;
581 Ok(Self {
582 _guard: guard,
583 tunn: driver.tunn.clone(),
584 discarded_datagrams: driver.discarded_datagrams.clone(),
585 closed: driver.closed.clone(),
586 underlay_socket,
587 dataplane_address,
588 local_sockaddr: socket_addr,
589 receive_queue: packet_receiver,
590 recv_future: Mutex::new(None),
591 driver_task: tokio::spawn(driver.main_loop()),
592 })
593 }
594
595 #[instrument(name = "st-client", skip_all, fields(socket_addr= ?self.local_sockaddr, payload_len= packet.len()))]
600 pub async fn send(&self, packet: Packet) -> io::Result<()> {
601 if self.is_closed() {
602 return Err(closed_error());
603 }
604 let encapsulated_packet = self.tunn.lock().unwrap().handle_outgoing_packet(packet);
605 match encapsulated_packet {
606 Some(wg) => {
607 let bytes = match wg {
608 WgKind::HandshakeInit(p) => p.into_bytes(),
609 WgKind::HandshakeResp(p) => p.into_bytes(),
610 WgKind::CookieReply(p) => p.into_bytes(),
611 WgKind::Data(p) => p.into_bytes(),
612 };
613 tracing::trace!(dataplane_address=?self.dataplane_address, "sending packet");
614 self.underlay_socket
615 .send_to(bytes.as_bytes(), self.dataplane_address)
616 .await?;
617 Ok(())
618 }
619 None => {
620 tracing::trace!("handshake ongoing, queueing packet");
624 Ok(())
625 }
626 }
627 }
628
629 #[instrument(name = "st-client", skip_all, fields(socket_addr= ?self.local_sockaddr, payload_len= packet.len()))]
633 pub fn try_send(&self, packet: Packet) -> io::Result<()> {
634 if self.is_closed() {
635 return Err(closed_error());
636 }
637 match self.tunn.lock().unwrap().handle_outgoing_packet(packet) {
638 Some(wg) => {
639 let bytes = match wg {
640 WgKind::HandshakeInit(p) => p.into_bytes(),
641 WgKind::HandshakeResp(p) => p.into_bytes(),
642 WgKind::CookieReply(p) => p.into_bytes(),
643 WgKind::Data(p) => p.into_bytes(),
644 };
645 tracing::trace!(dataplane_address=?self.dataplane_address, "trying to send packet");
646 self.underlay_socket
647 .try_send_to(bytes.as_bytes(), self.dataplane_address)?;
648 Ok(())
649 }
650 None => {
651 Ok(())
655 }
656 }
657 }
658
659 pub async fn recv(&self) -> Result<Bytes, SnapTunnelReceiveError> {
663 match self.receive_queue.recv().await {
664 Ok(packet) => Ok(packet.into()),
665 Err(_) => Err(SnapTunnelReceiveError::ReceiveQueueClosed),
666 }
667 }
668
669 pub fn try_recv(&self) -> Result<Option<Bytes>, SnapTunnelReceiveError> {
673 match self.receive_queue.try_recv() {
674 Ok(packet) => Ok(Some(packet.into())),
675 Err(async_channel::TryRecvError::Empty) => Ok(None),
676 Err(async_channel::TryRecvError::Closed) => {
677 Err(SnapTunnelReceiveError::ReceiveQueueClosed)
678 }
679 }
680 }
681
682 pub fn poll_recv(
684 &self,
685 cx: &mut std::task::Context<'_>,
686 ) -> std::task::Poll<Result<Bytes, SnapTunnelReceiveError>> {
687 let mut fut_guard = self.recv_future.lock().expect("lock poisoned");
688
689 if fut_guard.is_none() {
691 let receiver = self.receive_queue.clone();
693 *fut_guard = Some(Box::pin(async move { receiver.recv().await }));
694 }
695
696 let fut = fut_guard.as_mut().expect("future cannot be none");
698 match fut.as_mut().poll(cx) {
699 std::task::Poll::Ready(Ok(packet)) => {
700 *fut_guard = None;
702 std::task::Poll::Ready(Ok(packet.into()))
703 }
704 std::task::Poll::Ready(Err(_)) => {
705 tracing::trace!("receive queue closed, returning error");
706 *fut_guard = None;
707 std::task::Poll::Ready(Err(SnapTunnelReceiveError::ReceiveQueueClosed))
708 }
709 std::task::Poll::Pending => std::task::Poll::Pending,
710 }
711 }
712
713 pub fn local_addr(&self) -> SocketAddr {
715 self.local_sockaddr
716 }
717
718 pub fn is_closed(&self) -> bool {
727 self.closed.load(Ordering::Acquire)
728 }
729
730 pub async fn writable(&self) -> io::Result<()> {
732 self.underlay_socket.writable().await
733 }
734
735 pub fn data_plane_address(&self) -> SocketAddr {
737 self.dataplane_address
738 }
739
740 pub fn discarded_datagrams(&self) -> u64 {
748 self.discarded_datagrams.load(Ordering::Relaxed)
749 }
750}
751
752struct BackoffState {
753 last: Instant,
754 exp_backoff: ExponentialBackoff,
755 attempt: usize,
756}
757
758impl BackoffState {
759 fn new() -> Self {
760 Self {
761 last: Instant::now(),
762 exp_backoff: ExponentialBackoff::new(
763 5.0, 180.0, 1.3, 0.5,
765 ),
766 attempt: 0,
767 }
768 }
769
770 fn next_delay(&mut self) -> Option<Duration> {
772 let now = Instant::now();
773 let until_next = (self.last + self.exp_backoff.duration(self.attempt as u32))
774 .checked_duration_since(now);
775 self.attempt += 1;
776 self.last = now;
777 until_next
778 }
779
780 fn backoff(&mut self) -> impl Future<Output = ()> {
781 let until_next = self.next_delay();
782 async move {
783 if let Some(d) = until_next {
784 tokio::time::sleep(d).await;
785 }
786 }
787 }
788}
789
790fn closed_error() -> io::Error {
791 io::Error::new(io::ErrorKind::ConnectionReset, "SNAP tunnel closed")
792}
793
794fn to_bytes(wg: WgKind) -> Packet<[u8]> {
795 match wg {
796 WgKind::HandshakeInit(p) => p.into_bytes(),
797 WgKind::HandshakeResp(p) => p.into_bytes(),
798 WgKind::CookieReply(p) => p.into_bytes(),
799 WgKind::Data(p) => p.into_bytes(),
800 }
801}
802
803#[cfg(test)]
804mod tests {
805 use super::*;
806
807 #[test]
808 fn driver_error_transient_classification() {
809 assert!(SnapTunnelDriverError::SendIoError(io::Error::other("boom")).is_transient());
811 assert!(SnapTunnelDriverError::ReceiveIoError(io::Error::other("boom")).is_transient());
812 assert!(SnapTunnelDriverError::ConnectionExpired.is_transient());
813 for error in [
816 WireGuardError::NoCurrentSession,
817 WireGuardError::WrongIndex,
818 WireGuardError::UnexpectedPacket,
819 WireGuardError::InvalidCounter,
820 WireGuardError::DuplicateCounter,
821 ] {
822 assert!(
823 SnapTunnelDriverError::WireguardError(error).is_transient(),
824 "a handshake-timing condition was reported as permanent"
825 );
826 }
827 assert!(!SnapTunnelDriverError::WireguardError(WireGuardError::WrongKey).is_transient());
830 assert!(!SnapTunnelDriverError::ReceiveQueueClosed.is_transient());
831 }
832
833 fn addr(s: &str) -> SocketAddr {
834 s.parse().unwrap()
835 }
836
837 #[test]
838 fn rehandshake_recovers_on_the_same_address() {
839 let expected = addr("192.0.2.1:4000");
840 assert_eq!(
841 rehandshake_outcome(expected, &Ok(expected), Duration::ZERO),
842 Recovery::Recovered
843 );
844 }
845
846 #[test]
847 fn rehandshake_stops_when_the_address_changed() {
848 let expected = addr("192.0.2.1:4000");
849 let new_addr = addr("198.51.100.7:4000");
850 assert_eq!(
851 rehandshake_outcome(expected, &Ok(new_addr), Duration::ZERO),
852 Recovery::Stop(StopReason::AddressChanged { new_addr })
853 );
854 }
855
856 #[test]
857 fn rehandshake_retries_transient_errors_within_the_budget() {
858 let expected = addr("192.0.2.1:4000");
859 let expired = Err(SnapTunnelDriverError::ConnectionExpired);
860 assert_eq!(
861 rehandshake_outcome(expected, &expired, Duration::ZERO),
862 Recovery::Retry
863 );
864 assert_eq!(
865 rehandshake_outcome(
866 expected,
867 &expired,
868 REHANDSHAKE_BUDGET - Duration::from_secs(1)
869 ),
870 Recovery::Retry
871 );
872 let send_failed = Err(SnapTunnelDriverError::SendIoError(io::Error::other("down")));
873 assert_eq!(
874 rehandshake_outcome(expected, &send_failed, Duration::ZERO),
875 Recovery::Retry
876 );
877 }
878
879 #[test]
880 fn rehandshake_stops_when_the_budget_is_spent() {
881 let expected = addr("192.0.2.1:4000");
882 let expired = Err(SnapTunnelDriverError::ConnectionExpired);
883 assert_eq!(
884 rehandshake_outcome(expected, &expired, REHANDSHAKE_BUDGET),
885 Recovery::Stop(StopReason::BudgetExhausted)
886 );
887 }
888
889 #[test]
890 fn rehandshake_stops_on_a_permanent_error() {
891 let expected = addr("192.0.2.1:4000");
892 let gone = Err(SnapTunnelDriverError::ReceiveQueueClosed);
893 assert_eq!(
894 rehandshake_outcome(expected, &gone, Duration::ZERO),
895 Recovery::Stop(StopReason::PermanentError)
896 );
897 }
898
899 #[test]
900 fn backoff_delay_grows_across_attempts() {
901 let mut backoff = BackoffState::new();
904 let first = backoff.next_delay().expect("first attempt waits");
905 let second = backoff.next_delay().expect("second attempt waits");
906 let third = backoff.next_delay().expect("third attempt waits");
907 assert!(
908 first < second && second < third,
909 "{first:?} {second:?} {third:?}"
910 );
911 }
912}