mirror of
https://github.com/golang/net.git
synced 2026-03-31 18:37:08 +09:00
For golang/go#58547 Change-Id: I119d820824f82bfdd236c6826f960d0c934745ca Reviewed-on: https://go-review.googlesource.com/c/net/+/566295 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Jonathan Amsterdam <jba@google.com>
192 lines
7.1 KiB
Go
192 lines
7.1 KiB
Go
// 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 (
|
|
"time"
|
|
)
|
|
|
|
// ackState tracks packets received from a peer within a number space.
|
|
// It handles packet deduplication (don't process the same packet twice) and
|
|
// determines the timing and content of ACK frames.
|
|
type ackState struct {
|
|
seen rangeset[packetNumber]
|
|
|
|
// The time at which we must send an ACK frame, even if we have no other data to send.
|
|
nextAck time.Time
|
|
|
|
// The time we received the largest-numbered packet in seen.
|
|
maxRecvTime time.Time
|
|
|
|
// The largest-numbered ack-eliciting packet in seen.
|
|
maxAckEliciting packetNumber
|
|
|
|
// The number of ack-eliciting packets in seen that we have not yet acknowledged.
|
|
unackedAckEliciting int
|
|
}
|
|
|
|
// shouldProcess reports whether a packet should be handled or discarded.
|
|
func (acks *ackState) shouldProcess(num packetNumber) bool {
|
|
if packetNumber(acks.seen.min()) > num {
|
|
// We've discarded the state for this range of packet numbers.
|
|
// Discard the packet rather than potentially processing a duplicate.
|
|
// https://www.rfc-editor.org/rfc/rfc9000.html#section-13.2.3-5
|
|
return false
|
|
}
|
|
if acks.seen.contains(num) {
|
|
// Discard duplicate packets.
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// receive records receipt of a packet.
|
|
func (acks *ackState) receive(now time.Time, space numberSpace, num packetNumber, ackEliciting bool) {
|
|
if ackEliciting {
|
|
acks.unackedAckEliciting++
|
|
if acks.mustAckImmediately(space, num) {
|
|
acks.nextAck = now
|
|
} else if acks.nextAck.IsZero() {
|
|
// This packet does not need to be acknowledged immediately,
|
|
// but the ack must not be intentionally delayed by more than
|
|
// the max_ack_delay transport parameter we sent to the peer.
|
|
//
|
|
// We always delay acks by the maximum allowed, less the timer
|
|
// granularity. ("[max_ack_delay] SHOULD include the receiver's
|
|
// expected delays in alarms firing.")
|
|
//
|
|
// https://www.rfc-editor.org/rfc/rfc9000#section-18.2-4.28.1
|
|
acks.nextAck = now.Add(maxAckDelay - timerGranularity)
|
|
}
|
|
if num > acks.maxAckEliciting {
|
|
acks.maxAckEliciting = num
|
|
}
|
|
}
|
|
|
|
acks.seen.add(num, num+1)
|
|
if num == acks.seen.max() {
|
|
acks.maxRecvTime = now
|
|
}
|
|
|
|
// Limit the total number of ACK ranges by dropping older ranges.
|
|
//
|
|
// Remembering more ranges results in larger ACK frames.
|
|
//
|
|
// Remembering a large number of ranges could result in ACK frames becoming
|
|
// too large to fit in a packet, in which case we will silently drop older
|
|
// ranges during packet construction.
|
|
//
|
|
// Remembering fewer ranges can result in unnecessary retransmissions,
|
|
// since we cannot accept packets older than the oldest remembered range.
|
|
//
|
|
// The limit here is completely arbitrary. If it seems wrong, it probably is.
|
|
//
|
|
// https://www.rfc-editor.org/rfc/rfc9000#section-13.2.3
|
|
const maxAckRanges = 8
|
|
if overflow := acks.seen.numRanges() - maxAckRanges; overflow > 0 {
|
|
acks.seen.removeranges(0, overflow)
|
|
}
|
|
}
|
|
|
|
// mustAckImmediately reports whether an ack-eliciting packet must be acknowledged immediately,
|
|
// or whether the ack may be deferred.
|
|
func (acks *ackState) mustAckImmediately(space numberSpace, num packetNumber) bool {
|
|
// https://www.rfc-editor.org/rfc/rfc9000.html#section-13.2.1
|
|
if space != appDataSpace {
|
|
// "[...] all ack-eliciting Initial and Handshake packets [...]"
|
|
// https://www.rfc-editor.org/rfc/rfc9000.html#section-13.2.1-2
|
|
return true
|
|
}
|
|
if num < acks.maxAckEliciting {
|
|
// "[...] when the received packet has a packet number less than another
|
|
// ack-eliciting packet that has been received [...]"
|
|
// https://www.rfc-editor.org/rfc/rfc9000.html#section-13.2.1-8.1
|
|
return true
|
|
}
|
|
if acks.seen.rangeContaining(acks.maxAckEliciting).end != num {
|
|
// "[...] when the packet has a packet number larger than the highest-numbered
|
|
// ack-eliciting packet that has been received and there are missing packets
|
|
// between that packet and this packet."
|
|
// https://www.rfc-editor.org/rfc/rfc9000.html#section-13.2.1-8.2
|
|
//
|
|
// This case is a bit tricky. Let's say we've received:
|
|
// 0, ack-eliciting
|
|
// 1, ack-eliciting
|
|
// 3, NOT ack eliciting
|
|
//
|
|
// We have sent ACKs for 0 and 1. If we receive ack-eliciting packet 2,
|
|
// we do not need to send an immediate ACK, because there are no missing
|
|
// packets between it and the highest-numbered ack-eliciting packet (1).
|
|
// If we receive ack-eliciting packet 4, we do need to send an immediate ACK,
|
|
// because there's a gap (the missing packet 2).
|
|
//
|
|
// We check for this by looking up the ACK range which contains the
|
|
// highest-numbered ack-eliciting packet: [0, 1) in the above example.
|
|
// If the range ends just before the packet we are now processing,
|
|
// there are no gaps. If it does not, there must be a gap.
|
|
return true
|
|
}
|
|
// "[...] SHOULD send an ACK frame after receiving at least two ack-eliciting packets."
|
|
// https://www.rfc-editor.org/rfc/rfc9000.html#section-13.2.2
|
|
//
|
|
// This ack frequency takes a substantial toll on performance, however.
|
|
// Follow the behavior of Google QUICHE:
|
|
// Ack every other packet for the first 100 packets, and then ack every 10th packet.
|
|
// This keeps ack frequency high during the beginning of slow start when CWND is
|
|
// increasing rapidly.
|
|
packetsBeforeAck := 2
|
|
if acks.seen.max() > 100 {
|
|
packetsBeforeAck = 10
|
|
}
|
|
return acks.unackedAckEliciting >= packetsBeforeAck
|
|
}
|
|
|
|
// shouldSendAck reports whether the connection should send an ACK frame at this time,
|
|
// in an ACK-only packet if necessary.
|
|
func (acks *ackState) shouldSendAck(now time.Time) bool {
|
|
return !acks.nextAck.IsZero() && !acks.nextAck.After(now)
|
|
}
|
|
|
|
// acksToSend returns the set of packet numbers to ACK at this time, and the current ack delay.
|
|
// It may return acks even if shouldSendAck returns false, when there are unacked
|
|
// ack-eliciting packets whose ack is being delayed.
|
|
func (acks *ackState) acksToSend(now time.Time) (nums rangeset[packetNumber], ackDelay time.Duration) {
|
|
if acks.nextAck.IsZero() && acks.unackedAckEliciting == 0 {
|
|
return nil, 0
|
|
}
|
|
// "[...] the delays intentionally introduced between the time the packet with the
|
|
// largest packet number is received and the time an acknowledgement is sent."
|
|
// https://www.rfc-editor.org/rfc/rfc9000#section-13.2.5-1
|
|
delay := now.Sub(acks.maxRecvTime)
|
|
if delay < 0 {
|
|
delay = 0
|
|
}
|
|
return acks.seen, delay
|
|
}
|
|
|
|
// sentAck records that an ACK frame has been sent.
|
|
func (acks *ackState) sentAck() {
|
|
acks.nextAck = time.Time{}
|
|
acks.unackedAckEliciting = 0
|
|
}
|
|
|
|
// handleAck records that an ack has been received for a ACK frame we sent
|
|
// containing the given Largest Acknowledged field.
|
|
func (acks *ackState) handleAck(largestAcked packetNumber) {
|
|
// We can stop acking packets less or equal to largestAcked.
|
|
// https://www.rfc-editor.org/rfc/rfc9000.html#section-13.2.4-1
|
|
//
|
|
// We rely on acks.seen containing the largest packet number that has been successfully
|
|
// processed, so we retain the range containing largestAcked and discard previous ones.
|
|
acks.seen.sub(0, acks.seen.rangeContaining(largestAcked).start)
|
|
}
|
|
|
|
// largestSeen reports the largest seen packet.
|
|
func (acks *ackState) largestSeen() packetNumber {
|
|
return acks.seen.max()
|
|
}
|