@@ -9,6 +9,7 @@ package quic
9
9
import (
10
10
"bytes"
11
11
"crypto/rand"
12
+ "slices"
12
13
)
13
14
14
15
// connIDState is a conn's connection IDs.
@@ -25,8 +26,16 @@ type connIDState struct {
25
26
remote []remoteConnID
26
27
27
28
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
30
39
31
40
originalDstConnID []byte // expected original_destination_connection_id param
32
41
retrySrcConnID []byte // expected retry_source_connection_id param
@@ -45,9 +54,6 @@ type connID struct {
45
54
// For the transient destination ID in a client's Initial packet, this is -1.
46
55
seq int64
47
56
48
- // retired is set when the connection ID is retired.
49
- retired bool
50
-
51
57
// send is set when the connection ID's state needs to be sent to the peer.
52
58
//
53
59
// For local IDs, this indicates a new ID that should be sent
@@ -144,24 +150,20 @@ func (s *connIDState) srcConnID() []byte {
144
150
// dstConnID is the Destination Connection ID to use in a sent packet.
145
151
func (s * connIDState ) dstConnID () (cid []byte , ok bool ) {
146
152
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
150
154
}
151
155
return nil , false
152
156
}
153
157
154
158
// isValidStatelessResetToken reports whether the given reset token is
155
159
// associated with a non-retired connection ID which we have used.
156
160
func (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
163
163
}
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
165
167
}
166
168
167
169
// setPeerActiveConnIDLimit sets the active_connection_id_limit
@@ -174,7 +176,7 @@ func (s *connIDState) setPeerActiveConnIDLimit(c *Conn, lim int64) error {
174
176
func (s * connIDState ) issueLocalIDs (c * Conn ) error {
175
177
toIssue := min (int (s .peerActiveConnIDLimit ), maxPeerActiveConnIDLimit )
176
178
for i := range s .local {
177
- if s .local [i ].seq != - 1 && ! s . local [ i ]. retired {
179
+ if s .local [i ].seq != - 1 {
178
180
toIssue --
179
181
}
180
182
}
@@ -271,7 +273,7 @@ func (s *connIDState) handlePacket(c *Conn, ptype packetType, srcConnID []byte)
271
273
}
272
274
}
273
275
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 {
275
277
// We're a server connection processing the first Handshake packet from
276
278
// the client. Discard the transient, client-chosen connection ID used
277
279
// 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
304
306
}
305
307
}
306
308
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
+
307
315
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 )
308
322
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
+ })
309
327
}
310
328
311
329
have := false // do we already have this connection ID?
312
- active := 0
313
330
for i := range s .remote {
314
331
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
- }
324
332
if rcid .seq == seq {
325
333
if ! bytes .Equal (rcid .cid , cid ) {
326
334
return localTransportError {
@@ -329,6 +337,7 @@ func (s *connIDState) handleNewConnID(c *Conn, seq, retire int64, cid []byte, re
329
337
}
330
338
}
331
339
have = true // yes, we've seen this sequence number
340
+ break
332
341
}
333
342
}
334
343
@@ -345,18 +354,12 @@ func (s *connIDState) handleNewConnID(c *Conn, seq, retire int64, cid []byte, re
345
354
},
346
355
resetToken : resetToken ,
347
356
})
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
+ })
357
360
}
358
361
359
- if active > activeConnIDLimit {
362
+ if len ( s . remote ) > activeConnIDLimit {
360
363
// Retired connection IDs (including newly-retired ones) do not count
361
364
// against the limit.
362
365
// 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
370
373
// for which RETIRE_CONNECTION_ID frames have not yet been acknowledged."
371
374
// https://www.rfc-editor.org/rfc/rfc9000#section-5.1.2-6
372
375
//
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 {
376
379
return localTransportError {
377
380
code : errConnectionIDLimit ,
378
- reason : "too many unacknowledged RETIRE_CONNECTION_ID frames " ,
381
+ reason : "too many unacknowledged retired connection ids " ,
379
382
}
380
383
}
381
384
382
385
return nil
383
386
}
384
387
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
-
392
388
func (s * connIDState ) handleRetireConnID (c * Conn , seq int64 ) error {
393
389
if seq >= s .nextLocalSeq {
394
390
return localTransportError {
@@ -424,20 +420,11 @@ func (s *connIDState) ackOrLossNewConnectionID(pnum packetNumber, seq int64, fat
424
420
}
425
421
426
422
func (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
441
428
}
442
429
}
443
430
@@ -469,14 +456,22 @@ func (s *connIDState) appendFrames(c *Conn, pnum packetNumber, pto bool) bool {
469
456
}
470
457
s .local [i ].send .setSent (pnum )
471
458
}
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
+ }
475
466
}
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 ) {
477
471
return false
478
472
}
479
- s .remote [i ].send .setSent (pnum )
473
+ s .remoteRetiring .sub (cid , cid + 1 )
474
+ s .remoteRetiringSent .add (cid , cid + 1 )
480
475
}
481
476
s .needSend = false
482
477
return true
0 commit comments