Skip to content

Commit 952fc9c

Browse files
committed
quic: move ack_delay_exponent handling out of frame parsing
The ACK Delay field of ACK frames contains a duration. The field contains an integer which is multiplied by two to the power of the sender's ack_delay_exponent transport parameter to arrive at the delay in microseconds. Change the frame parsing and encoding layer to operate on the unscaled field value, rather than passing the ack_delay_exponent and a duration. This better expresses the fact that we may parse an ACK frame without knowing the ack_delay_exponent, if the ACK is received before transport parameters. For golang/go#58547 Change-Id: Ic26256761961ce89aea0618b849e5661b0502b12 Reviewed-on: https://go-review.googlesource.com/c/net/+/504855 TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Damien Neil <[email protected]> Reviewed-by: Jonathan Amsterdam <[email protected]>
1 parent 02fe9a5 commit 952fc9c

File tree

6 files changed

+121
-20
lines changed

6 files changed

+121
-20
lines changed

internal/quic/ack_delay.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
"math"
11+
"time"
12+
)
13+
14+
// An unscaledAckDelay is an ACK Delay field value from an ACK packet,
15+
// without the ack_delay_exponent scaling applied.
16+
type unscaledAckDelay int64
17+
18+
func unscaledAckDelayFromDuration(d time.Duration, ackDelayExponent uint8) unscaledAckDelay {
19+
return unscaledAckDelay(d.Microseconds() >> ackDelayExponent)
20+
}
21+
22+
func (d unscaledAckDelay) Duration(ackDelayExponent uint8) time.Duration {
23+
if int64(d) > (math.MaxInt64>>ackDelayExponent)/int64(time.Microsecond) {
24+
// If scaling the delay would overflow, ignore the delay.
25+
return 0
26+
}
27+
return time.Duration(d<<ackDelayExponent) * time.Microsecond
28+
}

internal/quic/ack_delay_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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+
"math"
11+
"testing"
12+
"time"
13+
)
14+
15+
func TestAckDelayFromDuration(t *testing.T) {
16+
for _, test := range []struct {
17+
d time.Duration
18+
ackDelayExponent uint8
19+
want unscaledAckDelay
20+
}{{
21+
d: 8 * time.Microsecond,
22+
ackDelayExponent: 3,
23+
want: 1,
24+
}, {
25+
d: 1 * time.Nanosecond,
26+
ackDelayExponent: 3,
27+
want: 0, // rounds to zero
28+
}, {
29+
d: 3 * (1 << 20) * time.Microsecond,
30+
ackDelayExponent: 20,
31+
want: 3,
32+
}} {
33+
got := unscaledAckDelayFromDuration(test.d, test.ackDelayExponent)
34+
if got != test.want {
35+
t.Errorf("unscaledAckDelayFromDuration(%v, %v) = %v, want %v",
36+
test.d, test.ackDelayExponent, got, test.want)
37+
}
38+
}
39+
}
40+
41+
func TestAckDelayToDuration(t *testing.T) {
42+
for _, test := range []struct {
43+
d unscaledAckDelay
44+
ackDelayExponent uint8
45+
want time.Duration
46+
}{{
47+
d: 1,
48+
ackDelayExponent: 3,
49+
want: 8 * time.Microsecond,
50+
}, {
51+
d: 0,
52+
ackDelayExponent: 3,
53+
want: 0,
54+
}, {
55+
d: 3,
56+
ackDelayExponent: 20,
57+
want: 3 * (1 << 20) * time.Microsecond,
58+
}, {
59+
d: math.MaxInt64 / 1000,
60+
ackDelayExponent: 0,
61+
want: (math.MaxInt64 / 1000) * time.Microsecond,
62+
}, {
63+
d: (math.MaxInt64 / 1000) + 1,
64+
ackDelayExponent: 0,
65+
want: 0, // return 0 on overflow
66+
}, {
67+
d: math.MaxInt64 / 1000 / 8,
68+
ackDelayExponent: 3,
69+
want: (math.MaxInt64 / 1000 / 8) * 8 * time.Microsecond,
70+
}, {
71+
d: (math.MaxInt64 / 1000 / 8) + 1,
72+
ackDelayExponent: 3,
73+
want: 0, // return 0 on overflow
74+
}} {
75+
got := test.d.Duration(test.ackDelayExponent)
76+
if got != test.want {
77+
t.Errorf("unscaledAckDelay(%v).Duration(%v) = %v, want %v",
78+
test.d, test.ackDelayExponent, int64(got), int64(test.want))
79+
}
80+
}
81+
}

internal/quic/frame_debug.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ package quic
88

99
import (
1010
"fmt"
11-
"time"
1211
)
1312

1413
// A debugFrame is a representation of the contents of a QUIC frame,
@@ -115,13 +114,13 @@ func (f debugFramePing) write(w *packetWriter) bool {
115114

116115
// debugFrameAck is an ACK frame.
117116
type debugFrameAck struct {
118-
ackDelay time.Duration
117+
ackDelay unscaledAckDelay
119118
ranges []i64range[packetNumber]
120119
}
121120

122121
func parseDebugFrameAck(b []byte) (f debugFrameAck, n int) {
123122
f.ranges = nil
124-
_, f.ackDelay, n = consumeAckFrame(b, ackDelayExponent, func(start, end packetNumber) {
123+
_, f.ackDelay, n = consumeAckFrame(b, func(start, end packetNumber) {
125124
f.ranges = append(f.ranges, i64range[packetNumber]{
126125
start: start,
127126
end: end,
@@ -144,7 +143,7 @@ func (f debugFrameAck) String() string {
144143
}
145144

146145
func (f debugFrameAck) write(w *packetWriter) bool {
147-
return w.appendAckFrame(rangeset[packetNumber](f.ranges), ackDelayExponent, f.ackDelay)
146+
return w.appendAckFrame(rangeset[packetNumber](f.ranges), f.ackDelay)
148147
}
149148

150149
// debugFrameResetStream is a RESET_STREAM frame.

internal/quic/packet_codec_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"crypto/tls"
1212
"reflect"
1313
"testing"
14-
"time"
1514
)
1615

1716
func TestParseLongHeaderPacket(t *testing.T) {
@@ -219,9 +218,9 @@ func TestFrameEncodeDecode(t *testing.T) {
219218
0x01, // TYPE(i) = 0x01
220219
},
221220
}, {
222-
s: "ACK Delay=80µs [0,16) [17,32) [48,64)",
221+
s: "ACK Delay=10 [0,16) [17,32) [48,64)",
223222
f: debugFrameAck{
224-
ackDelay: (10 << ackDelayExponent) * time.Microsecond,
223+
ackDelay: 10,
225224
ranges: []i64range[packetNumber]{
226225
{0x00, 0x10},
227226
{0x11, 0x20},
@@ -594,7 +593,7 @@ func TestFrameDecode(t *testing.T) {
594593
}, {
595594
desc: "ACK frame with ECN counts",
596595
want: debugFrameAck{
597-
ackDelay: (10 << ackDelayExponent) * time.Microsecond,
596+
ackDelay: 10,
598597
ranges: []i64range[packetNumber]{
599598
{0, 1},
600599
},

internal/quic/packet_parser.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66

77
package quic
88

9-
import (
10-
"time"
11-
)
12-
139
// parseLongHeaderPacket parses a QUIC long header packet.
1410
//
1511
// It does not parse Version Negotiation packets.
@@ -166,7 +162,7 @@ func parse1RTTPacket(pkt []byte, k keys, pnumMax packetNumber) (p shortPacket, n
166162
// which includes both general parse failures and specific violations of frame
167163
// constraints.
168164

169-
func consumeAckFrame(frame []byte, ackDelayExponent uint8, f func(start, end packetNumber)) (largest packetNumber, ackDelay time.Duration, n int) {
165+
func consumeAckFrame(frame []byte, f func(start, end packetNumber)) (largest packetNumber, ackDelay unscaledAckDelay, n int) {
170166
b := frame[1:] // type
171167

172168
largestAck, n := consumeVarint(b)
@@ -175,12 +171,12 @@ func consumeAckFrame(frame []byte, ackDelayExponent uint8, f func(start, end pac
175171
}
176172
b = b[n:]
177173

178-
ackDelayScaled, n := consumeVarint(b)
174+
v, n := consumeVarintInt64(b)
179175
if n < 0 {
180176
return 0, 0, -1
181177
}
182178
b = b[n:]
183-
ackDelay = time.Duration(ackDelayScaled*(1<<ackDelayExponent)) * time.Microsecond
179+
ackDelay = unscaledAckDelay(v)
184180

185181
ackRangeCount, n := consumeVarint(b)
186182
if n < 0 {

internal/quic/packet_writer.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ package quic
88

99
import (
1010
"encoding/binary"
11-
"time"
1211
)
1312

1413
// A packetWriter constructs QUIC datagrams.
@@ -257,21 +256,20 @@ func (w *packetWriter) appendPingFrame() (added bool) {
257256
// to the peer potentially failing to receive an acknowledgement
258257
// for an older packet during a period of high packet loss or
259258
// reordering. This may result in unnecessary retransmissions.
260-
func (w *packetWriter) appendAckFrame(seen rangeset[packetNumber], ackDelayExponent uint8, delay time.Duration) (added bool) {
259+
func (w *packetWriter) appendAckFrame(seen rangeset[packetNumber], delay unscaledAckDelay) (added bool) {
261260
if len(seen) == 0 {
262261
return false
263262
}
264263
var (
265264
largest = uint64(seen.max())
266-
mdelay = uint64(delay.Microseconds() / (1 << ackDelayExponent))
267265
firstRange = uint64(seen[len(seen)-1].size() - 1)
268266
)
269-
if w.avail() < 1+sizeVarint(largest)+sizeVarint(mdelay)+1+sizeVarint(firstRange) {
267+
if w.avail() < 1+sizeVarint(largest)+sizeVarint(uint64(delay))+1+sizeVarint(firstRange) {
270268
return false
271269
}
272270
w.b = append(w.b, frameTypeAck)
273271
w.b = appendVarint(w.b, largest)
274-
w.b = appendVarint(w.b, mdelay)
272+
w.b = appendVarint(w.b, uint64(delay))
275273
// The range count is technically a varint, but we'll reserve a single byte for it
276274
// and never add more than 62 ranges (the maximum varint that fits in a byte).
277275
rangeCountOff := len(w.b)

0 commit comments

Comments
 (0)