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
278 .max_udp_payload_size
279 .try_into()
280 .unwrap_or(c.max_send_udp_payload_size),
281 c.max_send_udp_payload_size,
282 );
283 Some(pmtud::Pmtud::new(maximum_supported_mtu, c.pmtud_max_probes))
284 } else {
285 None
286 }
287 });
288
289 Self {
290 local_addr,
291 peer_addr,
292 active_scid_seq,
293 active_dcid_seq,
294 state,
295 active: false,
296 recovery: recovery::Recovery::new_with_config(recovery_config),
297 pmtud,
298 in_flight_challenges: VecDeque::new(),
299 max_challenge_size: 0,
300 probing_lost: 0,
301 last_probe_lost_time: None,
302 received_challenges: VecDeque::with_capacity(
303 path_challenge_recv_max_queue_len,
304 ),
305 received_challenges_max_len: path_challenge_recv_max_queue_len,
306 sent_count: 0,
307 recv_count: 0,
308 retrans_count: 0,
309 total_pto_count: 0,
310 dgram_sent_count: 0,
311 dgram_lost_count: 0,
312 dgram_recv_count: 0,
313 sent_bytes: 0,
314 recv_bytes: 0,
315 stream_retrans_bytes: 0,
316 max_send_bytes: 0,
317 verified_peer_address: false,
318 peer_verified_local_address: false,
319 challenge_requested: false,
320 failure_notified: false,
321 migrating: false,
322 needs_ack_eliciting: false,
323 }
324 }
325
326 #[inline]
328 pub fn local_addr(&self) -> SocketAddr {
329 self.local_addr
330 }
331
332 #[inline]
334 pub fn peer_addr(&self) -> SocketAddr {
335 self.peer_addr
336 }
337
338 #[inline]
340 fn working(&self) -> bool {
341 self.state > PathState::Failed
342 }
343
344 #[inline]
346 pub fn active(&self) -> bool {
347 self.active && self.working() && self.active_dcid_seq.is_some()
348 }
349
350 #[inline]
352 pub fn usable(&self) -> bool {
353 self.active() ||
354 (self.state == PathState::Validated &&
355 self.active_dcid_seq.is_some())
356 }
357
358 #[inline]
360 fn unused(&self) -> bool {
361 !self.active() && self.active_dcid_seq.is_none()
363 }
364
365 #[inline]
367 pub fn probing_required(&self) -> bool {
368 !self.received_challenges.is_empty() || self.validation_requested()
369 }
370
371 fn promote_to(&mut self, state: PathState) {
374 if self.state < state {
375 self.state = state;
376 }
377 }
378
379 #[inline]
381 pub fn validated(&self) -> bool {
382 self.state == PathState::Validated
383 }
384
385 #[inline]
387 fn validation_failed(&self) -> bool {
388 self.state == PathState::Failed
389 }
390
391 #[inline]
393 pub fn under_validation(&self) -> bool {
394 matches!(self.state, PathState::Validating | PathState::ValidatingMTU)
395 }
396
397 #[inline]
399 pub fn request_validation(&mut self) {
400 self.challenge_requested = true;
401 }
402
403 #[inline]
405 pub fn validation_requested(&self) -> bool {
406 self.challenge_requested
407 }
408
409 pub fn should_send_pmtu_probe(
410 &mut self, hs_confirmed: bool, hs_done: bool, out_len: usize,
411 is_closing: bool, frames_empty: bool,
412 ) -> bool {
413 let Some(pmtud) = self.pmtud.as_mut() else {
414 return false;
415 };
416
417 (hs_confirmed && hs_done) &&
418 self.recovery.cwnd_available() > pmtud.get_probe_size() &&
419 out_len >= pmtud.get_probe_size() &&
420 pmtud.should_probe() &&
421 !is_closing &&
422 frames_empty
423 }
424
425 pub fn on_challenge_sent(&mut self) {
426 self.promote_to(PathState::Validating);
427 self.challenge_requested = false;
428 }
429
430 pub fn add_challenge_sent(
432 &mut self, data: [u8; 8], pkt_size: usize, sent_time: Instant,
433 ) {
434 self.on_challenge_sent();
435 self.in_flight_challenges
436 .push_back((data, pkt_size, sent_time));
437 }
438
439 pub fn on_challenge_received(&mut self, data: [u8; 8]) {
440 if self.received_challenges.len() == self.received_challenges_max_len {
442 return;
443 }
444
445 self.received_challenges.push_back(data);
446 self.peer_verified_local_address = true;
447 }
448
449 pub fn has_pending_challenge(&self, data: [u8; 8]) -> bool {
450 self.in_flight_challenges.iter().any(|(d, ..)| *d == data)
451 }
452
453 pub fn on_response_received(&mut self, data: [u8; 8]) -> bool {
455 self.verified_peer_address = true;
456 self.probing_lost = 0;
457
458 let mut challenge_size = 0;
459 self.in_flight_challenges.retain(|(d, s, _)| {
460 if *d == data {
461 challenge_size = *s;
462 false
463 } else {
464 true
465 }
466 });
467
468 self.promote_to(PathState::ValidatingMTU);
470
471 self.max_challenge_size =
472 std::cmp::max(self.max_challenge_size, challenge_size);
473
474 if self.state == PathState::ValidatingMTU {
475 if self.max_challenge_size >= crate::MIN_CLIENT_INITIAL_LEN {
476 self.promote_to(PathState::Validated);
478 return true;
479 }
480
481 self.request_validation();
483 }
484
485 false
486 }
487
488 fn on_failed_validation(&mut self) {
489 self.state = PathState::Failed;
490 self.active = false;
491 }
492
493 #[inline]
494 pub fn pop_received_challenge(&mut self) -> Option<[u8; 8]> {
495 self.received_challenges.pop_front()
496 }
497
498 pub fn on_loss_detection_timeout(
499 &mut self, handshake_status: HandshakeStatus, now: Instant,
500 is_server: bool, trace_id: &str,
501 ) -> OnLossDetectionTimeoutOutcome {
502 let outcome = self.recovery.on_loss_detection_timeout(
503 handshake_status,
504 now,
505 trace_id,
506 );
507
508 let mut lost_probe_time = None;
509 self.in_flight_challenges.retain(|(_, _, sent_time)| {
510 if *sent_time <= now {
511 if lost_probe_time.is_none() {
512 lost_probe_time = Some(*sent_time);
513 }
514 false
515 } else {
516 true
517 }
518 });
519
520 if let Some(lost_probe_time) = lost_probe_time {
523 self.last_probe_lost_time = match self.last_probe_lost_time {
524 Some(last) => {
525 if lost_probe_time - last >= self.recovery.rtt() {
527 self.probing_lost += 1;
528 Some(lost_probe_time)
529 } else {
530 Some(last)
531 }
532 },
533 None => {
534 self.probing_lost += 1;
535 Some(lost_probe_time)
536 },
537 };
538 if self.probing_lost >= crate::MAX_PROBING_TIMEOUTS ||
542 (is_server && self.max_send_bytes < crate::MIN_PROBING_SIZE)
543 {
544 self.on_failed_validation();
545 } else {
546 self.request_validation();
547 }
548 }
549
550 self.total_pto_count += 1;
552
553 outcome
554 }
555
556 pub fn can_reinit_recovery(&self) -> bool {
560 self.recovery.bytes_in_flight() == 0 &&
565 self.recovery.bytes_in_flight_duration() == Duration::ZERO
566 }
567
568 pub fn reinit_recovery(
569 &mut self, recovery_config: &recovery::RecoveryConfig,
570 ) {
571 self.recovery = recovery::Recovery::new_with_config(recovery_config)
572 }
573
574 pub fn stats(&self) -> PathStats {
575 let pmtu = match self.pmtud.as_ref().map(|p| p.get_current_mtu()) {
576 Some(v) => v,
577
578 None => self.recovery.max_datagram_size(),
579 };
580
581 PathStats {
582 local_addr: self.local_addr,
583 peer_addr: self.peer_addr,
584 validation_state: self.state,
585 active: self.active,
586 recv: self.recv_count,
587 sent: self.sent_count,
588 lost: self.recovery.lost_count(),
589 retrans: self.retrans_count,
590 total_pto_count: self.total_pto_count,
591 dgram_recv: self.dgram_recv_count,
592 dgram_sent: self.dgram_sent_count,
593 dgram_lost: self.dgram_lost_count,
594 rtt: self.recovery.rtt(),
595 min_rtt: self.recovery.min_rtt(),
596 max_rtt: self.recovery.max_rtt(),
597 rttvar: self.recovery.rttvar(),
598 cwnd: self.recovery.cwnd(),
599 sent_bytes: self.sent_bytes,
600 recv_bytes: self.recv_bytes,
601 lost_bytes: self.recovery.bytes_lost(),
602 stream_retrans_bytes: self.stream_retrans_bytes,
603 pmtu,
604 delivery_rate: self.recovery.delivery_rate().to_bytes_per_second(),
605 max_bandwidth: self
606 .recovery
607 .max_bandwidth()
608 .map(Bandwidth::to_bytes_per_second),
609 rtt_persistent_jump_count: self.recovery.rtt_persistent_jump_count(),
610 startup_exit: self.recovery.startup_exit(),
611 }
612 }
613
614 pub fn bytes_in_flight_duration(&self) -> Duration {
615 self.recovery.bytes_in_flight_duration()
616 }
617}
618
619#[derive(Default)]
621pub struct SocketAddrIter {
622 pub(crate) sockaddrs: SmallVec<[SocketAddr; 8]>,
623 pub(crate) index: usize,
624}
625
626impl Iterator for SocketAddrIter {
627 type Item = SocketAddr;
628
629 #[inline]
630 fn next(&mut self) -> Option<Self::Item> {
631 let v = self.sockaddrs.get(self.index)?;
632 self.index += 1;
633 Some(*v)
634 }
635}
636
637impl ExactSizeIterator for SocketAddrIter {
638 #[inline]
639 fn len(&self) -> usize {
640 self.sockaddrs.len() - self.index
641 }
642}
643
644pub struct PathMap {
646 paths: Slab<Path>,
649
650 max_concurrent_paths: usize,
652
653 addrs_to_paths: BTreeMap<(SocketAddr, SocketAddr), usize>,
656
657 events: VecDeque<PathEvent>,
659
660 is_server: bool,
662}
663
664impl PathMap {
665 pub fn new(
668 mut initial_path: Path, max_concurrent_paths: usize, is_server: bool,
669 ) -> Self {
670 let mut paths = Slab::with_capacity(1);
672 let mut addrs_to_paths = BTreeMap::new();
673
674 let local_addr = initial_path.local_addr;
675 let peer_addr = initial_path.peer_addr;
676
677 initial_path.active = true;
679
680 let active_path_id = paths.insert(initial_path);
681 addrs_to_paths.insert((local_addr, peer_addr), active_path_id);
682
683 Self {
684 paths,
685 max_concurrent_paths,
686 addrs_to_paths,
687 events: VecDeque::new(),
688 is_server,
689 }
690 }
691
692 #[inline]
698 pub fn get(&self, path_id: usize) -> Result<&Path> {
699 self.paths.get(path_id).ok_or(Error::InvalidState)
700 }
701
702 #[inline]
708 pub fn get_mut(&mut self, path_id: usize) -> Result<&mut Path> {
709 self.paths.get_mut(path_id).ok_or(Error::InvalidState)
710 }
711
712 #[inline]
713 pub fn get_active_with_pid(&self) -> Option<(usize, &Path)> {
716 self.paths.iter().find(|(_, p)| p.active())
717 }
718
719 #[inline]
724 pub fn get_active(&self) -> Result<&Path> {
725 self.get_active_with_pid()
726 .map(|(_, p)| p)
727 .ok_or(Error::InvalidState)
728 }
729
730 #[inline]
735 pub fn get_active_path_id(&self) -> Result<usize> {
736 self.get_active_with_pid()
737 .map(|(pid, _)| pid)
738 .ok_or(Error::InvalidState)
739 }
740
741 #[inline]
746 pub fn get_active_mut(&mut self) -> Result<&mut Path> {
747 self.paths
748 .iter_mut()
749 .map(|(_, p)| p)
750 .find(|p| p.active())
751 .ok_or(Error::InvalidState)
752 }
753
754 #[inline]
756 pub fn iter(&self) -> slab::Iter<'_, Path> {
757 self.paths.iter()
758 }
759
760 #[inline]
762 pub fn iter_mut(&mut self) -> slab::IterMut<'_, Path> {
763 self.paths.iter_mut()
764 }
765
766 #[inline]
769 pub(crate) fn iter_mut_and_events(
770 &mut self,
771 ) -> (slab::IterMut<'_, Path>, &mut VecDeque<PathEvent>) {
772 (self.paths.iter_mut(), &mut self.events)
773 }
774
775 #[inline]
777 pub fn len(&self) -> usize {
778 self.paths.len()
779 }
780
781 #[inline]
783 pub fn path_id_from_addrs(
784 &self, addrs: &(SocketAddr, SocketAddr),
785 ) -> Option<usize> {
786 self.addrs_to_paths.get(addrs).copied()
787 }
788
789 fn make_room_for_new_path(&mut self) -> Result<()> {
795 if self.paths.len() < self.max_concurrent_paths {
796 return Ok(());
797 }
798
799 let (pid_to_remove, _) = self
800 .paths
801 .iter()
802 .find(|(_, p)| p.unused())
803 .ok_or(Error::Done)?;
804
805 let path = self.paths.remove(pid_to_remove);
806 self.addrs_to_paths
807 .remove(&(path.local_addr, path.peer_addr));
808
809 self.notify_event(PathEvent::Closed(path.local_addr, path.peer_addr));
810
811 Ok(())
812 }
813
814 pub fn insert_path(&mut self, path: Path, is_server: bool) -> Result<usize> {
825 self.make_room_for_new_path()?;
826
827 let local_addr = path.local_addr;
828 let peer_addr = path.peer_addr;
829
830 let pid = self.paths.insert(path);
831 self.addrs_to_paths.insert((local_addr, peer_addr), pid);
832
833 if is_server {
835 self.notify_event(PathEvent::New(local_addr, peer_addr));
836 }
837
838 Ok(pid)
839 }
840
841 pub fn notify_event(&mut self, ev: PathEvent) {
843 self.events.push_back(ev);
844 }
845
846 pub fn pop_event(&mut self) -> Option<PathEvent> {
848 self.events.pop_front()
849 }
850
851 pub fn notify_failed_validations(&mut self) {
853 let validation_failed = self
854 .paths
855 .iter_mut()
856 .filter(|(_, p)| p.validation_failed() && !p.failure_notified);
857
858 for (_, p) in validation_failed {
859 self.events.push_back(PathEvent::FailedValidation(
860 p.local_addr,
861 p.peer_addr,
862 ));
863
864 p.failure_notified = true;
865 }
866 }
867
868 pub fn find_candidate_path(&self) -> Option<usize> {
870 self.paths
872 .iter()
873 .find(|(_, p)| p.usable())
874 .map(|(pid, _)| pid)
875 }
876
877 pub fn on_response_received(&mut self, data: [u8; 8]) -> Result<()> {
879 let active_pid = self.get_active_path_id()?;
880
881 let challenge_pending =
882 self.iter_mut().find(|(_, p)| p.has_pending_challenge(data));
883
884 if let Some((pid, p)) = challenge_pending {
885 if p.on_response_received(data) {
886 let local_addr = p.local_addr;
887 let peer_addr = p.peer_addr;
888 let was_migrating = p.migrating;
889
890 p.migrating = false;
891
892 self.notify_event(PathEvent::Validated(local_addr, peer_addr));
894
895 if pid == active_pid && was_migrating {
898 self.notify_event(PathEvent::PeerMigrated(
899 local_addr, peer_addr,
900 ));
901 }
902 }
903 }
904 Ok(())
905 }
906
907 pub fn set_active_path(&mut self, path_id: usize) -> Result<()> {
918 let is_server = self.is_server;
919
920 if let Ok(old_active_path) = self.get_active_mut() {
921 old_active_path.active = false;
922 }
923
924 let new_active_path = self.get_mut(path_id)?;
925 new_active_path.active = true;
926
927 if is_server {
928 if new_active_path.validated() {
929 let local_addr = new_active_path.local_addr();
930 let peer_addr = new_active_path.peer_addr();
931
932 self.notify_event(PathEvent::PeerMigrated(local_addr, peer_addr));
933 } else {
934 new_active_path.migrating = true;
935
936 if !new_active_path.under_validation() {
938 new_active_path.request_validation();
939 }
940 }
941 }
942
943 Ok(())
944 }
945
946 pub fn set_discover_pmtu_on_existing_paths(
948 &mut self, discover: bool, max_send_udp_payload_size: usize,
949 pmtud_max_probes: u8,
950 ) {
951 for (_, path) in self.paths.iter_mut() {
952 let old_pmtu = path
953 .pmtud
954 .as_ref()
955 .map_or(path.recovery.max_datagram_size(), |pmtud| {
956 pmtud.get_current_mtu()
957 });
958
959 path.pmtud = if discover {
960 Some(pmtud::Pmtud::new(
961 max_send_udp_payload_size,
962 pmtud_max_probes,
963 ))
964 } else {
965 None
966 };
967
968 if let Some(pmtud) = path.pmtud.as_ref() {
969 if let Some(event) = pmtu_event(
970 path.local_addr,
971 path.peer_addr,
972 old_pmtu,
973 pmtud.get_current_mtu(),
974 ) {
975 self.events.push_back(event);
976 }
977 }
978 }
979 }
980}
981
982#[derive(Clone)]
989#[non_exhaustive]
990pub struct PathStats {
991 pub local_addr: SocketAddr,
993
994 pub peer_addr: SocketAddr,
996
997 pub validation_state: PathState,
999
1000 pub active: bool,
1002
1003 pub recv: usize,
1005
1006 pub sent: usize,
1008
1009 pub lost: usize,
1011
1012 pub retrans: usize,
1014
1015 pub total_pto_count: usize,
1022
1023 pub dgram_recv: usize,
1025
1026 pub dgram_sent: usize,
1028
1029 pub dgram_lost: usize,
1031
1032 pub rtt: Duration,
1034
1035 pub min_rtt: Option<Duration>,
1037
1038 pub max_rtt: Option<Duration>,
1040
1041 pub rttvar: Duration,
1044
1045 pub cwnd: usize,
1047
1048 pub sent_bytes: u64,
1050
1051 pub recv_bytes: u64,
1053
1054 pub lost_bytes: u64,
1056
1057 pub stream_retrans_bytes: u64,
1059
1060 pub pmtu: usize,
1062
1063 pub delivery_rate: u64,
1072
1073 pub max_bandwidth: Option<u64>,
1078
1079 pub rtt_persistent_jump_count: u64,
1081
1082 pub startup_exit: Option<StartupExit>,
1084}
1085
1086impl std::fmt::Debug for PathStats {
1087 #[inline]
1088 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1089 write!(
1090 f,
1091 "local_addr={:?} peer_addr={:?} ",
1092 self.local_addr, self.peer_addr,
1093 )?;
1094 write!(
1095 f,
1096 "validation_state={:?} active={} ",
1097 self.validation_state, self.active,
1098 )?;
1099 write!(
1100 f,
1101 "recv={} sent={} lost={} retrans={} rtt={:?} min_rtt={:?} rttvar={:?} cwnd={}",
1102 self.recv, self.sent, self.lost, self.retrans, self.rtt, self.min_rtt, self.rttvar, self.cwnd,
1103 )?;
1104
1105 write!(
1106 f,
1107 " sent_bytes={} recv_bytes={} lost_bytes={}",
1108 self.sent_bytes, self.recv_bytes, self.lost_bytes,
1109 )?;
1110
1111 write!(
1112 f,
1113 " stream_retrans_bytes={} pmtu={} delivery_rate={} rtt_persistent_jump_count={}",
1114 self.stream_retrans_bytes,
1115 self.pmtu,
1116 self.delivery_rate,
1117 self.rtt_persistent_jump_count,
1118 )
1119 }
1120}
1121
1122#[cfg(test)]
1123mod tests {
1124 use crate::rand;
1125 use crate::test_utils;
1126 use crate::MIN_CLIENT_INITIAL_LEN;
1127
1128 use crate::recovery::RecoveryConfig;
1129 use crate::Config;
1130
1131 use super::*;
1132
1133 #[test]
1134 fn pmtud_probe_respects_cwnd_during_pto() {
1135 let local = "127.0.0.1:1234".parse().unwrap();
1136 let peer = "127.0.0.1:4321".parse().unwrap();
1137 let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1138 config.discover_pmtu(true);
1139 config.set_max_send_udp_payload_size(1400);
1140 let recovery_config = RecoveryConfig::from_config(&config);
1141 let mut path = Path::new(
1142 local,
1143 peer,
1144 &recovery_config,
1145 config.path_challenge_recv_max_queue_len,
1146 true,
1147 Some(&config),
1148 );
1149 let now = Instant::now();
1150 let cwnd = path.recovery.cwnd();
1151
1152 path.recovery.on_packet_sent(
1153 test_utils::helper_packet_sent(0, now, cwnd),
1154 crate::packet::Epoch::Application,
1155 HandshakeStatus {
1156 has_handshake_keys: false,
1157 peer_verified_address: true,
1158 completed: true,
1159 },
1160 now,
1161 "",
1162 );
1163 path.recovery
1164 .inc_loss_probes(crate::packet::Epoch::Application);
1165
1166 assert!(path.pmtud.as_ref().unwrap().should_probe());
1167 assert!(!path.should_send_pmtu_probe(true, true, 1400, false, true));
1168 }
1169
1170 #[test]
1171 fn reinitializing_pmtud_notifies_fallback_limit() {
1172 let local = "127.0.0.1:1234".parse().unwrap();
1173 let peer = "127.0.0.1:4321".parse().unwrap();
1174 let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1175 config.discover_pmtu(true);
1176 config.set_max_send_udp_payload_size(1400);
1177 let recovery_config = RecoveryConfig::from_config(&config);
1178 let mut path = Path::new(
1179 local,
1180 peer,
1181 &recovery_config,
1182 config.path_challenge_recv_max_queue_len,
1183 true,
1184 Some(&config),
1185 );
1186 path.pmtud.as_mut().unwrap().successful_probe(1400);
1187 let mut paths = PathMap::new(path, 1, false);
1188
1189 paths.set_discover_pmtu_on_existing_paths(false, 1400, 1);
1190 assert_eq!(paths.pop_event(), None);
1191
1192 paths.set_discover_pmtu_on_existing_paths(true, 1400, 1);
1193 assert_eq!(
1194 paths.pop_event(),
1195 Some(PathEvent::PmtuUpdated {
1196 local,
1197 peer,
1198 pmtu: MIN_CLIENT_INITIAL_LEN,
1199 })
1200 );
1201 }
1202
1203 #[test]
1204 fn path_validation_limited_mtu() {
1205 let client_addr = "127.0.0.1:1234".parse().unwrap();
1206 let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
1207 let server_addr = "127.0.0.1:4321".parse().unwrap();
1208
1209 let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1210 let recovery_config = RecoveryConfig::from_config(&config);
1211
1212 let path = Path::new(
1213 client_addr,
1214 server_addr,
1215 &recovery_config,
1216 config.path_challenge_recv_max_queue_len,
1217 true,
1218 None,
1219 );
1220 let mut path_mgr = PathMap::new(path, 2, false);
1221
1222 let probed_path = Path::new(
1223 client_addr_2,
1224 server_addr,
1225 &recovery_config,
1226 config.path_challenge_recv_max_queue_len,
1227 false,
1228 None,
1229 );
1230 path_mgr.insert_path(probed_path, false).unwrap();
1231
1232 let pid = path_mgr
1233 .path_id_from_addrs(&(client_addr_2, server_addr))
1234 .unwrap();
1235 path_mgr.get_mut(pid).unwrap().request_validation();
1236 assert!(path_mgr.get_mut(pid).unwrap().validation_requested());
1237 assert!(path_mgr.get_mut(pid).unwrap().probing_required());
1238
1239 let data = rand::rand_u64().to_be_bytes();
1241 path_mgr.get_mut(pid).unwrap().add_challenge_sent(
1242 data,
1243 MIN_CLIENT_INITIAL_LEN - 1,
1244 Instant::now(),
1245 );
1246
1247 assert!(!path_mgr.get_mut(pid).unwrap().validation_requested());
1248 assert!(!path_mgr.get_mut(pid).unwrap().probing_required());
1249 assert!(path_mgr.get_mut(pid).unwrap().under_validation());
1250 assert!(!path_mgr.get_mut(pid).unwrap().validated());
1251 assert_eq!(path_mgr.get_mut(pid).unwrap().state, PathState::Validating);
1252 assert_eq!(path_mgr.pop_event(), None);
1253
1254 path_mgr.on_response_received(data).unwrap();
1257
1258 assert!(path_mgr.get_mut(pid).unwrap().validation_requested());
1259 assert!(path_mgr.get_mut(pid).unwrap().probing_required());
1260 assert!(path_mgr.get_mut(pid).unwrap().under_validation());
1261 assert!(!path_mgr.get_mut(pid).unwrap().validated());
1262 assert_eq!(
1263 path_mgr.get_mut(pid).unwrap().state,
1264 PathState::ValidatingMTU
1265 );
1266 assert_eq!(path_mgr.pop_event(), None);
1267
1268 let data = rand::rand_u64().to_be_bytes();
1271 path_mgr.get_mut(pid).unwrap().add_challenge_sent(
1272 data,
1273 MIN_CLIENT_INITIAL_LEN,
1274 Instant::now(),
1275 );
1276
1277 path_mgr.on_response_received(data).unwrap();
1278
1279 assert!(!path_mgr.get_mut(pid).unwrap().validation_requested());
1280 assert!(!path_mgr.get_mut(pid).unwrap().probing_required());
1281 assert!(!path_mgr.get_mut(pid).unwrap().under_validation());
1282 assert!(path_mgr.get_mut(pid).unwrap().validated());
1283 assert_eq!(path_mgr.get_mut(pid).unwrap().state, PathState::Validated);
1284 assert_eq!(
1285 path_mgr.pop_event(),
1286 Some(PathEvent::Validated(client_addr_2, server_addr))
1287 );
1288 }
1289
1290 #[test]
1291 fn multiple_probes() {
1292 let client_addr = "127.0.0.1:1234".parse().unwrap();
1293 let server_addr = "127.0.0.1:4321".parse().unwrap();
1294
1295 let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1296 let recovery_config = RecoveryConfig::from_config(&config);
1297
1298 let path = Path::new(
1299 client_addr,
1300 server_addr,
1301 &recovery_config,
1302 config.path_challenge_recv_max_queue_len,
1303 true,
1304 None,
1305 );
1306 let mut client_path_mgr = PathMap::new(path, 2, false);
1307 let mut server_path = Path::new(
1308 server_addr,
1309 client_addr,
1310 &recovery_config,
1311 config.path_challenge_recv_max_queue_len,
1312 false,
1313 None,
1314 );
1315
1316 let client_pid = client_path_mgr
1317 .path_id_from_addrs(&(client_addr, server_addr))
1318 .unwrap();
1319
1320 let data = rand::rand_u64().to_be_bytes();
1322
1323 client_path_mgr
1324 .get_mut(client_pid)
1325 .unwrap()
1326 .add_challenge_sent(data, MIN_CLIENT_INITIAL_LEN, Instant::now());
1327
1328 let data_2 = rand::rand_u64().to_be_bytes();
1330
1331 client_path_mgr
1332 .get_mut(client_pid)
1333 .unwrap()
1334 .add_challenge_sent(data_2, MIN_CLIENT_INITIAL_LEN, Instant::now());
1335 assert_eq!(
1336 client_path_mgr
1337 .get(client_pid)
1338 .unwrap()
1339 .in_flight_challenges
1340 .len(),
1341 2
1342 );
1343
1344 server_path.on_challenge_received(data);
1346 assert_eq!(server_path.received_challenges.len(), 1);
1347 server_path.on_challenge_received(data_2);
1348 assert_eq!(server_path.received_challenges.len(), 2);
1349
1350 client_path_mgr.on_response_received(data).unwrap();
1352 assert_eq!(
1353 client_path_mgr
1354 .get(client_pid)
1355 .unwrap()
1356 .in_flight_challenges
1357 .len(),
1358 1
1359 );
1360
1361 client_path_mgr.on_response_received(data_2).unwrap();
1363 assert_eq!(
1364 client_path_mgr
1365 .get(client_pid)
1366 .unwrap()
1367 .in_flight_challenges
1368 .len(),
1369 0
1370 );
1371 }
1372
1373 #[test]
1374 fn too_many_probes() {
1375 let client_addr = "127.0.0.1:1234".parse().unwrap();
1376 let server_addr = "127.0.0.1:4321".parse().unwrap();
1377
1378 let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1380 let recovery_config = RecoveryConfig::from_config(&config);
1381
1382 let path = Path::new(
1383 client_addr,
1384 server_addr,
1385 &recovery_config,
1386 config.path_challenge_recv_max_queue_len,
1387 true,
1388 None,
1389 );
1390 let mut client_path_mgr = PathMap::new(path, 2, false);
1391 let mut server_path = Path::new(
1392 server_addr,
1393 client_addr,
1394 &recovery_config,
1395 config.path_challenge_recv_max_queue_len,
1396 false,
1397 None,
1398 );
1399
1400 let client_pid = client_path_mgr
1401 .path_id_from_addrs(&(client_addr, server_addr))
1402 .unwrap();
1403
1404 let data = rand::rand_u64().to_be_bytes();
1406
1407 client_path_mgr
1408 .get_mut(client_pid)
1409 .unwrap()
1410 .add_challenge_sent(data, MIN_CLIENT_INITIAL_LEN, Instant::now());
1411
1412 let data_2 = rand::rand_u64().to_be_bytes();
1414
1415 client_path_mgr
1416 .get_mut(client_pid)
1417 .unwrap()
1418 .add_challenge_sent(data_2, MIN_CLIENT_INITIAL_LEN, Instant::now());
1419 assert_eq!(
1420 client_path_mgr
1421 .get(client_pid)
1422 .unwrap()
1423 .in_flight_challenges
1424 .len(),
1425 2
1426 );
1427
1428 let data_3 = rand::rand_u64().to_be_bytes();
1430
1431 client_path_mgr
1432 .get_mut(client_pid)
1433 .unwrap()
1434 .add_challenge_sent(data_3, MIN_CLIENT_INITIAL_LEN, Instant::now());
1435 assert_eq!(
1436 client_path_mgr
1437 .get(client_pid)
1438 .unwrap()
1439 .in_flight_challenges
1440 .len(),
1441 3
1442 );
1443
1444 let data_4 = rand::rand_u64().to_be_bytes();
1446
1447 client_path_mgr
1448 .get_mut(client_pid)
1449 .unwrap()
1450 .add_challenge_sent(data_4, MIN_CLIENT_INITIAL_LEN, Instant::now());
1451 assert_eq!(
1452 client_path_mgr
1453 .get(client_pid)
1454 .unwrap()
1455 .in_flight_challenges
1456 .len(),
1457 4
1458 );
1459
1460 server_path.on_challenge_received(data);
1463 assert_eq!(server_path.received_challenges.len(), 1);
1464 server_path.on_challenge_received(data_2);
1465 assert_eq!(server_path.received_challenges.len(), 2);
1466 server_path.on_challenge_received(data_3);
1467 assert_eq!(server_path.received_challenges.len(), 3);
1468 server_path.on_challenge_received(data_4);
1469 assert_eq!(server_path.received_challenges.len(), 3);
1470
1471 client_path_mgr.on_response_received(data).unwrap();
1473 assert_eq!(
1474 client_path_mgr
1475 .get(client_pid)
1476 .unwrap()
1477 .in_flight_challenges
1478 .len(),
1479 3
1480 );
1481
1482 client_path_mgr.on_response_received(data_2).unwrap();
1484 assert_eq!(
1485 client_path_mgr
1486 .get(client_pid)
1487 .unwrap()
1488 .in_flight_challenges
1489 .len(),
1490 2
1491 );
1492
1493 client_path_mgr.on_response_received(data_3).unwrap();
1495 assert_eq!(
1496 client_path_mgr
1497 .get(client_pid)
1498 .unwrap()
1499 .in_flight_challenges
1500 .len(),
1501 1
1502 );
1503
1504 }
1506}