Skip to content

Commit ff58f71

Browse files
authored
ack-frequencies is allowed together with multipath (#55)
This moves the immediate-ack state to the path, I think this is multipath-compatible as much as normal acks are.
2 parents 0cb9867 + e345964 commit ff58f71

File tree

3 files changed

+34
-23
lines changed

3 files changed

+34
-23
lines changed

quinn-proto/src/connection/mod.rs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,9 @@ impl Connection {
571571
// Whether the next packet will contain ack-eliciting frames.
572572
let mut ack_eliciting = !self.spaces[space_id].pending.is_empty(&self.streams)
573573
|| self.spaces[space_id].for_path(path_id).ping_pending
574-
|| self.spaces[space_id].immediate_ack_pending;
574+
|| self.spaces[space_id]
575+
.for_path(path_id)
576+
.immediate_ack_pending;
575577
if space_id == SpaceId::Data {
576578
let pn = self.spaces[SpaceId::Data]
577579
.for_path(path_id)
@@ -821,7 +823,7 @@ impl Connection {
821823
.buf
822824
.segment_size()
823825
.saturating_sub(self.predict_1rtt_overhead(pn, path_id));
824-
let can_send = self.space_can_send(space_id, frame_space_1rtt);
826+
let can_send = self.space_can_send(space_id, path_id, frame_space_1rtt);
825827
debug_assert!(
826828
!(sent_frames.is_ack_only(&self.streams)
827829
&& !can_send.acks
@@ -911,7 +913,7 @@ impl Connection {
911913
.segment_size()
912914
.saturating_sub(self.predict_1rtt_overhead(pn, path_id));
913915
if self
914-
.space_can_send(next_space_id, frame_space_1rtt)
916+
.space_can_send(next_space_id, path_id, frame_space_1rtt)
915917
.is_empty()
916918
{
917919
break;
@@ -1043,7 +1045,7 @@ impl Connection {
10431045
.saturating_sub(self.predict_1rtt_overhead(pn, path_id));
10441046
let mut space_id = current_space_id;
10451047
loop {
1046-
let can_send = self.space_can_send(space_id, frame_space_1rtt);
1048+
let can_send = self.space_can_send(space_id, path_id, frame_space_1rtt);
10471049
if !can_send.is_empty() || (close && self.spaces[space_id].crypto.is_some()) {
10481050
return Some(space_id);
10491051
}
@@ -1113,7 +1115,12 @@ impl Connection {
11131115
}
11141116

11151117
/// Indicate what types of frames are ready to send for the given space
1116-
fn space_can_send(&self, space_id: SpaceId, frame_space_1rtt: usize) -> SendableFrames {
1118+
fn space_can_send(
1119+
&self,
1120+
space_id: SpaceId,
1121+
path_id: PathId,
1122+
frame_space_1rtt: usize,
1123+
) -> SendableFrames {
11171124
if self.spaces[space_id].crypto.is_none()
11181125
&& (space_id != SpaceId::Data
11191126
|| self.zero_rtt_crypto.is_none()
@@ -1122,8 +1129,7 @@ impl Connection {
11221129
// No keys available for this space
11231130
return SendableFrames::empty();
11241131
}
1125-
let space = &self.spaces[space_id];
1126-
let mut can_send = space.can_send(&self.streams, space.immediate_ack_pending);
1132+
let mut can_send = self.spaces[space_id].can_send(path_id, &self.streams);
11271133
if space_id == SpaceId::Data {
11281134
can_send.other |= self.can_send_1rtt(frame_space_1rtt);
11291135
}
@@ -3021,7 +3027,7 @@ impl Connection {
30213027
// PATH_CHALLENGE on active path, possible off-path packet forwarding
30223028
// attack. Send a non-probing packet to recover the active path.
30233029
match self.peer_supports_ack_frequency() {
3024-
true => self.immediate_ack(),
3030+
true => self.immediate_ack(path_id),
30253031
false => self.ping(),
30263032
}
30273033
}
@@ -3523,7 +3529,7 @@ impl Connection {
35233529
}
35243530

35253531
// IMMEDIATE_ACK
3526-
if mem::replace(&mut space.immediate_ack_pending, false) {
3532+
if mem::replace(&mut space.for_path(path_id).immediate_ack_pending, false) {
35273533
trace!("IMMEDIATE_ACK");
35283534
buf.write(frame::FrameType::IMMEDIATE_ACK);
35293535
sent.non_retransmits = true;
@@ -4029,8 +4035,10 @@ impl Connection {
40294035
///
40304036
/// According to the spec, this will result in an error if the remote endpoint does not support
40314037
/// the Acknowledgement Frequency extension
4032-
pub(crate) fn immediate_ack(&mut self) {
4033-
self.spaces[self.highest_space].immediate_ack_pending = true;
4038+
pub(crate) fn immediate_ack(&mut self, path_id: PathId) {
4039+
self.spaces[self.highest_space]
4040+
.for_path(path_id)
4041+
.immediate_ack_pending = true;
40344042
}
40354043

40364044
/// Decodes a packet, returning its decrypted payload, so it can be inspected in tests

quinn-proto/src/connection/spaces.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ pub(super) struct PacketSpace {
3131
/// Current offset of outgoing cryptographic handshake stream
3232
pub(super) crypto_offset: u64,
3333

34-
pub(super) immediate_ack_pending: bool,
35-
3634
/// Multipath packet number spaces
3735
///
3836
/// Each [`PathId`] has it's own [`PacketNumberSpace`]. Only the [`SpaceId::Data`] can
@@ -52,7 +50,6 @@ impl PacketSpace {
5250
pending_acks: PendingAcks::new(),
5351
crypto_stream: Assembler::new(),
5452
crypto_offset: 0,
55-
immediate_ack_pending: false,
5653
number_spaces: BTreeMap::from([(PathId(0), number_space_0)]),
5754
}
5855
}
@@ -68,7 +65,6 @@ impl PacketSpace {
6865
pending_acks: PendingAcks::new(),
6966
crypto_stream: Assembler::new(),
7067
crypto_offset: 0,
71-
immediate_ack_pending: false,
7268
number_spaces: BTreeMap::from([(PathId(0), number_space_0)]),
7369
}
7470
}
@@ -125,7 +121,7 @@ impl PacketSpace {
125121
if request_immediate_ack {
126122
// The probe should be ACKed without delay (should only be used in the Data space and
127123
// when the peer supports the acknowledgement frequency extension)
128-
self.immediate_ack_pending = true;
124+
self.for_path(path_id).immediate_ack_pending = true;
129125
}
130126

131127
// Retransmit the data of the oldest in-flight packet
@@ -154,19 +150,20 @@ impl PacketSpace {
154150
// TODO(flub): Sending a ping on all paths is wasteful, but we also need per-path
155151
// pings so doing this is easier for now. Maybe later introduce a
156152
// connection-level ping again.
157-
if !self.immediate_ack_pending {
153+
if !self.for_path(path_id).immediate_ack_pending {
158154
self.number_spaces
159155
.values_mut()
160156
.for_each(|s| s.ping_pending = true);
161157
}
162158
}
163159

164160
/// Whether there is anything to send.
165-
pub(super) fn can_send(
166-
&self,
167-
streams: &StreamsState,
168-
immediate_ack_pending: bool,
169-
) -> SendableFrames {
161+
pub(super) fn can_send(&self, path_id: PathId, streams: &StreamsState) -> SendableFrames {
162+
let immediate_ack_pending = self
163+
.number_spaces
164+
.get(&path_id)
165+
.map(|pns| pns.immediate_ack_pending)
166+
.unwrap_or_default();
170167
let acks = self.pending_acks.can_send();
171168
let other = !self.pending.is_empty(streams)
172169
|| self
@@ -230,7 +227,10 @@ pub(super) struct PacketNumberSpace {
230227
pub(super) in_flight: u64,
231228
/// Number of packets sent in the current key phase
232229
pub(super) sent_with_keys: u64,
230+
/// A PING frame needs to be sent on this path
233231
pub(super) ping_pending: bool,
232+
/// An IMMEDIATE_ACK (draft-ietf-quic-ack-frequency) frame needs to be sent on this path
233+
pub(super) immediate_ack_pending: bool,
234234

235235
//
236236
// Loss Detection
@@ -272,6 +272,7 @@ impl PacketNumberSpace {
272272
in_flight: 0,
273273
sent_with_keys: 0,
274274
ping_pending: false,
275+
immediate_ack_pending: false,
275276
pto_count: 0,
276277
time_of_last_ack_eliciting_packet: None,
277278
loss_time: None,
@@ -298,6 +299,7 @@ impl PacketNumberSpace {
298299
in_flight: 0,
299300
sent_with_keys: 0,
300301
ping_pending: false,
302+
immediate_ack_pending: false,
301303
pto_count: 0,
302304
time_of_last_ack_eliciting_packet: None,
303305
loss_time: None,
@@ -325,6 +327,7 @@ impl PacketNumberSpace {
325327
in_flight: 0,
326328
sent_with_keys: 0,
327329
ping_pending: false,
330+
immediate_ack_pending: false,
328331
pto_count: 0,
329332
time_of_last_ack_eliciting_packet: None,
330333
loss_time: None,

quinn-proto/src/tests/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2601,7 +2601,7 @@ fn immediate_ack_triggers_ack() {
26012601

26022602
let acks_after_connect = pair.client_conn_mut(client_ch).stats().frame_rx.acks;
26032603

2604-
pair.client_conn_mut(client_ch).immediate_ack();
2604+
pair.client_conn_mut(client_ch).immediate_ack(PathId(0));
26052605
pair.drive_client(); // Send immediate ack
26062606
pair.drive_server(); // Process immediate ack
26072607
pair.drive_client(); // Give the client a chance to process the ack

0 commit comments

Comments
 (0)