-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
quic: transport parameter encoding and decoding
Transport parameters are passed in the extension_data field of the quic_transport_parameters TLS extension. RFC 9000, Section 18. RFC 9001, Section 8.2. For golang/go#58547 Change-Id: I294ab6cdef19256f5db02dc269e8b417b1d5e54b Reviewed-on: https://go-review.googlesource.com/c/net/+/510575 Auto-Submit: Damien Neil <[email protected]> Reviewed-by: Jonathan Amsterdam <[email protected]> Run-TryBot: Damien Neil <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
- Loading branch information
Showing
2 changed files
with
651 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,277 @@ | ||
// Copyright 2023 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
//go:build go1.21 | ||
|
||
package quic | ||
|
||
import ( | ||
"encoding/binary" | ||
"net/netip" | ||
"time" | ||
) | ||
|
||
// transportParameters transferred in the quic_transport_parameters TLS extension. | ||
// https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2 | ||
type transportParameters struct { | ||
originalDstConnID []byte | ||
maxIdleTimeout time.Duration | ||
statelessResetToken []byte | ||
maxUDPPayloadSize int64 | ||
initialMaxData int64 | ||
initialMaxStreamDataBidiLocal int64 | ||
initialMaxStreamDataBidiRemote int64 | ||
initialMaxStreamDataUni int64 | ||
initialMaxStreamsBidi int64 | ||
initialMaxStreamsUni int64 | ||
ackDelayExponent uint8 | ||
maxAckDelay time.Duration | ||
disableActiveMigration bool | ||
preferredAddrV4 netip.AddrPort | ||
preferredAddrV6 netip.AddrPort | ||
preferredAddrConnID []byte | ||
preferredAddrResetToken []byte | ||
activeConnIDLimit int64 | ||
initialSrcConnID []byte | ||
retrySrcConnID []byte | ||
} | ||
|
||
const ( | ||
defaultParamMaxUDPPayloadSize = 65527 | ||
defaultParamAckDelayExponent = 3 | ||
defaultParamMaxAckDelayMilliseconds = 25 | ||
defaultParamActiveConnIDLimit = 2 | ||
) | ||
|
||
// defaultTransportParameters is initialized to the RFC 9000 default values. | ||
func defaultTransportParameters() transportParameters { | ||
return transportParameters{ | ||
maxUDPPayloadSize: defaultParamMaxUDPPayloadSize, | ||
ackDelayExponent: defaultParamAckDelayExponent, | ||
maxAckDelay: defaultParamMaxAckDelayMilliseconds * time.Millisecond, | ||
activeConnIDLimit: defaultParamActiveConnIDLimit, | ||
} | ||
} | ||
|
||
const ( | ||
paramOriginalDestinationConnectionID = 0x00 | ||
paramMaxIdleTimeout = 0x01 | ||
paramStatelessResetToken = 0x02 | ||
paramMaxUDPPayloadSize = 0x03 | ||
paramInitialMaxData = 0x04 | ||
paramInitialMaxStreamDataBidiLocal = 0x05 | ||
paramInitialMaxStreamDataBidiRemote = 0x06 | ||
paramInitialMaxStreamDataUni = 0x07 | ||
paramInitialMaxStreamsBidi = 0x08 | ||
paramInitialMaxStreamsUni = 0x09 | ||
paramAckDelayExponent = 0x0a | ||
paramMaxAckDelay = 0x0b | ||
paramDisableActiveMigration = 0x0c | ||
paramPreferredAddress = 0x0d | ||
paramActiveConnectionIDLimit = 0x0e | ||
paramInitialSourceConnectionID = 0x0f | ||
paramRetrySourceConnectionID = 0x10 | ||
) | ||
|
||
func marshalTransportParameters(p transportParameters) []byte { | ||
var b []byte | ||
if v := p.originalDstConnID; v != nil { | ||
b = appendVarint(b, paramOriginalDestinationConnectionID) | ||
b = appendVarintBytes(b, v) | ||
} | ||
if v := uint64(p.maxIdleTimeout / time.Millisecond); v != 0 { | ||
b = appendVarint(b, paramMaxIdleTimeout) | ||
b = appendVarint(b, uint64(sizeVarint(v))) | ||
b = appendVarint(b, uint64(v)) | ||
} | ||
if v := p.statelessResetToken; v != nil { | ||
b = appendVarint(b, paramStatelessResetToken) | ||
b = appendVarintBytes(b, v) | ||
} | ||
if v := p.maxUDPPayloadSize; v != defaultParamMaxUDPPayloadSize { | ||
b = appendVarint(b, paramMaxUDPPayloadSize) | ||
b = appendVarint(b, uint64(sizeVarint(uint64(v)))) | ||
b = appendVarint(b, uint64(v)) | ||
} | ||
if v := p.initialMaxData; v != 0 { | ||
b = appendVarint(b, paramInitialMaxData) | ||
b = appendVarint(b, uint64(sizeVarint(uint64(v)))) | ||
b = appendVarint(b, uint64(v)) | ||
} | ||
if v := p.initialMaxStreamDataBidiLocal; v != 0 { | ||
b = appendVarint(b, paramInitialMaxStreamDataBidiLocal) | ||
b = appendVarint(b, uint64(sizeVarint(uint64(v)))) | ||
b = appendVarint(b, uint64(v)) | ||
} | ||
if v := p.initialMaxStreamDataBidiRemote; v != 0 { | ||
b = appendVarint(b, paramInitialMaxStreamDataBidiRemote) | ||
b = appendVarint(b, uint64(sizeVarint(uint64(v)))) | ||
b = appendVarint(b, uint64(v)) | ||
} | ||
if v := p.initialMaxStreamDataUni; v != 0 { | ||
b = appendVarint(b, paramInitialMaxStreamDataUni) | ||
b = appendVarint(b, uint64(sizeVarint(uint64(v)))) | ||
b = appendVarint(b, uint64(v)) | ||
} | ||
if v := p.initialMaxStreamsBidi; v != 0 { | ||
b = appendVarint(b, paramInitialMaxStreamsBidi) | ||
b = appendVarint(b, uint64(sizeVarint(uint64(v)))) | ||
b = appendVarint(b, uint64(v)) | ||
} | ||
if v := p.initialMaxStreamsUni; v != 0 { | ||
b = appendVarint(b, paramInitialMaxStreamsUni) | ||
b = appendVarint(b, uint64(sizeVarint(uint64(v)))) | ||
b = appendVarint(b, uint64(v)) | ||
} | ||
if v := p.ackDelayExponent; v != defaultParamAckDelayExponent { | ||
b = appendVarint(b, paramAckDelayExponent) | ||
b = appendVarint(b, uint64(sizeVarint(uint64(v)))) | ||
b = appendVarint(b, uint64(v)) | ||
} | ||
if v := uint64(p.maxAckDelay / time.Millisecond); v != defaultParamMaxAckDelayMilliseconds { | ||
b = appendVarint(b, paramMaxAckDelay) | ||
b = appendVarint(b, uint64(sizeVarint(v))) | ||
b = appendVarint(b, v) | ||
} | ||
if p.disableActiveMigration { | ||
b = appendVarint(b, paramDisableActiveMigration) | ||
b = append(b, 0) // 0-length value | ||
} | ||
if p.preferredAddrConnID != nil { | ||
b = append(b, paramPreferredAddress) | ||
b = appendVarint(b, uint64(4+2+16+2+1+len(p.preferredAddrConnID)+16)) | ||
b = append(b, p.preferredAddrV4.Addr().AsSlice()...) // 4 bytes | ||
b = binary.BigEndian.AppendUint16(b, p.preferredAddrV4.Port()) // 2 bytes | ||
b = append(b, p.preferredAddrV6.Addr().AsSlice()...) // 16 bytes | ||
b = binary.BigEndian.AppendUint16(b, p.preferredAddrV6.Port()) // 2 bytes | ||
b = appendUint8Bytes(b, p.preferredAddrConnID) // 1 byte + len(conn_id) | ||
b = append(b, p.preferredAddrResetToken...) // 16 bytes | ||
} | ||
if v := p.activeConnIDLimit; v != defaultParamActiveConnIDLimit { | ||
b = appendVarint(b, paramActiveConnectionIDLimit) | ||
b = appendVarint(b, uint64(sizeVarint(uint64(v)))) | ||
b = appendVarint(b, uint64(v)) | ||
} | ||
if v := p.initialSrcConnID; v != nil { | ||
b = appendVarint(b, paramInitialSourceConnectionID) | ||
b = appendVarintBytes(b, v) | ||
} | ||
if v := p.retrySrcConnID; v != nil { | ||
b = appendVarint(b, paramRetrySourceConnectionID) | ||
b = appendVarintBytes(b, v) | ||
} | ||
return b | ||
} | ||
|
||
func unmarshalTransportParams(params []byte) (transportParameters, error) { | ||
p := defaultTransportParameters() | ||
for len(params) > 0 { | ||
id, n := consumeVarint(params) | ||
if n < 0 { | ||
return p, localTransportError(errTransportParameter) | ||
} | ||
params = params[n:] | ||
val, n := consumeVarintBytes(params) | ||
if n < 0 { | ||
return p, localTransportError(errTransportParameter) | ||
} | ||
params = params[n:] | ||
n = 0 | ||
switch id { | ||
case paramOriginalDestinationConnectionID: | ||
p.originalDstConnID = val | ||
n = len(val) | ||
case paramMaxIdleTimeout: | ||
var v uint64 | ||
v, n = consumeVarint(val) | ||
// If this is unreasonably large, consider it as no timeout to avoid | ||
// time.Duration overflows. | ||
if v > 1<<32 { | ||
v = 0 | ||
} | ||
p.maxIdleTimeout = time.Duration(v) * time.Millisecond | ||
case paramStatelessResetToken: | ||
if len(val) != 16 { | ||
return p, localTransportError(errTransportParameter) | ||
} | ||
p.statelessResetToken = val | ||
n = 16 | ||
case paramMaxUDPPayloadSize: | ||
p.maxUDPPayloadSize, n = consumeVarintInt64(val) | ||
if p.maxUDPPayloadSize < 1200 { | ||
return p, localTransportError(errTransportParameter) | ||
} | ||
case paramInitialMaxData: | ||
p.initialMaxData, n = consumeVarintInt64(val) | ||
case paramInitialMaxStreamDataBidiLocal: | ||
p.initialMaxStreamDataBidiLocal, n = consumeVarintInt64(val) | ||
case paramInitialMaxStreamDataBidiRemote: | ||
p.initialMaxStreamDataBidiRemote, n = consumeVarintInt64(val) | ||
case paramInitialMaxStreamDataUni: | ||
p.initialMaxStreamDataUni, n = consumeVarintInt64(val) | ||
case paramInitialMaxStreamsBidi: | ||
p.initialMaxStreamsBidi, n = consumeVarintInt64(val) | ||
case paramInitialMaxStreamsUni: | ||
p.initialMaxStreamsUni, n = consumeVarintInt64(val) | ||
case paramAckDelayExponent: | ||
var v uint64 | ||
v, n = consumeVarint(val) | ||
if v > 20 { | ||
return p, localTransportError(errTransportParameter) | ||
} | ||
p.ackDelayExponent = uint8(v) | ||
case paramMaxAckDelay: | ||
var v uint64 | ||
v, n = consumeVarint(val) | ||
if v >= 1<<14 { | ||
return p, localTransportError(errTransportParameter) | ||
} | ||
p.maxAckDelay = time.Duration(v) * time.Millisecond | ||
case paramDisableActiveMigration: | ||
p.disableActiveMigration = true | ||
case paramPreferredAddress: | ||
if len(val) < 4+2+16+2+1 { | ||
return p, localTransportError(errTransportParameter) | ||
} | ||
p.preferredAddrV4 = netip.AddrPortFrom( | ||
netip.AddrFrom4(*(*[4]byte)(val[:4])), | ||
binary.BigEndian.Uint16(val[4:][:2]), | ||
) | ||
val = val[4+2:] | ||
p.preferredAddrV6 = netip.AddrPortFrom( | ||
netip.AddrFrom16(*(*[16]byte)(val[:16])), | ||
binary.BigEndian.Uint16(val[16:][:2]), | ||
) | ||
val = val[16+2:] | ||
var nn int | ||
p.preferredAddrConnID, nn = consumeUint8Bytes(val) | ||
if nn < 0 { | ||
return p, localTransportError(errTransportParameter) | ||
} | ||
val = val[nn:] | ||
if len(val) != 16 { | ||
return p, localTransportError(errTransportParameter) | ||
} | ||
p.preferredAddrResetToken = val | ||
val = nil | ||
case paramActiveConnectionIDLimit: | ||
p.activeConnIDLimit, n = consumeVarintInt64(val) | ||
if p.activeConnIDLimit < 2 { | ||
return p, localTransportError(errTransportParameter) | ||
} | ||
case paramInitialSourceConnectionID: | ||
p.initialSrcConnID = val | ||
n = len(val) | ||
case paramRetrySourceConnectionID: | ||
p.retrySrcConnID = val | ||
n = len(val) | ||
default: | ||
n = len(val) | ||
} | ||
if n != len(val) { | ||
return p, localTransportError(errTransportParameter) | ||
} | ||
} | ||
return p, nil | ||
} |
Oops, something went wrong.