Skip to content

Commit d4a2c13

Browse files
committed
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>
1 parent 0d6f3cb commit d4a2c13

File tree

2 files changed

+255
-0
lines changed

2 files changed

+255
-0
lines changed

internal/quic/packet_number.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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+
package quic
6+
7+
// A packetNumber is a QUIC packet number.
8+
// Packet numbers are integers in the range [0, 2^62-1].
9+
//
10+
// https://www.rfc-editor.org/rfc/rfc9000.html#section-12.3
11+
type packetNumber int64
12+
13+
const maxPacketNumber = 1<<62 - 1 // https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1-1
14+
15+
// decodePacketNumber decodes a truncated packet number, given
16+
// the largest acknowledged packet number in this number space,
17+
// the truncated number received in a packet, and the size of the
18+
// number received in bytes.
19+
//
20+
// https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1
21+
// https://www.rfc-editor.org/rfc/rfc9000.html#section-a.3
22+
func decodePacketNumber(largest, truncated packetNumber, numLenInBytes int) packetNumber {
23+
expected := largest + 1
24+
win := packetNumber(1) << (uint(numLenInBytes) * 8)
25+
hwin := win / 2
26+
mask := win - 1
27+
candidate := (expected &^ mask) | truncated
28+
if candidate <= expected-hwin && candidate < (1<<62)-win {
29+
return candidate + win
30+
}
31+
if candidate > expected+hwin && candidate >= win {
32+
return candidate - win
33+
}
34+
return candidate
35+
}
36+
37+
// appendPacketNumber appends an encoded packet number to b.
38+
// The packet number must be larger than the largest acknowledged packet number.
39+
// When no packets have been acknowledged yet, largestAck is -1.
40+
//
41+
// https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1-5
42+
func appendPacketNumber(b []byte, pnum, largestAck packetNumber) []byte {
43+
switch packetNumberLength(pnum, largestAck) {
44+
case 1:
45+
return append(b, byte(pnum))
46+
case 2:
47+
return append(b, byte(pnum>>8), byte(pnum))
48+
case 3:
49+
return append(b, byte(pnum>>16), byte(pnum>>8), byte(pnum))
50+
default:
51+
return append(b, byte(pnum>>24), byte(pnum>>16), byte(pnum>>8), byte(pnum))
52+
}
53+
}
54+
55+
// packetNumberLength returns the minimum length, in bytes, needed to encode
56+
// a packet number given the largest acknowledged packet number.
57+
// The packet number must be larger than the largest acknowledged packet number.
58+
//
59+
// https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1-5
60+
func packetNumberLength(pnum, largestAck packetNumber) int {
61+
d := pnum - largestAck
62+
switch {
63+
case d < 0x80:
64+
return 1
65+
case d < 0x8000:
66+
return 2
67+
case d < 0x800000:
68+
return 3
69+
default:
70+
return 4
71+
}
72+
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
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+
package quic
6+
7+
import (
8+
"bytes"
9+
"encoding/binary"
10+
"testing"
11+
)
12+
13+
func TestDecodePacketNumber(t *testing.T) {
14+
for _, test := range []struct {
15+
largest packetNumber
16+
truncated packetNumber
17+
want packetNumber
18+
size int
19+
}{{
20+
largest: 0,
21+
truncated: 1,
22+
size: 4,
23+
want: 1,
24+
}, {
25+
largest: 0,
26+
truncated: 0,
27+
size: 1,
28+
want: 0,
29+
}, {
30+
largest: 0x00,
31+
truncated: 0x01,
32+
size: 1,
33+
want: 0x01,
34+
}, {
35+
largest: 0x00,
36+
truncated: 0xff,
37+
size: 1,
38+
want: 0xff,
39+
}, {
40+
largest: 0xff,
41+
truncated: 0x01,
42+
size: 1,
43+
want: 0x101,
44+
}, {
45+
largest: 0x1000,
46+
truncated: 0xff,
47+
size: 1,
48+
want: 0xfff,
49+
}, {
50+
largest: 0xa82f30ea,
51+
truncated: 0x9b32,
52+
size: 2,
53+
want: 0xa82f9b32,
54+
}} {
55+
got := decodePacketNumber(test.largest, test.truncated, test.size)
56+
if got != test.want {
57+
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)
58+
}
59+
}
60+
}
61+
62+
func TestEncodePacketNumber(t *testing.T) {
63+
for _, test := range []struct {
64+
largestAcked packetNumber
65+
pnum packetNumber
66+
wantSize int
67+
}{{
68+
largestAcked: -1,
69+
pnum: 0,
70+
wantSize: 1,
71+
}, {
72+
largestAcked: 1000,
73+
pnum: 1000 + 0x7f,
74+
wantSize: 1,
75+
}, {
76+
largestAcked: 1000,
77+
pnum: 1000 + 0x80, // 0x468
78+
wantSize: 2,
79+
}, {
80+
largestAcked: 0x12345678,
81+
pnum: 0x12345678 + 0x7fff, // 0x305452663
82+
wantSize: 2,
83+
}, {
84+
largestAcked: 0x12345678,
85+
pnum: 0x12345678 + 0x8000,
86+
wantSize: 3,
87+
}, {
88+
largestAcked: 0,
89+
pnum: 0x7fffff,
90+
wantSize: 3,
91+
}, {
92+
largestAcked: 0,
93+
pnum: 0x800000,
94+
wantSize: 4,
95+
}, {
96+
largestAcked: 0xabe8bc,
97+
pnum: 0xac5c02,
98+
wantSize: 2,
99+
}, {
100+
largestAcked: 0xabe8bc,
101+
pnum: 0xace8fe,
102+
wantSize: 3,
103+
}} {
104+
size := packetNumberLength(test.pnum, test.largestAcked)
105+
if got, want := size, test.wantSize; got != want {
106+
t.Errorf("packetNumberLength(num=%x, maxAck=%x) = %v, want %v", test.pnum, test.largestAcked, got, want)
107+
}
108+
var enc packetNumber
109+
switch size {
110+
case 1:
111+
enc = test.pnum & 0xff
112+
case 2:
113+
enc = test.pnum & 0xffff
114+
case 3:
115+
enc = test.pnum & 0xffffff
116+
case 4:
117+
enc = test.pnum & 0xffffffff
118+
}
119+
wantBytes := binary.BigEndian.AppendUint32(nil, uint32(enc))[4-size:]
120+
gotBytes := appendPacketNumber(nil, test.pnum, test.largestAcked)
121+
if !bytes.Equal(gotBytes, wantBytes) {
122+
t.Errorf("appendPacketNumber(num=%v, maxAck=%x) = {%x}, want {%x}", test.pnum, test.largestAcked, gotBytes, wantBytes)
123+
}
124+
gotNum := decodePacketNumber(test.largestAcked, enc, size)
125+
if got, want := gotNum, test.pnum; got != want {
126+
t.Errorf("packetNumberLength(num=%x, maxAck=%x) = %v, but decoded number=%x", test.pnum, test.largestAcked, size, got)
127+
}
128+
}
129+
}
130+
131+
func FuzzPacketNumber(f *testing.F) {
132+
truncatedNumber := func(in []byte) packetNumber {
133+
var truncated packetNumber
134+
for _, b := range in {
135+
truncated = (truncated << 8) | packetNumber(b)
136+
}
137+
return truncated
138+
}
139+
f.Fuzz(func(t *testing.T, in []byte, largestAckedInt64 int64) {
140+
largestAcked := packetNumber(largestAckedInt64)
141+
if len(in) < 1 || len(in) > 4 || largestAcked < 0 || largestAcked > maxPacketNumber {
142+
return
143+
}
144+
truncatedIn := truncatedNumber(in)
145+
decoded := decodePacketNumber(largestAcked, truncatedIn, len(in))
146+
147+
// Check that the decoded packet number's least significant bits match the input.
148+
var mask packetNumber
149+
for i := 0; i < len(in); i++ {
150+
mask = (mask << 8) | 0xff
151+
}
152+
if truncatedIn != decoded&mask {
153+
t.Fatalf("decoding mismatch: input=%x largestAcked=%v decoded=0x%x", in, largestAcked, decoded)
154+
}
155+
156+
// We don't support encoding packet numbers less than largestAcked (since packet numbers
157+
// never decrease), so skip the encoder tests if this would make us go backwards.
158+
if decoded < largestAcked {
159+
return
160+
}
161+
162+
// We might encode this number using a different length than we received,
163+
// but the common portions should match.
164+
encoded := appendPacketNumber(nil, decoded, largestAcked)
165+
a, b := in, encoded
166+
if len(b) < len(a) {
167+
a, b = b, a
168+
}
169+
for len(a) < len(b) {
170+
b = b[1:]
171+
}
172+
if len(a) == 0 || !bytes.Equal(a, b) {
173+
t.Fatalf("encoding mismatch: input=%x largestAcked=%v decoded=%v reencoded=%x", in, largestAcked, decoded, encoded)
174+
}
175+
176+
if g := decodePacketNumber(largestAcked, truncatedNumber(encoded), len(encoded)); g != decoded {
177+
t.Fatalf("packet encode/decode mismatch: pnum=%v largestAcked=%v encoded=%x got=%v", decoded, largestAcked, encoded, g)
178+
}
179+
if l := packetNumberLength(decoded, largestAcked); l != len(encoded) {
180+
t.Fatalf("packet number length mismatch: pnum=%v largestAcked=%v encoded=%x len=%v", decoded, largestAcked, encoded, l)
181+
}
182+
})
183+
}

0 commit comments

Comments
 (0)