зеркало из https://github.com/golang/net.git
quic: packet number encoding/decoding
QUIC packet numbers are integers in the range [0, 2^62). Packet numbers are encoded as the 1-4 least significant bytes of the full number, with the remaining bytes extrapolated based on the largest packet number seen by the receiver. RFC 9000, Section 17.1. For golang/go#58547 Change-Id: I9e111fe6c9c437fdcd9dc57336e094512c0b52b0 Reviewed-on: https://go-review.googlesource.com/c/net/+/475436 Reviewed-by: Matt Layher <mdlayher@gmail.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Roland Shoemaker <roland@golang.org> Run-TryBot: Damien Neil <dneil@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Jonathan Amsterdam <jba@google.com>
This commit is contained in:
Родитель
0d6f3cba5e
Коммит
d4a2c13d06
|
@ -0,0 +1,72 @@
|
|||
// 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.
|
||||
|
||||
package quic
|
||||
|
||||
// A packetNumber is a QUIC packet number.
|
||||
// Packet numbers are integers in the range [0, 2^62-1].
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9000.html#section-12.3
|
||||
type packetNumber int64
|
||||
|
||||
const maxPacketNumber = 1<<62 - 1 // https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1-1
|
||||
|
||||
// decodePacketNumber decodes a truncated packet number, given
|
||||
// the largest acknowledged packet number in this number space,
|
||||
// the truncated number received in a packet, and the size of the
|
||||
// number received in bytes.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1
|
||||
// https://www.rfc-editor.org/rfc/rfc9000.html#section-a.3
|
||||
func decodePacketNumber(largest, truncated packetNumber, numLenInBytes int) packetNumber {
|
||||
expected := largest + 1
|
||||
win := packetNumber(1) << (uint(numLenInBytes) * 8)
|
||||
hwin := win / 2
|
||||
mask := win - 1
|
||||
candidate := (expected &^ mask) | truncated
|
||||
if candidate <= expected-hwin && candidate < (1<<62)-win {
|
||||
return candidate + win
|
||||
}
|
||||
if candidate > expected+hwin && candidate >= win {
|
||||
return candidate - win
|
||||
}
|
||||
return candidate
|
||||
}
|
||||
|
||||
// appendPacketNumber appends an encoded packet number to b.
|
||||
// The packet number must be larger than the largest acknowledged packet number.
|
||||
// When no packets have been acknowledged yet, largestAck is -1.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1-5
|
||||
func appendPacketNumber(b []byte, pnum, largestAck packetNumber) []byte {
|
||||
switch packetNumberLength(pnum, largestAck) {
|
||||
case 1:
|
||||
return append(b, byte(pnum))
|
||||
case 2:
|
||||
return append(b, byte(pnum>>8), byte(pnum))
|
||||
case 3:
|
||||
return append(b, byte(pnum>>16), byte(pnum>>8), byte(pnum))
|
||||
default:
|
||||
return append(b, byte(pnum>>24), byte(pnum>>16), byte(pnum>>8), byte(pnum))
|
||||
}
|
||||
}
|
||||
|
||||
// packetNumberLength returns the minimum length, in bytes, needed to encode
|
||||
// a packet number given the largest acknowledged packet number.
|
||||
// The packet number must be larger than the largest acknowledged packet number.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1-5
|
||||
func packetNumberLength(pnum, largestAck packetNumber) int {
|
||||
d := pnum - largestAck
|
||||
switch {
|
||||
case d < 0x80:
|
||||
return 1
|
||||
case d < 0x8000:
|
||||
return 2
|
||||
case d < 0x800000:
|
||||
return 3
|
||||
default:
|
||||
return 4
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
// 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.
|
||||
|
||||
package quic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDecodePacketNumber(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
largest packetNumber
|
||||
truncated packetNumber
|
||||
want packetNumber
|
||||
size int
|
||||
}{{
|
||||
largest: 0,
|
||||
truncated: 1,
|
||||
size: 4,
|
||||
want: 1,
|
||||
}, {
|
||||
largest: 0,
|
||||
truncated: 0,
|
||||
size: 1,
|
||||
want: 0,
|
||||
}, {
|
||||
largest: 0x00,
|
||||
truncated: 0x01,
|
||||
size: 1,
|
||||
want: 0x01,
|
||||
}, {
|
||||
largest: 0x00,
|
||||
truncated: 0xff,
|
||||
size: 1,
|
||||
want: 0xff,
|
||||
}, {
|
||||
largest: 0xff,
|
||||
truncated: 0x01,
|
||||
size: 1,
|
||||
want: 0x101,
|
||||
}, {
|
||||
largest: 0x1000,
|
||||
truncated: 0xff,
|
||||
size: 1,
|
||||
want: 0xfff,
|
||||
}, {
|
||||
largest: 0xa82f30ea,
|
||||
truncated: 0x9b32,
|
||||
size: 2,
|
||||
want: 0xa82f9b32,
|
||||
}} {
|
||||
got := decodePacketNumber(test.largest, test.truncated, test.size)
|
||||
if got != test.want {
|
||||
t.Errorf("decodePacketNumber(largest=0x%x, truncated=0x%x, size=%v) = 0x%x, want 0x%x", test.largest, test.truncated, test.size, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodePacketNumber(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
largestAcked packetNumber
|
||||
pnum packetNumber
|
||||
wantSize int
|
||||
}{{
|
||||
largestAcked: -1,
|
||||
pnum: 0,
|
||||
wantSize: 1,
|
||||
}, {
|
||||
largestAcked: 1000,
|
||||
pnum: 1000 + 0x7f,
|
||||
wantSize: 1,
|
||||
}, {
|
||||
largestAcked: 1000,
|
||||
pnum: 1000 + 0x80, // 0x468
|
||||
wantSize: 2,
|
||||
}, {
|
||||
largestAcked: 0x12345678,
|
||||
pnum: 0x12345678 + 0x7fff, // 0x305452663
|
||||
wantSize: 2,
|
||||
}, {
|
||||
largestAcked: 0x12345678,
|
||||
pnum: 0x12345678 + 0x8000,
|
||||
wantSize: 3,
|
||||
}, {
|
||||
largestAcked: 0,
|
||||
pnum: 0x7fffff,
|
||||
wantSize: 3,
|
||||
}, {
|
||||
largestAcked: 0,
|
||||
pnum: 0x800000,
|
||||
wantSize: 4,
|
||||
}, {
|
||||
largestAcked: 0xabe8bc,
|
||||
pnum: 0xac5c02,
|
||||
wantSize: 2,
|
||||
}, {
|
||||
largestAcked: 0xabe8bc,
|
||||
pnum: 0xace8fe,
|
||||
wantSize: 3,
|
||||
}} {
|
||||
size := packetNumberLength(test.pnum, test.largestAcked)
|
||||
if got, want := size, test.wantSize; got != want {
|
||||
t.Errorf("packetNumberLength(num=%x, maxAck=%x) = %v, want %v", test.pnum, test.largestAcked, got, want)
|
||||
}
|
||||
var enc packetNumber
|
||||
switch size {
|
||||
case 1:
|
||||
enc = test.pnum & 0xff
|
||||
case 2:
|
||||
enc = test.pnum & 0xffff
|
||||
case 3:
|
||||
enc = test.pnum & 0xffffff
|
||||
case 4:
|
||||
enc = test.pnum & 0xffffffff
|
||||
}
|
||||
wantBytes := binary.BigEndian.AppendUint32(nil, uint32(enc))[4-size:]
|
||||
gotBytes := appendPacketNumber(nil, test.pnum, test.largestAcked)
|
||||
if !bytes.Equal(gotBytes, wantBytes) {
|
||||
t.Errorf("appendPacketNumber(num=%v, maxAck=%x) = {%x}, want {%x}", test.pnum, test.largestAcked, gotBytes, wantBytes)
|
||||
}
|
||||
gotNum := decodePacketNumber(test.largestAcked, enc, size)
|
||||
if got, want := gotNum, test.pnum; got != want {
|
||||
t.Errorf("packetNumberLength(num=%x, maxAck=%x) = %v, but decoded number=%x", test.pnum, test.largestAcked, size, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzPacketNumber(f *testing.F) {
|
||||
truncatedNumber := func(in []byte) packetNumber {
|
||||
var truncated packetNumber
|
||||
for _, b := range in {
|
||||
truncated = (truncated << 8) | packetNumber(b)
|
||||
}
|
||||
return truncated
|
||||
}
|
||||
f.Fuzz(func(t *testing.T, in []byte, largestAckedInt64 int64) {
|
||||
largestAcked := packetNumber(largestAckedInt64)
|
||||
if len(in) < 1 || len(in) > 4 || largestAcked < 0 || largestAcked > maxPacketNumber {
|
||||
return
|
||||
}
|
||||
truncatedIn := truncatedNumber(in)
|
||||
decoded := decodePacketNumber(largestAcked, truncatedIn, len(in))
|
||||
|
||||
// Check that the decoded packet number's least significant bits match the input.
|
||||
var mask packetNumber
|
||||
for i := 0; i < len(in); i++ {
|
||||
mask = (mask << 8) | 0xff
|
||||
}
|
||||
if truncatedIn != decoded&mask {
|
||||
t.Fatalf("decoding mismatch: input=%x largestAcked=%v decoded=0x%x", in, largestAcked, decoded)
|
||||
}
|
||||
|
||||
// We don't support encoding packet numbers less than largestAcked (since packet numbers
|
||||
// never decrease), so skip the encoder tests if this would make us go backwards.
|
||||
if decoded < largestAcked {
|
||||
return
|
||||
}
|
||||
|
||||
// We might encode this number using a different length than we received,
|
||||
// but the common portions should match.
|
||||
encoded := appendPacketNumber(nil, decoded, largestAcked)
|
||||
a, b := in, encoded
|
||||
if len(b) < len(a) {
|
||||
a, b = b, a
|
||||
}
|
||||
for len(a) < len(b) {
|
||||
b = b[1:]
|
||||
}
|
||||
if len(a) == 0 || !bytes.Equal(a, b) {
|
||||
t.Fatalf("encoding mismatch: input=%x largestAcked=%v decoded=%v reencoded=%x", in, largestAcked, decoded, encoded)
|
||||
}
|
||||
|
||||
if g := decodePacketNumber(largestAcked, truncatedNumber(encoded), len(encoded)); g != decoded {
|
||||
t.Fatalf("packet encode/decode mismatch: pnum=%v largestAcked=%v encoded=%x got=%v", decoded, largestAcked, encoded, g)
|
||||
}
|
||||
if l := packetNumberLength(decoded, largestAcked); l != len(encoded) {
|
||||
t.Fatalf("packet number length mismatch: pnum=%v largestAcked=%v encoded=%x len=%v", decoded, largestAcked, encoded, l)
|
||||
}
|
||||
})
|
||||
}
|
Загрузка…
Ссылка в новой задаче