1use crate::{
7 connection,
8 contexts::WriteContext,
9 endpoint,
10 endpoint::Type,
11 recovery::{congestion_controller, CongestionController, RttEstimator},
12 transmission::{self, Mode},
13};
14use s2n_quic_core::{
15 counter::{Counter, Saturating},
16 event::{self, builder::DatagramDropReason, IntoEvent},
17 frame,
18 inet::DatagramInfo,
19 packet, random,
20 time::{timer, Timestamp},
21};
22
23mod challenge;
24mod manager;
25
26pub use challenge::Challenge;
27pub use manager::*;
28
29pub use s2n_quic_core::path::*;
31
32#[derive(Debug, Clone, Copy, PartialEq)]
33enum State {
34 Validated,
36
37 AmplificationLimited {
39 tx_allowance: Counter<u32, Saturating>,
40 },
41}
42
43#[derive(Debug)]
44pub struct Path<Config: endpoint::Config> {
45 pub handle: Config::PathHandle,
47 pub peer_connection_id: connection::PeerId,
49 pub local_connection_id: connection::LocalId,
51 pub rtt_estimator: RttEstimator,
53 pub congestion_controller: <Config::CongestionControllerEndpoint as congestion_controller::Endpoint>::CongestionController,
55 pub pto_backoff: u32,
57 state: State,
59 pub mtu_controller: mtu::Controller,
61 pub ecn_controller: ecn::Controller,
63
64 peer_validated: bool,
66
67 challenge: Challenge,
69
70 response_data: Option<challenge::Data>,
72
73 activated: bool,
80
81 is_active: bool,
83 anti_amplification_multiplier: u8,
84 pto_jitter_percentage: u8,
86}
87
88impl<Config: endpoint::Config> Clone for Path<Config> {
89 fn clone(&self) -> Self {
90 Self {
91 handle: self.handle,
92 peer_connection_id: self.peer_connection_id,
93 local_connection_id: self.local_connection_id,
94 rtt_estimator: self.rtt_estimator,
95 congestion_controller: self.congestion_controller.clone(),
96 pto_backoff: self.pto_backoff,
97 state: self.state,
98 mtu_controller: self.mtu_controller.clone(),
99 ecn_controller: self.ecn_controller.clone(),
100 peer_validated: self.peer_validated,
101 challenge: self.challenge.clone(),
102 response_data: self.response_data,
103 activated: self.activated,
104 is_active: self.is_active,
105 anti_amplification_multiplier: self.anti_amplification_multiplier,
106 pto_jitter_percentage: self.pto_jitter_percentage,
107 }
108 }
109}
110
111impl<Config: endpoint::Config> Path<Config> {
114 pub fn new(
115 handle: Config::PathHandle,
116 peer_connection_id: connection::PeerId,
117 local_connection_id: connection::LocalId,
118 rtt_estimator: RttEstimator,
119 congestion_controller: <Config::CongestionControllerEndpoint as congestion_controller::Endpoint>::CongestionController,
120 peer_validated: bool,
121 mtu_config: mtu::Config,
122 anti_amplification_multiplier: u8,
123 pto_jitter_percentage: u8,
124 ) -> Path<Config> {
125 let state = match Config::ENDPOINT_TYPE {
126 Type::Server => {
127 State::AmplificationLimited {
132 tx_allowance: Default::default(),
133 }
134 }
135 Type::Client => State::Validated,
138 };
139 let peer_socket_address = handle.remote_address();
140 Path {
141 handle,
142 peer_connection_id,
143 local_connection_id,
144 rtt_estimator,
145 congestion_controller,
146 pto_backoff: INITIAL_PTO_BACKOFF,
147 state,
148 mtu_controller: mtu::Controller::new(mtu_config, &peer_socket_address),
149 ecn_controller: ecn::Controller::default(),
150 peer_validated,
151 challenge: Challenge::disabled(),
152 response_data: None,
153 activated: false,
154 is_active: false,
155 anti_amplification_multiplier,
156 pto_jitter_percentage,
157 }
158 }
159
160 #[inline]
161 pub fn remote_address(&self) -> RemoteAddress {
162 self.handle.remote_address()
163 }
164
165 #[inline]
166 pub fn local_address(&self) -> LocalAddress {
167 self.handle.local_address()
168 }
169
170 #[inline]
171 pub fn set_challenge(&mut self, challenge: Challenge) {
172 self.challenge = challenge;
173 }
174
175 #[inline]
176 pub fn abandon_challenge<Pub: event::ConnectionPublisher>(
177 &mut self,
178 publisher: &mut Pub,
179 path_id: u64,
180 ) {
181 self.challenge
182 .abandon(publisher, path_event!(self, path_id));
183 }
184
185 #[inline]
186 pub fn is_active(&self) -> bool {
187 self.is_active
188 }
189
190 #[inline]
192 pub fn on_bytes_transmitted(&mut self, bytes: usize) {
193 if bytes == 0 {
194 return;
195 }
196
197 debug_assert_ne!(
198 self.clamp_datagram_size(bytes, transmission::Mode::Normal),
199 0,
200 "path should not transmit when amplification limited; tried to transmit {bytes}"
201 );
202
203 if let State::AmplificationLimited { tx_allowance, .. } = &mut self.state {
204 *tx_allowance -= bytes as u32
205 }
206 }
207
208 #[inline]
212 pub fn on_bytes_received(&mut self, bytes: usize) -> AmplificationOutcome {
213 let was_at_amplification_limit = self.at_amplification_limit();
214
215 if let State::AmplificationLimited { tx_allowance } = &mut self.state {
226 *tx_allowance +=
227 bytes.saturating_mul(self.anti_amplification_multiplier as usize) as u32;
228 }
229
230 let unblocked = was_at_amplification_limit && !self.at_amplification_limit();
231
232 match (unblocked, self.is_active()) {
233 (true, true) => AmplificationOutcome::ActivePathUnblocked,
234 (true, false) => AmplificationOutcome::InactivePathUnblocked,
235 _ => AmplificationOutcome::Unchanged,
236 }
237 }
238
239 #[inline]
240 pub fn on_datagram_received(
241 &mut self,
242 path_handle: &Config::PathHandle,
243 datagram: &DatagramInfo,
244 valid_initial_received: bool,
245 ) -> Result<AmplificationOutcome, DatagramDropReason> {
246 let source_cid_changed = datagram
247 .source_connection_id
248 .is_some_and(|scid| scid != self.peer_connection_id && valid_initial_received);
249
250 if source_cid_changed {
251 return Err(DatagramDropReason::InvalidSourceConnectionId);
263 }
264
265 if Config::ENDPOINT_TYPE.is_client() {
279 self.handle.maybe_update(path_handle);
280 }
281
282 let amplification_outcome = self.on_bytes_received(datagram.payload_len);
283
284 Ok(amplification_outcome)
285 }
286
287 #[inline]
288 pub fn on_timeout<Pub: event::ConnectionPublisher>(
289 &mut self,
290 timestamp: Timestamp,
291 path_id: Id,
292 random_generator: &mut dyn random::Generator,
293 publisher: &mut Pub,
294 ) {
295 self.challenge
296 .on_timeout(timestamp, publisher, path_event!(self, path_id));
297 self.mtu_controller.on_timeout(timestamp);
298 self.ecn_controller.on_timeout(
299 timestamp,
300 path_event!(self, path_id),
301 random_generator,
302 self.rtt_estimator.smoothed_rtt(),
303 publisher,
304 );
305 }
306
307 #[inline]
309 pub fn can_transmit(&self, timestamp: Timestamp) -> bool {
310 !self.at_amplification_limit()
311 && self
312 .congestion_controller
313 .earliest_departure_time()
314 .is_none_or(|edt| edt.has_elapsed(timestamp))
315 }
316
317 #[inline]
319 pub fn on_transmit<W: WriteContext>(&mut self, context: &mut W) {
320 if let Some(response_data) = &mut self.response_data {
324 let frame = frame::PathResponse {
325 data: response_data,
326 };
327 if context.write_frame(&frame).is_some() {
328 self.response_data = None;
332 }
333 }
334
335 self.challenge.on_transmit(context)
336 }
337
338 #[inline]
340 pub fn failed_validation(&self) -> bool {
341 !self.challenge.is_disabled() && !self.is_validated() && !self.is_challenge_pending()
345 }
346
347 #[inline]
348 pub fn is_challenge_pending(&self) -> bool {
349 self.challenge.is_pending()
350 }
351
352 #[inline]
353 pub fn is_response_pending(&self) -> bool {
354 self.response_data.is_some()
355 }
356
357 #[inline]
358 pub fn on_path_challenge(&mut self, response: &challenge::Data) {
359 self.response_data = Some(*response);
364 }
365
366 #[inline]
369 pub fn on_path_response(&mut self, response: &[u8]) -> bool {
370 if self.challenge.on_validated(response) {
371 self.on_validated();
372
373 return true;
374
375 }
380
381 false
382 }
383
384 #[inline]
388 pub fn on_handshake_packet(&mut self) {
389 self.on_validated();
390 }
391
392 pub fn on_process_local_connection_id<Pub: event::ConnectionPublisher>(
397 &mut self,
398 path_id: Id,
399 packet: &packet::short::CleartextShort<'_>,
400 local_connection_id: &connection::LocalId,
401 publisher: &mut Pub,
402 ) {
403 debug_assert_eq!(
404 packet.destination_connection_id(),
405 local_connection_id.as_ref()
406 );
407
408 if &self.local_connection_id != local_connection_id {
409 publisher.on_connection_id_updated(event::builder::ConnectionIdUpdated {
410 path_id: path_id.into_event(),
411 cid_consumer: endpoint::Location::Remote,
412 previous: self.local_connection_id.into_event(),
413 current: local_connection_id.into_event(),
414 });
415 self.local_connection_id = *local_connection_id;
416 }
417 }
418
419 #[inline]
421 fn on_validated(&mut self) {
422 self.state = State::Validated;
423
424 if self.is_peer_validated() {
425 self.on_fully_validated();
426 }
427 }
428
429 #[inline]
431 pub fn is_validated(&self) -> bool {
432 self.state == State::Validated
433 }
434
435 #[inline]
437 pub fn on_activated(&mut self) {
438 self.activated = true;
439 }
440
441 #[inline]
443 pub fn is_activated(&self) -> bool {
444 self.activated
445 }
446
447 #[inline]
449 pub fn on_peer_validated(&mut self) {
450 self.peer_validated = true;
451
452 if self.is_validated() {
453 self.on_fully_validated();
454 }
455 }
456
457 #[inline]
459 pub fn is_peer_validated(&self) -> bool {
460 self.peer_validated
461 }
462
463 fn on_fully_validated(&mut self) {
465 self.mtu_controller.enable()
467 }
468
469 #[inline]
470 pub fn max_datagram_size(&self, transmission_mode: transmission::Mode) -> usize {
471 match transmission_mode {
472 Mode::LossRecoveryProbing | Mode::PathValidationOnly => {
478 MINIMUM_MAX_DATAGRAM_SIZE as usize
479 }
480 Mode::MtuProbing => self.mtu_controller.probed_sized(),
482 Mode::Normal => self.mtu_controller.max_datagram_size(),
484 }
485 }
486
487 #[inline]
508 pub fn clamp_datagram_size(
509 &self,
510 requested_size: usize,
511 transmission_mode: transmission::Mode,
512 ) -> usize {
513 debug_assert!(
514 !self.at_amplification_limit(),
515 "amplification limits should be checked before clamping datagram size values"
516 );
517
518 requested_size.min(self.max_datagram_size(transmission_mode))
519 }
520
521 #[inline]
522 pub fn transmission_constraint(&self) -> transmission::Constraint {
523 if self.at_amplification_limit() {
524 transmission::Constraint::AmplificationLimited
529 } else if self.congestion_controller.is_congestion_limited() {
530 if self.congestion_controller.requires_fast_retransmission() {
531 transmission::Constraint::RetransmissionOnly
537 } else {
538 transmission::Constraint::CongestionLimited
544 }
545 } else {
546 transmission::Constraint::None
547 }
548 }
549
550 #[inline]
556 pub fn at_amplification_limit(&self) -> bool {
557 match self.state {
562 State::Validated => false,
563 State::AmplificationLimited { tx_allowance } => tx_allowance == 0,
564 }
565 }
566
567 #[inline]
569 pub fn pto_period(
570 &self,
571 space: s2n_quic_core::packet::number::PacketNumberSpace,
572 ) -> core::time::Duration {
573 self.rtt_estimator.pto_period(self.pto_backoff, space)
574 }
575
576 #[inline]
578 pub fn pto_period_with_jitter(
579 &self,
580 space: s2n_quic_core::packet::number::PacketNumberSpace,
581 random_generator: &mut dyn random::Generator,
582 ) -> core::time::Duration {
583 self.rtt_estimator.pto_period_with_jitter(
584 self.pto_backoff,
585 space,
586 self.pto_jitter_percentage,
587 random_generator,
588 )
589 }
590
591 #[inline]
593 pub fn reset_pto_backoff(&mut self) {
594 self.pto_backoff = INITIAL_PTO_BACKOFF;
595 }
596
597 #[inline]
600 pub fn is_congestion_limited(&self, bytes_sent: usize) -> bool {
601 let cwnd = self.congestion_controller.congestion_window();
602 let bytes_in_flight = self
603 .congestion_controller
604 .bytes_in_flight()
605 .saturating_add(bytes_sent as u32);
606 let max_datagram_size = self.max_datagram_size(transmission::Mode::Normal) as u32;
607
608 cwnd.saturating_sub(bytes_in_flight) < max_datagram_size
609 }
610
611 #[inline]
618 fn eq_by_handle(&self, handle: &Config::PathHandle) -> bool {
619 self.handle
620 .remote_address()
621 .unmapped_eq(&handle.remote_address())
622 }
623}
624
625impl<Config: endpoint::Config> timer::Provider for Path<Config> {
626 #[inline]
627 fn timers<Q: timer::Query>(&self, query: &mut Q) -> timer::Result {
628 self.challenge.timers(query)?;
629 self.mtu_controller.timers(query)?;
630 self.ecn_controller.timers(query)?;
631
632 Ok(())
633 }
634}
635
636impl<Config: endpoint::Config> transmission::interest::Provider for Path<Config> {
637 #[inline]
640 fn transmission_interest<Q: transmission::interest::Query>(
641 &self,
642 query: &mut Q,
643 ) -> transmission::interest::Result {
644 if self.response_data.is_some() {
649 query.on_new_data()?;
650 }
651
652 self.challenge.transmission_interest(query)?;
653
654 Ok(())
655 }
656}
657
658#[cfg(any(test, feature = "testing"))]
659pub mod testing {
660 use crate::{endpoint, path::Path};
661 use core::time::Duration;
662 use s2n_quic_core::{
663 connection, connection::limits::ANTI_AMPLIFICATION_MULTIPLIER, path::mtu,
664 recovery::RttEstimator,
665 };
666
667 pub fn helper_path_server() -> Path<endpoint::testing::Server> {
668 Path::new(
669 Default::default(),
670 connection::PeerId::try_from_bytes(&[]).unwrap(),
671 connection::LocalId::TEST_ID,
672 RttEstimator::new(Duration::from_millis(30)),
673 Default::default(),
674 true,
675 mtu::Config::default(),
676 ANTI_AMPLIFICATION_MULTIPLIER,
677 0, )
679 }
680
681 pub fn helper_path_client() -> Path<endpoint::testing::Client> {
682 Path::new(
683 Default::default(),
684 connection::PeerId::try_from_bytes(&[]).unwrap(),
685 connection::LocalId::TEST_ID,
686 RttEstimator::new(Duration::from_millis(30)),
687 Default::default(),
688 false,
689 mtu::Config::default(),
690 ANTI_AMPLIFICATION_MULTIPLIER,
691 0, )
693 }
694}
695
696#[cfg(test)]
697mod tests {
698 use super::*;
699 use crate::{
700 contexts::testing::{MockWriteContext, OutgoingFrameBuffer},
701 endpoint::testing::Server as Config,
702 path,
703 path::{challenge::testing::helper_challenge, testing, testing::helper_path_client},
704 };
705 use core::time::Duration;
706 use s2n_quic_core::{
707 connection,
708 connection::limits::ANTI_AMPLIFICATION_MULTIPLIER,
709 endpoint,
710 event::testing::Publisher,
711 path::MINIMUM_MAX_DATAGRAM_SIZE,
712 recovery::{CongestionController, RttEstimator},
713 time::{Clock, NoopClock},
714 transmission,
715 };
716
717 type Path = super::Path<Config>;
718
719 #[test]
720 fn custom_anti_amplification_multiplier() {
721 let bytes_received = 100;
722
723 let mut default_path = testing::helper_path_server();
725 let _ = default_path.on_bytes_received(bytes_received);
726 let allowance = match default_path.state {
727 path::State::AmplificationLimited { tx_allowance } => tx_allowance,
728 _ => unreachable!("path is amplification limited"),
729 };
730 let expected = ANTI_AMPLIFICATION_MULTIPLIER as u32 * bytes_received as u32;
731 assert_eq!(allowance, Counter::new(expected));
732
733 let mut custom_path = testing::helper_path_server();
735 custom_path.anti_amplification_multiplier = ANTI_AMPLIFICATION_MULTIPLIER + 10;
736 let _ = custom_path.on_bytes_received(bytes_received);
737 let allowance = match custom_path.state {
738 path::State::AmplificationLimited { tx_allowance } => tx_allowance,
739 _ => unreachable!("path is amplification limited"),
740 };
741 let expected = (ANTI_AMPLIFICATION_MULTIPLIER + 10) as u32 * bytes_received as u32;
742 assert_eq!(allowance, Counter::new(expected));
743 }
744
745 #[test]
746 fn response_data_should_only_be_sent_once() {
747 let mut path = testing::helper_path_server();
749 let now = NoopClock {}.get_time();
750
751 let mut frame_buffer = OutgoingFrameBuffer::new();
752 let mut context = MockWriteContext::new(
753 now,
754 &mut frame_buffer,
755 transmission::Constraint::None,
756 transmission::Mode::Normal,
757 endpoint::Type::Client,
758 );
759
760 let expected_data: [u8; 8] = [0; 8];
762 path.on_path_challenge(&expected_data);
763 assert_eq!(path.response_data.unwrap(), expected_data);
764
765 path.on_transmit(&mut context); assert!(path.response_data.is_none());
774
775 assert_eq!(context.frame_buffer.len(), 1);
776 let written_data = match context.frame_buffer.pop_front().unwrap().as_frame() {
777 frame::Frame::PathResponse(frame) => Some(*frame.data),
778 _ => None,
779 };
780 assert_eq!(written_data.unwrap(), expected_data);
781 }
782
783 #[test]
784 fn on_timeout_should_set_challenge_to_none_on_challenge_abandonment() {
785 let mut publisher = Publisher::snapshot();
787 let mut path = testing::helper_path_server();
788 let helper_challenge = helper_challenge();
789 let expiration_time = helper_challenge.now + helper_challenge.abandon_duration;
790 path.set_challenge(helper_challenge.challenge);
791
792 let mut frame_buffer = OutgoingFrameBuffer::new();
793 let mut context = MockWriteContext::new(
794 helper_challenge.now,
795 &mut frame_buffer,
796 transmission::Constraint::None,
797 transmission::Mode::Normal,
798 endpoint::Type::Client,
799 );
800 path.on_transmit(&mut context); assert!(path.is_challenge_pending());
804 assert!(path.challenge.is_pending());
805
806 path.on_timeout(
808 expiration_time + Duration::from_millis(10),
809 path::Id::test_id(),
810 &mut random::testing::Generator(123),
811 &mut publisher,
812 );
813
814 assert!(!path.is_challenge_pending());
816 assert!(!path.challenge.is_pending());
817 }
818
819 #[test]
820 fn is_challenge_pending_should_return_false_if_challenge_is_not_set() {
821 let mut path = testing::helper_path_server();
823 let helper_challenge = helper_challenge();
824
825 assert!(!path.is_challenge_pending());
827 assert!(!path.challenge.is_pending());
828
829 path.set_challenge(helper_challenge.challenge);
831
832 assert!(path.is_challenge_pending());
834 assert!(path.challenge.is_pending());
835 }
836
837 #[test]
838 fn first_path_in_disabled_state_cant_fail_validation() {
839 let path = testing::helper_path_server();
841
842 assert!(path.challenge.is_disabled());
844 assert!(!path.is_challenge_pending());
845 assert!(!path.is_validated());
846
847 assert!(!path.failed_validation());
848 }
849
850 #[test]
851 fn failed_validation() {
852 let mut publisher = Publisher::snapshot();
854 let mut path = testing::helper_path_server();
855 let helper_challenge = helper_challenge();
856
857 path.set_challenge(helper_challenge.challenge);
858 let mut frame_buffer = OutgoingFrameBuffer::new();
859 let mut context = MockWriteContext::new(
860 helper_challenge.now,
861 &mut frame_buffer,
862 transmission::Constraint::None,
863 transmission::Mode::Normal,
864 endpoint::Type::Client,
865 );
866 path.on_transmit(&mut context); let expiration_time = helper_challenge.now + helper_challenge.abandon_duration;
869
870 path.on_timeout(
872 expiration_time + Duration::from_millis(10),
873 path::Id::test_id(),
874 &mut random::testing::Generator(123),
875 &mut publisher,
876 );
877
878 assert!(!path.challenge.is_disabled());
880 assert!(!path.is_challenge_pending());
881 assert!(!path.is_validated());
882
883 assert!(path.failed_validation());
884 }
885
886 #[test]
887 fn abandon_challenge() {
888 let mut path = testing::helper_path_server();
890 let helper_challenge = helper_challenge();
891 path.set_challenge(helper_challenge.challenge);
892 let mut publisher = event::testing::Publisher::snapshot();
893
894 path.abandon_challenge(&mut publisher, 0);
896
897 assert!(!path.challenge.is_pending());
899 }
900
901 #[test]
902 fn on_path_challenge_should_set_response_data() {
903 let mut path = testing::helper_path_server();
905
906 assert!(path.response_data.is_none());
908
909 let expected_data: [u8; 8] = [0; 8];
911 path.on_path_challenge(&expected_data);
912
913 assert!(path.response_data.is_some());
915 }
916
917 #[test]
923 fn on_path_challenge_should_replace_response_data() {
924 let mut path = testing::helper_path_server();
926 let expected_data: [u8; 8] = [0; 8];
927
928 path.on_path_challenge(&expected_data);
930
931 assert_eq!(path.response_data.unwrap(), expected_data);
933
934 let new_expected_data: [u8; 8] = [1; 8];
936 path.on_path_challenge(&new_expected_data);
937
938 assert_ne!(expected_data, new_expected_data);
940 assert_eq!(path.response_data.unwrap(), new_expected_data);
941 }
942
943 #[test]
944 fn validate_path_response_should_only_validate_if_challenge_is_set() {
945 let mut path = testing::helper_path_server();
947 let helper_challenge = helper_challenge();
948
949 assert!(!path.is_validated());
951
952 path.set_challenge(helper_challenge.challenge);
954 assert!(path.on_path_response(&helper_challenge.expected_data));
955
956 assert!(path.is_validated());
958 }
959
960 #[test]
961 fn on_validated_should_change_state_to_validated_and_clear_challenge() {
962 let mut path = testing::helper_path_server();
964 let helper_challenge = helper_challenge();
965 path.set_challenge(helper_challenge.challenge);
966
967 assert!(!path.is_validated());
968 assert!(path.challenge.is_pending());
969
970 path.on_validated();
972
973 assert!(path.is_validated());
975 assert!(path.challenge.is_pending());
976 }
977
978 #[test]
979 fn on_validated_when_already_validated_does_nothing() {
980 let mut path = testing::helper_path_server();
982 path.set_challenge(helper_challenge().challenge);
983 path.on_validated();
984
985 path.on_validated();
987
988 assert!(path.is_validated());
990 assert!(path.challenge.is_pending());
991 }
992
993 #[test]
994 fn amplification_limit_test() {
995 let mut path = testing::helper_path_server();
1015
1016 let mut amplification_outcome = path.on_bytes_received(1200);
1019 assert!(amplification_outcome.is_inactivate_path_unblocked());
1020 path.on_bytes_transmitted((1200 * 2) + 1);
1021
1022 assert!(!path.at_amplification_limit());
1024 assert_eq!(
1025 path.transmission_constraint(),
1026 transmission::Constraint::None
1027 );
1028
1029 amplification_outcome = path.on_bytes_received(1200);
1030 assert!(!path.at_amplification_limit());
1031 assert_eq!(
1032 path.transmission_constraint(),
1033 transmission::Constraint::None
1034 );
1035 assert!(amplification_outcome.is_unchanged());
1038
1039 path.on_bytes_transmitted((1200 * 6) + 1);
1040 assert!(path.at_amplification_limit());
1041 assert_eq!(
1042 path.transmission_constraint(),
1043 transmission::Constraint::AmplificationLimited
1044 );
1045 amplification_outcome = path.on_bytes_received(1200);
1046 assert!(amplification_outcome.is_inactivate_path_unblocked());
1047
1048 path.on_validated();
1049 path.on_bytes_transmitted(24);
1050 assert!(!path.at_amplification_limit());
1052 assert_eq!(
1053 path.transmission_constraint(),
1054 transmission::Constraint::None
1055 );
1056
1057 amplification_outcome = path.on_bytes_received(1200);
1060 assert!(amplification_outcome.is_unchanged());
1061
1062 let path = helper_path_client();
1064 assert!(path.is_validated());
1065 }
1066
1067 #[test]
1068 fn amplification_limited_mtu_test() {
1069 for &transmission_mode in &[
1096 Mode::Normal,
1097 Mode::PathValidationOnly,
1098 Mode::MtuProbing,
1099 Mode::LossRecoveryProbing,
1100 ] {
1101 let mut path = testing::helper_path_server();
1102 let max_datagram_size = path.max_datagram_size(transmission_mode);
1104
1105 let amplification_outcome = path.on_bytes_received(3);
1106 path.on_bytes_transmitted(8);
1107
1108 assert!(amplification_outcome.is_inactivate_path_unblocked());
1109 assert_eq!(path.clamp_datagram_size(1, transmission_mode), 1);
1110 assert_eq!(path.clamp_datagram_size(10, transmission_mode), 10);
1111 assert_eq!(
1112 path.clamp_datagram_size(1800, transmission_mode),
1113 max_datagram_size
1114 );
1115
1116 path.on_bytes_transmitted(1);
1117 assert!(path.at_amplification_limit());
1119
1120 let amplification_outcome = path.on_bytes_received(1);
1121 assert!(amplification_outcome.is_inactivate_path_unblocked());
1123 assert_eq!(path.clamp_datagram_size(1, transmission_mode), 1);
1124 assert_eq!(path.clamp_datagram_size(10, transmission_mode), 10);
1125 assert_eq!(
1126 path.clamp_datagram_size(1800, transmission_mode),
1127 max_datagram_size
1128 );
1129
1130 path.on_validated();
1131 assert_eq!(path.clamp_datagram_size(4, transmission_mode), 4);
1133 }
1134 }
1135
1136 #[test]
1137 fn clamp_mtu_for_validated_path() {
1138 let mut path = testing::helper_path_server();
1139 path.on_validated();
1140 let mtu = 1472;
1141 let probed_size = 1500;
1142 path.mtu_controller = mtu::testing::test_controller(mtu, probed_size);
1143
1144 assert_eq!(
1145 path.mtu_controller.max_datagram_size(),
1146 path.clamp_datagram_size(10000, transmission::Mode::Normal)
1147 );
1148 assert_eq!(
1149 MINIMUM_MAX_DATAGRAM_SIZE as usize,
1150 path.clamp_datagram_size(10000, transmission::Mode::PathValidationOnly)
1151 );
1152 assert_eq!(
1153 MINIMUM_MAX_DATAGRAM_SIZE as usize,
1154 path.clamp_datagram_size(10000, transmission::Mode::LossRecoveryProbing)
1155 );
1156 assert_eq!(
1157 path.mtu_controller.probed_sized(),
1158 path.clamp_datagram_size(10000, transmission::Mode::MtuProbing)
1159 );
1160 }
1161
1162 #[test]
1163 fn path_mtu() {
1164 let mut path = testing::helper_path_server();
1165 let amplification_outcome = path.on_bytes_received(1);
1166 assert!(amplification_outcome.is_inactivate_path_unblocked());
1167
1168 let mtu = 1472;
1169 let probed_size = 1500;
1170 path.mtu_controller = mtu::testing::test_controller(mtu, probed_size);
1171
1172 assert_eq!(
1173 path.mtu_controller.max_datagram_size(),
1174 path.max_datagram_size(transmission::Mode::Normal)
1175 );
1176 assert_eq!(
1177 MINIMUM_MAX_DATAGRAM_SIZE as usize,
1178 path.max_datagram_size(transmission::Mode::PathValidationOnly)
1179 );
1180 assert_eq!(
1181 MINIMUM_MAX_DATAGRAM_SIZE as usize,
1182 path.max_datagram_size(transmission::Mode::LossRecoveryProbing)
1183 );
1184 assert_eq!(
1185 path.mtu_controller.probed_sized(),
1186 path.max_datagram_size(transmission::Mode::MtuProbing)
1187 );
1188 }
1189
1190 #[test]
1191 #[should_panic]
1192 fn clamp_mtu_when_tx_more_than_rx() {
1193 let mut path = testing::helper_path_server();
1194 let mtu = 1472;
1195 let probed_size = 1500;
1196 path.mtu_controller = mtu::testing::test_controller(mtu, probed_size);
1197
1198 assert_eq!(
1199 0,
1200 path.clamp_datagram_size(10000, transmission::Mode::Normal)
1201 );
1202 }
1203
1204 #[test]
1205 fn peer_validated_test() {
1206 let mut path = testing::helper_path_client();
1207
1208 assert!(!path.is_peer_validated());
1209
1210 path.on_peer_validated();
1211
1212 assert!(path.is_peer_validated());
1213 }
1214
1215 #[test]
1216 fn transmission_constraint_test() {
1217 let mut path = Path::new(
1218 Default::default(),
1219 connection::PeerId::try_from_bytes(&[]).unwrap(),
1220 connection::LocalId::TEST_ID,
1221 RttEstimator::new(Duration::from_millis(30)),
1222 Default::default(),
1223 false,
1224 mtu::Config::default(),
1225 ANTI_AMPLIFICATION_MULTIPLIER,
1226 0, );
1228 let now = NoopClock.get_time();
1229 let random = &mut random::testing::Generator::default();
1230 let mut publisher = event::testing::Publisher::snapshot();
1231 let mut publisher =
1232 congestion_controller::PathPublisher::new(&mut publisher, path::Id::test_id());
1233 path.on_validated();
1234
1235 assert_eq!(
1236 path.transmission_constraint(),
1237 transmission::Constraint::None
1238 );
1239
1240 let packet_info = path.congestion_controller.on_packet_sent(
1242 now,
1243 path.congestion_controller.congestion_window() as usize,
1244 None,
1245 &path.rtt_estimator,
1246 &mut publisher,
1247 );
1248
1249 assert_eq!(
1250 path.transmission_constraint(),
1251 transmission::Constraint::CongestionLimited
1252 );
1253
1254 path.congestion_controller.on_packet_lost(
1256 1,
1257 packet_info,
1258 false,
1259 false,
1260 random,
1261 now,
1262 &mut publisher,
1263 );
1264 path.congestion_controller.requires_fast_retransmission = true;
1265
1266 assert_eq!(
1267 path.transmission_constraint(),
1268 transmission::Constraint::RetransmissionOnly
1269 );
1270
1271 path.congestion_controller.on_packet_lost(
1273 path.congestion_controller.congestion_window(),
1274 packet_info,
1275 false,
1276 false,
1277 random,
1278 now,
1279 &mut publisher,
1280 );
1281 path.congestion_controller.requires_fast_retransmission = false;
1282
1283 assert_eq!(
1285 path.transmission_constraint(),
1286 transmission::Constraint::None
1287 );
1288 }
1289
1290 #[test]
1291 fn is_congestion_limited() {
1292 let mut path = testing::helper_path_client();
1293 let max_datagram_size = path.mtu_controller.max_datagram_size() as u32;
1294
1295 path.congestion_controller.congestion_window = 12000;
1296 path.congestion_controller.bytes_in_flight = 12000 - 500 - max_datagram_size;
1297
1298 assert!(!path.is_congestion_limited(500));
1300
1301 assert!(path.is_congestion_limited(501));
1303 }
1304
1305 #[test]
1306 fn pto_period_with_jitter_configuration() {
1307 use s2n_quic_core::{
1308 connection::limits::ANTI_AMPLIFICATION_MULTIPLIER, packet::number::PacketNumberSpace,
1309 };
1310
1311 let path_no_jitter = Path::new(
1313 Default::default(),
1314 connection::PeerId::try_from_bytes(&[]).unwrap(),
1315 connection::LocalId::TEST_ID,
1316 RttEstimator::new(Duration::from_millis(100)),
1317 Default::default(),
1318 false,
1319 mtu::Config::default(),
1320 ANTI_AMPLIFICATION_MULTIPLIER,
1321 0, );
1323
1324 let mut rng = random::testing::Generator::default();
1325 let pto_no_jitter = path_no_jitter.pto_period(PacketNumberSpace::ApplicationData);
1326 let pto_no_jitter_with_method =
1327 path_no_jitter.pto_period_with_jitter(PacketNumberSpace::ApplicationData, &mut rng);
1328
1329 let path_with_jitter = Path::new(
1331 Default::default(),
1332 connection::PeerId::try_from_bytes(&[]).unwrap(),
1333 connection::LocalId::TEST_ID,
1334 RttEstimator::new(Duration::from_millis(100)),
1335 Default::default(),
1336 false,
1337 mtu::Config::default(),
1338 ANTI_AMPLIFICATION_MULTIPLIER,
1339 25, );
1341
1342 let pto_with_jitter =
1343 path_with_jitter.pto_period_with_jitter(PacketNumberSpace::ApplicationData, &mut rng);
1344
1345 assert_eq!(pto_no_jitter, pto_no_jitter_with_method);
1347
1348 assert_eq!(path_no_jitter.pto_jitter_percentage, 0);
1350 assert_eq!(path_with_jitter.pto_jitter_percentage, 25);
1351
1352 assert!(pto_no_jitter > Duration::ZERO);
1354 assert!(pto_with_jitter > Duration::ZERO);
1355 }
1356}