Skip to content

Commit f33411d

Browse files
committed
lib: improve connection ID retirement
This change improves sender side handling of RETIRE_CONNECTION_ID frames, especially when there may be multiple paths available to use.
1 parent ecaf004 commit f33411d

File tree

3 files changed

+98
-15
lines changed

3 files changed

+98
-15
lines changed

quiche/src/cid.rs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,6 @@ impl BoundedConnectionIdSeqSet {
7171
self.inner.remove(e)
7272
}
7373

74-
fn front(&self) -> Option<u64> {
75-
self.inner.iter().next().copied()
76-
}
77-
7874
fn is_empty(&self) -> bool {
7975
self.inner.is_empty()
8076
}
@@ -757,14 +753,15 @@ impl ConnectionIdentifiers {
757753
self.advertise_new_scid_seqs.front().copied()
758754
}
759755

760-
/// Gets a destination Connection IDs's sequence number that need to send
761-
/// RETIRE_CONNECTION_ID frames.
756+
/// Returns a copy of the set of destination Connection IDs's sequence
757+
/// numbers to send RETIRE_CONNECTION_ID frames.
762758
///
763-
/// If `Some`, it always returns the same value until it has been removed
764-
/// using `mark_retire_dcid_seq`.
759+
/// Note that the set includes sequence numbers at the time the copy was
760+
/// created. To account for newly inserted or removed sequence numbers, a
761+
/// new copy needs to be created.
765762
#[inline]
766-
pub fn next_retire_dcid_seq(&self) -> Option<u64> {
767-
self.retire_dcid_seqs.front()
763+
pub fn retire_dcid_seqs(&self) -> HashSet<u64> {
764+
self.retire_dcid_seqs.inner.clone()
768765
}
769766

770767
/// Returns true if there are new source Connection IDs to advertise.
@@ -927,12 +924,12 @@ mod tests {
927924
assert_eq!(ids.available_dcids(), 1);
928925
assert_eq!(ids.dcids.len(), 2);
929926
assert!(ids.has_retire_dcids());
930-
assert_eq!(ids.next_retire_dcid_seq(), Some(0));
927+
assert_eq!(ids.retire_dcid_seqs().iter().next(), Some(&0));
931928

932929
// Fake RETIRE_CONNECTION_ID sending.
933930
let _ = ids.mark_retire_dcid_seq(0, false);
934931
assert!(!ids.has_retire_dcids());
935-
assert_eq!(ids.next_retire_dcid_seq(), None);
932+
assert_eq!(ids.retire_dcid_seqs().iter().next(), None);
936933

937934
// Now tries to experience CID retirement. If the server tries to remove
938935
// non-existing DCIDs, it fails.
@@ -947,13 +944,13 @@ mod tests {
947944
ids.link_dcid_to_path_id(2, 0).unwrap();
948945
assert_eq!(ids.available_dcids(), 0);
949946
assert!(ids.has_retire_dcids());
950-
assert_eq!(ids.next_retire_dcid_seq(), Some(1));
947+
assert_eq!(ids.retire_dcid_seqs().iter().next(), Some(&1));
951948
assert_eq!(ids.dcids.len(), 1);
952949

953950
// Fake RETIRE_CONNECTION_ID sending.
954951
let _ = ids.mark_retire_dcid_seq(1, false);
955952
assert!(!ids.has_retire_dcids());
956-
assert_eq!(ids.next_retire_dcid_seq(), None);
953+
assert_eq!(ids.retire_dcid_seqs().iter().next(), None);
957954

958955
// Trying to remove the last DCID triggers an error.
959956
assert_eq!(ids.retire_dcid(2), Err(Error::OutOfIdentifiers));

quiche/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4564,7 +4564,9 @@ impl<F: BufFactory> Connection<F> {
45644564
}
45654565

45664566
// Create RETIRE_CONNECTION_ID frames as needed.
4567-
while let Some(seq_num) = self.ids.next_retire_dcid_seq() {
4567+
let retire_dcid_seqs = self.ids.retire_dcid_seqs();
4568+
4569+
for seq_num in retire_dcid_seqs {
45684570
// The sequence number specified in a RETIRE_CONNECTION_ID frame
45694571
// MUST NOT refer to the Destination Connection ID field of the
45704572
// packet in which the frame is contained.

quiche/src/tests.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7955,6 +7955,90 @@ fn connection_id_retire_limit(
79557955
);
79567956
}
79577957

7958+
#[rstest]
7959+
fn connection_id_retire_exotic_sequence(
7960+
#[values("cubic", "bbr2", "bbr2_gcongestion")] cc_algorithm_name: &str,
7961+
) {
7962+
let mut buf = [0; 65535];
7963+
7964+
let mut config = Config::new(PROTOCOL_VERSION).unwrap();
7965+
assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(()));
7966+
config
7967+
.load_cert_chain_from_pem_file("examples/cert.crt")
7968+
.unwrap();
7969+
config
7970+
.load_priv_key_from_pem_file("examples/cert.key")
7971+
.unwrap();
7972+
config
7973+
.set_application_protos(&[b"proto1", b"proto2"])
7974+
.unwrap();
7975+
config.verify_peer(false);
7976+
config.set_active_connection_id_limit(2);
7977+
config.set_initial_max_data(30);
7978+
config.set_initial_max_stream_data_bidi_local(15);
7979+
config.set_initial_max_stream_data_bidi_remote(15);
7980+
config.set_initial_max_stream_data_uni(10);
7981+
config.set_initial_max_streams_uni(3);
7982+
config.set_initial_max_streams_bidi(3);
7983+
7984+
let mut pipe = test_utils::Pipe::with_config(&mut config).unwrap();
7985+
assert_eq!(pipe.handshake(), Ok(()));
7986+
7987+
// Inject an exotic sequence of NEW_CONNECTION_ID frames, unbeknowst to
7988+
// quiche client connection object.
7989+
let frames = [
7990+
frame::Frame::NewConnectionId {
7991+
seq_num: 8,
7992+
retire_prior_to: 1,
7993+
conn_id: vec![0],
7994+
reset_token: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
7995+
},
7996+
frame::Frame::NewConnectionId {
7997+
seq_num: 1,
7998+
retire_prior_to: 0,
7999+
conn_id: vec![02],
8000+
reset_token: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2],
8001+
},
8002+
frame::Frame::NewConnectionId {
8003+
seq_num: 6,
8004+
retire_prior_to: 6,
8005+
conn_id: vec![0x15],
8006+
reset_token: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
8007+
},
8008+
frame::Frame::NewConnectionId {
8009+
seq_num: 8,
8010+
retire_prior_to: 1,
8011+
conn_id: vec![0],
8012+
reset_token: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4],
8013+
},
8014+
frame::Frame::NewConnectionId {
8015+
seq_num: 48,
8016+
retire_prior_to: 8,
8017+
conn_id: vec![1],
8018+
reset_token: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5],
8019+
},
8020+
];
8021+
8022+
let pkt_type = Type::Short;
8023+
pipe.send_pkt_to_server(pkt_type, &frames, &mut buf)
8024+
.unwrap();
8025+
8026+
// Ensure operations continue to be allowed.
8027+
assert_eq!(pipe.client.stream_send(0, b"data", true), Ok(4));
8028+
assert_eq!(pipe.server.stream_send(1, b"data", true), Ok(4));
8029+
assert_eq!(pipe.client.stream_send(2, b"data", true), Ok(4));
8030+
assert_eq!(pipe.server.stream_send(3, b"data", true), Ok(4));
8031+
8032+
assert_eq!(pipe.advance(), Ok(()));
8033+
8034+
let mut b = [0; 15];
8035+
assert_eq!(pipe.server.stream_recv(0, &mut b), Ok((4, true)));
8036+
assert_eq!(pipe.server.stream_recv(2, &mut b), Ok((4, true)));
8037+
8038+
// The exotic sequence insertion messes with the client object's
8039+
// worldview so we can't check its side of things.
8040+
}
8041+
79588042
// Utility function.
79598043
fn pipe_with_exchanged_cids(
79608044
config: &mut Config, client_scid_len: usize, server_scid_len: usize,

0 commit comments

Comments
 (0)