Skip to content

Commit 2eddc9d

Browse files
committed
feat(transport): update ack frequency draft from 00 to 10
1 parent a7e2fb0 commit 2eddc9d

File tree

7 files changed

+131
-47
lines changed

7 files changed

+131
-47
lines changed

neqo-transport/src/ackrate.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@
44
// option. This file may not be copied, modified, or distributed
55
// except according to those terms.
66

7-
// Management of the peer's ack rate.
7+
//! Local node (i.e. data sender and ack receiver) managing the remote node's
8+
//! (i.e. data receiver and ack sender) ack rate.
9+
//!
10+
//! Implementation of QUIC Acknowledgment Frequency draft 10.
11+
//!
12+
//! <https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-10.html>
13+
//!
14+
//! See [`crate::tracking::RecvdPackets`] for data receiver side.
815
916
use std::{cmp::max, time::Duration};
1017

@@ -47,7 +54,14 @@ impl AckRate {
4754
seqno,
4855
u64::try_from(self.packets + 1).unwrap(),
4956
u64::try_from(self.delay.as_micros()).unwrap(),
50-
0,
57+
// > If no ACK_FREQUENCY frames have been received, the data receiver
58+
// > immediately acknowledges any subsequent packets that are received
59+
// > out-of-order, as specified in Section 13.2 of [QUIC-TRANSPORT],
60+
// > corresponding to a default value of 1. A value of 0 indicates
61+
// > out-of-order packets do not elicit an immediate ACK.
62+
//
63+
// <https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-10.html#section-4>
64+
1,
5165
])
5266
}
5367

@@ -134,6 +148,7 @@ impl FlexibleAckRate {
134148
}
135149
}
136150

151+
// TODO: Rename to PeerAckFrequency
137152
#[derive(Debug, Clone)]
138153
pub enum PeerAckDelay {
139154
Fixed(Duration),

neqo-transport/src/connection/mod.rs

+24-3
Original file line numberDiff line numberDiff line change
@@ -2896,6 +2896,13 @@ impl Connection {
28962896
self.stats.borrow_mut().frame_rx.ping += 1;
28972897
self.crypto.resend_unacked(space);
28982898
// Send an ACK immediately if we might not otherwise do so.
2899+
//
2900+
// TODO: Ackording to ack frequency draft, only
2901+
// IMMEDIATE_ACK_FRAME will send immediate ack, not ping frame.
2902+
// Maybe do so anyways? Or only if peer did not advertise
2903+
// support via transport parameter?
2904+
//
2905+
// <https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-10.html#section-5>
28992906
self.acks.immediate_ack(space, now);
29002907
}
29012908
Frame::Ack {
@@ -3029,17 +3036,31 @@ impl Connection {
30293036
}
30303037
Frame::AckFrequency {
30313038
seqno,
3032-
tolerance,
3039+
ack_eliciting_threshold: tolerance,
30333040
delay,
3034-
ignore_order,
3041+
reordering_threshold,
30353042
} => {
30363043
self.stats.borrow_mut().frame_rx.ack_frequency += 1;
3044+
// > The value of this field is in microseconds, unlike the
3045+
// > max_ack_delay transport parameter, which is in milliseconds.
3046+
//
3047+
// <https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-10.html#section-4>
30373048
let delay = Duration::from_micros(delay);
30383049
if delay < GRANULARITY {
3050+
// > A value smaller than the min_ack_delay advertised by
3051+
// > the peer is also invalid. Receipt of an invalid value
3052+
// > MUST be treated as a connection error of type
3053+
// > PROTOCOL_VIOLATION.
3054+
//
3055+
// <https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-10.html#section-4>
30393056
return Err(Error::ProtocolViolation);
30403057
}
30413058
self.acks
3042-
.ack_freq(seqno, tolerance - 1, delay, ignore_order);
3059+
.ack_freq(seqno, tolerance - 1, delay, reordering_threshold);
3060+
}
3061+
Frame::ImmediateAck {} => {
3062+
self.stats.borrow_mut().frame_rx.immediate_ack += 1;
3063+
self.acks.immediate_ack(space, now);
30433064
}
30443065
Frame::Datagram { data, .. } => {
30453066
self.stats.borrow_mut().frame_rx.datagram += 1;

neqo-transport/src/frame.rs

+36-18
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,14 @@ pub const FRAME_TYPE_PATH_RESPONSE: FrameType = 0x1b;
4848
pub const FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT: FrameType = 0x1c;
4949
pub const FRAME_TYPE_CONNECTION_CLOSE_APPLICATION: FrameType = 0x1d;
5050
pub const FRAME_TYPE_HANDSHAKE_DONE: FrameType = 0x1e;
51-
// draft-ietf-quic-ack-delay
51+
/// > A data sender signals the conditions under which it wants to receive ACK
52+
/// > frames using an ACK_FREQUENCY frame
53+
///
54+
/// <https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-10.html#section-4>
5255
pub const FRAME_TYPE_ACK_FREQUENCY: FrameType = 0xaf;
56+
// TODO: Use
57+
/// <https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-10.html#section-4>
58+
pub const FRAME_IMMEDIATE_ACK: FrameType = 0x1f;
5359
// draft-ietf-quic-datagram
5460
pub const FRAME_TYPE_DATAGRAM: FrameType = 0x30;
5561
pub const FRAME_TYPE_DATAGRAM_WITH_LEN: FrameType = 0x31;
@@ -189,18 +195,30 @@ pub enum Frame<'a> {
189195
reason_phrase: String,
190196
},
191197
HandshakeDone,
198+
/// > A data sender signals the conditions under which it wants to receive
199+
/// > ACK frames using an ACK_FREQUENCY frame
200+
///
201+
/// <https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-10.html#section-4>
192202
AckFrequency {
193-
/// The current ACK frequency sequence number.
203+
/// > the sequence number assigned to the ACK_FREQUENCY frame by the
204+
/// > sender so receivers ignore obsolete frames.
194205
seqno: u64,
195-
/// The number of contiguous packets that can be received without
196-
/// acknowledging immediately.
197-
tolerance: u64,
198-
/// The time to delay after receiving the first packet that is
199-
/// not immediately acknowledged.
206+
/// > the maximum number of ack-eliciting packets the recipient of this
207+
/// > frame receives before sending an acknowledgment.
208+
ack_eliciting_threshold: u64,
209+
/// > the value to which the data sender requests the data receiver
210+
/// > update its max_ack_delay
200211
delay: u64,
201-
/// Ignore reordering when deciding to immediately acknowledge.
202-
ignore_order: bool,
212+
/// > the maximum packet reordering before eliciting an immediate ACK, as
213+
/// > specified in Section 6.2. If no ACK_FREQUENCY frames have been
214+
/// > received, the data receiver immediately acknowledges any subsequent
215+
/// > packets that are received out-of-order, as specified in Section 13.2
216+
/// > of [QUIC-TRANSPORT], corresponding to a default value of 1. A value
217+
/// > of 0 indicates out-of-order packets do not elicit an immediate ACK.
218+
reordering_threshold: u64,
203219
},
220+
/// <https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-10.html#section-5>
221+
ImmediateAck,
204222
Datagram {
205223
data: &'a [u8],
206224
fill: bool,
@@ -255,6 +273,7 @@ impl<'a> Frame<'a> {
255273
}
256274
Self::HandshakeDone => FRAME_TYPE_HANDSHAKE_DONE,
257275
Self::AckFrequency { .. } => FRAME_TYPE_ACK_FREQUENCY,
276+
Self::ImmediateAck {} => FRAME_IMMEDIATE_ACK,
258277
Self::Datagram { fill, .. } => {
259278
if *fill {
260279
FRAME_TYPE_DATAGRAM
@@ -622,25 +641,24 @@ impl<'a> Frame<'a> {
622641
})
623642
}
624643
FRAME_TYPE_HANDSHAKE_DONE => Ok(Self::HandshakeDone),
644+
// <https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-10.html#section-4>
625645
FRAME_TYPE_ACK_FREQUENCY => {
626646
let seqno = dv(dec)?;
627647
let tolerance = dv(dec)?;
628648
if tolerance == 0 {
629649
return Err(Error::FrameEncodingError);
630650
}
631651
let delay = dv(dec)?;
632-
let ignore_order = match d(dec.decode_uint::<u8>())? {
633-
0 => false,
634-
1 => true,
635-
_ => return Err(Error::FrameEncodingError),
636-
};
652+
let reordering_threshold = dv(dec)?;
637653
Ok(Self::AckFrequency {
638654
seqno,
639-
tolerance,
655+
ack_eliciting_threshold: tolerance,
640656
delay,
641-
ignore_order,
657+
reordering_threshold,
642658
})
643659
}
660+
// <https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-10.html#section-5>
661+
FRAME_IMMEDIATE_ACK => Ok(Self::ImmediateAck {}),
644662
FRAME_TYPE_DATAGRAM | FRAME_TYPE_DATAGRAM_WITH_LEN => {
645663
let fill = (t & DATAGRAM_FRAME_BIT_LEN) == 0;
646664
let data = if fill {
@@ -981,9 +999,9 @@ mod tests {
981999
fn ack_frequency() {
9821000
let f = Frame::AckFrequency {
9831001
seqno: 10,
984-
tolerance: 5,
1002+
ack_eliciting_threshold: 5,
9851003
delay: 2000,
986-
ignore_order: true,
1004+
reordering_threshold: 1,
9871005
};
9881006
just_dec(&f, "40af0a0547d001");
9891007
}

neqo-transport/src/qlog.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ impl From<Frame<'_>> for QuicFrame {
582582
trigger_frame_type: Some(frame_type),
583583
},
584584
Frame::HandshakeDone => Self::HandshakeDone,
585-
Frame::AckFrequency { .. } => Self::Unknown {
585+
Frame::AckFrequency { .. } | Frame::ImmediateAck { .. } => Self::Unknown {
586586
frame_type_value: None,
587587
raw_frame_type: frame.get_type(),
588588
raw: None,

neqo-transport/src/stats.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub struct FrameStats {
5353
pub new_token: usize,
5454

5555
pub ack_frequency: usize,
56+
pub immediate_ack: usize,
5657
pub datagram: usize,
5758
}
5859

@@ -92,7 +93,11 @@ impl Debug for FrameStats {
9293
self.path_challenge,
9394
self.path_response,
9495
)?;
95-
writeln!(f, " ack_frequency {}", self.ack_frequency)
96+
writeln!(
97+
f,
98+
" ack_frequency {}, immediate_ack {}",
99+
self.ack_frequency, self.immediate_ack
100+
)
96101
}
97102
}
98103

neqo-transport/src/tparams.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,17 @@ pub const INITIAL_SOURCE_CONNECTION_ID: TransportParameterId = 0x0f;
4747
pub const RETRY_SOURCE_CONNECTION_ID: TransportParameterId = 0x10;
4848
pub const VERSION_INFORMATION: TransportParameterId = 0x11;
4949
pub const GREASE_QUIC_BIT: TransportParameterId = 0x2ab2;
50-
pub const MIN_ACK_DELAY: TransportParameterId = 0xff02_de1a;
50+
/// QUIC Ack Frequency draft 10
51+
///
52+
/// > A variable-length integer representing the minimum amount of time, in
53+
/// > microseconds, that the endpoint sending this value is willing to delay an
54+
/// > acknowledgment. This limit could be based on the data receiver's clock or
55+
/// > timer granularity. min_ack_delay is used by the data sender to avoid
56+
/// > requesting too small a value in the Requested Max Ack Delay field of the
57+
/// > ACK_FREQUENCY frame.
58+
///
59+
/// <https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-10.html#section-3>
60+
pub const MIN_ACK_DELAY: TransportParameterId = 0xff04_de1b;
5161
pub const MAX_DATAGRAM_FRAME_SIZE: TransportParameterId = 0x0020;
5262

5363
#[derive(Clone, Debug)]

neqo-transport/src/tracking.rs

+36-21
Original file line numberDiff line numberDiff line change
@@ -266,10 +266,11 @@ pub struct RecvdPackets {
266266
unacknowledged_count: PacketNumber,
267267
/// The number of contiguous packets that can be received without
268268
/// acknowledging immediately.
269-
unacknowledged_tolerance: PacketNumber,
270-
/// Whether we are ignoring packets that arrive out of order
271-
/// for the purposes of generating immediate acknowledgment.
272-
ignore_order: bool,
269+
ack_eliciting_threshold: PacketNumber,
270+
/// > the maximum packet reordering before eliciting an immediate ACK
271+
///
272+
/// <https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-10.html#section-4>
273+
reordering_threshold: u64,
273274
// The counts of different ECN marks that have been received.
274275
ecn_count: ecn::Count,
275276
}
@@ -287,13 +288,19 @@ impl RecvdPackets {
287288
ack_frequency_seqno: 0,
288289
ack_delay: DEFAULT_ACK_DELAY,
289290
unacknowledged_count: 0,
290-
unacknowledged_tolerance: if space == PacketNumberSpace::ApplicationData {
291+
ack_eliciting_threshold: if space == PacketNumberSpace::ApplicationData {
291292
DEFAULT_ACK_PACKET_TOLERANCE
292293
} else {
293294
// ACK more aggressively
294295
0
295296
},
296-
ignore_order: false,
297+
// > If no ACK_FREQUENCY frames have been received, the data receiver
298+
// > immediately acknowledges any subsequent packets that are received
299+
// > out-of-order, as specified in Section 13.2 of [QUIC-TRANSPORT],
300+
// > corresponding to a default value of 1.
301+
//
302+
// <https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-10.html#section-4>
303+
reordering_threshold: 1,
297304
ecn_count: ecn::Count::default(),
298305
}
299306
}
@@ -312,18 +319,18 @@ impl RecvdPackets {
312319
pub fn ack_freq(
313320
&mut self,
314321
seqno: u64,
315-
tolerance: PacketNumber,
322+
ack_eliciting_threshold: PacketNumber,
316323
delay: Duration,
317-
ignore_order: bool,
324+
reordering_threshold: u64,
318325
) {
319326
// Yes, this means that we will overwrite values if a sequence number is
320327
// reused, but that is better than using an `Option<PacketNumber>`
321328
// when it will always be `Some`.
322329
if seqno >= self.ack_frequency_seqno {
323330
self.ack_frequency_seqno = seqno;
324-
self.unacknowledged_tolerance = tolerance;
331+
self.ack_eliciting_threshold = ack_eliciting_threshold;
325332
self.ack_delay = delay;
326-
self.ignore_order = ignore_order;
333+
self.reordering_threshold = reordering_threshold;
327334
}
328335
}
329336

@@ -399,8 +406,16 @@ impl RecvdPackets {
399406
self.unacknowledged_count += 1;
400407

401408
let immediate_ack = self.space != PacketNumberSpace::ApplicationData
402-
|| (pn != next_in_order_pn && !self.ignore_order)
403-
|| self.unacknowledged_count > self.unacknowledged_tolerance;
409+
// > If no ACK_FREQUENCY frames have been received, the data
410+
// > receiver immediately acknowledges any subsequent packets that are
411+
// > received out-of-order, as specified in Section 13.2 of
412+
// > [QUIC-TRANSPORT], corresponding to a default value of 1. A value
413+
// > of 0 indicates out-of-order packets do not elicit an immediate
414+
// > ACK.
415+
//
416+
// <https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-10.html#section-4>
417+
|| (self.reordering_threshold != 0 && pn.saturating_sub(next_in_order_pn) >= self.reordering_threshold)
418+
|| self.unacknowledged_count > self.ack_eliciting_threshold;
404419

405420
let ack_time = if immediate_ack {
406421
now
@@ -579,13 +594,13 @@ impl AckTracker {
579594
pub fn ack_freq(
580595
&mut self,
581596
seqno: u64,
582-
tolerance: PacketNumber,
597+
ack_eliciting_threshold: PacketNumber,
583598
delay: Duration,
584-
ignore_order: bool,
599+
reordering_threshold: u64,
585600
) {
586601
// Only ApplicationData ever delays ACK.
587602
if let Some(space) = self.get_mut(PacketNumberSpace::ApplicationData) {
588-
space.ack_freq(seqno, tolerance, delay, ignore_order);
603+
space.ack_freq(seqno, ack_eliciting_threshold, delay, reordering_threshold);
589604
}
590605
}
591606

@@ -758,7 +773,7 @@ mod tests {
758773
assert!(rp.ack_time().is_none());
759774
assert!(!rp.ack_now(now(), RTT));
760775

761-
rp.ack_freq(0, COUNT, DELAY, false);
776+
rp.ack_freq(0, COUNT, DELAY, 0);
762777

763778
// Some packets won't cause an ACK to be needed.
764779
for i in 0..COUNT {
@@ -848,7 +863,7 @@ mod tests {
848863
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
849864

850865
// Set tolerance to 2 and then it takes three packets.
851-
rp.ack_freq(0, 2, Duration::from_millis(10), true);
866+
rp.ack_freq(0, 2, Duration::from_millis(10), 1);
852867

853868
rp.set_received(now(), 1, true);
854869
assert_ne!(Some(now()), rp.ack_time());
@@ -865,7 +880,7 @@ mod tests {
865880
write_frame(&mut rp);
866881

867882
// Set tolerance to 2 and then it takes three packets.
868-
rp.ack_freq(0, 2, Duration::from_millis(10), true);
883+
rp.ack_freq(0, 2, Duration::from_millis(10), 1);
869884

870885
rp.set_received(now(), 3, true);
871886
assert_ne!(Some(now()), rp.ack_time());
@@ -880,7 +895,7 @@ mod tests {
880895
#[test]
881896
fn non_ack_eliciting_skip() {
882897
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
883-
rp.ack_freq(0, 1, Duration::from_millis(10), true);
898+
rp.ack_freq(0, 1, Duration::from_millis(10), 1);
884899

885900
// This should be ignored.
886901
rp.set_received(now(), 0, false);
@@ -896,7 +911,7 @@ mod tests {
896911
#[test]
897912
fn non_ack_eliciting_reorder() {
898913
let mut rp = RecvdPackets::new(PacketNumberSpace::ApplicationData);
899-
rp.ack_freq(0, 1, Duration::from_millis(10), false);
914+
rp.ack_freq(0, 1, Duration::from_millis(10), 1);
900915

901916
// These are out of order, but they are not ack-eliciting.
902917
rp.set_received(now(), 1, false);
@@ -915,7 +930,7 @@ mod tests {
915930
fn aggregate_ack_time() {
916931
const DELAY: Duration = Duration::from_millis(17);
917932
let mut tracker = AckTracker::default();
918-
tracker.ack_freq(0, 1, DELAY, false);
933+
tracker.ack_freq(0, 1, DELAY, 1);
919934
// This packet won't trigger an ACK.
920935
tracker
921936
.get_mut(PacketNumberSpace::Handshake)

0 commit comments

Comments
 (0)