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)]
85#[non_exhaustive]
86pub enum PathEvent {
87 New(SocketAddr, SocketAddr),
92
93 Validated(SocketAddr, SocketAddr),
96
97 FailedValidation(SocketAddr, SocketAddr),
101
102 Closed(SocketAddr, SocketAddr),
105
106 ReusedSourceConnectionId(
110 u64,
111 (SocketAddr, SocketAddr),
112 (SocketAddr, SocketAddr),
113 ),
114
115 PeerMigrated(SocketAddr, SocketAddr),
121
122 PmtuUpdated {
130 local: SocketAddr,
132
133 peer: SocketAddr,
135
136 pmtu: usize,
138 },
139}
140
141pub(crate) fn pmtu_event(
142 local: SocketAddr, peer: SocketAddr, old: usize, new: usize,
143) -> Option<PathEvent> {
144 (old != new).then_some(PathEvent::PmtuUpdated {
145 local,
146 peer,
147 pmtu: new,
148 })
149}
150
151#[derive(Debug)]
153pub struct Path {
154 local_addr: SocketAddr,
156
157 peer_addr: SocketAddr,
159
160 pub active_scid_seq: Option<u64>,
162
163 pub active_dcid_seq: Option<u64>,
165
166 state: PathState,
168
169 active: bool,
171
172 pub recovery: recovery::Recovery,
174
175 pub pmtud: Option<pmtud::Pmtud>,
177
178 in_flight_challenges: VecDeque<([u8; 8], usize, Instant)>,
181
182 max_challenge_size: usize,
184
185 probing_lost: usize,
187
188 last_probe_lost_time: Option<Instant>,
190
191 received_challenges: VecDeque<[u8; 8]>,
193
194 received_challenges_max_len: usize,
196
197 pub sent_count: usize,
199
200 pub recv_count: usize,
202
203 pub retrans_count: usize,
205
206 pub total_pto_count: usize,
213
214 pub dgram_sent_count: usize,
216
217 pub dgram_lost_count: usize,
219
220 pub dgram_recv_count: usize,
222
223 pub sent_bytes: u64,
225
226 pub recv_bytes: u64,
228
229 pub stream_retrans_bytes: u64,
232
233 pub max_send_bytes: usize,
236
237 pub verified_peer_address: bool,
239
240 pub peer_verified_local_address: bool,
242
243 challenge_requested: bool,
245
246 failure_notified: bool,
248
249 migrating: bool,
252
253 pub needs_ack_eliciting: bool,
255}
256
257impl Path {
258 pub fn new(
261 local_addr: SocketAddr, peer_addr: SocketAddr,
262 recovery_config: &recovery::RecoveryConfig,
263 path_challenge_recv_max_queue_len: usize, is_initial: bool,
264 config: Option<&Config>,
265 ) -> Self {
266 let (state, active_scid_seq, active_dcid_seq) = if is_initial {
267 (PathState::Validated, Some(0), Some(0))
268 } else {
269 (PathState::Unknown, None, None)
270 };
271
272 let pmtud = config.and_then(|c| {
273 if c.pmtud {
274 let maximum_supported_mtu: usize = std::cmp::min(
275 c.local_transport_params
279 .max_udp_payload_size
280 .try_into()
281 .unwrap_or(c.max_send_udp_payload_size),
282 c.max_send_udp_payload_size,
283 );
284 Some(pmtud::Pmtud::new(maximum_supported_mtu, c.pmtud_max_probes))
285 } else {
286 None
287 }
288 });
289
290 Self {
291 local_addr,
292 peer_addr,
293 active_scid_seq,
294 active_dcid_seq,
295 state,
296 active: false,
297 recovery: recovery::Recovery::new_with_config(recovery_config),
298 pmtud,
299 in_flight_challenges: VecDeque::new(),
300 max_challenge_size: 0,
301 probing_lost: 0,
302 last_probe_lost_time: None,
303 received_challenges: VecDeque::with_capacity(
304 path_challenge_recv_max_queue_len,
305 ),
306 received_challenges_max_len: path_challenge_recv_max_queue_len,
307 sent_count: 0,
308 recv_count: 0,
309 retrans_count: 0,
310 total_pto_count: 0,
311 dgram_sent_count: 0,
312 dgram_lost_count: 0,
313 dgram_recv_count: 0,
314 sent_bytes: 0,
315 recv_bytes: 0,
316 stream_retrans_bytes: 0,
317 max_send_bytes: 0,
318 verified_peer_address: false,
319 peer_verified_local_address: false,
320 challenge_requested: false,
321 failure_notified: false,
322 migrating: false,
323 needs_ack_eliciting: false,
324 }
325 }
326
327 #[inline]
329 pub fn local_addr(&self) -> SocketAddr {
330 self.local_addr
331 }
332
333 #[inline]
335 pub fn peer_addr(&self) -> SocketAddr {
336 self.peer_addr
337 }
338
339 #[inline]
341 fn working(&self) -> bool {
342 self.state > PathState::Failed
343 }
344
345 #[inline]
347 pub fn active(&self) -> bool {
348 self.active && self.working() && self.active_dcid_seq.is_some()
349 }
350
351 #[inline]
353 pub fn usable(&self) -> bool {
354 self.active() ||
355 (self.state == PathState::Validated &&
356 self.active_dcid_seq.is_some())
357 }
358
359 #[inline]
361 fn unused(&self) -> bool {
362 !self.active() && self.active_dcid_seq.is_none()
364 }
365
366 #[inline]
368 pub fn probing_required(&self) -> bool {
369 !self.received_challenges.is_empty() || self.validation_requested()
370 }
371
372 fn promote_to(&mut self, state: PathState) {
375 if self.state < state {
376 self.state = state;
377 }
378 }
379
380 #[inline]
382 pub fn validated(&self) -> bool {
383 self.state == PathState::Validated
384 }
385
386 #[inline]
388 fn validation_failed(&self) -> bool {
389 self.state == PathState::Failed
390 }
391
392 #[inline]
394 pub fn under_validation(&self) -> bool {
395 matches!(self.state, PathState::Validating | PathState::ValidatingMTU)
396 }
397
398 #[inline]
400 pub fn request_validation(&mut self) {
401 self.challenge_requested = true;
402 }
403
404 #[inline]
406 pub fn validation_requested(&self) -> bool {
407 self.challenge_requested
408 }
409
410 pub fn should_send_pmtu_probe(
411 &mut self, hs_confirmed: bool, hs_done: bool, out_len: usize,
412 is_closing: bool, frames_empty: bool,
413 ) -> bool {
414 let Some(pmtud) = self.pmtud.as_mut() else {
415 return false;
416 };
417
418 (hs_confirmed && hs_done) &&
419 self.recovery.cwnd_available() > pmtud.get_probe_size() &&
420 out_len >= pmtud.get_probe_size() &&
421 pmtud.should_probe() &&
422 !is_closing &&
423 frames_empty
424 }
425
426 pub fn on_challenge_sent(&mut self) {
427 self.promote_to(PathState::Validating);
428 self.challenge_requested = false;
429 }
430
431 pub fn add_challenge_sent(
433 &mut self, data: [u8; 8], pkt_size: usize, sent_time: Instant,
434 ) {
435 self.on_challenge_sent();
436 self.in_flight_challenges
437 .push_back((data, pkt_size, sent_time));
438 }
439
440 pub fn on_challenge_received(&mut self, data: [u8; 8]) {
441 if self.received_challenges.len() == self.received_challenges_max_len {
443 return;
444 }
445
446 self.received_challenges.push_back(data);
447 self.peer_verified_local_address = true;
448 }
449
450 pub fn has_pending_challenge(&self, data: [u8; 8]) -> bool {
451 self.in_flight_challenges.iter().any(|(d, ..)| *d == data)
452 }
453
454 pub fn on_response_received(&mut self, data: [u8; 8]) -> bool {
456 self.verified_peer_address = true;
457 self.probing_lost = 0;
458
459 let mut challenge_size = 0;
460 self.in_flight_challenges.retain(|(d, s, _)| {
461 if *d == data {
462 challenge_size = *s;
463 false
464 } else {
465 true
466 }
467 });
468
469 self.promote_to(PathState::ValidatingMTU);
471
472 self.max_challenge_size =
473 std::cmp::max(self.max_challenge_size, challenge_size);
474
475 if self.state == PathState::ValidatingMTU {
476 if self.max_challenge_size >= crate::MIN_CLIENT_INITIAL_LEN {
477 self.promote_to(PathState::Validated);
479 return true;
480 }
481
482 self.request_validation();
484 }
485
486 false
487 }
488
489 fn on_failed_validation(&mut self) {
490 self.state = PathState::Failed;
491 self.active = false;
492 }
493
494 #[inline]
495 pub fn pop_received_challenge(&mut self) -> Option<[u8; 8]> {
496 self.received_challenges.pop_front()
497 }
498
499 pub fn on_loss_detection_timeout(
500 &mut self, handshake_status: HandshakeStatus, now: Instant,
501 is_server: bool, trace_id: &str,
502 ) -> OnLossDetectionTimeoutOutcome {
503 let outcome = self.recovery.on_loss_detection_timeout(
504 handshake_status,
505 now,
506 trace_id,
507 );
508
509 let mut lost_probe_time = None;
510 self.in_flight_challenges.retain(|(_, _, sent_time)| {
511 if *sent_time <= now {
512 if lost_probe_time.is_none() {
513 lost_probe_time = Some(*sent_time);
514 }
515 false
516 } else {
517 true
518 }
519 });
520
521 if let Some(lost_probe_time) = lost_probe_time {
524 self.last_probe_lost_time = match self.last_probe_lost_time {
525 Some(last) => {
526 if lost_probe_time - last >= self.recovery.rtt() {
528 self.probing_lost += 1;
529 Some(lost_probe_time)
530 } else {
531 Some(last)
532 }
533 },
534 None => {
535 self.probing_lost += 1;
536 Some(lost_probe_time)
537 },
538 };
539 if self.probing_lost >= crate::MAX_PROBING_TIMEOUTS ||
543 (is_server && self.max_send_bytes < crate::MIN_PROBING_SIZE)
544 {
545 self.on_failed_validation();
546 } else {
547 self.request_validation();
548 }
549 }
550
551 self.total_pto_count += 1;
553
554 outcome
555 }
556
557 pub fn can_reinit_recovery(&self) -> bool {
561 self.recovery.bytes_in_flight() == 0 &&
566 self.recovery.bytes_in_flight_duration() == Duration::ZERO
567 }
568
569 pub fn reinit_recovery(
570 &mut self, recovery_config: &recovery::RecoveryConfig,
571 ) {
572 self.recovery = recovery::Recovery::new_with_config(recovery_config)
573 }
574
575 pub fn stats(&self) -> PathStats {
576 let pmtu = match self.pmtud.as_ref().map(|p| p.get_current_mtu()) {
577 Some(v) => v,
578
579 None => self.recovery.max_datagram_size(),
580 };
581
582 PathStats {
583 local_addr: self.local_addr,
584 peer_addr: self.peer_addr,
585 validation_state: self.state,
586 active: self.active,
587 recv: self.recv_count,
588 sent: self.sent_count,
589 lost: self.recovery.lost_count(),
590 retrans: self.retrans_count,
591 total_pto_count: self.total_pto_count,
592 dgram_recv: self.dgram_recv_count,
593 dgram_sent: self.dgram_sent_count,
594 dgram_lost: self.dgram_lost_count,
595 rtt: self.recovery.rtt(),
596 min_rtt: self.recovery.min_rtt(),
597 max_rtt: self.recovery.max_rtt(),
598 rttvar: self.recovery.rttvar(),
599 cwnd: self.recovery.cwnd(),
600 sent_bytes: self.sent_bytes,
601 recv_bytes: self.recv_bytes,
602 lost_bytes: self.recovery.bytes_lost(),
603 stream_retrans_bytes: self.stream_retrans_bytes,
604 pmtu,
605 delivery_rate: self.recovery.delivery_rate().to_bytes_per_second(),
606 max_bandwidth: self
607 .recovery
608 .max_bandwidth()
609 .map(Bandwidth::to_bytes_per_second),
610 rtt_persistent_jump_count: self.recovery.rtt_persistent_jump_count(),
611 startup_exit: self.recovery.startup_exit(),
612 }
613 }
614
615 pub fn bytes_in_flight_duration(&self) -> Duration {
616 self.recovery.bytes_in_flight_duration()
617 }
618}
619
620#[derive(Default)]
622pub struct SocketAddrIter {
623 pub(crate) sockaddrs: SmallVec<[SocketAddr; 8]>,
624 pub(crate) index: usize,
625}
626
627impl Iterator for SocketAddrIter {
628 type Item = SocketAddr;
629
630 #[inline]
631 fn next(&mut self) -> Option<Self::Item> {
632 let v = self.sockaddrs.get(self.index)?;
633 self.index += 1;
634 Some(*v)
635 }
636}
637
638impl ExactSizeIterator for SocketAddrIter {
639 #[inline]
640 fn len(&self) -> usize {
641 self.sockaddrs.len() - self.index
642 }
643}
644
645pub struct PathMap {
647 paths: Slab<Path>,
650
651 max_concurrent_paths: usize,
653
654 addrs_to_paths: BTreeMap<(SocketAddr, SocketAddr), usize>,
657
658 events: VecDeque<PathEvent>,
660
661 is_server: bool,
663}
664
665impl PathMap {
666 pub fn new(
669 mut initial_path: Path, max_concurrent_paths: usize, is_server: bool,
670 ) -> Self {
671 let mut paths = Slab::with_capacity(1);
673 let mut addrs_to_paths = BTreeMap::new();
674
675 let local_addr = initial_path.local_addr;
676 let peer_addr = initial_path.peer_addr;
677
678 initial_path.active = true;
680
681 let active_path_id = paths.insert(initial_path);
682 addrs_to_paths.insert((local_addr, peer_addr), active_path_id);
683
684 Self {
685 paths,
686 max_concurrent_paths,
687 addrs_to_paths,
688 events: VecDeque::new(),
689 is_server,
690 }
691 }
692
693 #[inline]
699 pub fn get(&self, path_id: usize) -> Result<&Path> {
700 self.paths.get(path_id).ok_or(Error::InvalidState)
701 }
702
703 #[inline]
709 pub fn get_mut(&mut self, path_id: usize) -> Result<&mut Path> {
710 self.paths.get_mut(path_id).ok_or(Error::InvalidState)
711 }
712
713 #[inline]
714 pub fn get_active_with_pid(&self) -> Option<(usize, &Path)> {
717 self.paths.iter().find(|(_, p)| p.active())
718 }
719
720 #[inline]
725 pub fn get_active(&self) -> Result<&Path> {
726 self.get_active_with_pid()
727 .map(|(_, p)| p)
728 .ok_or(Error::InvalidState)
729 }
730
731 #[inline]
736 pub fn get_active_path_id(&self) -> Result<usize> {
737 self.get_active_with_pid()
738 .map(|(pid, _)| pid)
739 .ok_or(Error::InvalidState)
740 }
741
742 #[inline]
747 pub fn get_active_mut(&mut self) -> Result<&mut Path> {
748 self.paths
749 .iter_mut()
750 .map(|(_, p)| p)
751 .find(|p| p.active())
752 .ok_or(Error::InvalidState)
753 }
754
755 #[inline]
757 pub fn iter(&self) -> slab::Iter<'_, Path> {
758 self.paths.iter()
759 }
760
761 #[inline]
763 pub fn iter_mut(&mut self) -> slab::IterMut<'_, Path> {
764 self.paths.iter_mut()
765 }
766
767 #[inline]
770 pub(crate) fn iter_mut_and_events(
771 &mut self,
772 ) -> (slab::IterMut<'_, Path>, &mut VecDeque<PathEvent>) {
773 (self.paths.iter_mut(), &mut self.events)
774 }
775
776 #[inline]
778 pub fn len(&self) -> usize {
779 self.paths.len()
780 }
781
782 #[inline]
784 pub fn path_id_from_addrs(
785 &self, addrs: &(SocketAddr, SocketAddr),
786 ) -> Option<usize> {
787 self.addrs_to_paths.get(addrs).copied()
788 }
789
790 fn make_room_for_new_path(&mut self) -> Result<()> {
796 if self.paths.len() < self.max_concurrent_paths {
797 return Ok(());
798 }
799
800 let (pid_to_remove, _) = self
801 .paths
802 .iter()
803 .find(|(_, p)| p.unused())
804 .ok_or(Error::Done)?;
805
806 let path = self.paths.remove(pid_to_remove);
807 self.addrs_to_paths
808 .remove(&(path.local_addr, path.peer_addr));
809
810 self.notify_event(PathEvent::Closed(path.local_addr, path.peer_addr));
811
812 Ok(())
813 }
814
815 pub fn insert_path(&mut self, path: Path, is_server: bool) -> Result<usize> {
826 self.make_room_for_new_path()?;
827
828 let local_addr = path.local_addr;
829 let peer_addr = path.peer_addr;
830
831 let pid = self.paths.insert(path);
832 self.addrs_to_paths.insert((local_addr, peer_addr), pid);
833
834 if is_server {
836 self.notify_event(PathEvent::New(local_addr, peer_addr));
837 }
838
839 Ok(pid)
840 }
841
842 pub fn notify_event(&mut self, ev: PathEvent) {
844 self.events.push_back(ev);
845 }
846
847 pub fn pop_event(&mut self) -> Option<PathEvent> {
849 self.events.pop_front()
850 }
851
852 pub fn notify_failed_validations(&mut self) {
854 let validation_failed = self
855 .paths
856 .iter_mut()
857 .filter(|(_, p)| p.validation_failed() && !p.failure_notified);
858
859 for (_, p) in validation_failed {
860 self.events.push_back(PathEvent::FailedValidation(
861 p.local_addr,
862 p.peer_addr,
863 ));
864
865 p.failure_notified = true;
866 }
867 }
868
869 pub fn find_candidate_path(&self) -> Option<usize> {
871 self.paths
873 .iter()
874 .find(|(_, p)| p.usable())
875 .map(|(pid, _)| pid)
876 }
877
878 pub fn on_response_received(&mut self, data: [u8; 8]) -> Result<()> {
880 let active_pid = self.get_active_path_id()?;
881
882 let challenge_pending =
883 self.iter_mut().find(|(_, p)| p.has_pending_challenge(data));
884
885 if let Some((pid, p)) = challenge_pending {
886 if p.on_response_received(data) {
887 let local_addr = p.local_addr;
888 let peer_addr = p.peer_addr;
889 let was_migrating = p.migrating;
890
891 p.migrating = false;
892
893 self.notify_event(PathEvent::Validated(local_addr, peer_addr));
895
896 if pid == active_pid && was_migrating {
899 self.notify_event(PathEvent::PeerMigrated(
900 local_addr, peer_addr,
901 ));
902 }
903 }
904 }
905 Ok(())
906 }
907
908 pub fn set_active_path(&mut self, path_id: usize) -> Result<()> {
919 let is_server = self.is_server;
920
921 if let Ok(old_active_path) = self.get_active_mut() {
922 old_active_path.active = false;
923 }
924
925 let new_active_path = self.get_mut(path_id)?;
926 new_active_path.active = true;
927
928 if is_server {
929 if new_active_path.validated() {
930 let local_addr = new_active_path.local_addr();
931 let peer_addr = new_active_path.peer_addr();
932
933 self.notify_event(PathEvent::PeerMigrated(local_addr, peer_addr));
934 } else {
935 new_active_path.migrating = true;
936
937 if !new_active_path.under_validation() {
939 new_active_path.request_validation();
940 }
941 }
942 }
943
944 Ok(())
945 }
946
947 pub fn set_discover_pmtu_on_existing_paths(
949 &mut self, discover: bool, max_send_udp_payload_size: usize,
950 pmtud_max_probes: u8,
951 ) {
952 for (_, path) in self.paths.iter_mut() {
953 let old_pmtu = path
954 .pmtud
955 .as_ref()
956 .map_or(path.recovery.max_datagram_size(), |pmtud| {
957 pmtud.get_current_mtu()
958 });
959
960 path.pmtud = if discover {
961 Some(pmtud::Pmtud::new(
962 max_send_udp_payload_size,
963 pmtud_max_probes,
964 ))
965 } else {
966 None
967 };
968
969 if let Some(pmtud) = path.pmtud.as_ref() {
970 if let Some(event) = pmtu_event(
971 path.local_addr,
972 path.peer_addr,
973 old_pmtu,
974 pmtud.get_current_mtu(),
975 ) {
976 self.events.push_back(event);
977 }
978 }
979 }
980 }
981}
982
983#[derive(Clone)]
990#[non_exhaustive]
991pub struct PathStats {
992 pub local_addr: SocketAddr,
994
995 pub peer_addr: SocketAddr,
997
998 pub validation_state: PathState,
1000
1001 pub active: bool,
1003
1004 pub recv: usize,
1006
1007 pub sent: usize,
1009
1010 pub lost: usize,
1012
1013 pub retrans: usize,
1015
1016 pub total_pto_count: usize,
1023
1024 pub dgram_recv: usize,
1026
1027 pub dgram_sent: usize,
1029
1030 pub dgram_lost: usize,
1032
1033 pub rtt: Duration,
1035
1036 pub min_rtt: Option<Duration>,
1038
1039 pub max_rtt: Option<Duration>,
1041
1042 pub rttvar: Duration,
1045
1046 pub cwnd: usize,
1048
1049 pub sent_bytes: u64,
1051
1052 pub recv_bytes: u64,
1054
1055 pub lost_bytes: u64,
1057
1058 pub stream_retrans_bytes: u64,
1060
1061 pub pmtu: usize,
1063
1064 pub delivery_rate: u64,
1073
1074 pub max_bandwidth: Option<u64>,
1079
1080 pub rtt_persistent_jump_count: u64,
1082
1083 pub startup_exit: Option<StartupExit>,
1085}
1086
1087impl std::fmt::Debug for PathStats {
1088 #[inline]
1089 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1090 write!(
1091 f,
1092 "local_addr={:?} peer_addr={:?} ",
1093 self.local_addr, self.peer_addr,
1094 )?;
1095 write!(
1096 f,
1097 "validation_state={:?} active={} ",
1098 self.validation_state, self.active,
1099 )?;
1100 write!(
1101 f,
1102 "recv={} sent={} lost={} retrans={} rtt={:?} min_rtt={:?} rttvar={:?} cwnd={}",
1103 self.recv, self.sent, self.lost, self.retrans, self.rtt, self.min_rtt, self.rttvar, self.cwnd,
1104 )?;
1105
1106 write!(
1107 f,
1108 " sent_bytes={} recv_bytes={} lost_bytes={}",
1109 self.sent_bytes, self.recv_bytes, self.lost_bytes,
1110 )?;
1111
1112 write!(
1113 f,
1114 " stream_retrans_bytes={} pmtu={} delivery_rate={} rtt_persistent_jump_count={}",
1115 self.stream_retrans_bytes,
1116 self.pmtu,
1117 self.delivery_rate,
1118 self.rtt_persistent_jump_count,
1119 )
1120 }
1121}
1122
1123#[cfg(test)]
1124mod tests {
1125 use crate::rand;
1126 use crate::test_utils;
1127 use crate::MIN_CLIENT_INITIAL_LEN;
1128
1129 use crate::recovery::RecoveryConfig;
1130 use crate::Config;
1131
1132 use super::*;
1133
1134 #[test]
1135 fn pmtud_probe_respects_cwnd_during_pto() {
1136 let local = "127.0.0.1:1234".parse().unwrap();
1137 let peer = "127.0.0.1:4321".parse().unwrap();
1138 let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1139 config.discover_pmtu(true);
1140 config.set_max_send_udp_payload_size(1400);
1141 let recovery_config = RecoveryConfig::from_config(&config);
1142 let mut path = Path::new(
1143 local,
1144 peer,
1145 &recovery_config,
1146 config.path_challenge_recv_max_queue_len,
1147 true,
1148 Some(&config),
1149 );
1150 let now = Instant::now();
1151 let cwnd = path.recovery.cwnd();
1152
1153 path.recovery.on_packet_sent(
1154 test_utils::helper_packet_sent(0, now, cwnd),
1155 crate::packet::Epoch::Application,
1156 HandshakeStatus {
1157 has_handshake_keys: false,
1158 peer_verified_address: true,
1159 completed: true,
1160 },
1161 now,
1162 "",
1163 );
1164 path.recovery
1165 .inc_loss_probes(crate::packet::Epoch::Application);
1166
1167 assert!(path.pmtud.as_ref().unwrap().should_probe());
1168 assert!(!path.should_send_pmtu_probe(true, true, 1400, false, true));
1169 }
1170
1171 #[test]
1172 fn reinitializing_pmtud_notifies_fallback_limit() {
1173 let local = "127.0.0.1:1234".parse().unwrap();
1174 let peer = "127.0.0.1:4321".parse().unwrap();
1175 let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1176 config.discover_pmtu(true);
1177 config.set_max_send_udp_payload_size(1400);
1178 let recovery_config = RecoveryConfig::from_config(&config);
1179 let mut path = Path::new(
1180 local,
1181 peer,
1182 &recovery_config,
1183 config.path_challenge_recv_max_queue_len,
1184 true,
1185 Some(&config),
1186 );
1187 path.pmtud.as_mut().unwrap().successful_probe(1400);
1188 let mut paths = PathMap::new(path, 1, false);
1189
1190 paths.set_discover_pmtu_on_existing_paths(false, 1400, 1);
1191 assert_eq!(paths.pop_event(), None);
1192
1193 paths.set_discover_pmtu_on_existing_paths(true, 1400, 1);
1194 assert_eq!(
1195 paths.pop_event(),
1196 Some(PathEvent::PmtuUpdated {
1197 local,
1198 peer,
1199 pmtu: MIN_CLIENT_INITIAL_LEN,
1200 })
1201 );
1202 }
1203
1204 #[test]
1205 fn path_validation_limited_mtu() {
1206 let client_addr = "127.0.0.1:1234".parse().unwrap();
1207 let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
1208 let server_addr = "127.0.0.1:4321".parse().unwrap();
1209
1210 let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1211 let recovery_config = RecoveryConfig::from_config(&config);
1212
1213 let path = Path::new(
1214 client_addr,
1215 server_addr,
1216 &recovery_config,
1217 config.path_challenge_recv_max_queue_len,
1218 true,
1219 None,
1220 );
1221 let mut path_mgr = PathMap::new(path, 2, false);
1222
1223 let probed_path = Path::new(
1224 client_addr_2,
1225 server_addr,
1226 &recovery_config,
1227 config.path_challenge_recv_max_queue_len,
1228 false,
1229 None,
1230 );
1231 path_mgr.insert_path(probed_path, false).unwrap();
1232
1233 let pid = path_mgr
1234 .path_id_from_addrs(&(client_addr_2, server_addr))
1235 .unwrap();
1236 path_mgr.get_mut(pid).unwrap().request_validation();
1237 assert!(path_mgr.get_mut(pid).unwrap().validation_requested());
1238 assert!(path_mgr.get_mut(pid).unwrap().probing_required());
1239
1240 let data = rand::rand_u64().to_be_bytes();
1242 path_mgr.get_mut(pid).unwrap().add_challenge_sent(
1243 data,
1244 MIN_CLIENT_INITIAL_LEN - 1,
1245 Instant::now(),
1246 );
1247
1248 assert!(!path_mgr.get_mut(pid).unwrap().validation_requested());
1249 assert!(!path_mgr.get_mut(pid).unwrap().probing_required());
1250 assert!(path_mgr.get_mut(pid).unwrap().under_validation());
1251 assert!(!path_mgr.get_mut(pid).unwrap().validated());
1252 assert_eq!(path_mgr.get_mut(pid).unwrap().state, PathState::Validating);
1253 assert_eq!(path_mgr.pop_event(), None);
1254
1255 path_mgr.on_response_received(data).unwrap();
1258
1259 assert!(path_mgr.get_mut(pid).unwrap().validation_requested());
1260 assert!(path_mgr.get_mut(pid).unwrap().probing_required());
1261 assert!(path_mgr.get_mut(pid).unwrap().under_validation());
1262 assert!(!path_mgr.get_mut(pid).unwrap().validated());
1263 assert_eq!(
1264 path_mgr.get_mut(pid).unwrap().state,
1265 PathState::ValidatingMTU
1266 );
1267 assert_eq!(path_mgr.pop_event(), None);
1268
1269 let data = rand::rand_u64().to_be_bytes();
1272 path_mgr.get_mut(pid).unwrap().add_challenge_sent(
1273 data,
1274 MIN_CLIENT_INITIAL_LEN,
1275 Instant::now(),
1276 );
1277
1278 path_mgr.on_response_received(data).unwrap();
1279
1280 assert!(!path_mgr.get_mut(pid).unwrap().validation_requested());
1281 assert!(!path_mgr.get_mut(pid).unwrap().probing_required());
1282 assert!(!path_mgr.get_mut(pid).unwrap().under_validation());
1283 assert!(path_mgr.get_mut(pid).unwrap().validated());
1284 assert_eq!(path_mgr.get_mut(pid).unwrap().state, PathState::Validated);
1285 assert_eq!(
1286 path_mgr.pop_event(),
1287 Some(PathEvent::Validated(client_addr_2, server_addr))
1288 );
1289 }
1290
1291 #[test]
1292 fn multiple_probes() {
1293 let client_addr = "127.0.0.1:1234".parse().unwrap();
1294 let server_addr = "127.0.0.1:4321".parse().unwrap();
1295
1296 let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1297 let recovery_config = RecoveryConfig::from_config(&config);
1298
1299 let path = Path::new(
1300 client_addr,
1301 server_addr,
1302 &recovery_config,
1303 config.path_challenge_recv_max_queue_len,
1304 true,
1305 None,
1306 );
1307 let mut client_path_mgr = PathMap::new(path, 2, false);
1308 let mut server_path = Path::new(
1309 server_addr,
1310 client_addr,
1311 &recovery_config,
1312 config.path_challenge_recv_max_queue_len,
1313 false,
1314 None,
1315 );
1316
1317 let client_pid = client_path_mgr
1318 .path_id_from_addrs(&(client_addr, server_addr))
1319 .unwrap();
1320
1321 let data = rand::rand_u64().to_be_bytes();
1323
1324 client_path_mgr
1325 .get_mut(client_pid)
1326 .unwrap()
1327 .add_challenge_sent(data, MIN_CLIENT_INITIAL_LEN, Instant::now());
1328
1329 let data_2 = rand::rand_u64().to_be_bytes();
1331
1332 client_path_mgr
1333 .get_mut(client_pid)
1334 .unwrap()
1335 .add_challenge_sent(data_2, MIN_CLIENT_INITIAL_LEN, Instant::now());
1336 assert_eq!(
1337 client_path_mgr
1338 .get(client_pid)
1339 .unwrap()
1340 .in_flight_challenges
1341 .len(),
1342 2
1343 );
1344
1345 server_path.on_challenge_received(data);
1347 assert_eq!(server_path.received_challenges.len(), 1);
1348 server_path.on_challenge_received(data_2);
1349 assert_eq!(server_path.received_challenges.len(), 2);
1350
1351 client_path_mgr.on_response_received(data).unwrap();
1353 assert_eq!(
1354 client_path_mgr
1355 .get(client_pid)
1356 .unwrap()
1357 .in_flight_challenges
1358 .len(),
1359 1
1360 );
1361
1362 client_path_mgr.on_response_received(data_2).unwrap();
1364 assert_eq!(
1365 client_path_mgr
1366 .get(client_pid)
1367 .unwrap()
1368 .in_flight_challenges
1369 .len(),
1370 0
1371 );
1372 }
1373
1374 #[test]
1375 fn too_many_probes() {
1376 let client_addr = "127.0.0.1:1234".parse().unwrap();
1377 let server_addr = "127.0.0.1:4321".parse().unwrap();
1378
1379 let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1381 let recovery_config = RecoveryConfig::from_config(&config);
1382
1383 let path = Path::new(
1384 client_addr,
1385 server_addr,
1386 &recovery_config,
1387 config.path_challenge_recv_max_queue_len,
1388 true,
1389 None,
1390 );
1391 let mut client_path_mgr = PathMap::new(path, 2, false);
1392 let mut server_path = Path::new(
1393 server_addr,
1394 client_addr,
1395 &recovery_config,
1396 config.path_challenge_recv_max_queue_len,
1397 false,
1398 None,
1399 );
1400
1401 let client_pid = client_path_mgr
1402 .path_id_from_addrs(&(client_addr, server_addr))
1403 .unwrap();
1404
1405 let data = rand::rand_u64().to_be_bytes();
1407
1408 client_path_mgr
1409 .get_mut(client_pid)
1410 .unwrap()
1411 .add_challenge_sent(data, MIN_CLIENT_INITIAL_LEN, Instant::now());
1412
1413 let data_2 = rand::rand_u64().to_be_bytes();
1415
1416 client_path_mgr
1417 .get_mut(client_pid)
1418 .unwrap()
1419 .add_challenge_sent(data_2, MIN_CLIENT_INITIAL_LEN, Instant::now());
1420 assert_eq!(
1421 client_path_mgr
1422 .get(client_pid)
1423 .unwrap()
1424 .in_flight_challenges
1425 .len(),
1426 2
1427 );
1428
1429 let data_3 = rand::rand_u64().to_be_bytes();
1431
1432 client_path_mgr
1433 .get_mut(client_pid)
1434 .unwrap()
1435 .add_challenge_sent(data_3, MIN_CLIENT_INITIAL_LEN, Instant::now());
1436 assert_eq!(
1437 client_path_mgr
1438 .get(client_pid)
1439 .unwrap()
1440 .in_flight_challenges
1441 .len(),
1442 3
1443 );
1444
1445 let data_4 = rand::rand_u64().to_be_bytes();
1447
1448 client_path_mgr
1449 .get_mut(client_pid)
1450 .unwrap()
1451 .add_challenge_sent(data_4, MIN_CLIENT_INITIAL_LEN, Instant::now());
1452 assert_eq!(
1453 client_path_mgr
1454 .get(client_pid)
1455 .unwrap()
1456 .in_flight_challenges
1457 .len(),
1458 4
1459 );
1460
1461 server_path.on_challenge_received(data);
1464 assert_eq!(server_path.received_challenges.len(), 1);
1465 server_path.on_challenge_received(data_2);
1466 assert_eq!(server_path.received_challenges.len(), 2);
1467 server_path.on_challenge_received(data_3);
1468 assert_eq!(server_path.received_challenges.len(), 3);
1469 server_path.on_challenge_received(data_4);
1470 assert_eq!(server_path.received_challenges.len(), 3);
1471
1472 client_path_mgr.on_response_received(data).unwrap();
1474 assert_eq!(
1475 client_path_mgr
1476 .get(client_pid)
1477 .unwrap()
1478 .in_flight_challenges
1479 .len(),
1480 3
1481 );
1482
1483 client_path_mgr.on_response_received(data_2).unwrap();
1485 assert_eq!(
1486 client_path_mgr
1487 .get(client_pid)
1488 .unwrap()
1489 .in_flight_challenges
1490 .len(),
1491 2
1492 );
1493
1494 client_path_mgr.on_response_received(data_3).unwrap();
1496 assert_eq!(
1497 client_path_mgr
1498 .get(client_pid)
1499 .unwrap()
1500 .in_flight_challenges
1501 .len(),
1502 1
1503 );
1504
1505 }
1507}