use std::{
mem,
ops::{Index, IndexMut},
time::Duration,
};
use super::{Tunn, TunnResult, errors::WireGuardError};
#[cfg(not(feature = "mock-instant"))]
use crate::sleepyinstant::Instant;
#[cfg(feature = "mock-instant")]
use mock_instant::global::Instant;
pub(crate) const REKEY_AFTER_TIME: Duration = Duration::from_mins(2);
const REJECT_AFTER_TIME: Duration = Duration::from_mins(3);
const REKEY_ATTEMPT_TIME: Duration = Duration::from_secs(90);
pub(crate) const REKEY_TIMEOUT: Duration = Duration::from_secs(5);
const KEEPALIVE_TIMEOUT: Duration = Duration::from_secs(10);
const COOKIE_EXPIRATION_TIME: Duration = Duration::from_mins(2);
#[derive(Debug)]
pub enum TimerName {
TimeCurrent,
TimeSessionEstablished,
TimeLastHandshakeStarted,
TimeLastPacketReceived,
TimeLastPacketSent,
TimeLastDataPacketReceived,
TimeLastDataPacketSent,
TimeCookieReceived,
TimePersistentKeepalive,
Top,
}
use self::TimerName::{
TimeCookieReceived, TimeCurrent, TimeLastDataPacketReceived, TimeLastDataPacketSent,
TimeLastHandshakeStarted, TimeLastPacketReceived, TimeLastPacketSent, TimePersistentKeepalive,
TimeSessionEstablished,
};
#[derive(Debug)]
pub struct Timers {
is_initiator: bool,
time_started: Instant,
timers: [Duration; TimerName::Top as usize],
pub(super) session_timers: [Duration; super::N_SESSIONS],
want_keepalive: bool,
want_handshake: bool,
persistent_keepalive: u16,
pub(super) should_reset_rr: bool,
}
impl Timers {
pub(super) fn new(persistent_keepalive: Option<u16>, reset_rr: bool) -> Timers {
Timers {
is_initiator: false,
time_started: Instant::now(),
timers: Default::default(),
session_timers: Default::default(),
want_keepalive: Default::default(),
want_handshake: Default::default(),
persistent_keepalive: persistent_keepalive.unwrap_or_default(),
should_reset_rr: reset_rr,
}
}
fn is_initiator(&self) -> bool {
self.is_initiator
}
pub(super) fn clear(&mut self) {
let now = Instant::now().duration_since(self.time_started);
for t in &mut self.timers[..] {
*t = now;
}
self.want_handshake = false;
self.want_keepalive = false;
}
}
impl Index<TimerName> for Timers {
type Output = Duration;
fn index(&self, index: TimerName) -> &Duration {
&self.timers[index as usize]
}
}
impl IndexMut<TimerName> for Timers {
fn index_mut(&mut self, index: TimerName) -> &mut Duration {
&mut self.timers[index as usize]
}
}
impl Tunn {
pub(super) fn timer_tick(&mut self, timer_name: TimerName) {
match timer_name {
TimeLastPacketReceived => {
self.timers.want_keepalive = true;
self.timers.want_handshake = false;
}
TimeLastPacketSent => {
self.timers.want_handshake = true;
self.timers.want_keepalive = false;
}
_ => {}
}
let time = self.timers[TimeCurrent];
self.timers[timer_name] = time;
}
pub(super) fn timer_tick_session_established(
&mut self,
is_initiator: bool,
session_idx: usize,
) {
self.timer_tick(TimeSessionEstablished);
self.timers.session_timers[session_idx % crate::noise::N_SESSIONS] =
self.timers[TimeCurrent];
self.timers.is_initiator = is_initiator;
}
fn clear_all(&mut self) {
for session in &mut self.sessions {
*session = None;
}
self.packet_queue.clear();
self.timers.clear();
}
fn update_session_timers(&mut self, time_now: Duration) {
let timers = &mut self.timers;
for (i, t) in timers.session_timers.iter_mut().enumerate() {
if time_now.checked_sub(*t).unwrap() > REJECT_AFTER_TIME {
if let Some(session) = self.sessions[i].take() {
tracing::debug!(
message = "SESSION_EXPIRED(REJECT_AFTER_TIME)",
session = session.receiving_index
);
}
*t = time_now;
}
}
}
pub fn update_timers<'a>(&mut self, dst: &'a mut [u8]) -> TunnResult<'a> {
let mut handshake_initiation_required = false;
let mut keepalive_required = false;
let time = Instant::now();
if self.timers.should_reset_rr {
self.rate_limiter.reset_count();
}
let now = time.duration_since(self.timers.time_started);
self.timers[TimeCurrent] = now;
self.update_session_timers(now);
let session_established = self.timers[TimeSessionEstablished];
let handshake_started = self.timers[TimeLastHandshakeStarted];
let aut_packet_received = self.timers[TimeLastPacketReceived];
let aut_packet_sent = self.timers[TimeLastPacketSent];
let data_packet_received = self.timers[TimeLastDataPacketReceived];
let data_packet_sent = self.timers[TimeLastDataPacketSent];
let persistent_keepalive = self.timers.persistent_keepalive;
if self.handshake.is_expired() {
return TunnResult::Err(WireGuardError::ConnectionExpired);
}
if self.handshake.has_cookie()
&& now.checked_sub(self.timers[TimeCookieReceived]).unwrap() >= COOKIE_EXPIRATION_TIME
{
self.handshake.clear_cookie();
}
if now.checked_sub(session_established).unwrap() >= REJECT_AFTER_TIME * 3 {
tracing::error!("CONNECTION_EXPIRED(REJECT_AFTER_TIME * 3)");
self.handshake.set_expired();
self.clear_all();
return TunnResult::Err(WireGuardError::ConnectionExpired);
}
if let Some(time_init_sent) = self.handshake.timer() {
if now.checked_sub(handshake_started).unwrap() >= REKEY_ATTEMPT_TIME {
tracing::error!("CONNECTION_EXPIRED(REKEY_ATTEMPT_TIME)");
self.handshake.set_expired();
self.clear_all();
return TunnResult::Err(WireGuardError::ConnectionExpired);
}
if time_init_sent.elapsed() >= REKEY_TIMEOUT {
tracing::warn!("HANDSHAKE(REKEY_TIMEOUT)");
handshake_initiation_required = true;
}
} else {
if self.timers.is_initiator() {
if session_established < data_packet_sent
&& now.checked_sub(session_established).unwrap() >= REKEY_AFTER_TIME
{
tracing::debug!("HANDSHAKE(REKEY_AFTER_TIME (on send))");
handshake_initiation_required = true;
}
if session_established < data_packet_received
&& now.checked_sub(session_established).unwrap()
>= REJECT_AFTER_TIME
.checked_sub(KEEPALIVE_TIMEOUT)
.unwrap()
.checked_sub(REKEY_TIMEOUT)
.unwrap()
{
tracing::warn!(
"HANDSHAKE(REJECT_AFTER_TIME - KEEPALIVE_TIMEOUT - \
REKEY_TIMEOUT \
(on receive))"
);
handshake_initiation_required = true;
}
}
if data_packet_sent > aut_packet_received
&& now.checked_sub(aut_packet_received).unwrap()
>= KEEPALIVE_TIMEOUT + REKEY_TIMEOUT
&& mem::replace(&mut self.timers.want_handshake, false)
{
tracing::warn!("HANDSHAKE(KEEPALIVE + REKEY_TIMEOUT)");
handshake_initiation_required = true;
}
if !handshake_initiation_required {
if data_packet_received > aut_packet_sent
&& now.checked_sub(aut_packet_sent).unwrap() >= KEEPALIVE_TIMEOUT
&& mem::replace(&mut self.timers.want_keepalive, false)
{
tracing::debug!("KEEPALIVE(KEEPALIVE_TIMEOUT)");
keepalive_required = true;
}
if persistent_keepalive > 0
&& (now
.checked_sub(self.timers[TimePersistentKeepalive])
.unwrap()
>= Duration::from_secs(persistent_keepalive.into()))
{
tracing::debug!("KEEPALIVE(PERSISTENT_KEEPALIVE)");
self.timer_tick(TimePersistentKeepalive);
keepalive_required = true;
}
}
}
if handshake_initiation_required {
return self.format_handshake_initiation(dst, true);
}
if keepalive_required {
return self.encapsulate(&[], dst);
}
TunnResult::Done
}
pub fn time_since_last_handshake(&self) -> Option<Duration> {
let current_session = self.current;
if self.sessions[current_session % super::N_SESSIONS].is_some() {
let duration_since_tun_start = Instant::now().duration_since(self.timers.time_started);
let duration_since_session_established = self.timers[TimeSessionEstablished];
Some(
duration_since_tun_start
.checked_sub(duration_since_session_established)
.unwrap(),
)
} else {
None
}
}
pub fn persistent_keepalive(&self) -> Option<u16> {
let keepalive = self.timers.persistent_keepalive;
if keepalive > 0 { Some(keepalive) } else { None }
}
}