1use std::collections::BTreeMap;
28use std::collections::VecDeque;
29
30use std::net::SocketAddr;
31
32use std::time::Duration;
33use std::time::Instant;
34
35use smallvec::SmallVec;
36
37use slab::Slab;
38
39use crate::Config;
40use crate::Error;
41use crate::Result;
42use crate::StartupExit;
43
44use crate::pmtud;
45use crate::recovery;
46use crate::recovery::Bandwidth;
47use crate::recovery::HandshakeStatus;
48use crate::recovery::OnLossDetectionTimeoutOutcome;
49use crate::recovery::RecoveryOps;
50
51#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
53pub enum PathState {
54 Failed,
56
57 Unknown,
59
60 Validating,
62
63 ValidatingMTU,
65
66 Validated,
68}
69
70impl PathState {
71 #[cfg(feature = "ffi")]
72 pub fn to_c(self) -> libc::ssize_t {
73 match self {
74 PathState::Failed => -1,
75 PathState::Unknown => 0,
76 PathState::Validating => 1,
77 PathState::ValidatingMTU => 2,
78 PathState::Validated => 3,
79 }
80 }
81}
82
83#[derive(Clone, Debug, PartialEq, Eq)]
85pub enum PathEvent {
86 New(SocketAddr, SocketAddr),
91
92 Validated(SocketAddr, SocketAddr),
95
96 FailedValidation(SocketAddr, SocketAddr),
100
101 Closed(SocketAddr, SocketAddr),
104
105 ReusedSourceConnectionId(
109 u64,
110 (SocketAddr, SocketAddr),
111 (SocketAddr, SocketAddr),
112 ),
113
114 PeerMigrated(SocketAddr, SocketAddr),
120}
121
122#[derive(Debug)]
124pub struct Path {
125 local_addr: SocketAddr,
127
128 peer_addr: SocketAddr,
130
131 pub active_scid_seq: Option<u64>,
133
134 pub active_dcid_seq: Option<u64>,
136
137 state: PathState,
139
140 active: bool,
142
143 pub recovery: recovery::Recovery,
145
146 pub pmtud: Option<pmtud::Pmtud>,
148
149 in_flight_challenges: VecDeque<([u8; 8], usize, Instant)>,
152
153 max_challenge_size: usize,
155
156 probing_lost: usize,
158
159 last_probe_lost_time: Option<Instant>,
161
162 received_challenges: VecDeque<[u8; 8]>,
164
165 received_challenges_max_len: usize,
167
168 pub sent_count: usize,
170
171 pub recv_count: usize,
173
174 pub retrans_count: usize,
176
177 pub total_pto_count: usize,
184
185 pub dgram_sent_count: usize,
187
188 pub dgram_recv_count: usize,
190
191 pub sent_bytes: u64,
193
194 pub recv_bytes: u64,
196
197 pub stream_retrans_bytes: u64,
200
201 pub max_send_bytes: usize,
204
205 pub verified_peer_address: bool,
207
208 pub peer_verified_local_address: bool,
210
211 challenge_requested: bool,
213
214 failure_notified: bool,
216
217 migrating: bool,
220
221 pub needs_ack_eliciting: bool,
223}
224
225impl Path {
226 pub fn new(
229 local_addr: SocketAddr, peer_addr: SocketAddr,
230 recovery_config: &recovery::RecoveryConfig,
231 path_challenge_recv_max_queue_len: usize, is_initial: bool,
232 config: Option<&Config>,
233 ) -> Self {
234 let (state, active_scid_seq, active_dcid_seq) = if is_initial {
235 (PathState::Validated, Some(0), Some(0))
236 } else {
237 (PathState::Unknown, None, None)
238 };
239
240 let pmtud = config.and_then(|c| {
241 if c.pmtud {
242 let maximum_supported_mtu: usize = std::cmp::min(
243 c.local_transport_params
246 .max_udp_payload_size
247 .try_into()
248 .unwrap_or(c.max_send_udp_payload_size),
249 c.max_send_udp_payload_size,
250 );
251 Some(pmtud::Pmtud::new(maximum_supported_mtu, c.pmtud_max_probes))
252 } else {
253 None
254 }
255 });
256
257 Self {
258 local_addr,
259 peer_addr,
260 active_scid_seq,
261 active_dcid_seq,
262 state,
263 active: false,
264 recovery: recovery::Recovery::new_with_config(recovery_config),
265 pmtud,
266 in_flight_challenges: VecDeque::new(),
267 max_challenge_size: 0,
268 probing_lost: 0,
269 last_probe_lost_time: None,
270 received_challenges: VecDeque::with_capacity(
271 path_challenge_recv_max_queue_len,
272 ),
273 received_challenges_max_len: path_challenge_recv_max_queue_len,
274 sent_count: 0,
275 recv_count: 0,
276 retrans_count: 0,
277 total_pto_count: 0,
278 dgram_sent_count: 0,
279 dgram_recv_count: 0,
280 sent_bytes: 0,
281 recv_bytes: 0,
282 stream_retrans_bytes: 0,
283 max_send_bytes: 0,
284 verified_peer_address: false,
285 peer_verified_local_address: false,
286 challenge_requested: false,
287 failure_notified: false,
288 migrating: false,
289 needs_ack_eliciting: false,
290 }
291 }
292
293 #[inline]
295 pub fn local_addr(&self) -> SocketAddr {
296 self.local_addr
297 }
298
299 #[inline]
301 pub fn peer_addr(&self) -> SocketAddr {
302 self.peer_addr
303 }
304
305 #[inline]
307 fn working(&self) -> bool {
308 self.state > PathState::Failed
309 }
310
311 #[inline]
313 pub fn active(&self) -> bool {
314 self.active && self.working() && self.active_dcid_seq.is_some()
315 }
316
317 #[inline]
319 pub fn usable(&self) -> bool {
320 self.active() ||
321 (self.state == PathState::Validated &&
322 self.active_dcid_seq.is_some())
323 }
324
325 #[inline]
327 fn unused(&self) -> bool {
328 !self.active() && self.active_dcid_seq.is_none()
330 }
331
332 #[inline]
334 pub fn probing_required(&self) -> bool {
335 !self.received_challenges.is_empty() || self.validation_requested()
336 }
337
338 fn promote_to(&mut self, state: PathState) {
341 if self.state < state {
342 self.state = state;
343 }
344 }
345
346 #[inline]
348 pub fn validated(&self) -> bool {
349 self.state == PathState::Validated
350 }
351
352 #[inline]
354 fn validation_failed(&self) -> bool {
355 self.state == PathState::Failed
356 }
357
358 #[inline]
360 pub fn under_validation(&self) -> bool {
361 matches!(self.state, PathState::Validating | PathState::ValidatingMTU)
362 }
363
364 #[inline]
366 pub fn request_validation(&mut self) {
367 self.challenge_requested = true;
368 }
369
370 #[inline]
372 pub fn validation_requested(&self) -> bool {
373 self.challenge_requested
374 }
375
376 pub fn should_send_pmtu_probe(
377 &mut self, hs_confirmed: bool, hs_done: bool, out_len: usize,
378 is_closing: bool, frames_empty: bool,
379 ) -> bool {
380 let Some(pmtud) = self.pmtud.as_mut() else {
381 return false;
382 };
383
384 (hs_confirmed && hs_done) &&
385 self.recovery.cwnd_available() > pmtud.get_probe_size() &&
386 out_len >= pmtud.get_probe_size() &&
387 pmtud.should_probe() &&
388 !is_closing &&
389 frames_empty
390 }
391
392 pub fn on_challenge_sent(&mut self) {
393 self.promote_to(PathState::Validating);
394 self.challenge_requested = false;
395 }
396
397 pub fn add_challenge_sent(
399 &mut self, data: [u8; 8], pkt_size: usize, sent_time: Instant,
400 ) {
401 self.on_challenge_sent();
402 self.in_flight_challenges
403 .push_back((data, pkt_size, sent_time));
404 }
405
406 pub fn on_challenge_received(&mut self, data: [u8; 8]) {
407 if self.received_challenges.len() == self.received_challenges_max_len {
409 return;
410 }
411
412 self.received_challenges.push_back(data);
413 self.peer_verified_local_address = true;
414 }
415
416 pub fn has_pending_challenge(&self, data: [u8; 8]) -> bool {
417 self.in_flight_challenges.iter().any(|(d, ..)| *d == data)
418 }
419
420 pub fn on_response_received(&mut self, data: [u8; 8]) -> bool {
422 self.verified_peer_address = true;
423 self.probing_lost = 0;
424
425 let mut challenge_size = 0;
426 self.in_flight_challenges.retain(|(d, s, _)| {
427 if *d == data {
428 challenge_size = *s;
429 false
430 } else {
431 true
432 }
433 });
434
435 self.promote_to(PathState::ValidatingMTU);
437
438 self.max_challenge_size =
439 std::cmp::max(self.max_challenge_size, challenge_size);
440
441 if self.state == PathState::ValidatingMTU {
442 if self.max_challenge_size >= crate::MIN_CLIENT_INITIAL_LEN {
443 self.promote_to(PathState::Validated);
445 return true;
446 }
447
448 self.request_validation();
450 }
451
452 false
453 }
454
455 fn on_failed_validation(&mut self) {
456 self.state = PathState::Failed;
457 self.active = false;
458 }
459
460 #[inline]
461 pub fn pop_received_challenge(&mut self) -> Option<[u8; 8]> {
462 self.received_challenges.pop_front()
463 }
464
465 pub fn on_loss_detection_timeout(
466 &mut self, handshake_status: HandshakeStatus, now: Instant,
467 is_server: bool, trace_id: &str,
468 ) -> OnLossDetectionTimeoutOutcome {
469 let outcome = self.recovery.on_loss_detection_timeout(
470 handshake_status,
471 now,
472 trace_id,
473 );
474
475 let mut lost_probe_time = None;
476 self.in_flight_challenges.retain(|(_, _, sent_time)| {
477 if *sent_time <= now {
478 if lost_probe_time.is_none() {
479 lost_probe_time = Some(*sent_time);
480 }
481 false
482 } else {
483 true
484 }
485 });
486
487 if let Some(lost_probe_time) = lost_probe_time {
490 self.last_probe_lost_time = match self.last_probe_lost_time {
491 Some(last) => {
492 if lost_probe_time - last >= self.recovery.rtt() {
494 self.probing_lost += 1;
495 Some(lost_probe_time)
496 } else {
497 Some(last)
498 }
499 },
500 None => {
501 self.probing_lost += 1;
502 Some(lost_probe_time)
503 },
504 };
505 if self.probing_lost >= crate::MAX_PROBING_TIMEOUTS ||
509 (is_server && self.max_send_bytes < crate::MIN_PROBING_SIZE)
510 {
511 self.on_failed_validation();
512 } else {
513 self.request_validation();
514 }
515 }
516
517 self.total_pto_count += 1;
519
520 outcome
521 }
522
523 pub fn can_reinit_recovery(&self) -> bool {
527 self.recovery.bytes_in_flight() == 0 &&
534 self.recovery.bytes_in_flight_duration() == Duration::ZERO
535 }
536
537 pub fn reinit_recovery(
538 &mut self, recovery_config: &recovery::RecoveryConfig,
539 ) {
540 self.recovery = recovery::Recovery::new_with_config(recovery_config)
541 }
542
543 pub fn stats(&self) -> PathStats {
544 let pmtu = match self.pmtud.as_ref().map(|p| p.get_current_mtu()) {
545 Some(v) => v,
546
547 None => self.recovery.max_datagram_size(),
548 };
549
550 PathStats {
551 local_addr: self.local_addr,
552 peer_addr: self.peer_addr,
553 validation_state: self.state,
554 active: self.active,
555 recv: self.recv_count,
556 sent: self.sent_count,
557 lost: self.recovery.lost_count(),
558 retrans: self.retrans_count,
559 total_pto_count: self.total_pto_count,
560 dgram_recv: self.dgram_recv_count,
561 dgram_sent: self.dgram_sent_count,
562 rtt: self.recovery.rtt(),
563 min_rtt: self.recovery.min_rtt(),
564 max_rtt: self.recovery.max_rtt(),
565 rttvar: self.recovery.rttvar(),
566 cwnd: self.recovery.cwnd(),
567 sent_bytes: self.sent_bytes,
568 recv_bytes: self.recv_bytes,
569 lost_bytes: self.recovery.bytes_lost(),
570 stream_retrans_bytes: self.stream_retrans_bytes,
571 pmtu,
572 delivery_rate: self.recovery.delivery_rate().to_bytes_per_second(),
573 max_bandwidth: self
574 .recovery
575 .max_bandwidth()
576 .map(Bandwidth::to_bytes_per_second),
577 startup_exit: self.recovery.startup_exit(),
578 }
579 }
580
581 pub fn bytes_in_flight_duration(&self) -> Duration {
582 self.recovery.bytes_in_flight_duration()
583 }
584}
585
586#[derive(Default)]
588pub struct SocketAddrIter {
589 pub(crate) sockaddrs: SmallVec<[SocketAddr; 8]>,
590 pub(crate) index: usize,
591}
592
593impl Iterator for SocketAddrIter {
594 type Item = SocketAddr;
595
596 #[inline]
597 fn next(&mut self) -> Option<Self::Item> {
598 let v = self.sockaddrs.get(self.index)?;
599 self.index += 1;
600 Some(*v)
601 }
602}
603
604impl ExactSizeIterator for SocketAddrIter {
605 #[inline]
606 fn len(&self) -> usize {
607 self.sockaddrs.len() - self.index
608 }
609}
610
611pub struct PathMap {
613 paths: Slab<Path>,
616
617 max_concurrent_paths: usize,
619
620 addrs_to_paths: BTreeMap<(SocketAddr, SocketAddr), usize>,
623
624 events: VecDeque<PathEvent>,
626
627 is_server: bool,
629}
630
631impl PathMap {
632 pub fn new(
635 mut initial_path: Path, max_concurrent_paths: usize, is_server: bool,
636 ) -> Self {
637 let mut paths = Slab::with_capacity(1); let mut addrs_to_paths = BTreeMap::new();
639
640 let local_addr = initial_path.local_addr;
641 let peer_addr = initial_path.peer_addr;
642
643 initial_path.active = true;
645
646 let active_path_id = paths.insert(initial_path);
647 addrs_to_paths.insert((local_addr, peer_addr), active_path_id);
648
649 Self {
650 paths,
651 max_concurrent_paths,
652 addrs_to_paths,
653 events: VecDeque::new(),
654 is_server,
655 }
656 }
657
658 #[inline]
664 pub fn get(&self, path_id: usize) -> Result<&Path> {
665 self.paths.get(path_id).ok_or(Error::InvalidState)
666 }
667
668 #[inline]
674 pub fn get_mut(&mut self, path_id: usize) -> Result<&mut Path> {
675 self.paths.get_mut(path_id).ok_or(Error::InvalidState)
676 }
677
678 #[inline]
679 pub fn get_active_with_pid(&self) -> Option<(usize, &Path)> {
682 self.paths.iter().find(|(_, p)| p.active())
683 }
684
685 #[inline]
690 pub fn get_active(&self) -> Result<&Path> {
691 self.get_active_with_pid()
692 .map(|(_, p)| p)
693 .ok_or(Error::InvalidState)
694 }
695
696 #[inline]
701 pub fn get_active_path_id(&self) -> Result<usize> {
702 self.get_active_with_pid()
703 .map(|(pid, _)| pid)
704 .ok_or(Error::InvalidState)
705 }
706
707 #[inline]
712 pub fn get_active_mut(&mut self) -> Result<&mut Path> {
713 self.paths
714 .iter_mut()
715 .map(|(_, p)| p)
716 .find(|p| p.active())
717 .ok_or(Error::InvalidState)
718 }
719
720 #[inline]
722 pub fn iter(&self) -> slab::Iter<'_, Path> {
723 self.paths.iter()
724 }
725
726 #[inline]
728 pub fn iter_mut(&mut self) -> slab::IterMut<'_, Path> {
729 self.paths.iter_mut()
730 }
731
732 #[inline]
734 pub fn len(&self) -> usize {
735 self.paths.len()
736 }
737
738 #[inline]
740 pub fn path_id_from_addrs(
741 &self, addrs: &(SocketAddr, SocketAddr),
742 ) -> Option<usize> {
743 self.addrs_to_paths.get(addrs).copied()
744 }
745
746 fn make_room_for_new_path(&mut self) -> Result<()> {
752 if self.paths.len() < self.max_concurrent_paths {
753 return Ok(());
754 }
755
756 let (pid_to_remove, _) = self
757 .paths
758 .iter()
759 .find(|(_, p)| p.unused())
760 .ok_or(Error::Done)?;
761
762 let path = self.paths.remove(pid_to_remove);
763 self.addrs_to_paths
764 .remove(&(path.local_addr, path.peer_addr));
765
766 self.notify_event(PathEvent::Closed(path.local_addr, path.peer_addr));
767
768 Ok(())
769 }
770
771 pub fn insert_path(&mut self, path: Path, is_server: bool) -> Result<usize> {
782 self.make_room_for_new_path()?;
783
784 let local_addr = path.local_addr;
785 let peer_addr = path.peer_addr;
786
787 let pid = self.paths.insert(path);
788 self.addrs_to_paths.insert((local_addr, peer_addr), pid);
789
790 if is_server {
792 self.notify_event(PathEvent::New(local_addr, peer_addr));
793 }
794
795 Ok(pid)
796 }
797
798 pub fn notify_event(&mut self, ev: PathEvent) {
800 self.events.push_back(ev);
801 }
802
803 pub fn pop_event(&mut self) -> Option<PathEvent> {
805 self.events.pop_front()
806 }
807
808 pub fn notify_failed_validations(&mut self) {
810 let validation_failed = self
811 .paths
812 .iter_mut()
813 .filter(|(_, p)| p.validation_failed() && !p.failure_notified);
814
815 for (_, p) in validation_failed {
816 self.events.push_back(PathEvent::FailedValidation(
817 p.local_addr,
818 p.peer_addr,
819 ));
820
821 p.failure_notified = true;
822 }
823 }
824
825 pub fn find_candidate_path(&self) -> Option<usize> {
827 self.paths
829 .iter()
830 .find(|(_, p)| p.usable())
831 .map(|(pid, _)| pid)
832 }
833
834 pub fn on_response_received(&mut self, data: [u8; 8]) -> Result<()> {
836 let active_pid = self.get_active_path_id()?;
837
838 let challenge_pending =
839 self.iter_mut().find(|(_, p)| p.has_pending_challenge(data));
840
841 if let Some((pid, p)) = challenge_pending {
842 if p.on_response_received(data) {
843 let local_addr = p.local_addr;
844 let peer_addr = p.peer_addr;
845 let was_migrating = p.migrating;
846
847 p.migrating = false;
848
849 self.notify_event(PathEvent::Validated(local_addr, peer_addr));
851
852 if pid == active_pid && was_migrating {
855 self.notify_event(PathEvent::PeerMigrated(
856 local_addr, peer_addr,
857 ));
858 }
859 }
860 }
861 Ok(())
862 }
863
864 pub fn set_active_path(&mut self, path_id: usize) -> Result<()> {
875 let is_server = self.is_server;
876
877 if let Ok(old_active_path) = self.get_active_mut() {
878 old_active_path.active = false;
879 }
880
881 let new_active_path = self.get_mut(path_id)?;
882 new_active_path.active = true;
883
884 if is_server {
885 if new_active_path.validated() {
886 let local_addr = new_active_path.local_addr();
887 let peer_addr = new_active_path.peer_addr();
888
889 self.notify_event(PathEvent::PeerMigrated(local_addr, peer_addr));
890 } else {
891 new_active_path.migrating = true;
892
893 if !new_active_path.under_validation() {
895 new_active_path.request_validation();
896 }
897 }
898 }
899
900 Ok(())
901 }
902
903 pub fn set_discover_pmtu_on_existing_paths(
905 &mut self, discover: bool, max_send_udp_payload_size: usize,
906 pmtud_max_probes: u8,
907 ) {
908 for (_, path) in self.paths.iter_mut() {
909 path.pmtud = if discover {
910 Some(pmtud::Pmtud::new(
911 max_send_udp_payload_size,
912 pmtud_max_probes,
913 ))
914 } else {
915 None
916 };
917 }
918 }
919}
920
921#[derive(Clone)]
928pub struct PathStats {
929 pub local_addr: SocketAddr,
931
932 pub peer_addr: SocketAddr,
934
935 pub validation_state: PathState,
937
938 pub active: bool,
940
941 pub recv: usize,
943
944 pub sent: usize,
946
947 pub lost: usize,
949
950 pub retrans: usize,
952
953 pub total_pto_count: usize,
960
961 pub dgram_recv: usize,
963
964 pub dgram_sent: usize,
966
967 pub rtt: Duration,
969
970 pub min_rtt: Option<Duration>,
972
973 pub max_rtt: Option<Duration>,
975
976 pub rttvar: Duration,
979
980 pub cwnd: usize,
982
983 pub sent_bytes: u64,
985
986 pub recv_bytes: u64,
988
989 pub lost_bytes: u64,
991
992 pub stream_retrans_bytes: u64,
994
995 pub pmtu: usize,
997
998 pub delivery_rate: u64,
1007
1008 pub max_bandwidth: Option<u64>,
1013
1014 pub startup_exit: Option<StartupExit>,
1016}
1017
1018impl std::fmt::Debug for PathStats {
1019 #[inline]
1020 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1021 write!(
1022 f,
1023 "local_addr={:?} peer_addr={:?} ",
1024 self.local_addr, self.peer_addr,
1025 )?;
1026 write!(
1027 f,
1028 "validation_state={:?} active={} ",
1029 self.validation_state, self.active,
1030 )?;
1031 write!(
1032 f,
1033 "recv={} sent={} lost={} retrans={} rtt={:?} min_rtt={:?} rttvar={:?} cwnd={}",
1034 self.recv, self.sent, self.lost, self.retrans, self.rtt, self.min_rtt, self.rttvar, self.cwnd,
1035 )?;
1036
1037 write!(
1038 f,
1039 " sent_bytes={} recv_bytes={} lost_bytes={}",
1040 self.sent_bytes, self.recv_bytes, self.lost_bytes,
1041 )?;
1042
1043 write!(
1044 f,
1045 " stream_retrans_bytes={} pmtu={} delivery_rate={}",
1046 self.stream_retrans_bytes, self.pmtu, self.delivery_rate,
1047 )
1048 }
1049}
1050
1051#[cfg(test)]
1052mod tests {
1053 use crate::rand;
1054 use crate::MIN_CLIENT_INITIAL_LEN;
1055
1056 use crate::recovery::RecoveryConfig;
1057 use crate::Config;
1058
1059 use super::*;
1060
1061 #[test]
1062 fn path_validation_limited_mtu() {
1063 let client_addr = "127.0.0.1:1234".parse().unwrap();
1064 let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
1065 let server_addr = "127.0.0.1:4321".parse().unwrap();
1066
1067 let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1068 let recovery_config = RecoveryConfig::from_config(&config);
1069
1070 let path = Path::new(
1071 client_addr,
1072 server_addr,
1073 &recovery_config,
1074 config.path_challenge_recv_max_queue_len,
1075 true,
1076 None,
1077 );
1078 let mut path_mgr = PathMap::new(path, 2, false);
1079
1080 let probed_path = Path::new(
1081 client_addr_2,
1082 server_addr,
1083 &recovery_config,
1084 config.path_challenge_recv_max_queue_len,
1085 false,
1086 None,
1087 );
1088 path_mgr.insert_path(probed_path, false).unwrap();
1089
1090 let pid = path_mgr
1091 .path_id_from_addrs(&(client_addr_2, server_addr))
1092 .unwrap();
1093 path_mgr.get_mut(pid).unwrap().request_validation();
1094 assert!(path_mgr.get_mut(pid).unwrap().validation_requested());
1095 assert!(path_mgr.get_mut(pid).unwrap().probing_required());
1096
1097 let data = rand::rand_u64().to_be_bytes();
1100 path_mgr.get_mut(pid).unwrap().add_challenge_sent(
1101 data,
1102 MIN_CLIENT_INITIAL_LEN - 1,
1103 Instant::now(),
1104 );
1105
1106 assert!(!path_mgr.get_mut(pid).unwrap().validation_requested());
1107 assert!(!path_mgr.get_mut(pid).unwrap().probing_required());
1108 assert!(path_mgr.get_mut(pid).unwrap().under_validation());
1109 assert!(!path_mgr.get_mut(pid).unwrap().validated());
1110 assert_eq!(path_mgr.get_mut(pid).unwrap().state, PathState::Validating);
1111 assert_eq!(path_mgr.pop_event(), None);
1112
1113 path_mgr.on_response_received(data).unwrap();
1116
1117 assert!(path_mgr.get_mut(pid).unwrap().validation_requested());
1118 assert!(path_mgr.get_mut(pid).unwrap().probing_required());
1119 assert!(path_mgr.get_mut(pid).unwrap().under_validation());
1120 assert!(!path_mgr.get_mut(pid).unwrap().validated());
1121 assert_eq!(
1122 path_mgr.get_mut(pid).unwrap().state,
1123 PathState::ValidatingMTU
1124 );
1125 assert_eq!(path_mgr.pop_event(), None);
1126
1127 let data = rand::rand_u64().to_be_bytes();
1130 path_mgr.get_mut(pid).unwrap().add_challenge_sent(
1131 data,
1132 MIN_CLIENT_INITIAL_LEN,
1133 Instant::now(),
1134 );
1135
1136 path_mgr.on_response_received(data).unwrap();
1137
1138 assert!(!path_mgr.get_mut(pid).unwrap().validation_requested());
1139 assert!(!path_mgr.get_mut(pid).unwrap().probing_required());
1140 assert!(!path_mgr.get_mut(pid).unwrap().under_validation());
1141 assert!(path_mgr.get_mut(pid).unwrap().validated());
1142 assert_eq!(path_mgr.get_mut(pid).unwrap().state, PathState::Validated);
1143 assert_eq!(
1144 path_mgr.pop_event(),
1145 Some(PathEvent::Validated(client_addr_2, server_addr))
1146 );
1147 }
1148
1149 #[test]
1150 fn multiple_probes() {
1151 let client_addr = "127.0.0.1:1234".parse().unwrap();
1152 let server_addr = "127.0.0.1:4321".parse().unwrap();
1153
1154 let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1155 let recovery_config = RecoveryConfig::from_config(&config);
1156
1157 let path = Path::new(
1158 client_addr,
1159 server_addr,
1160 &recovery_config,
1161 config.path_challenge_recv_max_queue_len,
1162 true,
1163 None,
1164 );
1165 let mut client_path_mgr = PathMap::new(path, 2, false);
1166 let mut server_path = Path::new(
1167 server_addr,
1168 client_addr,
1169 &recovery_config,
1170 config.path_challenge_recv_max_queue_len,
1171 false,
1172 None,
1173 );
1174
1175 let client_pid = client_path_mgr
1176 .path_id_from_addrs(&(client_addr, server_addr))
1177 .unwrap();
1178
1179 let data = rand::rand_u64().to_be_bytes();
1181
1182 client_path_mgr
1183 .get_mut(client_pid)
1184 .unwrap()
1185 .add_challenge_sent(data, MIN_CLIENT_INITIAL_LEN, Instant::now());
1186
1187 let data_2 = rand::rand_u64().to_be_bytes();
1189
1190 client_path_mgr
1191 .get_mut(client_pid)
1192 .unwrap()
1193 .add_challenge_sent(data_2, MIN_CLIENT_INITIAL_LEN, Instant::now());
1194 assert_eq!(
1195 client_path_mgr
1196 .get(client_pid)
1197 .unwrap()
1198 .in_flight_challenges
1199 .len(),
1200 2
1201 );
1202
1203 server_path.on_challenge_received(data);
1205 assert_eq!(server_path.received_challenges.len(), 1);
1206 server_path.on_challenge_received(data_2);
1207 assert_eq!(server_path.received_challenges.len(), 2);
1208
1209 client_path_mgr.on_response_received(data).unwrap();
1211 assert_eq!(
1212 client_path_mgr
1213 .get(client_pid)
1214 .unwrap()
1215 .in_flight_challenges
1216 .len(),
1217 1
1218 );
1219
1220 client_path_mgr.on_response_received(data_2).unwrap();
1222 assert_eq!(
1223 client_path_mgr
1224 .get(client_pid)
1225 .unwrap()
1226 .in_flight_challenges
1227 .len(),
1228 0
1229 );
1230 }
1231
1232 #[test]
1233 fn too_many_probes() {
1234 let client_addr = "127.0.0.1:1234".parse().unwrap();
1235 let server_addr = "127.0.0.1:4321".parse().unwrap();
1236
1237 let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1239 let recovery_config = RecoveryConfig::from_config(&config);
1240
1241 let path = Path::new(
1242 client_addr,
1243 server_addr,
1244 &recovery_config,
1245 config.path_challenge_recv_max_queue_len,
1246 true,
1247 None,
1248 );
1249 let mut client_path_mgr = PathMap::new(path, 2, false);
1250 let mut server_path = Path::new(
1251 server_addr,
1252 client_addr,
1253 &recovery_config,
1254 config.path_challenge_recv_max_queue_len,
1255 false,
1256 None,
1257 );
1258
1259 let client_pid = client_path_mgr
1260 .path_id_from_addrs(&(client_addr, server_addr))
1261 .unwrap();
1262
1263 let data = rand::rand_u64().to_be_bytes();
1265
1266 client_path_mgr
1267 .get_mut(client_pid)
1268 .unwrap()
1269 .add_challenge_sent(data, MIN_CLIENT_INITIAL_LEN, Instant::now());
1270
1271 let data_2 = rand::rand_u64().to_be_bytes();
1273
1274 client_path_mgr
1275 .get_mut(client_pid)
1276 .unwrap()
1277 .add_challenge_sent(data_2, MIN_CLIENT_INITIAL_LEN, Instant::now());
1278 assert_eq!(
1279 client_path_mgr
1280 .get(client_pid)
1281 .unwrap()
1282 .in_flight_challenges
1283 .len(),
1284 2
1285 );
1286
1287 let data_3 = rand::rand_u64().to_be_bytes();
1289
1290 client_path_mgr
1291 .get_mut(client_pid)
1292 .unwrap()
1293 .add_challenge_sent(data_3, MIN_CLIENT_INITIAL_LEN, Instant::now());
1294 assert_eq!(
1295 client_path_mgr
1296 .get(client_pid)
1297 .unwrap()
1298 .in_flight_challenges
1299 .len(),
1300 3
1301 );
1302
1303 let data_4 = rand::rand_u64().to_be_bytes();
1305
1306 client_path_mgr
1307 .get_mut(client_pid)
1308 .unwrap()
1309 .add_challenge_sent(data_4, MIN_CLIENT_INITIAL_LEN, Instant::now());
1310 assert_eq!(
1311 client_path_mgr
1312 .get(client_pid)
1313 .unwrap()
1314 .in_flight_challenges
1315 .len(),
1316 4
1317 );
1318
1319 server_path.on_challenge_received(data);
1322 assert_eq!(server_path.received_challenges.len(), 1);
1323 server_path.on_challenge_received(data_2);
1324 assert_eq!(server_path.received_challenges.len(), 2);
1325 server_path.on_challenge_received(data_3);
1326 assert_eq!(server_path.received_challenges.len(), 3);
1327 server_path.on_challenge_received(data_4);
1328 assert_eq!(server_path.received_challenges.len(), 3);
1329
1330 client_path_mgr.on_response_received(data).unwrap();
1332 assert_eq!(
1333 client_path_mgr
1334 .get(client_pid)
1335 .unwrap()
1336 .in_flight_challenges
1337 .len(),
1338 3
1339 );
1340
1341 client_path_mgr.on_response_received(data_2).unwrap();
1343 assert_eq!(
1344 client_path_mgr
1345 .get(client_pid)
1346 .unwrap()
1347 .in_flight_challenges
1348 .len(),
1349 2
1350 );
1351
1352 client_path_mgr.on_response_received(data_3).unwrap();
1354 assert_eq!(
1355 client_path_mgr
1356 .get(client_pid)
1357 .unwrap()
1358 .in_flight_challenges
1359 .len(),
1360 1
1361 );
1362
1363 }
1365}