diff --git a/quinn-proto/src/connection.rs b/quinn-proto/src/connection.rs index 4b0b870a7..e82830838 100644 --- a/quinn-proto/src/connection.rs +++ b/quinn-proto/src/connection.rs @@ -34,6 +34,7 @@ pub struct Connection { pub(crate) init_cid: ConnectionId, loc_cids: HashMap, rem_cid: ConnectionId, + rem_cid_seq: u64, pub(crate) remote: SocketAddrV6, state: State, side: Side, @@ -217,6 +218,7 @@ impl Connection { init_cid, loc_cids, rem_cid, + rem_cid_seq: 0, remote, side, state, @@ -1579,6 +1581,7 @@ impl Connection { // We're a server using the initial remote CID for the client, so let's // switch immediately to enable clientside stateless resets. debug_assert!(self.side.is_server()); + debug_assert_eq!(self.rem_cid_seq, 0); self.update_rem_cid(frame); } else { trace!(self.log, "ignoring NEW_CONNECTION_ID (unimplemented)"); @@ -1600,7 +1603,9 @@ impl Connection { sequence = new.sequence, connection_id = new.id ); + self.pending.retire_cids.push(self.rem_cid_seq); self.rem_cid = new.id; + self.rem_cid_seq = new.sequence; self.params.stateless_reset_token = Some(new.reset_token); } @@ -1882,6 +1887,19 @@ impl Connection { sent.new_cids.push(frame); } + // RETIRE_CONNECTION_ID + while buf.len() + frame::RETIRE_CONNECTION_ID_SIZE_BOUND < max_size { + let seq = if let Some(x) = pending.retire_cids.pop() { + x + } else { + break; + }; + trace!(self.log, "RETIRE_CONNECTION_ID {sequence}", sequence = seq); + buf.write(frame::Type::RETIRE_CONNECTION_ID); + buf.write_var(seq); + sent.retire_cids.push(seq); + } + // STREAM while buf.len() + frame::Stream::::SIZE_BOUND < max_size { let mut stream = if let Some(x) = pending.stream.pop_front() { @@ -2602,6 +2620,7 @@ pub struct Retransmits { max_stream_data: FnvHashSet, crypto: VecDeque, new_cids: Vec, + retire_cids: Vec, } impl Retransmits { @@ -2617,6 +2636,7 @@ impl Retransmits { && self.max_stream_data.is_empty() && self.crypto.is_empty() && self.new_cids.is_empty() + && self.retire_cids.is_empty() } pub fn path_challenge(&mut self, packet: u64, token: u64) { @@ -2646,6 +2666,7 @@ impl Default for Retransmits { max_stream_data: FnvHashSet::default(), crypto: VecDeque::new(), new_cids: Vec::new(), + retire_cids: Vec::new(), } } } @@ -2665,6 +2686,7 @@ impl ::std::ops::AddAssign for Retransmits { self.max_stream_data.extend(&rhs.max_stream_data); self.crypto.extend(rhs.crypto.into_iter()); self.new_cids.extend(&rhs.new_cids); + self.retire_cids.extend(rhs.retire_cids); } } diff --git a/quinn-proto/src/frame.rs b/quinn-proto/src/frame.rs index b375a5914..e2c78b0a0 100644 --- a/quinn-proto/src/frame.rs +++ b/quinn-proto/src/frame.rs @@ -737,6 +737,9 @@ impl NewConnectionId { } } +/// Smallest number of bytes this type of frame is guaranteed to fit within. +pub const RETIRE_CONNECTION_ID_SIZE_BOUND: usize = 9; + #[cfg(test)] mod test { use super::*;