blob: 784c5e2c489582974ea03fe74b1203bd97aaae00 [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 }
50 wantRemote := []connID{{
Damien Neil47caaff2023-09-12 15:19:59 -070051 cid: testPeerConnID(0),
Damien Neil57553cb2022-10-13 12:09:20 -070052 seq: 0,
53 }}
Damien Neil47caaff2023-09-12 15:19:59 -070054 if got := tc.conn.connIDState.remote; !connIDListEqual(got, wantRemote) {
55 t.Errorf("remote ids: %v, want %v", fmtConnIDList(got), fmtConnIDList(wantRemote))
Damien Neil57553cb2022-10-13 12:09:20 -070056 }
57}
58
59func TestConnIDServerHandshake(t *testing.T) {
Damien Neil47caaff2023-09-12 15:19:59 -070060 tc := newTestConn(t, serverSide)
Damien Neil57553cb2022-10-13 12:09:20 -070061 // On initialization, the server is provided with the client-chosen
62 // transient connection ID, and allocates an ID of its own.
63 // The Initial packet sets the remote connection ID.
Damien Neil47caaff2023-09-12 15:19:59 -070064 tc.writeFrames(packetTypeInitial,
65 debugFrameCrypto{
66 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial][:1],
67 })
68 if got, want := tc.conn.connIDState.srcConnID(), testLocalConnID(0); !bytes.Equal(got, want) {
Damien Neil57553cb2022-10-13 12:09:20 -070069 t.Errorf("after initClient: srcConnID = %q, want %q", got, want)
70 }
Damien Neil47caaff2023-09-12 15:19:59 -070071 dstConnID, _ := tc.conn.connIDState.dstConnID()
72 if got, want := dstConnID, testPeerConnID(0); !bytes.Equal(got, want) {
Damien Neil57553cb2022-10-13 12:09:20 -070073 t.Errorf("after initClient: dstConnID = %q, want %q", got, want)
74 }
75
Damien Neil47caaff2023-09-12 15:19:59 -070076 // The Initial flight of CRYPTO data includes transport parameters,
77 // which cause us to allocate another local connection ID.
78 tc.writeFrames(packetTypeInitial,
79 debugFrameCrypto{
80 off: 1,
81 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial][1:],
82 })
Damien Neil57553cb2022-10-13 12:09:20 -070083 wantLocal := []connID{{
Damien Neil47caaff2023-09-12 15:19:59 -070084 cid: testPeerConnID(-1),
Damien Neil57553cb2022-10-13 12:09:20 -070085 seq: -1,
86 }, {
Damien Neil47caaff2023-09-12 15:19:59 -070087 cid: testLocalConnID(0),
Damien Neil57553cb2022-10-13 12:09:20 -070088 seq: 0,
Damien Neil47caaff2023-09-12 15:19:59 -070089 }, {
90 cid: testLocalConnID(1),
91 seq: 1,
Damien Neil57553cb2022-10-13 12:09:20 -070092 }}
Damien Neil47caaff2023-09-12 15:19:59 -070093 if got := tc.conn.connIDState.local; !connIDListEqual(got, wantLocal) {
94 t.Errorf("local ids: %v, want %v", fmtConnIDList(got), fmtConnIDList(wantLocal))
Damien Neil57553cb2022-10-13 12:09:20 -070095 }
96 wantRemote := []connID{{
Damien Neil47caaff2023-09-12 15:19:59 -070097 cid: testPeerConnID(0),
Damien Neil57553cb2022-10-13 12:09:20 -070098 seq: 0,
99 }}
Damien Neil47caaff2023-09-12 15:19:59 -0700100 if got := tc.conn.connIDState.remote; !connIDListEqual(got, wantRemote) {
101 t.Errorf("remote ids: %v, want %v", fmtConnIDList(got), fmtConnIDList(wantRemote))
Damien Neil57553cb2022-10-13 12:09:20 -0700102 }
103
104 // The client's first Handshake packet permits the server to discard the
105 // transient connection ID.
Damien Neil47caaff2023-09-12 15:19:59 -0700106 tc.writeFrames(packetTypeHandshake,
107 debugFrameCrypto{
108 data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
109 })
Damien Neil57553cb2022-10-13 12:09:20 -0700110 wantLocal = []connID{{
Damien Neil47caaff2023-09-12 15:19:59 -0700111 cid: testLocalConnID(0),
Damien Neil57553cb2022-10-13 12:09:20 -0700112 seq: 0,
Damien Neil47caaff2023-09-12 15:19:59 -0700113 }, {
114 cid: testLocalConnID(1),
115 seq: 1,
Damien Neil57553cb2022-10-13 12:09:20 -0700116 }}
Damien Neil47caaff2023-09-12 15:19:59 -0700117 if got := tc.conn.connIDState.local; !connIDListEqual(got, wantLocal) {
118 t.Errorf("local ids: %v, want %v", fmtConnIDList(got), fmtConnIDList(wantLocal))
Damien Neil57553cb2022-10-13 12:09:20 -0700119 }
120}
121
Damien Neil47caaff2023-09-12 15:19:59 -0700122func connIDListEqual(a, b []connID) bool {
123 if len(a) != len(b) {
124 return false
Damien Neil57553cb2022-10-13 12:09:20 -0700125 }
Damien Neil47caaff2023-09-12 15:19:59 -0700126 for i := range a {
127 if a[i].seq != b[i].seq {
128 return false
129 }
130 if !bytes.Equal(a[i].cid, b[i].cid) {
131 return false
132 }
133 }
134 return true
135}
136
137func fmtConnIDList(s []connID) string {
138 var strs []string
139 for _, cid := range s {
140 strs = append(strs, fmt.Sprintf("[seq:%v cid:{%x}]", cid.seq, cid.cid))
141 }
142 return "{" + strings.Join(strs, " ") + "}"
Damien Neil57553cb2022-10-13 12:09:20 -0700143}
144
145func TestNewRandomConnID(t *testing.T) {
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700146 cid, err := newRandomConnID(0)
Damien Neil57553cb2022-10-13 12:09:20 -0700147 if len(cid) != connIDLen || err != nil {
148 t.Fatalf("newConnID() = %x, %v; want %v bytes", cid, connIDLen, err)
149 }
150}
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700151
152func TestConnIDPeerRequestsManyIDs(t *testing.T) {
153 // "An endpoint SHOULD ensure that its peer has a sufficient number
154 // of available and unused connection IDs."
155 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-4
156 //
157 // "An endpoint MAY limit the total number of connection IDs
158 // issued for each connection [...]"
159 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-6
160 //
161 // Peer requests 100 connection IDs.
162 // We give them 4 in total.
163 tc := newTestConn(t, serverSide, func(p *transportParameters) {
164 p.activeConnIDLimit = 100
165 })
166 tc.ignoreFrame(frameTypeAck)
167 tc.ignoreFrame(frameTypeCrypto)
168
169 tc.writeFrames(packetTypeInitial,
170 debugFrameCrypto{
171 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
172 })
173 tc.wantFrame("provide additional connection ID 1",
174 packetType1RTT, debugFrameNewConnectionID{
175 seq: 1,
176 connID: testLocalConnID(1),
177 })
178 tc.wantFrame("provide additional connection ID 2",
179 packetType1RTT, debugFrameNewConnectionID{
180 seq: 2,
181 connID: testLocalConnID(2),
182 })
183 tc.wantFrame("provide additional connection ID 3",
184 packetType1RTT, debugFrameNewConnectionID{
185 seq: 3,
186 connID: testLocalConnID(3),
187 })
188 tc.wantIdle("connection ID limit reached, no more to provide")
189}
190
191func TestConnIDPeerProvidesTooManyIDs(t *testing.T) {
192 // "An endpoint MUST NOT provide more connection IDs than the peer's limit."
193 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-4
194 tc := newTestConn(t, serverSide)
195 tc.handshake()
196 tc.ignoreFrame(frameTypeAck)
197
198 tc.writeFrames(packetType1RTT,
199 debugFrameNewConnectionID{
200 seq: 2,
201 connID: testLocalConnID(2),
202 })
203 tc.wantFrame("peer provided 3 connection IDs, our limit is 2",
204 packetType1RTT, debugFrameConnectionCloseTransport{
205 code: errConnectionIDLimit,
206 })
207}
208
209func TestConnIDPeerTemporarilyExceedsActiveConnIDLimit(t *testing.T) {
210 // "An endpoint MAY send connection IDs that temporarily exceed a peer's limit
211 // if the NEW_CONNECTION_ID frame also requires the retirement of any excess [...]"
212 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-4
213 tc := newTestConn(t, serverSide)
214 tc.handshake()
215 tc.ignoreFrame(frameTypeAck)
216
217 tc.writeFrames(packetType1RTT,
218 debugFrameNewConnectionID{
219 retirePriorTo: 2,
220 seq: 2,
221 connID: testPeerConnID(2),
222 }, debugFrameNewConnectionID{
223 retirePriorTo: 2,
224 seq: 3,
225 connID: testPeerConnID(3),
226 })
227 tc.wantFrame("peer requested we retire conn id 0",
228 packetType1RTT, debugFrameRetireConnectionID{
229 seq: 0,
230 })
231 tc.wantFrame("peer requested we retire conn id 1",
232 packetType1RTT, debugFrameRetireConnectionID{
233 seq: 1,
234 })
235}
236
237func TestConnIDPeerRetiresConnID(t *testing.T) {
238 // "An endpoint SHOULD supply a new connection ID when the peer retires a connection ID."
239 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-6
240 for _, side := range []connSide{
241 clientSide,
242 serverSide,
243 } {
244 t.Run(side.String(), func(t *testing.T) {
245 tc := newTestConn(t, side)
246 tc.handshake()
247 tc.ignoreFrame(frameTypeAck)
248
249 tc.writeFrames(packetType1RTT,
250 debugFrameRetireConnectionID{
251 seq: 0,
252 })
253 tc.wantFrame("provide replacement connection ID",
254 packetType1RTT, debugFrameNewConnectionID{
255 seq: 2,
256 retirePriorTo: 1,
257 connID: testLocalConnID(2),
258 })
259 })
260 }
261}
262
263func TestConnIDPeerWithZeroLengthConnIDSendsNewConnectionID(t *testing.T) {
Damien Neil21814e72023-09-20 18:29:51 -0700264 // "An endpoint that selects a zero-length connection ID during the handshake
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700265 // cannot issue a new connection ID."
266 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.1-8
Damien Neil21814e72023-09-20 18:29:51 -0700267 tc := newTestConn(t, clientSide, func(p *transportParameters) {
268 p.initialSrcConnID = []byte{}
269 })
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700270 tc.peerConnID = []byte{}
271 tc.ignoreFrame(frameTypeAck)
272 tc.uncheckedHandshake()
273
274 tc.writeFrames(packetType1RTT,
275 debugFrameNewConnectionID{
276 seq: 1,
277 connID: testPeerConnID(1),
278 })
279 tc.wantFrame("invalid NEW_CONNECTION_ID: previous conn id is zero-length",
280 packetType1RTT, debugFrameConnectionCloseTransport{
281 code: errProtocolViolation,
282 })
283}
284
285func TestConnIDPeerRequestsRetirement(t *testing.T) {
286 // "Upon receipt of an increased Retire Prior To field, the peer MUST
287 // stop using the corresponding connection IDs and retire them with
288 // RETIRE_CONNECTION_ID frames [...]"
289 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.2-5
290 tc := newTestConn(t, clientSide)
291 tc.handshake()
292 tc.ignoreFrame(frameTypeAck)
293
294 tc.writeFrames(packetType1RTT,
295 debugFrameNewConnectionID{
296 seq: 2,
297 retirePriorTo: 1,
298 connID: testPeerConnID(2),
299 })
300 tc.wantFrame("peer asked for conn id 0 to be retired",
301 packetType1RTT, debugFrameRetireConnectionID{
302 seq: 0,
303 })
Damien Neil52fbe372023-08-13 10:33:31 -0400304 if got, want := tc.lastPacket.dstConnID, testPeerConnID(1); !bytes.Equal(got, want) {
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700305 t.Fatalf("used destination conn id {%x}, want {%x}", got, want)
306 }
307}
308
309func TestConnIDPeerDoesNotAcknowledgeRetirement(t *testing.T) {
310 // "An endpoint SHOULD limit the number of connection IDs it has retired locally
311 // for which RETIRE_CONNECTION_ID frames have not yet been acknowledged."
312 // https://www.rfc-editor.org/rfc/rfc9000#section-5.1.2-6
313 tc := newTestConn(t, clientSide)
314 tc.handshake()
315 tc.ignoreFrame(frameTypeAck)
316 tc.ignoreFrame(frameTypeRetireConnectionID)
317
318 // Send a number of NEW_CONNECTION_ID frames, each retiring an old one.
319 for seq := int64(0); seq < 7; seq++ {
320 tc.writeFrames(packetType1RTT,
321 debugFrameNewConnectionID{
322 seq: seq + 2,
323 retirePriorTo: seq + 1,
324 connID: testPeerConnID(seq + 2),
325 })
326 // We're ignoring the RETIRE_CONNECTION_ID frames.
327 }
328 tc.wantFrame("number of retired, unacked conn ids is too large",
329 packetType1RTT, debugFrameConnectionCloseTransport{
330 code: errConnectionIDLimit,
331 })
332}
333
334func TestConnIDRepeatedNewConnectionIDFrame(t *testing.T) {
335 // "Receipt of the same [NEW_CONNECTION_ID] frame multiple times
336 // MUST NOT be treated as a connection error.
337 // https://www.rfc-editor.org/rfc/rfc9000#section-19.15-7
338 tc := newTestConn(t, clientSide)
339 tc.handshake()
340 tc.ignoreFrame(frameTypeAck)
341
342 for i := 0; i < 4; i++ {
343 tc.writeFrames(packetType1RTT,
344 debugFrameNewConnectionID{
345 seq: 2,
346 retirePriorTo: 1,
347 connID: testPeerConnID(2),
348 })
349 }
350 tc.wantFrame("peer asked for conn id to be retired",
351 packetType1RTT, debugFrameRetireConnectionID{
352 seq: 0,
353 })
354 tc.wantIdle("repeated NEW_CONNECTION_ID frames are not an error")
355}
356
357func TestConnIDForSequenceNumberChanges(t *testing.T) {
358 // "[...] if a sequence number is used for different connection IDs,
359 // the endpoint MAY treat that receipt as a connection error
360 // of type PROTOCOL_VIOLATION."
361 // https://www.rfc-editor.org/rfc/rfc9000#section-19.15-8
362 tc := newTestConn(t, clientSide)
363 tc.handshake()
364 tc.ignoreFrame(frameTypeAck)
365 tc.ignoreFrame(frameTypeRetireConnectionID)
366
367 tc.writeFrames(packetType1RTT,
368 debugFrameNewConnectionID{
369 seq: 2,
370 retirePriorTo: 1,
371 connID: testPeerConnID(2),
372 })
373 tc.writeFrames(packetType1RTT,
374 debugFrameNewConnectionID{
375 seq: 2,
376 retirePriorTo: 1,
377 connID: testPeerConnID(3),
378 })
379 tc.wantFrame("connection ID for sequence 0 has changed",
380 packetType1RTT, debugFrameConnectionCloseTransport{
381 code: errProtocolViolation,
382 })
383}
384
385func TestConnIDRetirePriorToAfterNewConnID(t *testing.T) {
386 // "Receiving a value in the Retire Prior To field that is greater than
387 // that in the Sequence Number field MUST be treated as a connection error
388 // of type FRAME_ENCODING_ERROR.
389 // https://www.rfc-editor.org/rfc/rfc9000#section-19.15-9
390 tc := newTestConn(t, serverSide)
391 tc.handshake()
392 tc.ignoreFrame(frameTypeAck)
393
394 tc.writeFrames(packetType1RTT,
395 debugFrameNewConnectionID{
396 retirePriorTo: 3,
397 seq: 2,
398 connID: testPeerConnID(2),
399 })
400 tc.wantFrame("invalid NEW_CONNECTION_ID: retired the new conn id",
401 packetType1RTT, debugFrameConnectionCloseTransport{
402 code: errFrameEncoding,
403 })
404}
405
406func TestConnIDAlreadyRetired(t *testing.T) {
407 // "An endpoint that receives a NEW_CONNECTION_ID frame with a
408 // sequence number smaller than the Retire Prior To field of a
409 // previously received NEW_CONNECTION_ID frame MUST send a
410 // corresponding RETIRE_CONNECTION_ID frame [...]"
411 // https://www.rfc-editor.org/rfc/rfc9000#section-19.15-11
412 tc := newTestConn(t, clientSide)
413 tc.handshake()
414 tc.ignoreFrame(frameTypeAck)
415
416 tc.writeFrames(packetType1RTT,
417 debugFrameNewConnectionID{
418 seq: 4,
419 retirePriorTo: 3,
420 connID: testPeerConnID(4),
421 })
422 tc.wantFrame("peer asked for conn id to be retired",
423 packetType1RTT, debugFrameRetireConnectionID{
424 seq: 0,
425 })
426 tc.wantFrame("peer asked for conn id to be retired",
427 packetType1RTT, debugFrameRetireConnectionID{
428 seq: 1,
429 })
430 tc.writeFrames(packetType1RTT,
431 debugFrameNewConnectionID{
432 seq: 2,
433 retirePriorTo: 0,
434 connID: testPeerConnID(2),
435 })
436 tc.wantFrame("NEW_CONNECTION_ID was for an already-retired ID",
437 packetType1RTT, debugFrameRetireConnectionID{
438 seq: 2,
439 })
440}
441
442func TestConnIDRepeatedRetireConnectionIDFrame(t *testing.T) {
443 tc := newTestConn(t, clientSide)
444 tc.handshake()
445 tc.ignoreFrame(frameTypeAck)
446
447 for i := 0; i < 4; i++ {
448 tc.writeFrames(packetType1RTT,
449 debugFrameRetireConnectionID{
450 seq: 0,
451 })
452 }
453 tc.wantFrame("issue new conn id after peer retires one",
454 packetType1RTT, debugFrameNewConnectionID{
455 retirePriorTo: 1,
456 seq: 2,
457 connID: testLocalConnID(2),
458 })
459 tc.wantIdle("repeated RETIRE_CONNECTION_ID frames are not an error")
460}
461
462func TestConnIDRetiredUnsent(t *testing.T) {
463 // "Receipt of a RETIRE_CONNECTION_ID frame containing a sequence number
464 // greater than any previously sent to the peer MUST be treated as a
465 // connection error of type PROTOCOL_VIOLATION."
466 // https://www.rfc-editor.org/rfc/rfc9000#section-19.16-7
467 tc := newTestConn(t, clientSide)
468 tc.handshake()
469 tc.ignoreFrame(frameTypeAck)
470
471 tc.writeFrames(packetType1RTT,
472 debugFrameRetireConnectionID{
473 seq: 2,
474 })
475 tc.wantFrame("invalid NEW_CONNECTION_ID: previous conn id is zero-length",
476 packetType1RTT, debugFrameConnectionCloseTransport{
477 code: errProtocolViolation,
478 })
479}
480
481func TestConnIDUsePreferredAddressConnID(t *testing.T) {
482 // Peer gives us a connection ID in the preferred address transport parameter.
483 // We don't use the preferred address at this time, but we should use the
484 // connection ID. (It isn't tied to any specific address.)
485 //
486 // This test will probably need updating if/when we start using the preferred address.
487 cid := testPeerConnID(10)
488 tc := newTestConn(t, serverSide, func(p *transportParameters) {
489 p.preferredAddrV4 = netip.MustParseAddrPort("0.0.0.0:0")
490 p.preferredAddrV6 = netip.MustParseAddrPort("[::0]:0")
491 p.preferredAddrConnID = cid
492 p.preferredAddrResetToken = make([]byte, 16)
493 })
494 tc.uncheckedHandshake()
495 tc.ignoreFrame(frameTypeAck)
496
497 tc.writeFrames(packetType1RTT,
498 debugFrameNewConnectionID{
499 seq: 2,
500 retirePriorTo: 1,
501 connID: []byte{0xff},
502 })
503 tc.wantFrame("peer asked for conn id 0 to be retired",
504 packetType1RTT, debugFrameRetireConnectionID{
505 seq: 0,
506 })
Damien Neil52fbe372023-08-13 10:33:31 -0400507 if got, want := tc.lastPacket.dstConnID, cid; !bytes.Equal(got, want) {
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700508 t.Fatalf("used destination conn id {%x}, want {%x} from preferred address transport parameter", got, want)
509 }
510}
511
512func TestConnIDPeerProvidesPreferredAddrAndTooManyConnIDs(t *testing.T) {
513 // Peer gives us more conn ids than our advertised limit,
514 // including a conn id in the preferred address transport parameter.
515 cid := testPeerConnID(10)
516 tc := newTestConn(t, serverSide, func(p *transportParameters) {
517 p.preferredAddrV4 = netip.MustParseAddrPort("0.0.0.0:0")
518 p.preferredAddrV6 = netip.MustParseAddrPort("[::0]:0")
519 p.preferredAddrConnID = cid
520 p.preferredAddrResetToken = make([]byte, 16)
521 })
522 tc.uncheckedHandshake()
523 tc.ignoreFrame(frameTypeAck)
524
525 tc.writeFrames(packetType1RTT,
526 debugFrameNewConnectionID{
527 seq: 2,
528 retirePriorTo: 0,
529 connID: testPeerConnID(2),
530 })
531 tc.wantFrame("peer provided 3 connection IDs, our limit is 2",
532 packetType1RTT, debugFrameConnectionCloseTransport{
533 code: errConnectionIDLimit,
534 })
535}
536
537func TestConnIDPeerWithZeroLengthIDProvidesPreferredAddr(t *testing.T) {
538 // Peer gives us more conn ids than our advertised limit,
539 // including a conn id in the preferred address transport parameter.
540 tc := newTestConn(t, serverSide, func(p *transportParameters) {
Damien Neil21814e72023-09-20 18:29:51 -0700541 p.initialSrcConnID = []byte{}
Damien Neilbd8ac9e2023-07-20 16:45:15 -0700542 p.preferredAddrV4 = netip.MustParseAddrPort("0.0.0.0:0")
543 p.preferredAddrV6 = netip.MustParseAddrPort("[::0]:0")
544 p.preferredAddrConnID = testPeerConnID(1)
545 p.preferredAddrResetToken = make([]byte, 16)
546 })
547 tc.peerConnID = []byte{}
548
549 tc.writeFrames(packetTypeInitial,
550 debugFrameCrypto{
551 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
552 })
553 tc.wantFrame("peer with zero-length connection ID tried to provide another in transport parameters",
554 packetTypeInitial, debugFrameConnectionCloseTransport{
555 code: errProtocolViolation,
556 })
557}
Damien Neil21814e72023-09-20 18:29:51 -0700558
559func TestConnIDInitialSrcConnIDMismatch(t *testing.T) {
560 // "Endpoints MUST validate that received [initial_source_connection_id]
561 // parameters match received connection ID values."
562 // https://www.rfc-editor.org/rfc/rfc9000#section-7.3-3
563 testSides(t, "", func(t *testing.T, side connSide) {
564 tc := newTestConn(t, side, func(p *transportParameters) {
565 p.initialSrcConnID = []byte("invalid")
566 })
567 tc.ignoreFrame(frameTypeAck)
568 tc.ignoreFrame(frameTypeCrypto)
569 tc.writeFrames(packetTypeInitial,
570 debugFrameCrypto{
571 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
572 })
573 if side == clientSide {
574 // Server transport parameters are carried in the Handshake packet.
575 tc.writeFrames(packetTypeHandshake,
576 debugFrameCrypto{
577 data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
578 })
579 }
580 tc.wantFrame("initial_source_connection_id transport parameter mismatch",
581 packetTypeInitial, debugFrameConnectionCloseTransport{
582 code: errTransportParameter,
583 })
584 })
585}