blob: 314a6b3845dc885538760ff3c271106c3655440f [file] [log] [blame]
Damien Neil57553cb2022-10-13 12:09:20 -07001// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build go1.21
6
7package quic
8
9import (
Damien Neilbd8ac9e2023-07-20 16:45:15 -070010 "bytes"
11 "crypto/tls"
Damien Neil57553cb2022-10-13 12:09:20 -070012 "fmt"
Damien Neilbd8ac9e2023-07-20 16:45:15 -070013 "net/netip"
Damien Neil47caaff2023-09-12 15:19:59 -070014 "strings"
Damien Neil57553cb2022-10-13 12:09:20 -070015 "testing"
16)
17
18func TestConnIDClientHandshake(t *testing.T) {
Damien Neil47caaff2023-09-12 15:19:59 -070019 tc := newTestConn(t, clientSide)
Damien Neil57553cb2022-10-13 12:09:20 -070020 // On initialization, the client chooses local and remote IDs.
21 //
22 // The order in which we allocate the two isn't actually important,
23 // but test is a lot simpler if we assume.
Damien Neil47caaff2023-09-12 15:19:59 -070024 if got, want := tc.conn.connIDState.srcConnID(), testLocalConnID(0); !bytes.Equal(got, want) {
25 t.Errorf("after initialization: srcConnID = %x, want %x", got, want)
Damien Neil57553cb2022-10-13 12:09:20 -070026 }
Damien Neil47caaff2023-09-12 15:19:59 -070027 dstConnID, _ := tc.conn.connIDState.dstConnID()
28 if got, want := dstConnID, testLocalConnID(-1); !bytes.Equal(got, want) {
29 t.Errorf("after initialization: dstConnID = %x, want %x", got, want)
Damien Neil57553cb2022-10-13 12:09:20 -070030 }
31
32 // The server's first Initial packet provides the client with a
33 // non-transient remote connection ID.
Damien Neil47caaff2023-09-12 15:19:59 -070034 tc.writeFrames(packetTypeInitial,
35 debugFrameCrypto{
36 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
37 })
38 dstConnID, _ = tc.conn.connIDState.dstConnID()
39 if got, want := dstConnID, testPeerConnID(0); !bytes.Equal(got, want) {
40 t.Errorf("after receiving Initial: dstConnID = %x, want %x", got, want)
Damien Neil57553cb2022-10-13 12:09:20 -070041 }
42
43 wantLocal := []connID{{
Damien Neil47caaff2023-09-12 15:19:59 -070044 cid: testLocalConnID(0),
Damien Neil57553cb2022-10-13 12:09:20 -070045 seq: 0,
46 }}
Damien Neil47caaff2023-09-12 15:19:59 -070047 if got := tc.conn.connIDState.local; !connIDListEqual(got, wantLocal) {
48 t.Errorf("local ids: %v, want %v", fmtConnIDList(got), fmtConnIDList(wantLocal))
Damien Neil57553cb2022-10-13 12:09:20 -070049 }
Damien Neil642f15e2023-10-18 13:33:05 -040050 wantRemote := []remoteConnID{{
51 connID: connID{
52 cid: testPeerConnID(0),
53 seq: 0,
54 },
Damien Neil57553cb2022-10-13 12:09:20 -070055 }}
Damien Neil642f15e2023-10-18 13:33:05 -040056 if got := tc.conn.connIDState.remote; !remoteConnIDListEqual(got, wantRemote) {
57 t.Errorf("remote ids: %v, want %v", fmtRemoteConnIDList(got), fmtRemoteConnIDList(wantRemote))
Damien Neil57553cb2022-10-13 12:09:20 -070058 }
59}
60
61func TestConnIDServerHandshake(t *testing.T) {
Damien Neil47caaff2023-09-12 15:19:59 -070062 tc := newTestConn(t, serverSide)
Damien Neil57553cb2022-10-13 12:09:20 -070063 // On initialization, the server is provided with the client-chosen
64 // transient connection ID, and allocates an ID of its own.
65 // The Initial packet sets the remote connection ID.
Damien Neil47caaff2023-09-12 15:19:59 -070066 tc.writeFrames(packetTypeInitial,
67 debugFrameCrypto{
68 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial][:1],
69 })
70 if got, want := tc.conn.connIDState.srcConnID(), testLocalConnID(0); !bytes.Equal(got, want) {
Damien Neil57553cb2022-10-13 12:09:20 -070071 t.Errorf("after initClient: srcConnID = %q, want %q", got, want)
72 }
Damien Neil47caaff2023-09-12 15:19:59 -070073 dstConnID, _ := tc.conn.connIDState.dstConnID()
74 if got, want := dstConnID, testPeerConnID(0); !bytes.Equal(got, want) {
Damien Neil57553cb2022-10-13 12:09:20 -070075 t.Errorf("after initClient: dstConnID = %q, want %q", got, want)
76 }
77
Damien Neil47caaff2023-09-12 15:19:59 -070078 // The Initial flight of CRYPTO data includes transport parameters,
79 // which cause us to allocate another local connection ID.
80 tc.writeFrames(packetTypeInitial,
81 debugFrameCrypto{
82 off: 1,
83 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial][1:],
84 })
Damien Neil57553cb2022-10-13 12:09:20 -070085 wantLocal := []connID{{
Damien Neil47caaff2023-09-12 15:19:59 -070086 cid: testPeerConnID(-1),
Damien Neil57553cb2022-10-13 12:09:20 -070087 seq: -1,
88 }, {
Damien Neil47caaff2023-09-12 15:19:59 -070089 cid: testLocalConnID(0),
Damien Neil57553cb2022-10-13 12:09:20 -070090 seq: 0,
Damien Neil47caaff2023-09-12 15:19:59 -070091 }, {
92 cid: testLocalConnID(1),
93 seq: 1,
Damien Neil57553cb2022-10-13 12:09:20 -070094 }}
Damien Neil47caaff2023-09-12 15:19:59 -070095 if got := tc.conn.connIDState.local; !connIDListEqual(got, wantLocal) {
96 t.Errorf("local ids: %v, want %v", fmtConnIDList(got), fmtConnIDList(wantLocal))
Damien Neil57553cb2022-10-13 12:09:20 -070097 }
Damien Neil642f15e2023-10-18 13:33:05 -040098 wantRemote := []remoteConnID{{
99 connID: connID{
100 cid: testPeerConnID(0),
101 seq: 0,
102 },
Damien Neil57553cb2022-10-13 12:09:20 -0700103 }}
Damien Neil642f15e2023-10-18 13:33:05 -0400104 if got := tc.conn.connIDState.remote; !remoteConnIDListEqual(got, wantRemote) {
105 t.Errorf("remote ids: %v, want %v", fmtRemoteConnIDList(got), fmtRemoteConnIDList(wantRemote))
Damien Neil57553cb2022-10-13 12:09:20 -0700106 }
107
108 // The client's first Handshake packet permits the server to discard the
109 // transient connection ID.
Damien Neil47caaff2023-09-12 15:19:59 -0700110 tc.writeFrames(packetTypeHandshake,
111 debugFrameCrypto{
112 data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
113 })
Damien Neil57553cb2022-10-13 12:09:20 -0700114 wantLocal = []connID{{
Damien Neil47caaff2023-09-12 15:19:59 -0700115 cid: testLocalConnID(0),
Damien Neil57553cb2022-10-13 12:09:20 -0700116 seq: 0,
Damien Neil47caaff2023-09-12 15:19:59 -0700117 }, {
118 cid: testLocalConnID(1),
119 seq: 1,
Damien Neil57553cb2022-10-13 12:09:20 -0700120 }}
Damien Neil47caaff2023-09-12 15:19:59 -0700121 if got := tc.conn.connIDState.local; !connIDListEqual(got, wantLocal) {
122 t.Errorf("local ids: %v, want %v", fmtConnIDList(got), fmtConnIDList(wantLocal))
Damien Neil57553cb2022-10-13 12:09:20 -0700123 }
124}
125
Damien Neil47caaff2023-09-12 15:19:59 -0700126func connIDListEqual(a, b []connID) bool {
127 if len(a) != len(b) {
128 return false
Damien Neil57553cb2022-10-13 12:09:20 -0700129 }
Damien Neil47caaff2023-09-12 15:19:59 -0700130 for i := range a {
131 if a[i].seq != b[i].seq {
132 return false
133 }
134 if !bytes.Equal(a[i].cid, b[i].cid) {
135 return false
136 }
137 }
138 return true
139}
140
Damien Neil642f15e2023-10-18 13:33:05 -0400141func remoteConnIDListEqual(a, b []remoteConnID) bool {
142 if len(a) != len(b) {
143 return false
144 }
145 for i := range a {
146 if a[i].seq != b[i].seq {
147 return false
148 }
149 if !bytes.Equal(a[i].cid, b[i].cid) {
150 return false
151 }
152 if a[i].resetToken != b[i].resetToken {
153 return false
154 }
155 }
156 return true
157}
158
Damien Neil47caaff2023-09-12 15:19:59 -0700159func fmtConnIDList(s []connID) string {
160 var strs []string
161 for _, cid := range s {
162 strs = append(strs, fmt.Sprintf("[seq:%v cid:{%x}]", cid.seq, cid.cid))
163 }
164 return "{" + strings.Join(strs, " ") + "}"
Damien Neil57553cb2022-10-13 12:09:20 -0700165}
166
Damien Neil642f15e2023-10-18 13:33:05 -0400167func fmtRemoteConnIDList(s []remoteConnID) string {
168 var strs []string
169 for _, cid := range s {
170 strs = append(strs, fmt.Sprintf("[seq:%v cid:{%x} token:{%x}]", cid.seq, cid.cid, cid.resetToken))
171 }
172 return "{" + strings.Join(strs, " ") + "}"
173}
174
Damien Neil57553cb2022-10-13 12:09:20 -0700175func TestNewRandomConnID(t *testing.T) {
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700176 cid, err := newRandomConnID(0)
Damien Neil57553cb2022-10-13 12:09:20 -0700177 if len(cid) != connIDLen || err != nil {
178 t.Fatalf("newConnID() = %x, %v; want %v bytes", cid, connIDLen, err)
179 }
180}
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700181
182func TestConnIDPeerRequestsManyIDs(t *testing.T) {
183 // "An endpoint SHOULD ensure that its peer has a sufficient number
184 // of available and unused connection IDs."
185 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-4
186 //
187 // "An endpoint MAY limit the total number of connection IDs
188 // issued for each connection [...]"
189 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-6
190 //
191 // Peer requests 100 connection IDs.
192 // We give them 4 in total.
193 tc := newTestConn(t, serverSide, func(p *transportParameters) {
194 p.activeConnIDLimit = 100
195 })
196 tc.ignoreFrame(frameTypeAck)
197 tc.ignoreFrame(frameTypeCrypto)
198
199 tc.writeFrames(packetTypeInitial,
200 debugFrameCrypto{
201 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
202 })
203 tc.wantFrame("provide additional connection ID 1",
204 packetType1RTT, debugFrameNewConnectionID{
205 seq: 1,
206 connID: testLocalConnID(1),
Damien Neil642f15e2023-10-18 13:33:05 -0400207 token: testLocalStatelessResetToken(1),
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700208 })
209 tc.wantFrame("provide additional connection ID 2",
210 packetType1RTT, debugFrameNewConnectionID{
211 seq: 2,
212 connID: testLocalConnID(2),
Damien Neil642f15e2023-10-18 13:33:05 -0400213 token: testLocalStatelessResetToken(2),
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700214 })
215 tc.wantFrame("provide additional connection ID 3",
216 packetType1RTT, debugFrameNewConnectionID{
217 seq: 3,
218 connID: testLocalConnID(3),
Damien Neil642f15e2023-10-18 13:33:05 -0400219 token: testLocalStatelessResetToken(3),
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700220 })
221 tc.wantIdle("connection ID limit reached, no more to provide")
222}
223
224func TestConnIDPeerProvidesTooManyIDs(t *testing.T) {
225 // "An endpoint MUST NOT provide more connection IDs than the peer's limit."
226 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-4
227 tc := newTestConn(t, serverSide)
228 tc.handshake()
229 tc.ignoreFrame(frameTypeAck)
230
231 tc.writeFrames(packetType1RTT,
232 debugFrameNewConnectionID{
233 seq: 2,
234 connID: testLocalConnID(2),
235 })
236 tc.wantFrame("peer provided 3 connection IDs, our limit is 2",
237 packetType1RTT, debugFrameConnectionCloseTransport{
238 code: errConnectionIDLimit,
239 })
240}
241
242func TestConnIDPeerTemporarilyExceedsActiveConnIDLimit(t *testing.T) {
243 // "An endpoint MAY send connection IDs that temporarily exceed a peer's limit
244 // if the NEW_CONNECTION_ID frame also requires the retirement of any excess [...]"
245 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-4
246 tc := newTestConn(t, serverSide)
247 tc.handshake()
248 tc.ignoreFrame(frameTypeAck)
249
250 tc.writeFrames(packetType1RTT,
251 debugFrameNewConnectionID{
252 retirePriorTo: 2,
253 seq: 2,
254 connID: testPeerConnID(2),
255 }, debugFrameNewConnectionID{
256 retirePriorTo: 2,
257 seq: 3,
258 connID: testPeerConnID(3),
259 })
260 tc.wantFrame("peer requested we retire conn id 0",
261 packetType1RTT, debugFrameRetireConnectionID{
262 seq: 0,
263 })
264 tc.wantFrame("peer requested we retire conn id 1",
265 packetType1RTT, debugFrameRetireConnectionID{
266 seq: 1,
267 })
268}
269
270func TestConnIDPeerRetiresConnID(t *testing.T) {
271 // "An endpoint SHOULD supply a new connection ID when the peer retires a connection ID."
272 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-6
273 for _, side := range []connSide{
274 clientSide,
275 serverSide,
276 } {
277 t.Run(side.String(), func(t *testing.T) {
278 tc := newTestConn(t, side)
279 tc.handshake()
280 tc.ignoreFrame(frameTypeAck)
281
282 tc.writeFrames(packetType1RTT,
283 debugFrameRetireConnectionID{
284 seq: 0,
285 })
286 tc.wantFrame("provide replacement connection ID",
287 packetType1RTT, debugFrameNewConnectionID{
288 seq: 2,
289 retirePriorTo: 1,
290 connID: testLocalConnID(2),
Damien Neil642f15e2023-10-18 13:33:05 -0400291 token: testLocalStatelessResetToken(2),
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700292 })
293 })
294 }
295}
296
297func TestConnIDPeerWithZeroLengthConnIDSendsNewConnectionID(t *testing.T) {
Damien Neil21814e72023-09-20 18:29:51 -0700298 // "An endpoint that selects a zero-length connection ID during the handshake
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700299 // cannot issue a new connection ID."
300 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-8
Damien Neil21814e72023-09-20 18:29:51 -0700301 tc := newTestConn(t, clientSide, func(p *transportParameters) {
302 p.initialSrcConnID = []byte{}
303 })
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700304 tc.peerConnID = []byte{}
305 tc.ignoreFrame(frameTypeAck)
306 tc.uncheckedHandshake()
307
308 tc.writeFrames(packetType1RTT,
309 debugFrameNewConnectionID{
310 seq: 1,
311 connID: testPeerConnID(1),
312 })
313 tc.wantFrame("invalid NEW_CONNECTION_ID: previous conn id is zero-length",
314 packetType1RTT, debugFrameConnectionCloseTransport{
315 code: errProtocolViolation,
316 })
317}
318
319func TestConnIDPeerRequestsRetirement(t *testing.T) {
320 // "Upon receipt of an increased Retire Prior To field, the peer MUST
321 // stop using the corresponding connection IDs and retire them with
322 // RETIRE_CONNECTION_ID frames [...]"
323 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.2-5
324 tc := newTestConn(t, clientSide)
325 tc.handshake()
326 tc.ignoreFrame(frameTypeAck)
327
328 tc.writeFrames(packetType1RTT,
329 debugFrameNewConnectionID{
330 seq: 2,
331 retirePriorTo: 1,
332 connID: testPeerConnID(2),
333 })
334 tc.wantFrame("peer asked for conn id 0 to be retired",
335 packetType1RTT, debugFrameRetireConnectionID{
336 seq: 0,
337 })
Damien Neil52fbe372023-08-13 10:33:31 -0400338 if got, want := tc.lastPacket.dstConnID, testPeerConnID(1); !bytes.Equal(got, want) {
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700339 t.Fatalf("used destination conn id {%x}, want {%x}", got, want)
340 }
341}
342
343func TestConnIDPeerDoesNotAcknowledgeRetirement(t *testing.T) {
344 // "An endpoint SHOULD limit the number of connection IDs it has retired locally
345 // for which RETIRE_CONNECTION_ID frames have not yet been acknowledged."
346 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.2-6
347 tc := newTestConn(t, clientSide)
348 tc.handshake()
349 tc.ignoreFrame(frameTypeAck)
350 tc.ignoreFrame(frameTypeRetireConnectionID)
351
352 // Send a number of NEW_CONNECTION_ID frames, each retiring an old one.
353 for seq := int64(0); seq < 7; seq++ {
354 tc.writeFrames(packetType1RTT,
355 debugFrameNewConnectionID{
356 seq: seq + 2,
357 retirePriorTo: seq + 1,
358 connID: testPeerConnID(seq + 2),
359 })
360 // We're ignoring the RETIRE_CONNECTION_ID frames.
361 }
362 tc.wantFrame("number of retired, unacked conn ids is too large",
363 packetType1RTT, debugFrameConnectionCloseTransport{
364 code: errConnectionIDLimit,
365 })
366}
367
368func TestConnIDRepeatedNewConnectionIDFrame(t *testing.T) {
369 // "Receipt of the same [NEW_CONNECTION_ID] frame multiple times
370 // MUST NOT be treated as a connection error.
371 // https://www.rfc-editor.org/rfc/rfc9000#section-19.15-7
372 tc := newTestConn(t, clientSide)
373 tc.handshake()
374 tc.ignoreFrame(frameTypeAck)
375
376 for i := 0; i < 4; i++ {
377 tc.writeFrames(packetType1RTT,
378 debugFrameNewConnectionID{
379 seq: 2,
380 retirePriorTo: 1,
381 connID: testPeerConnID(2),
382 })
383 }
384 tc.wantFrame("peer asked for conn id to be retired",
385 packetType1RTT, debugFrameRetireConnectionID{
386 seq: 0,
387 })
388 tc.wantIdle("repeated NEW_CONNECTION_ID frames are not an error")
389}
390
391func TestConnIDForSequenceNumberChanges(t *testing.T) {
392 // "[...] if a sequence number is used for different connection IDs,
393 // the endpoint MAY treat that receipt as a connection error
394 // of type PROTOCOL_VIOLATION."
395 // https://www.rfc-editor.org/rfc/rfc9000#section-19.15-8
396 tc := newTestConn(t, clientSide)
397 tc.handshake()
398 tc.ignoreFrame(frameTypeAck)
399 tc.ignoreFrame(frameTypeRetireConnectionID)
400
401 tc.writeFrames(packetType1RTT,
402 debugFrameNewConnectionID{
403 seq: 2,
404 retirePriorTo: 1,
405 connID: testPeerConnID(2),
406 })
407 tc.writeFrames(packetType1RTT,
408 debugFrameNewConnectionID{
409 seq: 2,
410 retirePriorTo: 1,
411 connID: testPeerConnID(3),
412 })
413 tc.wantFrame("connection ID for sequence 0 has changed",
414 packetType1RTT, debugFrameConnectionCloseTransport{
415 code: errProtocolViolation,
416 })
417}
418
419func TestConnIDRetirePriorToAfterNewConnID(t *testing.T) {
420 // "Receiving a value in the Retire Prior To field that is greater than
421 // that in the Sequence Number field MUST be treated as a connection error
422 // of type FRAME_ENCODING_ERROR.
423 // https://www.rfc-editor.org/rfc/rfc9000#section-19.15-9
424 tc := newTestConn(t, serverSide)
425 tc.handshake()
426 tc.ignoreFrame(frameTypeAck)
427
428 tc.writeFrames(packetType1RTT,
429 debugFrameNewConnectionID{
430 retirePriorTo: 3,
431 seq: 2,
432 connID: testPeerConnID(2),
433 })
434 tc.wantFrame("invalid NEW_CONNECTION_ID: retired the new conn id",
435 packetType1RTT, debugFrameConnectionCloseTransport{
436 code: errFrameEncoding,
437 })
438}
439
440func TestConnIDAlreadyRetired(t *testing.T) {
441 // "An endpoint that receives a NEW_CONNECTION_ID frame with a
442 // sequence number smaller than the Retire Prior To field of a
443 // previously received NEW_CONNECTION_ID frame MUST send a
444 // corresponding RETIRE_CONNECTION_ID frame [...]"
445 // https://www.rfc-editor.org/rfc/rfc9000#section-19.15-11
446 tc := newTestConn(t, clientSide)
447 tc.handshake()
448 tc.ignoreFrame(frameTypeAck)
449
450 tc.writeFrames(packetType1RTT,
451 debugFrameNewConnectionID{
452 seq: 4,
453 retirePriorTo: 3,
454 connID: testPeerConnID(4),
455 })
456 tc.wantFrame("peer asked for conn id to be retired",
457 packetType1RTT, debugFrameRetireConnectionID{
458 seq: 0,
459 })
460 tc.wantFrame("peer asked for conn id to be retired",
461 packetType1RTT, debugFrameRetireConnectionID{
462 seq: 1,
463 })
464 tc.writeFrames(packetType1RTT,
465 debugFrameNewConnectionID{
466 seq: 2,
467 retirePriorTo: 0,
468 connID: testPeerConnID(2),
469 })
470 tc.wantFrame("NEW_CONNECTION_ID was for an already-retired ID",
471 packetType1RTT, debugFrameRetireConnectionID{
472 seq: 2,
473 })
474}
475
476func TestConnIDRepeatedRetireConnectionIDFrame(t *testing.T) {
477 tc := newTestConn(t, clientSide)
478 tc.handshake()
479 tc.ignoreFrame(frameTypeAck)
480
481 for i := 0; i < 4; i++ {
482 tc.writeFrames(packetType1RTT,
483 debugFrameRetireConnectionID{
484 seq: 0,
485 })
486 }
487 tc.wantFrame("issue new conn id after peer retires one",
488 packetType1RTT, debugFrameNewConnectionID{
489 retirePriorTo: 1,
490 seq: 2,
491 connID: testLocalConnID(2),
Damien Neil642f15e2023-10-18 13:33:05 -0400492 token: testLocalStatelessResetToken(2),
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700493 })
494 tc.wantIdle("repeated RETIRE_CONNECTION_ID frames are not an error")
495}
496
497func TestConnIDRetiredUnsent(t *testing.T) {
498 // "Receipt of a RETIRE_CONNECTION_ID frame containing a sequence number
499 // greater than any previously sent to the peer MUST be treated as a
500 // connection error of type PROTOCOL_VIOLATION."
501 // https://www.rfc-editor.org/rfc/rfc9000#section-19.16-7
502 tc := newTestConn(t, clientSide)
503 tc.handshake()
504 tc.ignoreFrame(frameTypeAck)
505
506 tc.writeFrames(packetType1RTT,
507 debugFrameRetireConnectionID{
508 seq: 2,
509 })
510 tc.wantFrame("invalid NEW_CONNECTION_ID: previous conn id is zero-length",
511 packetType1RTT, debugFrameConnectionCloseTransport{
512 code: errProtocolViolation,
513 })
514}
515
516func TestConnIDUsePreferredAddressConnID(t *testing.T) {
517 // Peer gives us a connection ID in the preferred address transport parameter.
518 // We don't use the preferred address at this time, but we should use the
519 // connection ID. (It isn't tied to any specific address.)
520 //
521 // This test will probably need updating if/when we start using the preferred address.
522 cid := testPeerConnID(10)
523 tc := newTestConn(t, serverSide, func(p *transportParameters) {
524 p.preferredAddrV4 = netip.MustParseAddrPort("0.0.0.0:0")
525 p.preferredAddrV6 = netip.MustParseAddrPort("[::0]:0")
526 p.preferredAddrConnID = cid
527 p.preferredAddrResetToken = make([]byte, 16)
528 })
529 tc.uncheckedHandshake()
530 tc.ignoreFrame(frameTypeAck)
531
532 tc.writeFrames(packetType1RTT,
533 debugFrameNewConnectionID{
534 seq: 2,
535 retirePriorTo: 1,
536 connID: []byte{0xff},
537 })
538 tc.wantFrame("peer asked for conn id 0 to be retired",
539 packetType1RTT, debugFrameRetireConnectionID{
540 seq: 0,
541 })
Damien Neil52fbe372023-08-13 10:33:31 -0400542 if got, want := tc.lastPacket.dstConnID, cid; !bytes.Equal(got, want) {
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700543 t.Fatalf("used destination conn id {%x}, want {%x} from preferred address transport parameter", got, want)
544 }
545}
546
547func TestConnIDPeerProvidesPreferredAddrAndTooManyConnIDs(t *testing.T) {
548 // Peer gives us more conn ids than our advertised limit,
549 // including a conn id in the preferred address transport parameter.
550 cid := testPeerConnID(10)
551 tc := newTestConn(t, serverSide, func(p *transportParameters) {
552 p.preferredAddrV4 = netip.MustParseAddrPort("0.0.0.0:0")
553 p.preferredAddrV6 = netip.MustParseAddrPort("[::0]:0")
554 p.preferredAddrConnID = cid
555 p.preferredAddrResetToken = make([]byte, 16)
556 })
557 tc.uncheckedHandshake()
558 tc.ignoreFrame(frameTypeAck)
559
560 tc.writeFrames(packetType1RTT,
561 debugFrameNewConnectionID{
562 seq: 2,
563 retirePriorTo: 0,
564 connID: testPeerConnID(2),
565 })
566 tc.wantFrame("peer provided 3 connection IDs, our limit is 2",
567 packetType1RTT, debugFrameConnectionCloseTransport{
568 code: errConnectionIDLimit,
569 })
570}
571
572func TestConnIDPeerWithZeroLengthIDProvidesPreferredAddr(t *testing.T) {
573 // Peer gives us more conn ids than our advertised limit,
574 // including a conn id in the preferred address transport parameter.
575 tc := newTestConn(t, serverSide, func(p *transportParameters) {
Damien Neil21814e72023-09-20 18:29:51 -0700576 p.initialSrcConnID = []byte{}
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700577 p.preferredAddrV4 = netip.MustParseAddrPort("0.0.0.0:0")
578 p.preferredAddrV6 = netip.MustParseAddrPort("[::0]:0")
579 p.preferredAddrConnID = testPeerConnID(1)
580 p.preferredAddrResetToken = make([]byte, 16)
Damien Neilec29a942023-11-03 16:37:26 -0700581 }, func(cids *newServerConnIDs) {
582 cids.srcConnID = []byte{}
583 }, func(tc *testConn) {
584 tc.peerConnID = []byte{}
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700585 })
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700586
587 tc.writeFrames(packetTypeInitial,
588 debugFrameCrypto{
589 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
590 })
591 tc.wantFrame("peer with zero-length connection ID tried to provide another in transport parameters",
592 packetTypeInitial, debugFrameConnectionCloseTransport{
593 code: errProtocolViolation,
594 })
595}
Damien Neil21814e72023-09-20 18:29:51 -0700596
597func TestConnIDInitialSrcConnIDMismatch(t *testing.T) {
598 // "Endpoints MUST validate that received [initial_source_connection_id]
599 // parameters match received connection ID values."
600 // https://www.rfc-editor.org/rfc/rfc9000#section-7.3-3
601 testSides(t, "", func(t *testing.T, side connSide) {
602 tc := newTestConn(t, side, func(p *transportParameters) {
603 p.initialSrcConnID = []byte("invalid")
604 })
605 tc.ignoreFrame(frameTypeAck)
606 tc.ignoreFrame(frameTypeCrypto)
607 tc.writeFrames(packetTypeInitial,
608 debugFrameCrypto{
609 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
610 })
611 if side == clientSide {
612 // Server transport parameters are carried in the Handshake packet.
613 tc.writeFrames(packetTypeHandshake,
614 debugFrameCrypto{
615 data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
616 })
617 }
618 tc.wantFrame("initial_source_connection_id transport parameter mismatch",
619 packetTypeInitial, debugFrameConnectionCloseTransport{
620 code: errTransportParameter,
621 })
622 })
623}
Damien Neil642f15e2023-10-18 13:33:05 -0400624
625func TestConnIDsCleanedUpAfterClose(t *testing.T) {
626 testSides(t, "", func(t *testing.T, side connSide) {
627 tc := newTestConn(t, side, func(p *transportParameters) {
628 if side == clientSide {
629 token := testPeerStatelessResetToken(0)
630 p.statelessResetToken = token[:]
631 }
632 })
633 tc.handshake()
634 tc.ignoreFrame(frameTypeAck)
635 tc.writeFrames(packetType1RTT,
636 debugFrameNewConnectionID{
637 seq: 2,
638 retirePriorTo: 1,
639 connID: testPeerConnID(2),
640 token: testPeerStatelessResetToken(0),
641 })
642 tc.wantFrame("peer asked for conn id 0 to be retired",
643 packetType1RTT, debugFrameRetireConnectionID{
644 seq: 0,
645 })
646 tc.writeFrames(packetType1RTT, debugFrameConnectionCloseTransport{})
647 tc.conn.Abort(nil)
648 tc.wantFrame("CONN_CLOSE sent after user closes connection",
649 packetType1RTT, debugFrameConnectionCloseTransport{})
650
651 // Wait for the conn to drain.
652 // Then wait for the conn loop to exit,
653 // and force an immediate sync of the connsMap updates
654 // (normally only done by the listener read loop).
655 tc.advanceToTimer()
656 <-tc.conn.donec
657 tc.listener.l.connsMap.applyUpdates()
658
659 if got := len(tc.listener.l.connsMap.byConnID); got != 0 {
660 t.Errorf("%v conn ids in listener map after closing, want 0", got)
661 }
662 if got := len(tc.listener.l.connsMap.byResetToken); got != 0 {
663 t.Errorf("%v reset tokens in listener map after closing, want 0", got)
664 }
665 })
666}