@@ -9,6 +9,7 @@ package quic
99import (
1010 "bytes"
1111 "crypto/rand"
12+ "slices"
1213)
1314
1415// connIDState is a conn's connection IDs.
@@ -25,8 +26,16 @@ type connIDState struct {
2526 remote []remoteConnID
2627
2728 nextLocalSeq int64
28- retireRemotePriorTo int64 // largest Retire Prior To value sent by the peer
29- peerActiveConnIDLimit int64 // peer's active_connection_id_limit transport parameter
29+ peerActiveConnIDLimit int64 // peer's active_connection_id_limit
30+
31+ // Handling of retirement of remote connection IDs.
32+ // The rangesets track ID sequence numbers.
33+ // IDs in need of retirement are added to remoteRetiring,
34+ // moved to remoteRetiringSent once we send a RETIRE_CONECTION_ID frame,
35+ // and removed from the set once retirement completes.
36+ retireRemotePriorTo int64 // largest Retire Prior To value sent by the peer
37+ remoteRetiring rangeset [int64 ] // remote IDs in need of retirement
38+ remoteRetiringSent rangeset [int64 ] // remote IDs waiting for ack of retirement
3039
3140 originalDstConnID []byte // expected original_destination_connection_id param
3241 retrySrcConnID []byte // expected retry_source_connection_id param
@@ -45,9 +54,6 @@ type connID struct {
4554 // For the transient destination ID in a client's Initial packet, this is -1.
4655 seq int64
4756
48- // retired is set when the connection ID is retired.
49- retired bool
50-
5157 // send is set when the connection ID's state needs to be sent to the peer.
5258 //
5359 // For local IDs, this indicates a new ID that should be sent
@@ -144,24 +150,20 @@ func (s *connIDState) srcConnID() []byte {
144150// dstConnID is the Destination Connection ID to use in a sent packet.
145151func (s * connIDState ) dstConnID () (cid []byte , ok bool ) {
146152 for i := range s .remote {
147- if ! s .remote [i ].retired {
148- return s .remote [i ].cid , true
149- }
153+ return s .remote [i ].cid , true
150154 }
151155 return nil , false
152156}
153157
154158// isValidStatelessResetToken reports whether the given reset token is
155159// associated with a non-retired connection ID which we have used.
156160func (s * connIDState ) isValidStatelessResetToken (resetToken statelessResetToken ) bool {
157- for i := range s .remote {
158- // We currently only use the first available remote connection ID,
159- // so any other reset token is not valid.
160- if ! s .remote [i ].retired {
161- return s .remote [i ].resetToken == resetToken
162- }
161+ if len (s .remote ) == 0 {
162+ return false
163163 }
164- return false
164+ // We currently only use the first available remote connection ID,
165+ // so any other reset token is not valid.
166+ return s .remote [0 ].resetToken == resetToken
165167}
166168
167169// setPeerActiveConnIDLimit sets the active_connection_id_limit
@@ -174,7 +176,7 @@ func (s *connIDState) setPeerActiveConnIDLimit(c *Conn, lim int64) error {
174176func (s * connIDState ) issueLocalIDs (c * Conn ) error {
175177 toIssue := min (int (s .peerActiveConnIDLimit ), maxPeerActiveConnIDLimit )
176178 for i := range s .local {
177- if s .local [i ].seq != - 1 && ! s . local [ i ]. retired {
179+ if s .local [i ].seq != - 1 {
178180 toIssue --
179181 }
180182 }
@@ -271,7 +273,7 @@ func (s *connIDState) handlePacket(c *Conn, ptype packetType, srcConnID []byte)
271273 }
272274 }
273275 case ptype == packetTypeHandshake && c .side == serverSide :
274- if len (s .local ) > 0 && s .local [0 ].seq == - 1 && ! s . local [ 0 ]. retired {
276+ if len (s .local ) > 0 && s .local [0 ].seq == - 1 {
275277 // We're a server connection processing the first Handshake packet from
276278 // the client. Discard the transient, client-chosen connection ID used
277279 // for Initial packets; the client will never send it again.
@@ -304,23 +306,29 @@ func (s *connIDState) handleNewConnID(c *Conn, seq, retire int64, cid []byte, re
304306 }
305307 }
306308
309+ if seq < s .retireRemotePriorTo {
310+ // This ID was already retired by a previous NEW_CONNECTION_ID frame.
311+ // Nothing to do.
312+ return nil
313+ }
314+
307315 if retire > s .retireRemotePriorTo {
316+ // Add newly-retired connection IDs to the set we need to send
317+ // RETIRE_CONNECTION_ID frames for, and remove them from s.remote.
318+ //
319+ // (This might cause us to send a RETIRE_CONNECTION_ID for an ID we've
320+ // never seen. That's fine.)
321+ s .remoteRetiring .add (s .retireRemotePriorTo , retire )
308322 s .retireRemotePriorTo = retire
323+ s .needSend = true
324+ s .remote = slices .DeleteFunc (s .remote , func (rcid remoteConnID ) bool {
325+ return rcid .seq < s .retireRemotePriorTo
326+ })
309327 }
310328
311329 have := false // do we already have this connection ID?
312- active := 0
313330 for i := range s .remote {
314331 rcid := & s .remote [i ]
315- if ! rcid .retired && rcid .seq >= 0 && rcid .seq < s .retireRemotePriorTo {
316- s .retireRemote (rcid )
317- c .endpoint .connsMap .updateConnIDs (func (conns * connsMap ) {
318- conns .retireResetToken (c , rcid .resetToken )
319- })
320- }
321- if ! rcid .retired {
322- active ++
323- }
324332 if rcid .seq == seq {
325333 if ! bytes .Equal (rcid .cid , cid ) {
326334 return localTransportError {
@@ -329,6 +337,7 @@ func (s *connIDState) handleNewConnID(c *Conn, seq, retire int64, cid []byte, re
329337 }
330338 }
331339 have = true // yes, we've seen this sequence number
340+ break
332341 }
333342 }
334343
@@ -345,18 +354,12 @@ func (s *connIDState) handleNewConnID(c *Conn, seq, retire int64, cid []byte, re
345354 },
346355 resetToken : resetToken ,
347356 })
348- if seq < s .retireRemotePriorTo {
349- // This ID was already retired by a previous NEW_CONNECTION_ID frame.
350- s .retireRemote (& s .remote [len (s .remote )- 1 ])
351- } else {
352- active ++
353- c .endpoint .connsMap .updateConnIDs (func (conns * connsMap ) {
354- conns .addResetToken (c , resetToken )
355- })
356- }
357+ c .endpoint .connsMap .updateConnIDs (func (conns * connsMap ) {
358+ conns .addResetToken (c , resetToken )
359+ })
357360 }
358361
359- if active > activeConnIDLimit {
362+ if len ( s . remote ) > activeConnIDLimit {
360363 // Retired connection IDs (including newly-retired ones) do not count
361364 // against the limit.
362365 // https://www.rfc-editor.org/rfc/rfc9000.html#section-5.1.1-5
@@ -370,25 +373,18 @@ func (s *connIDState) handleNewConnID(c *Conn, seq, retire int64, cid []byte, re
370373 // for which RETIRE_CONNECTION_ID frames have not yet been acknowledged."
371374 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.2-6
372375 //
373- // Set a limit of four times the active_connection_id_limit for
374- // the total number of remote connection IDs we keep state for locally .
375- if len ( s . remote ) > 4 * activeConnIDLimit {
376+ // Set a limit of three times the active_connection_id_limit for
377+ // the total number of remote connection IDs we keep retirement state for.
378+ if s . remoteRetiring . size () + s . remoteRetiringSent . size ( ) > 3 * activeConnIDLimit {
376379 return localTransportError {
377380 code : errConnectionIDLimit ,
378- reason : "too many unacknowledged RETIRE_CONNECTION_ID frames " ,
381+ reason : "too many unacknowledged retired connection ids " ,
379382 }
380383 }
381384
382385 return nil
383386}
384387
385- // retireRemote marks a remote connection ID as retired.
386- func (s * connIDState ) retireRemote (rcid * remoteConnID ) {
387- rcid .retired = true
388- rcid .send .setUnsent ()
389- s .needSend = true
390- }
391-
392388func (s * connIDState ) handleRetireConnID (c * Conn , seq int64 ) error {
393389 if seq >= s .nextLocalSeq {
394390 return localTransportError {
@@ -424,20 +420,11 @@ func (s *connIDState) ackOrLossNewConnectionID(pnum packetNumber, seq int64, fat
424420}
425421
426422func (s * connIDState ) ackOrLossRetireConnectionID (pnum packetNumber , seq int64 , fate packetFate ) {
427- for i := 0 ; i < len (s .remote ); i ++ {
428- if s .remote [i ].seq != seq {
429- continue
430- }
431- if fate == packetAcked {
432- // We have retired this connection ID, and the peer has acked.
433- // Discard its state completely.
434- s .remote = append (s .remote [:i ], s .remote [i + 1 :]... )
435- } else {
436- // RETIRE_CONNECTION_ID frame was lost, mark for retransmission.
437- s .needSend = true
438- s .remote [i ].send .ackOrLoss (pnum , fate )
439- }
440- return
423+ s .remoteRetiringSent .sub (seq , seq + 1 )
424+ if fate == packetLost {
425+ // RETIRE_CONNECTION_ID frame was lost, mark for retransmission.
426+ s .remoteRetiring .add (seq , seq + 1 )
427+ s .needSend = true
441428 }
442429}
443430
@@ -469,14 +456,22 @@ func (s *connIDState) appendFrames(c *Conn, pnum packetNumber, pto bool) bool {
469456 }
470457 s .local [i ].send .setSent (pnum )
471458 }
472- for i := range s .remote {
473- if ! s .remote [i ].send .shouldSendPTO (pto ) {
474- continue
459+ if pto {
460+ for _ , r := range s .remoteRetiringSent {
461+ for cid := r .start ; cid < r .end ; cid ++ {
462+ if ! c .w .appendRetireConnectionIDFrame (cid ) {
463+ return false
464+ }
465+ }
475466 }
476- if ! c .w .appendRetireConnectionIDFrame (s .remote [i ].seq ) {
467+ }
468+ for s .remoteRetiring .numRanges () > 0 {
469+ cid := s .remoteRetiring .min ()
470+ if ! c .w .appendRetireConnectionIDFrame (cid ) {
477471 return false
478472 }
479- s .remote [i ].send .setSent (pnum )
473+ s .remoteRetiring .sub (cid , cid + 1 )
474+ s .remoteRetiringSent .add (cid , cid + 1 )
480475 }
481476 s .needSend = false
482477 return true
0 commit comments