golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/packet_writer.go (about) 1 // 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 7 package quic 8 9 import ( 10 "encoding/binary" 11 ) 12 13 // A packetWriter constructs QUIC datagrams. 14 // 15 // A datagram consists of one or more packets. 16 // A packet consists of a header followed by one or more frames. 17 // 18 // Packets are written in three steps: 19 // - startProtectedLongHeaderPacket or start1RTT packet prepare the packet; 20 // - append*Frame appends frames to the payload; and 21 // - finishProtectedLongHeaderPacket or finish1RTT finalize the packet. 22 // 23 // The start functions are efficient, so we can start speculatively 24 // writing a packet before we know whether we have any frames to 25 // put in it. The finish functions will abandon the packet if the 26 // payload contains no data. 27 type packetWriter struct { 28 dgramLim int // max datagram size 29 pktLim int // max packet size 30 pktOff int // offset of the start of the current packet 31 payOff int // offset of the payload of the current packet 32 b []byte 33 sent *sentPacket 34 } 35 36 // reset prepares to write a datagram of at most lim bytes. 37 func (w *packetWriter) reset(lim int) { 38 if cap(w.b) < lim { 39 w.b = make([]byte, 0, lim) 40 } 41 w.dgramLim = lim 42 w.b = w.b[:0] 43 } 44 45 // datagram returns the current datagram. 46 func (w *packetWriter) datagram() []byte { 47 return w.b 48 } 49 50 // packet returns the size of the current packet. 51 func (w *packetWriter) packetLen() int { 52 return len(w.b[w.pktOff:]) + aeadOverhead 53 } 54 55 // payload returns the payload of the current packet. 56 func (w *packetWriter) payload() []byte { 57 return w.b[w.payOff:] 58 } 59 60 func (w *packetWriter) abandonPacket() { 61 w.b = w.b[:w.payOff] 62 w.sent.reset() 63 } 64 65 // startProtectedLongHeaderPacket starts writing an Initial, 0-RTT, or Handshake packet. 66 func (w *packetWriter) startProtectedLongHeaderPacket(pnumMaxAcked packetNumber, p longPacket) { 67 if w.sent == nil { 68 w.sent = newSentPacket() 69 } 70 w.pktOff = len(w.b) 71 hdrSize := 1 // packet type 72 hdrSize += 4 // version 73 hdrSize += 1 + len(p.dstConnID) 74 hdrSize += 1 + len(p.srcConnID) 75 switch p.ptype { 76 case packetTypeInitial: 77 hdrSize += sizeVarint(uint64(len(p.extra))) + len(p.extra) 78 } 79 hdrSize += 2 // length, hardcoded to a 2-byte varint 80 pnumOff := len(w.b) + hdrSize 81 hdrSize += packetNumberLength(p.num, pnumMaxAcked) 82 payOff := len(w.b) + hdrSize 83 // Check if we have enough space to hold the packet, including the header, 84 // header protection sample (RFC 9001, section 5.4.2), and encryption overhead. 85 if pnumOff+4+headerProtectionSampleSize+aeadOverhead >= w.dgramLim { 86 // Set the limit on the packet size to be the current write buffer length, 87 // ensuring that any writes to the payload fail. 88 w.payOff = len(w.b) 89 w.pktLim = len(w.b) 90 return 91 } 92 w.payOff = payOff 93 w.pktLim = w.dgramLim - aeadOverhead 94 // We hardcode the payload length field to be 2 bytes, which limits the payload 95 // (including the packet number) to 16383 bytes (the largest 2-byte QUIC varint). 96 // 97 // Most networks don't support datagrams over 1472 bytes, and even Ethernet 98 // jumbo frames are generally only about 9000 bytes. 99 if lim := pnumOff + 16383 - aeadOverhead; lim < w.pktLim { 100 w.pktLim = lim 101 } 102 w.b = w.b[:payOff] 103 } 104 105 // finishProtectedLongHeaderPacket finishes writing an Initial, 0-RTT, or Handshake packet, 106 // canceling the packet if it contains no payload. 107 // It returns a sentPacket describing the packet, or nil if no packet was written. 108 func (w *packetWriter) finishProtectedLongHeaderPacket(pnumMaxAcked packetNumber, k fixedKeys, p longPacket) *sentPacket { 109 if len(w.b) == w.payOff { 110 // The payload is empty, so just abandon the packet. 111 w.b = w.b[:w.pktOff] 112 return nil 113 } 114 pnumLen := packetNumberLength(p.num, pnumMaxAcked) 115 plen := w.padPacketLength(pnumLen) 116 hdr := w.b[:w.pktOff] 117 var typeBits byte 118 switch p.ptype { 119 case packetTypeInitial: 120 typeBits = longPacketTypeInitial 121 case packetType0RTT: 122 typeBits = longPacketType0RTT 123 case packetTypeHandshake: 124 typeBits = longPacketTypeHandshake 125 case packetTypeRetry: 126 typeBits = longPacketTypeRetry 127 } 128 hdr = append(hdr, headerFormLong|fixedBit|typeBits|byte(pnumLen-1)) 129 hdr = binary.BigEndian.AppendUint32(hdr, p.version) 130 hdr = appendUint8Bytes(hdr, p.dstConnID) 131 hdr = appendUint8Bytes(hdr, p.srcConnID) 132 switch p.ptype { 133 case packetTypeInitial: 134 hdr = appendVarintBytes(hdr, p.extra) // token 135 } 136 137 // Packet length, always encoded as a 2-byte varint. 138 hdr = append(hdr, 0x40|byte(plen>>8), byte(plen)) 139 140 pnumOff := len(hdr) 141 hdr = appendPacketNumber(hdr, p.num, pnumMaxAcked) 142 143 k.protect(hdr[w.pktOff:], w.b[len(hdr):], pnumOff-w.pktOff, p.num) 144 return w.finish(p.ptype, p.num) 145 } 146 147 // start1RTTPacket starts writing a 1-RTT (short header) packet. 148 func (w *packetWriter) start1RTTPacket(pnum, pnumMaxAcked packetNumber, dstConnID []byte) { 149 if w.sent == nil { 150 w.sent = newSentPacket() 151 } 152 w.pktOff = len(w.b) 153 hdrSize := 1 // packet type 154 hdrSize += len(dstConnID) 155 // Ensure we have enough space to hold the packet, including the header, 156 // header protection sample (RFC 9001, section 5.4.2), and encryption overhead. 157 if len(w.b)+hdrSize+4+headerProtectionSampleSize+aeadOverhead >= w.dgramLim { 158 w.payOff = len(w.b) 159 w.pktLim = len(w.b) 160 return 161 } 162 hdrSize += packetNumberLength(pnum, pnumMaxAcked) 163 w.payOff = len(w.b) + hdrSize 164 w.pktLim = w.dgramLim - aeadOverhead 165 w.b = w.b[:w.payOff] 166 } 167 168 // finish1RTTPacket finishes writing a 1-RTT packet, 169 // canceling the packet if it contains no payload. 170 // It returns a sentPacket describing the packet, or nil if no packet was written. 171 func (w *packetWriter) finish1RTTPacket(pnum, pnumMaxAcked packetNumber, dstConnID []byte, k *updatingKeyPair) *sentPacket { 172 if len(w.b) == w.payOff { 173 // The payload is empty, so just abandon the packet. 174 w.b = w.b[:w.pktOff] 175 return nil 176 } 177 // TODO: Spin 178 pnumLen := packetNumberLength(pnum, pnumMaxAcked) 179 hdr := w.b[:w.pktOff] 180 hdr = append(hdr, 0x40|byte(pnumLen-1)) 181 hdr = append(hdr, dstConnID...) 182 pnumOff := len(hdr) 183 hdr = appendPacketNumber(hdr, pnum, pnumMaxAcked) 184 w.padPacketLength(pnumLen) 185 k.protect(hdr[w.pktOff:], w.b[len(hdr):], pnumOff-w.pktOff, pnum) 186 return w.finish(packetType1RTT, pnum) 187 } 188 189 // padPacketLength pads out the payload of the current packet to the minimum size, 190 // and returns the combined length of the packet number and payload (used for the Length 191 // field of long header packets). 192 func (w *packetWriter) padPacketLength(pnumLen int) int { 193 plen := len(w.b) - w.payOff + pnumLen + aeadOverhead 194 // "To ensure that sufficient data is available for sampling, packets are 195 // padded so that the combined lengths of the encoded packet number and 196 // protected payload is at least 4 bytes longer than the sample required 197 // for header protection." 198 // https://www.rfc-editor.org/rfc/rfc9001.html#section-5.4.2 199 for plen < 4+headerProtectionSampleSize { 200 w.b = append(w.b, 0) 201 plen++ 202 } 203 return plen 204 } 205 206 // finish finishes the current packet after protection is applied. 207 func (w *packetWriter) finish(ptype packetType, pnum packetNumber) *sentPacket { 208 w.b = w.b[:len(w.b)+aeadOverhead] 209 w.sent.size = len(w.b) - w.pktOff 210 w.sent.ptype = ptype 211 w.sent.num = pnum 212 sent := w.sent 213 w.sent = nil 214 return sent 215 } 216 217 // avail reports how many more bytes may be written to the current packet. 218 func (w *packetWriter) avail() int { 219 return w.pktLim - len(w.b) 220 } 221 222 // appendPaddingTo appends PADDING frames until the total datagram size 223 // (including AEAD overhead of the current packet) is n. 224 func (w *packetWriter) appendPaddingTo(n int) { 225 n -= aeadOverhead 226 lim := w.pktLim 227 if n < lim { 228 lim = n 229 } 230 if len(w.b) >= lim { 231 return 232 } 233 for len(w.b) < lim { 234 w.b = append(w.b, frameTypePadding) 235 } 236 // Packets are considered in flight when they contain a PADDING frame. 237 // https://www.rfc-editor.org/rfc/rfc9002.html#section-2-3.6.1 238 w.sent.inFlight = true 239 } 240 241 func (w *packetWriter) appendPingFrame() (added bool) { 242 if len(w.b) >= w.pktLim { 243 return false 244 } 245 w.b = append(w.b, frameTypePing) 246 w.sent.markAckEliciting() // no need to record the frame itself 247 return true 248 } 249 250 // appendAckFrame appends an ACK frame to the payload. 251 // It includes at least the most recent range in the rangeset 252 // (the range with the largest packet numbers), 253 // followed by as many additional ranges as fit within the packet. 254 // 255 // We always place ACK frames at the start of packets, 256 // we limit the number of ack ranges retained, and 257 // we set a minimum packet payload size. 258 // As a result, appendAckFrame will rarely if ever drop ranges 259 // in practice. 260 // 261 // In the event that ranges are dropped, the impact is limited 262 // to the peer potentially failing to receive an acknowledgement 263 // for an older packet during a period of high packet loss or 264 // reordering. This may result in unnecessary retransmissions. 265 func (w *packetWriter) appendAckFrame(seen rangeset[packetNumber], delay unscaledAckDelay) (added bool) { 266 if len(seen) == 0 { 267 return false 268 } 269 var ( 270 largest = uint64(seen.max()) 271 firstRange = uint64(seen[len(seen)-1].size() - 1) 272 ) 273 if w.avail() < 1+sizeVarint(largest)+sizeVarint(uint64(delay))+1+sizeVarint(firstRange) { 274 return false 275 } 276 w.b = append(w.b, frameTypeAck) 277 w.b = appendVarint(w.b, largest) 278 w.b = appendVarint(w.b, uint64(delay)) 279 // The range count is technically a varint, but we'll reserve a single byte for it 280 // and never add more than 62 ranges (the maximum varint that fits in a byte). 281 rangeCountOff := len(w.b) 282 w.b = append(w.b, 0) 283 w.b = appendVarint(w.b, firstRange) 284 rangeCount := byte(0) 285 for i := len(seen) - 2; i >= 0; i-- { 286 gap := uint64(seen[i+1].start - seen[i].end - 1) 287 size := uint64(seen[i].size() - 1) 288 if w.avail() < sizeVarint(gap)+sizeVarint(size) || rangeCount > 62 { 289 break 290 } 291 w.b = appendVarint(w.b, gap) 292 w.b = appendVarint(w.b, size) 293 rangeCount++ 294 } 295 w.b[rangeCountOff] = rangeCount 296 w.sent.appendNonAckElicitingFrame(frameTypeAck) 297 w.sent.appendInt(uint64(seen.max())) 298 return true 299 } 300 301 func (w *packetWriter) appendNewTokenFrame(token []byte) (added bool) { 302 if w.avail() < 1+sizeVarint(uint64(len(token)))+len(token) { 303 return false 304 } 305 w.b = append(w.b, frameTypeNewToken) 306 w.b = appendVarintBytes(w.b, token) 307 return true 308 } 309 310 func (w *packetWriter) appendResetStreamFrame(id streamID, code uint64, finalSize int64) (added bool) { 311 if w.avail() < 1+sizeVarint(uint64(id))+sizeVarint(code)+sizeVarint(uint64(finalSize)) { 312 return false 313 } 314 w.b = append(w.b, frameTypeResetStream) 315 w.b = appendVarint(w.b, uint64(id)) 316 w.b = appendVarint(w.b, code) 317 w.b = appendVarint(w.b, uint64(finalSize)) 318 w.sent.appendAckElicitingFrame(frameTypeResetStream) 319 w.sent.appendInt(uint64(id)) 320 return true 321 } 322 323 func (w *packetWriter) appendStopSendingFrame(id streamID, code uint64) (added bool) { 324 if w.avail() < 1+sizeVarint(uint64(id))+sizeVarint(code) { 325 return false 326 } 327 w.b = append(w.b, frameTypeStopSending) 328 w.b = appendVarint(w.b, uint64(id)) 329 w.b = appendVarint(w.b, code) 330 w.sent.appendAckElicitingFrame(frameTypeStopSending) 331 w.sent.appendInt(uint64(id)) 332 return true 333 } 334 335 // appendCryptoFrame appends a CRYPTO frame. 336 // It returns a []byte into which the data should be written and whether a frame was added. 337 // The returned []byte may be smaller than size if the packet cannot hold all the data. 338 func (w *packetWriter) appendCryptoFrame(off int64, size int) (_ []byte, added bool) { 339 max := w.avail() 340 max -= 1 // frame type 341 max -= sizeVarint(uint64(off)) // offset 342 max -= sizeVarint(uint64(size)) // maximum length 343 if max <= 0 { 344 return nil, false 345 } 346 if max < size { 347 size = max 348 } 349 w.b = append(w.b, frameTypeCrypto) 350 w.b = appendVarint(w.b, uint64(off)) 351 w.b = appendVarint(w.b, uint64(size)) 352 start := len(w.b) 353 w.b = w.b[:start+size] 354 w.sent.appendAckElicitingFrame(frameTypeCrypto) 355 w.sent.appendOffAndSize(off, size) 356 return w.b[start:][:size], true 357 } 358 359 // appendStreamFrame appends a STREAM frame. 360 // It returns a []byte into which the data should be written and whether a frame was added. 361 // The returned []byte may be smaller than size if the packet cannot hold all the data. 362 func (w *packetWriter) appendStreamFrame(id streamID, off int64, size int, fin bool) (_ []byte, added bool) { 363 typ := uint8(frameTypeStreamBase | streamLenBit) 364 max := w.avail() 365 max -= 1 // frame type 366 max -= sizeVarint(uint64(id)) 367 if off != 0 { 368 max -= sizeVarint(uint64(off)) 369 typ |= streamOffBit 370 } 371 max -= sizeVarint(uint64(size)) // maximum length 372 if max < 0 || (max == 0 && size > 0) { 373 return nil, false 374 } 375 if max < size { 376 size = max 377 } else if fin { 378 typ |= streamFinBit 379 } 380 w.b = append(w.b, typ) 381 w.b = appendVarint(w.b, uint64(id)) 382 if off != 0 { 383 w.b = appendVarint(w.b, uint64(off)) 384 } 385 w.b = appendVarint(w.b, uint64(size)) 386 start := len(w.b) 387 w.b = w.b[:start+size] 388 w.sent.appendAckElicitingFrame(typ & (frameTypeStreamBase | streamFinBit)) 389 w.sent.appendInt(uint64(id)) 390 w.sent.appendOffAndSize(off, size) 391 return w.b[start:][:size], true 392 } 393 394 func (w *packetWriter) appendMaxDataFrame(max int64) (added bool) { 395 if w.avail() < 1+sizeVarint(uint64(max)) { 396 return false 397 } 398 w.b = append(w.b, frameTypeMaxData) 399 w.b = appendVarint(w.b, uint64(max)) 400 w.sent.appendAckElicitingFrame(frameTypeMaxData) 401 return true 402 } 403 404 func (w *packetWriter) appendMaxStreamDataFrame(id streamID, max int64) (added bool) { 405 if w.avail() < 1+sizeVarint(uint64(id))+sizeVarint(uint64(max)) { 406 return false 407 } 408 w.b = append(w.b, frameTypeMaxStreamData) 409 w.b = appendVarint(w.b, uint64(id)) 410 w.b = appendVarint(w.b, uint64(max)) 411 w.sent.appendAckElicitingFrame(frameTypeMaxStreamData) 412 w.sent.appendInt(uint64(id)) 413 return true 414 } 415 416 func (w *packetWriter) appendMaxStreamsFrame(streamType streamType, max int64) (added bool) { 417 if w.avail() < 1+sizeVarint(uint64(max)) { 418 return false 419 } 420 var typ byte 421 if streamType == bidiStream { 422 typ = frameTypeMaxStreamsBidi 423 } else { 424 typ = frameTypeMaxStreamsUni 425 } 426 w.b = append(w.b, typ) 427 w.b = appendVarint(w.b, uint64(max)) 428 w.sent.appendAckElicitingFrame(typ) 429 return true 430 } 431 432 func (w *packetWriter) appendDataBlockedFrame(max int64) (added bool) { 433 if w.avail() < 1+sizeVarint(uint64(max)) { 434 return false 435 } 436 w.b = append(w.b, frameTypeDataBlocked) 437 w.b = appendVarint(w.b, uint64(max)) 438 w.sent.appendAckElicitingFrame(frameTypeDataBlocked) 439 return true 440 } 441 442 func (w *packetWriter) appendStreamDataBlockedFrame(id streamID, max int64) (added bool) { 443 if w.avail() < 1+sizeVarint(uint64(id))+sizeVarint(uint64(max)) { 444 return false 445 } 446 w.b = append(w.b, frameTypeStreamDataBlocked) 447 w.b = appendVarint(w.b, uint64(id)) 448 w.b = appendVarint(w.b, uint64(max)) 449 w.sent.appendAckElicitingFrame(frameTypeStreamDataBlocked) 450 w.sent.appendInt(uint64(id)) 451 return true 452 } 453 454 func (w *packetWriter) appendStreamsBlockedFrame(typ streamType, max int64) (added bool) { 455 if w.avail() < 1+sizeVarint(uint64(max)) { 456 return false 457 } 458 var ftype byte 459 if typ == bidiStream { 460 ftype = frameTypeStreamsBlockedBidi 461 } else { 462 ftype = frameTypeStreamsBlockedUni 463 } 464 w.b = append(w.b, ftype) 465 w.b = appendVarint(w.b, uint64(max)) 466 w.sent.appendAckElicitingFrame(ftype) 467 return true 468 } 469 470 func (w *packetWriter) appendNewConnectionIDFrame(seq, retirePriorTo int64, connID []byte, token [16]byte) (added bool) { 471 if w.avail() < 1+sizeVarint(uint64(seq))+sizeVarint(uint64(retirePriorTo))+1+len(connID)+len(token) { 472 return false 473 } 474 w.b = append(w.b, frameTypeNewConnectionID) 475 w.b = appendVarint(w.b, uint64(seq)) 476 w.b = appendVarint(w.b, uint64(retirePriorTo)) 477 w.b = appendUint8Bytes(w.b, connID) 478 w.b = append(w.b, token[:]...) 479 w.sent.appendAckElicitingFrame(frameTypeNewConnectionID) 480 w.sent.appendInt(uint64(seq)) 481 return true 482 } 483 484 func (w *packetWriter) appendRetireConnectionIDFrame(seq int64) (added bool) { 485 if w.avail() < 1+sizeVarint(uint64(seq)) { 486 return false 487 } 488 w.b = append(w.b, frameTypeRetireConnectionID) 489 w.b = appendVarint(w.b, uint64(seq)) 490 w.sent.appendAckElicitingFrame(frameTypeRetireConnectionID) 491 w.sent.appendInt(uint64(seq)) 492 return true 493 } 494 495 func (w *packetWriter) appendPathChallengeFrame(data pathChallengeData) (added bool) { 496 if w.avail() < 1+8 { 497 return false 498 } 499 w.b = append(w.b, frameTypePathChallenge) 500 w.b = append(w.b, data[:]...) 501 w.sent.markAckEliciting() // no need to record the frame itself 502 return true 503 } 504 505 func (w *packetWriter) appendPathResponseFrame(data pathChallengeData) (added bool) { 506 if w.avail() < 1+8 { 507 return false 508 } 509 w.b = append(w.b, frameTypePathResponse) 510 w.b = append(w.b, data[:]...) 511 w.sent.markAckEliciting() // no need to record the frame itself 512 return true 513 } 514 515 // appendConnectionCloseTransportFrame appends a CONNECTION_CLOSE frame 516 // carrying a transport error code. 517 func (w *packetWriter) appendConnectionCloseTransportFrame(code transportError, frameType uint64, reason string) (added bool) { 518 if w.avail() < 1+sizeVarint(uint64(code))+sizeVarint(frameType)+sizeVarint(uint64(len(reason)))+len(reason) { 519 return false 520 } 521 w.b = append(w.b, frameTypeConnectionCloseTransport) 522 w.b = appendVarint(w.b, uint64(code)) 523 w.b = appendVarint(w.b, frameType) 524 w.b = appendVarintBytes(w.b, []byte(reason)) 525 // We don't record CONNECTION_CLOSE frames in w.sent, since they are never acked or 526 // detected as lost. 527 return true 528 } 529 530 // appendConnectionCloseTransportFrame appends a CONNECTION_CLOSE frame 531 // carrying an application protocol error code. 532 func (w *packetWriter) appendConnectionCloseApplicationFrame(code uint64, reason string) (added bool) { 533 if w.avail() < 1+sizeVarint(code)+sizeVarint(uint64(len(reason)))+len(reason) { 534 return false 535 } 536 w.b = append(w.b, frameTypeConnectionCloseApplication) 537 w.b = appendVarint(w.b, code) 538 w.b = appendVarintBytes(w.b, []byte(reason)) 539 // We don't record CONNECTION_CLOSE frames in w.sent, since they are never acked or 540 // detected as lost. 541 return true 542 } 543 544 func (w *packetWriter) appendHandshakeDoneFrame() (added bool) { 545 if w.avail() < 1 { 546 return false 547 } 548 w.b = append(w.b, frameTypeHandshakeDone) 549 w.sent.appendAckElicitingFrame(frameTypeHandshakeDone) 550 return true 551 }