use std::cmp;
use std::collections::BTreeMap;
use std::collections::VecDeque;
use std::net::IpAddr;
use std::net::SocketAddr;
use std::time;
use std::time::Duration;
use std::time::Instant;
use slab::Slab;
use super::pmtu::Dplpmtud;
use super::recovery::Recovery;
use super::timer;
use crate::connection::SpaceId;
use crate::error::Error;
use crate::multipath_scheduler::MultipathScheduler;
use crate::FourTuple;
use crate::PathStats;
use crate::RecoveryConfig;
use crate::Result;
use crate::TIMER_GRANULARITY;
pub(crate) const INITIAL_CHAL_TIMEOUT: u64 = 25;
pub(crate) const MAX_PROBING_TIMEOUTS: usize = 8;
pub(crate) const MAX_PATH_CHALS_RECV: usize = 8;
pub(crate) const MIN_PATH_PROBE_SIZE: usize = 64;
pub struct Path {
local_addr: SocketAddr,
remote_addr: SocketAddr,
pub(crate) scid_seq: Option<u64>,
pub(crate) dcid_seq: Option<u64>,
active: bool,
pub(crate) recovery: Recovery,
state: PathState,
recv_chals: VecDeque<[u8; 8]>,
sent_chals: VecDeque<([u8; 8], usize, time::Instant, time::Instant)>,
need_send_challenge: bool,
lost_chal: usize,
max_challenge_size: usize,
pub(super) verified_peer_address: bool,
pub(super) peer_verified_local_address: bool,
pub(super) anti_ampl_limit: usize,
pub(super) dplpmtud: Dplpmtud,
pub(super) need_send_ping: bool,
trace_id: String,
pub(crate) space_id: SpaceId,
pub(super) is_abandon: bool,
}
impl Path {
pub(crate) fn new(
local_addr: SocketAddr,
remote_addr: SocketAddr,
is_initial: bool,
conf: &RecoveryConfig,
trace_id: &str,
) -> Self {
let (state, scid_seq, dcid_seq) = if is_initial {
(PathState::Validated, Some(0), Some(0))
} else {
(PathState::Unknown, None, None)
};
let dplpmtud = Dplpmtud::new(
conf.enable_dplpmtud,
conf.max_datagram_size,
Self::is_ipv6(&remote_addr),
);
Self {
local_addr,
remote_addr,
scid_seq,
dcid_seq,
active: false,
recovery: Recovery::new(conf),
state,
recv_chals: VecDeque::new(),
sent_chals: VecDeque::new(),
need_send_challenge: false,
lost_chal: 0,
max_challenge_size: 0,
verified_peer_address: false,
peer_verified_local_address: false,
anti_ampl_limit: 0,
dplpmtud,
need_send_ping: false,
trace_id: trace_id.to_string(),
space_id: SpaceId::Data,
is_abandon: false,
}
}
#[doc(hidden)]
pub fn update_trace_id(&mut self, path_id: usize) {
self.trace_id.push_str(&(format!("-{}", path_id)));
self.recovery.set_trace_id(&self.trace_id);
}
pub fn local_addr(&self) -> SocketAddr {
self.local_addr
}
pub fn remote_addr(&self) -> SocketAddr {
self.remote_addr
}
pub(super) fn on_path_chal_received(&mut self, data: [u8; 8]) {
if self.recv_chals.len() >= MAX_PATH_CHALS_RECV {
let _ = self.recv_chals.pop_front();
}
self.recv_chals.push_back(data);
self.peer_verified_local_address = true;
}
pub(super) fn on_path_resp_received(&mut self, data: [u8; 8], multipath: bool) -> bool {
if self.state == PathState::Validated {
return false;
}
self.verified_peer_address = true;
self.lost_chal = 0;
let mut challenge_size = 0;
self.sent_chals.retain(|(d, s, sent_time, _)| {
if *d == data {
challenge_size = *s;
let initial_rtt = Instant::now().duration_since(*sent_time);
self.recovery.rtt.try_set_init_rtt(initial_rtt);
false
} else {
true
}
});
self.max_challenge_size = std::cmp::max(self.max_challenge_size, challenge_size);
self.promote_to(PathState::ValidatingMTU);
if self.max_challenge_size >= crate::MIN_CLIENT_INITIAL_LEN {
self.promote_to(PathState::Validated);
self.set_active(multipath);
self.sent_chals.clear();
return true;
}
self.need_send_challenge = true;
false
}
pub(super) fn pop_recv_chal(&mut self) -> Option<[u8; 8]> {
self.recv_chals.pop_front()
}
pub(super) fn path_chal_initiated(&self) -> bool {
self.need_send_challenge
}
pub(super) fn initiate_path_chal(&mut self) {
self.need_send_challenge = true;
}
pub(super) fn on_path_chal_sent(
&mut self,
data: [u8; 8],
pkt_size: usize,
sent_time: time::Instant,
) {
self.promote_to(PathState::Validating);
self.need_send_challenge = false;
let loss_time =
sent_time + time::Duration::from_millis(INITIAL_CHAL_TIMEOUT << self.lost_chal);
self.sent_chals
.push_back((data, pkt_size, sent_time, loss_time));
}
pub(super) fn on_path_chal_timeout(&mut self, now: time::Instant) {
if self.state != PathState::Validating && self.state != PathState::ValidatingMTU {
return;
}
while let Some(first_chal) = self.sent_chals.front() {
if first_chal.3 > now {
return;
}
self.sent_chals.pop_front();
self.lost_chal += 1;
if self.lost_chal < MAX_PROBING_TIMEOUTS {
self.initiate_path_chal();
} else {
self.state = PathState::Failed;
self.active = false;
self.sent_chals.clear();
return;
}
}
}
pub(super) fn need_send_validation_frames(&self, is_server: bool) -> bool {
if is_server && self.anti_ampl_limit < MIN_PATH_PROBE_SIZE {
return false;
}
self.need_send_challenge || !self.recv_chals.is_empty()
}
pub(super) fn need_expand_padding_frames(&self, is_server: bool) -> bool {
if self.validated() {
return false;
}
if is_server && self.anti_ampl_limit <= self.recovery.max_datagram_size {
return false;
}
true
}
fn promote_to(&mut self, state: PathState) {
if self.state < state {
self.state = state;
}
}
pub fn validated(&self) -> bool {
self.state == PathState::Validated
}
pub fn active(&self) -> bool {
self.active && self.dcid_seq.is_some()
}
pub(crate) fn set_active(&mut self, v: bool) {
self.active = v;
}
fn unused(&self) -> bool {
!self.active && self.dcid_seq.is_none()
}
pub fn stats(&mut self) -> &PathStats {
self.recovery.stat_lazy_update();
&self.recovery.stats
}
pub fn state(&self) -> PathState {
self.state
}
fn is_ipv6(addr: &SocketAddr) -> bool {
if let IpAddr::V6(ip) = addr.ip() {
if !matches!(ip.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) {
return true;
}
}
false
}
}
impl std::fmt::Debug for Path {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "local={:?} ", self.local_addr)?;
write!(f, "remote={:?}", self.remote_addr)?;
Ok(())
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum PathState {
Failed,
Unknown,
Validating,
ValidatingMTU,
Validated,
}
pub(crate) struct PathMap {
paths: Slab<Path>,
max_paths: usize,
addrs: BTreeMap<(SocketAddr, SocketAddr), usize>,
pub(crate) anti_ampl_factor: usize,
is_multipath: bool,
is_server: bool,
}
impl PathMap {
pub fn new(
mut initial_path: Path,
max_paths: usize,
anti_ampl_factor: usize,
is_server: bool,
) -> Self {
initial_path.active = true;
let local_addr = initial_path.local_addr;
let remote_addr = initial_path.remote_addr;
let mut paths = Slab::with_capacity(2);
let active_path_id = paths.insert(initial_path);
if let Some(path) = paths.get_mut(active_path_id) {
path.update_trace_id(active_path_id);
}
let mut addrs = BTreeMap::new();
addrs.insert((local_addr, remote_addr), active_path_id);
Self {
paths,
max_paths,
addrs,
anti_ampl_factor,
is_multipath: false,
is_server,
}
}
pub fn get(&self, path_id: usize) -> Result<&Path> {
self.paths.get(path_id).ok_or(Error::InternalError)
}
pub fn get_mut(&mut self, path_id: usize) -> Result<&mut Path> {
self.paths.get_mut(path_id).ok_or(Error::InternalError)
}
pub fn get_path_id(&self, addrs: &(SocketAddr, SocketAddr)) -> Option<usize> {
self.addrs.get(addrs).copied()
}
pub fn get_active(&self) -> Result<&Path> {
Ok(self.get_active_with_path_id()?.1)
}
pub fn get_active_mut(&mut self) -> Result<&mut Path> {
let path = self.paths.iter_mut().find(|(_, p)| p.active);
Ok(path.ok_or(Error::InternalError)?.1)
}
pub fn get_active_path_id(&self) -> Result<usize> {
Ok(self.get_active_with_path_id()?.0)
}
pub fn get_active_with_path_id(&self) -> Result<(usize, &Path)> {
self.paths
.iter()
.find(|(_, p)| p.active)
.ok_or(Error::InternalError)
}
pub fn insert_path(&mut self, path: Path) -> Result<usize> {
if self.paths.len() >= self.max_paths {
let (pid_to_remove, _) = self
.paths
.iter()
.find(|(_, p)| p.unused()) .ok_or(Error::Done)?;
let path = self.paths.remove(pid_to_remove);
self.addrs.remove(&(path.local_addr, path.remote_addr));
}
let local_addr = path.local_addr;
let remote_addr = path.remote_addr;
let pid = self.paths.insert(path);
self.addrs.insert((local_addr, remote_addr), pid);
Ok(pid)
}
pub fn iter(&self) -> slab::Iter<Path> {
self.paths.iter()
}
pub fn iter_mut(&mut self) -> slab::IterMut<Path> {
self.paths.iter_mut()
}
pub fn len(&self) -> usize {
self.paths.len()
}
pub fn on_path_chal_received(&mut self, path_id: usize, data: [u8; 8]) {
if let Some(path) = self.paths.get_mut(path_id) {
path.on_path_chal_received(data);
}
}
pub fn on_path_resp_received(&mut self, path_id: usize, data: [u8; 8]) -> bool {
if let Some(path) = self.paths.get_mut(path_id) {
return path.on_path_resp_received(data, self.is_multipath);
}
false
}
pub fn on_path_chal_sent(
&mut self,
path_id: usize,
data: [u8; 8],
pkt_size: usize,
sent_time: time::Instant,
) -> Result<()> {
let path = self.get_mut(path_id)?;
path.on_path_chal_sent(data, pkt_size, sent_time);
Ok(())
}
pub fn on_path_chal_timeout(&mut self, now: time::Instant) {
for (_, path) in self.paths.iter_mut() {
path.on_path_chal_timeout(now);
}
}
pub fn min_loss_detection_timer(&self) -> Option<time::Instant> {
self.paths
.iter()
.filter_map(|(_, p)| p.recovery.loss_detection_timer())
.min()
}
pub fn min_pacer_timer(&self) -> Option<time::Instant> {
self.paths
.iter()
.filter_map(|(_, p)| p.recovery.pacer_timer)
.min()
}
pub fn min_path_chal_timer(&self) -> Option<time::Instant> {
self.paths
.iter()
.filter_map(|(_, p)| p.sent_chals.front())
.min_by_key(|&(_, _, _, loss_time)| loss_time)
.map(|&(_, _, _, loss_time)| loss_time)
}
pub fn max_pto(&self) -> Option<Duration> {
self.iter()
.map(|(_, path)| path.recovery.rtt.pto_base())
.max()
}
pub fn inc_anti_ampl_limit(&mut self, pid: usize, pkt_len: usize) {
if !self.is_server {
return;
}
if let Some(path) = self.paths.get_mut(pid) {
if !path.verified_peer_address {
let inc = self.anti_ampl_factor.saturating_mul(pkt_len);
path.anti_ampl_limit = path.anti_ampl_limit.saturating_add(inc);
}
}
}
pub fn dec_anti_ampl_limit(&mut self, pid: usize, pkt_len: usize) {
if !self.is_server {
return;
}
if let Some(path) = self.paths.get_mut(pid) {
if !path.verified_peer_address {
path.anti_ampl_limit = path.anti_ampl_limit.saturating_sub(pkt_len);
}
}
}
pub fn cmp_anti_ampl_limit(&self, pid: usize, left: usize) -> usize {
if !self.is_server {
return left;
}
if let Some(path) = self.paths.get(pid) {
if !path.verified_peer_address {
return cmp::min(left, path.anti_ampl_limit);
}
}
left
}
pub fn mark_ping(&mut self, path_addr: Option<FourTuple>) -> Result<()> {
if !self.is_multipath {
self.get_active_mut()?.need_send_ping = true;
return Ok(());
}
if let Some(a) = path_addr {
let pid = match self.get_path_id(&(a.local, a.remote)) {
Some(pid) => pid,
None => return Ok(()),
};
self.get_mut(pid)?.need_send_ping = true;
return Ok(());
}
for (_, path) in self.paths.iter_mut() {
if path.active() {
path.need_send_ping = true;
}
}
Ok(())
}
pub fn enable_multipath(&mut self) {
self.is_multipath = true;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::congestion_control::CongestionControlAlgorithm;
use std::net::IpAddr;
use std::net::Ipv4Addr;
use std::time::Duration;
fn new_test_recovery_config() -> RecoveryConfig {
RecoveryConfig {
max_datagram_size: 1200,
max_ack_delay: time::Duration::from_millis(0),
congestion_control_algorithm: CongestionControlAlgorithm::Bbr,
min_congestion_window: 2_u64,
initial_congestion_window: 10_u64,
initial_rtt: crate::INITIAL_RTT,
pto_linear_factor: crate::DEFAULT_PTO_LINEAR_FACTOR,
max_pto: crate::MAX_PTO,
..RecoveryConfig::default()
}
}
fn new_path_mgr(
clients: &Vec<SocketAddr>,
server: SocketAddr,
path_num: usize,
is_server: bool,
) -> Result<PathMap> {
assert!(clients.len() > 0);
let conf = new_test_recovery_config();
let initial_path = Path::new(clients[0], server, true, &conf, "");
let mut path_mgr = PathMap::new(
initial_path,
path_num,
crate::ANTI_AMPLIFICATION_FACTOR,
is_server,
);
for i in 1..clients.len() {
let new_path = Path::new(clients[i], server, false, &conf, "");
path_mgr.insert_path(new_path)?;
}
Ok(path_mgr)
}
#[test]
fn path_initial() -> Result<()> {
let client_addrs = vec![SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
9443,
)];
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 443);
let mut path_mgr = new_path_mgr(&client_addrs, server_addr, 8, false)?;
assert_eq!(path_mgr.len(), 1);
assert_eq!(path_mgr.iter().count(), 1);
assert_eq!(path_mgr.iter_mut().count(), 1);
let client_addr = client_addrs[0];
let pid = path_mgr
.get_path_id(&(client_addr, server_addr))
.ok_or(Error::InternalError)?;
assert_eq!(pid, 0);
assert_eq!(path_mgr.get(pid)?.local_addr(), client_addr);
assert_eq!(path_mgr.get(pid)?.remote_addr(), server_addr);
assert_eq!(path_mgr.get(pid)?.active(), true);
assert_eq!(path_mgr.get(pid)?.unused(), false);
assert_eq!(path_mgr.get_mut(pid)?.stats().recv_count, 0);
assert_eq!(path_mgr.get_mut(pid)?.stats().sent_count, 0);
assert_eq!(path_mgr.get_active()?.local_addr(), client_addr);
assert_eq!(path_mgr.get_active_mut()?.remote_addr(), server_addr);
assert_eq!(path_mgr.get_active_path_id()?, 0);
Ok(())
}
#[test]
fn client_path_validation() -> Result<()> {
let client_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9443);
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 443);
let conf = new_test_recovery_config();
let initial_path = Path::new(client_addr, server_addr, true, &conf, "");
let mut path_mgr = PathMap::new(initial_path, 8, crate::ANTI_AMPLIFICATION_FACTOR, false);
let client_addr1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9444);
let new_path = Path::new(client_addr1, server_addr, false, &conf, "");
path_mgr.insert_path(new_path)?;
assert_eq!(path_mgr.len(), 2);
let pid = path_mgr
.get_path_id(&(client_addr1, server_addr))
.ok_or(Error::InternalError)?;
path_mgr.get_mut(pid)?.initiate_path_chal();
assert!(path_mgr.get_mut(pid)?.need_send_validation_frames(false));
assert_eq!(path_mgr.get_mut(pid)?.path_chal_initiated(), true);
let data = rand::random::<[u8; 8]>();
let now = time::Instant::now();
path_mgr.on_path_chal_sent(pid, data, 100, now)?;
assert_eq!(path_mgr.get_mut(pid)?.path_chal_initiated(), false);
assert_eq!(path_mgr.get_mut(pid)?.validated(), false);
assert_eq!(path_mgr.get_mut(pid)?.state, PathState::Validating);
assert_eq!(path_mgr.on_path_resp_received(pid, [0xab; 8]), false);
assert_eq!(path_mgr.get_mut(pid)?.state, PathState::ValidatingMTU);
assert_eq!(path_mgr.on_path_resp_received(pid, data), false);
assert_eq!(path_mgr.get_mut(pid)?.path_chal_initiated(), true);
assert_eq!(path_mgr.get_mut(pid)?.validated(), false);
assert_eq!(path_mgr.get_mut(pid)?.state, PathState::ValidatingMTU);
path_mgr.on_path_chal_sent(pid, data, 1300, now)?;
assert_eq!(path_mgr.on_path_resp_received(pid, data), true);
assert_eq!(path_mgr.get_mut(pid)?.path_chal_initiated(), false);
assert_eq!(path_mgr.get_mut(pid)?.validated(), true);
assert_eq!(path_mgr.get_mut(pid)?.state, PathState::Validated);
assert_eq!(path_mgr.get_mut(pid)?.sent_chals.len(), 0);
assert_eq!(path_mgr.on_path_resp_received(pid, data), false);
assert_eq!(path_mgr.get_mut(pid)?.validated(), true);
path_mgr.on_path_chal_timeout(now + time::Duration::from_millis(INITIAL_CHAL_TIMEOUT));
assert_eq!(path_mgr.get_mut(pid)?.lost_chal, 0);
assert_eq!(path_mgr.get_mut(pid)?.sent_chals.len(), 0);
assert_eq!(path_mgr.get_mut(pid)?.state, PathState::Validated);
Ok(())
}
#[test]
fn server_path_validation() -> Result<()> {
let client_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9443);
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 443);
let conf = new_test_recovery_config();
let initial_path = Path::new(server_addr, client_addr, true, &conf, "");
let mut path_mgr = PathMap::new(initial_path, 2, crate::ANTI_AMPLIFICATION_FACTOR, false);
let client_addr1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9444);
let new_path = Path::new(server_addr, client_addr1, false, &conf, "");
let pid = path_mgr.insert_path(new_path)?;
assert_eq!(path_mgr.len(), 2);
assert_eq!(pid, 1);
let data = rand::random::<[u8; 8]>();
path_mgr.on_path_chal_received(pid, data);
assert_eq!(path_mgr.get_mut(pid)?.recv_chals.len(), 1);
let chal = path_mgr.get_mut(pid)?.pop_recv_chal();
assert_eq!(chal, Some(data));
let client_addr2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9445);
let new_path = Path::new(server_addr, client_addr2, false, &conf, "");
let pid = path_mgr.insert_path(new_path)?;
assert_eq!(path_mgr.len(), 2);
assert_eq!(path_mgr.get_mut(pid)?.remote_addr(), client_addr2);
let data = rand::random::<[u8; 8]>();
path_mgr.on_path_chal_received(pid, data);
assert_eq!(path_mgr.get_mut(pid)?.recv_chals.len(), 1);
let chal = path_mgr.get_mut(pid)?.pop_recv_chal();
assert_eq!(chal, Some(data));
Ok(())
}
#[test]
fn path_chal_timeout() -> Result<()> {
let clients = vec![
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9443),
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9444),
];
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 443);
let mut path_mgr = new_path_mgr(&clients, server_addr, 8, false)?;
assert_eq!(path_mgr.len(), 2);
let pid = path_mgr
.get_path_id(&(clients[1], server_addr))
.ok_or(Error::InternalError)?;
path_mgr.get_mut(pid)?.initiate_path_chal();
assert!(path_mgr.get_mut(pid)?.need_send_validation_frames(false));
assert_eq!(path_mgr.get_mut(pid)?.path_chal_initiated(), true);
let data = rand::random::<[u8; 8]>();
let now = time::Instant::now();
path_mgr.on_path_chal_sent(pid, data, 1300, now)?;
assert_eq!(path_mgr.get_mut(pid)?.path_chal_initiated(), false);
assert_eq!(path_mgr.get_mut(pid)?.validated(), false);
assert_eq!(path_mgr.get_mut(pid)?.state, PathState::Validating);
path_mgr.on_path_chal_timeout(now + time::Duration::from_millis(1));
assert_eq!(path_mgr.get_mut(pid)?.sent_chals.len(), 1);
assert_eq!(path_mgr.get_mut(pid)?.lost_chal, 0);
assert_eq!(path_mgr.get_mut(pid)?.path_chal_initiated(), false);
let mut next_timeout = now;
for i in 0..MAX_PROBING_TIMEOUTS {
next_timeout += time::Duration::from_millis(INITIAL_CHAL_TIMEOUT << i);
path_mgr.on_path_chal_timeout(next_timeout);
assert_eq!(path_mgr.get_mut(pid)?.lost_chal, i + 1);
assert_eq!(path_mgr.get_mut(pid)?.sent_chals.len(), 0);
if i != MAX_PROBING_TIMEOUTS - 1 {
assert_eq!(path_mgr.get_mut(pid)?.path_chal_initiated(), true);
assert_eq!(path_mgr.get_mut(pid)?.state, PathState::Validating);
let data = rand::random::<[u8; 8]>();
path_mgr.on_path_chal_sent(pid, data, 1300, next_timeout)?;
assert_eq!(path_mgr.get_mut(pid)?.path_chal_initiated(), false);
}
}
assert_eq!(path_mgr.get_mut(pid)?.state, PathState::Failed);
assert_eq!(path_mgr.get_mut(pid)?.active(), false);
assert_eq!(path_mgr.get_mut(pid)?.lost_chal, MAX_PROBING_TIMEOUTS);
Ok(())
}
#[test]
fn min_path_chal_timeout() -> Result<()> {
let clients = vec![
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9443),
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9444),
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9445),
];
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 443);
let mut path_mgr = new_path_mgr(&clients, server_addr, 8, false)?;
assert_eq!(path_mgr.len(), 3);
assert!(path_mgr.min_path_chal_timer().is_none());
let pid1 = path_mgr
.get_path_id(&(clients[1], server_addr))
.ok_or(Error::InternalError)?;
let pid2 = path_mgr
.get_path_id(&(clients[2], server_addr))
.ok_or(Error::InternalError)?;
let now = time::Instant::now();
let sent_time1 = now;
let data = rand::random::<[u8; 8]>();
path_mgr.on_path_chal_sent(pid1, data, 1300, sent_time1)?;
assert_eq!(path_mgr.get_mut(pid1)?.state, PathState::Validating);
let timeout1 = sent_time1 + time::Duration::from_millis(INITIAL_CHAL_TIMEOUT);
assert_eq!(path_mgr.min_path_chal_timer(), Some(timeout1));
let sent_time2 = now + time::Duration::from_millis(1);
path_mgr.on_path_chal_sent(pid2, data, 1300, sent_time2)?;
assert_eq!(path_mgr.get_mut(pid2)?.state, PathState::Validating);
let timeout2 = sent_time2 + time::Duration::from_millis(INITIAL_CHAL_TIMEOUT);
assert_eq!(path_mgr.min_path_chal_timer(), Some(timeout1));
path_mgr.on_path_resp_received(pid1, data);
assert_eq!(path_mgr.min_path_chal_timer(), Some(timeout2));
Ok(())
}
#[test]
fn path_chals_flood() -> Result<()> {
let client_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9443);
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 443);
let conf = new_test_recovery_config();
let initial_path = Path::new(server_addr, client_addr, true, &conf, "");
let mut path_mgr = PathMap::new(initial_path, 2, crate::ANTI_AMPLIFICATION_FACTOR, false);
let client_addr1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9444);
let new_path = Path::new(server_addr, client_addr1, false, &conf, "");
let pid = path_mgr.insert_path(new_path)?;
assert_eq!(path_mgr.len(), 2);
assert_eq!(pid, 1);
for i in 0..1000 {
let data = rand::random::<[u8; 8]>();
path_mgr.on_path_chal_received(pid, data);
assert!(path_mgr.get_mut(pid)?.recv_chals.len() <= MAX_PATH_CHALS_RECV);
}
Ok(())
}
}