1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use crate::{packet::number::PacketNumber, time::Timestamp};
use core::time::Duration;
//= https://www.rfc-editor.org/rfc/rfc9002#section-6.1.1
//# The RECOMMENDED initial value for the packet reordering threshold
//# (kPacketThreshold) is 3, based on best practices for TCP loss
//# detection [RFC5681] [RFC6675]. In order to remain similar to TCP,
//# implementations SHOULD NOT use a packet threshold less than 3; see
//# [RFC5681].
pub const K_PACKET_THRESHOLD: u64 = 3;
#[derive(Debug, PartialEq, Eq)]
pub enum Outcome {
/// The packet is not lost yet, but will be considered lost at the
/// given `lost_time` if not acknowledged by then
NotLostYet { lost_time: Timestamp },
/// The packet is lost
Lost,
}
/// Detect if the given packet number is lost based on how long ago
/// it was sent and how far from the largest acked packet number it is.
pub fn detect(
time_threshold: Duration,
time_sent: Timestamp,
packet_number_threshold: u64,
packet_number: PacketNumber,
largest_acked_packet_number: PacketNumber,
now: Timestamp,
) -> Outcome {
// Tail packets are not considered lost until an acknowledgement is received for a packet sent
// after the tail packets. Therefore the `detect` method must only be called for packets sent
// prior to the largest acknowledged that could possibly be considered lost.
debug_assert!(
largest_acked_packet_number > packet_number,
"only packets sent before the largest acknowledged packet may be considered lost"
);
// Calculate at what time this particular packet is considered
// lost based on the `time_threshold`
let packet_lost_time = time_sent + time_threshold;
// If the `packet_lost_time` exceeds the current time, it's lost
let time_threshold_exceeded = packet_lost_time.has_elapsed(now);
let packet_number_threshold_exceeded = largest_acked_packet_number
.checked_distance(packet_number)
.expect("largest_acked_packet_number > packet_number")
>= packet_number_threshold;
//= https://www.rfc-editor.org/rfc/rfc9002#section-6.1
//# A packet is declared lost if it meets all of the following
//# conditions:
//#
//# * The packet is unacknowledged, in flight, and was sent prior to an
//# acknowledged packet.
//#
//# * The packet was sent kPacketThreshold packets before an
//# acknowledged packet (Section 6.1.1), or it was sent long enough in
//# the past (Section 6.1.2).
if time_threshold_exceeded || packet_number_threshold_exceeded {
return Outcome::Lost;
}
Outcome::NotLostYet {
lost_time: packet_lost_time,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{packet::number::PacketNumberSpace, time::testing::now};
//= https://www.rfc-editor.org/rfc/rfc9002#section-6.1.1
//= type=test
//# The RECOMMENDED initial value for the packet reordering threshold
//# (kPacketThreshold) is 3, based on best practices for TCP loss
//# detection [RFC5681] [RFC6675].
//= https://www.rfc-editor.org/rfc/rfc9002#section-6.1.1
//= type=test
//# In order to remain similar to TCP,
//# implementations SHOULD NOT use a packet threshold less than 3; see
//# [RFC5681].
#[allow(clippy::assertions_on_constants)]
#[test]
fn packet_reorder_threshold_at_least_three() {
assert!(K_PACKET_THRESHOLD >= 3);
}
#[test]
fn time_threshold() {
let time_threshold = Duration::from_secs(5);
let packet_number = new_packet_number(1);
// largest acked is within the K_PACKET_THRESHOLD
let largest_acked_packet_number =
new_packet_number(packet_number.as_u64() + K_PACKET_THRESHOLD - 1);
let time_sent = now();
let current_time = time_sent + time_threshold;
let outcome = detect(
time_threshold,
time_sent,
K_PACKET_THRESHOLD,
packet_number,
largest_acked_packet_number,
current_time,
);
assert_eq!(Outcome::Lost, outcome);
let time_sent = now();
let current_time = time_sent + time_threshold - Duration::from_secs(1);
let outcome = detect(
time_threshold,
time_sent,
K_PACKET_THRESHOLD,
packet_number,
largest_acked_packet_number,
current_time,
);
assert_eq!(
Outcome::NotLostYet {
lost_time: current_time + Duration::from_secs(1)
},
outcome
);
}
#[test]
fn packet_number_threshold() {
let time_threshold = Duration::from_secs(5);
let time_sent = now();
// packet was sent less than the time threshold in the past
let current_time = time_sent + time_threshold - Duration::from_secs(1);
let packet_number = new_packet_number(1);
// largest acked is K_PACKET_THRESHOLD larger than the current packet
let largest_acked_packet_number =
new_packet_number(packet_number.as_u64() + K_PACKET_THRESHOLD);
let outcome = detect(
time_threshold,
time_sent,
K_PACKET_THRESHOLD,
packet_number,
largest_acked_packet_number,
current_time,
);
assert_eq!(Outcome::Lost, outcome);
// largest acked is within the K_PACKET_THRESHOLD
let largest_acked_packet_number =
new_packet_number(packet_number.as_u64() + K_PACKET_THRESHOLD - 1);
let outcome = detect(
time_threshold,
time_sent,
K_PACKET_THRESHOLD,
packet_number,
largest_acked_packet_number,
current_time,
);
assert_eq!(
Outcome::NotLostYet {
lost_time: current_time + Duration::from_secs(1)
},
outcome
);
}
fn new_packet_number(packet_number: u64) -> PacketNumber {
PacketNumberSpace::ApplicationData.new_packet_number(packet_number.try_into().unwrap())
}
}