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::MIN_CLIENT_INITIAL_LEN;
1126
1127 use crate::recovery::RecoveryConfig;
1128 use crate::Config;
1129
1130 use super::*;
1131
1132 #[test]
1133 fn reinitializing_pmtud_notifies_fallback_limit() {
1134 let local = "127.0.0.1:1234".parse().unwrap();
1135 let peer = "127.0.0.1:4321".parse().unwrap();
1136 let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1137 config.discover_pmtu(true);
1138 config.set_max_send_udp_payload_size(1400);
1139 let recovery_config = RecoveryConfig::from_config(&config);
1140 let mut path = Path::new(
1141 local,
1142 peer,
1143 &recovery_config,
1144 config.path_challenge_recv_max_queue_len,
1145 true,
1146 Some(&config),
1147 );
1148 path.pmtud.as_mut().unwrap().successful_probe(1400);
1149 let mut paths = PathMap::new(path, 1, false);
1150
1151 paths.set_discover_pmtu_on_existing_paths(false, 1400, 1);
1152 assert_eq!(paths.pop_event(), None);
1153
1154 paths.set_discover_pmtu_on_existing_paths(true, 1400, 1);
1155 assert_eq!(
1156 paths.pop_event(),
1157 Some(PathEvent::PmtuUpdated {
1158 local,
1159 peer,
1160 pmtu: MIN_CLIENT_INITIAL_LEN,
1161 })
1162 );
1163 }
1164
1165 #[test]
1166 fn path_validation_limited_mtu() {
1167 let client_addr = "127.0.0.1:1234".parse().unwrap();
1168 let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
1169 let server_addr = "127.0.0.1:4321".parse().unwrap();
1170
1171 let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1172 let recovery_config = RecoveryConfig::from_config(&config);
1173
1174 let path = Path::new(
1175 client_addr,
1176 server_addr,
1177 &recovery_config,
1178 config.path_challenge_recv_max_queue_len,
1179 true,
1180 None,
1181 );
1182 let mut path_mgr = PathMap::new(path, 2, false);
1183
1184 let probed_path = Path::new(
1185 client_addr_2,
1186 server_addr,
1187 &recovery_config,
1188 config.path_challenge_recv_max_queue_len,
1189 false,
1190 None,
1191 );
1192 path_mgr.insert_path(probed_path, false).unwrap();
1193
1194 let pid = path_mgr
1195 .path_id_from_addrs(&(client_addr_2, server_addr))
1196 .unwrap();
1197 path_mgr.get_mut(pid).unwrap().request_validation();
1198 assert!(path_mgr.get_mut(pid).unwrap().validation_requested());
1199 assert!(path_mgr.get_mut(pid).unwrap().probing_required());
1200
1201 let data = rand::rand_u64().to_be_bytes();
1203 path_mgr.get_mut(pid).unwrap().add_challenge_sent(
1204 data,
1205 MIN_CLIENT_INITIAL_LEN - 1,
1206 Instant::now(),
1207 );
1208
1209 assert!(!path_mgr.get_mut(pid).unwrap().validation_requested());
1210 assert!(!path_mgr.get_mut(pid).unwrap().probing_required());
1211 assert!(path_mgr.get_mut(pid).unwrap().under_validation());
1212 assert!(!path_mgr.get_mut(pid).unwrap().validated());
1213 assert_eq!(path_mgr.get_mut(pid).unwrap().state, PathState::Validating);
1214 assert_eq!(path_mgr.pop_event(), None);
1215
1216 path_mgr.on_response_received(data).unwrap();
1219
1220 assert!(path_mgr.get_mut(pid).unwrap().validation_requested());
1221 assert!(path_mgr.get_mut(pid).unwrap().probing_required());
1222 assert!(path_mgr.get_mut(pid).unwrap().under_validation());
1223 assert!(!path_mgr.get_mut(pid).unwrap().validated());
1224 assert_eq!(
1225 path_mgr.get_mut(pid).unwrap().state,
1226 PathState::ValidatingMTU
1227 );
1228 assert_eq!(path_mgr.pop_event(), None);
1229
1230 let data = rand::rand_u64().to_be_bytes();
1233 path_mgr.get_mut(pid).unwrap().add_challenge_sent(
1234 data,
1235 MIN_CLIENT_INITIAL_LEN,
1236 Instant::now(),
1237 );
1238
1239 path_mgr.on_response_received(data).unwrap();
1240
1241 assert!(!path_mgr.get_mut(pid).unwrap().validation_requested());
1242 assert!(!path_mgr.get_mut(pid).unwrap().probing_required());
1243 assert!(!path_mgr.get_mut(pid).unwrap().under_validation());
1244 assert!(path_mgr.get_mut(pid).unwrap().validated());
1245 assert_eq!(path_mgr.get_mut(pid).unwrap().state, PathState::Validated);
1246 assert_eq!(
1247 path_mgr.pop_event(),
1248 Some(PathEvent::Validated(client_addr_2, server_addr))
1249 );
1250 }
1251
1252 #[test]
1253 fn multiple_probes() {
1254 let client_addr = "127.0.0.1:1234".parse().unwrap();
1255 let server_addr = "127.0.0.1:4321".parse().unwrap();
1256
1257 let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1258 let recovery_config = RecoveryConfig::from_config(&config);
1259
1260 let path = Path::new(
1261 client_addr,
1262 server_addr,
1263 &recovery_config,
1264 config.path_challenge_recv_max_queue_len,
1265 true,
1266 None,
1267 );
1268 let mut client_path_mgr = PathMap::new(path, 2, false);
1269 let mut server_path = Path::new(
1270 server_addr,
1271 client_addr,
1272 &recovery_config,
1273 config.path_challenge_recv_max_queue_len,
1274 false,
1275 None,
1276 );
1277
1278 let client_pid = client_path_mgr
1279 .path_id_from_addrs(&(client_addr, server_addr))
1280 .unwrap();
1281
1282 let data = rand::rand_u64().to_be_bytes();
1284
1285 client_path_mgr
1286 .get_mut(client_pid)
1287 .unwrap()
1288 .add_challenge_sent(data, MIN_CLIENT_INITIAL_LEN, Instant::now());
1289
1290 let data_2 = rand::rand_u64().to_be_bytes();
1292
1293 client_path_mgr
1294 .get_mut(client_pid)
1295 .unwrap()
1296 .add_challenge_sent(data_2, MIN_CLIENT_INITIAL_LEN, Instant::now());
1297 assert_eq!(
1298 client_path_mgr
1299 .get(client_pid)
1300 .unwrap()
1301 .in_flight_challenges
1302 .len(),
1303 2
1304 );
1305
1306 server_path.on_challenge_received(data);
1308 assert_eq!(server_path.received_challenges.len(), 1);
1309 server_path.on_challenge_received(data_2);
1310 assert_eq!(server_path.received_challenges.len(), 2);
1311
1312 client_path_mgr.on_response_received(data).unwrap();
1314 assert_eq!(
1315 client_path_mgr
1316 .get(client_pid)
1317 .unwrap()
1318 .in_flight_challenges
1319 .len(),
1320 1
1321 );
1322
1323 client_path_mgr.on_response_received(data_2).unwrap();
1325 assert_eq!(
1326 client_path_mgr
1327 .get(client_pid)
1328 .unwrap()
1329 .in_flight_challenges
1330 .len(),
1331 0
1332 );
1333 }
1334
1335 #[test]
1336 fn too_many_probes() {
1337 let client_addr = "127.0.0.1:1234".parse().unwrap();
1338 let server_addr = "127.0.0.1:4321".parse().unwrap();
1339
1340 let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1342 let recovery_config = RecoveryConfig::from_config(&config);
1343
1344 let path = Path::new(
1345 client_addr,
1346 server_addr,
1347 &recovery_config,
1348 config.path_challenge_recv_max_queue_len,
1349 true,
1350 None,
1351 );
1352 let mut client_path_mgr = PathMap::new(path, 2, false);
1353 let mut server_path = Path::new(
1354 server_addr,
1355 client_addr,
1356 &recovery_config,
1357 config.path_challenge_recv_max_queue_len,
1358 false,
1359 None,
1360 );
1361
1362 let client_pid = client_path_mgr
1363 .path_id_from_addrs(&(client_addr, server_addr))
1364 .unwrap();
1365
1366 let data = rand::rand_u64().to_be_bytes();
1368
1369 client_path_mgr
1370 .get_mut(client_pid)
1371 .unwrap()
1372 .add_challenge_sent(data, MIN_CLIENT_INITIAL_LEN, Instant::now());
1373
1374 let data_2 = rand::rand_u64().to_be_bytes();
1376
1377 client_path_mgr
1378 .get_mut(client_pid)
1379 .unwrap()
1380 .add_challenge_sent(data_2, MIN_CLIENT_INITIAL_LEN, Instant::now());
1381 assert_eq!(
1382 client_path_mgr
1383 .get(client_pid)
1384 .unwrap()
1385 .in_flight_challenges
1386 .len(),
1387 2
1388 );
1389
1390 let data_3 = rand::rand_u64().to_be_bytes();
1392
1393 client_path_mgr
1394 .get_mut(client_pid)
1395 .unwrap()
1396 .add_challenge_sent(data_3, MIN_CLIENT_INITIAL_LEN, Instant::now());
1397 assert_eq!(
1398 client_path_mgr
1399 .get(client_pid)
1400 .unwrap()
1401 .in_flight_challenges
1402 .len(),
1403 3
1404 );
1405
1406 let data_4 = rand::rand_u64().to_be_bytes();
1408
1409 client_path_mgr
1410 .get_mut(client_pid)
1411 .unwrap()
1412 .add_challenge_sent(data_4, MIN_CLIENT_INITIAL_LEN, Instant::now());
1413 assert_eq!(
1414 client_path_mgr
1415 .get(client_pid)
1416 .unwrap()
1417 .in_flight_challenges
1418 .len(),
1419 4
1420 );
1421
1422 server_path.on_challenge_received(data);
1425 assert_eq!(server_path.received_challenges.len(), 1);
1426 server_path.on_challenge_received(data_2);
1427 assert_eq!(server_path.received_challenges.len(), 2);
1428 server_path.on_challenge_received(data_3);
1429 assert_eq!(server_path.received_challenges.len(), 3);
1430 server_path.on_challenge_received(data_4);
1431 assert_eq!(server_path.received_challenges.len(), 3);
1432
1433 client_path_mgr.on_response_received(data).unwrap();
1435 assert_eq!(
1436 client_path_mgr
1437 .get(client_pid)
1438 .unwrap()
1439 .in_flight_challenges
1440 .len(),
1441 3
1442 );
1443
1444 client_path_mgr.on_response_received(data_2).unwrap();
1446 assert_eq!(
1447 client_path_mgr
1448 .get(client_pid)
1449 .unwrap()
1450 .in_flight_challenges
1451 .len(),
1452 2
1453 );
1454
1455 client_path_mgr.on_response_received(data_3).unwrap();
1457 assert_eq!(
1458 client_path_mgr
1459 .get(client_pid)
1460 .unwrap()
1461 .in_flight_challenges
1462 .len(),
1463 1
1464 );
1465
1466 }
1468}