mirror of
https://github.com/golang/net.git
synced 2026-03-31 02:17:08 +09:00
An "optimistic ACK attack" involves an attacker sending ACKs for packets it hasn't received, causing the victim's congestion controller to improperly send at a higher rate. The standard defense against this attack is to skip the occasional packet number, and to close the connection with an error if the peer ACKs an unsent packet. Implement this defense, increasing the gap between skipped packet numbers as a connection's lifetime grows and correspondingly the amount of work required on the part of the attacker. Change-Id: I01f44f13367821b86af6535ffb69d380e2b4d7b7 Reviewed-on: https://go-review.googlesource.com/c/net/+/664298 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Jonathan Amsterdam <jba@google.com> Auto-Submit: Damien Neil <dneil@google.com>
120 lines
3.0 KiB
Go
120 lines
3.0 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.
|
|
|
|
package quic
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
"golang.org/x/net/internal/quic/quicwire"
|
|
)
|
|
|
|
// A sentPacket tracks state related to an in-flight packet we sent,
|
|
// to be committed when the peer acks it or resent if the packet is lost.
|
|
type sentPacket struct {
|
|
num packetNumber
|
|
size int // size in bytes
|
|
time time.Time // time sent
|
|
ptype packetType
|
|
|
|
state sentPacketState
|
|
ackEliciting bool // https://www.rfc-editor.org/rfc/rfc9002.html#section-2-3.4.1
|
|
inFlight bool // https://www.rfc-editor.org/rfc/rfc9002.html#section-2-3.6.1
|
|
|
|
// Frames sent in the packet.
|
|
//
|
|
// This is an abbreviated version of the packet payload, containing only the information
|
|
// we need to process an ack for or loss of this packet.
|
|
// For example, a CRYPTO frame is recorded as the frame type (0x06), offset, and length,
|
|
// but does not include the sent data.
|
|
//
|
|
// This buffer is written by packetWriter.append* and read by Conn.handleAckOrLoss.
|
|
b []byte
|
|
n int // read offset into b
|
|
}
|
|
|
|
type sentPacketState uint8
|
|
|
|
const (
|
|
sentPacketSent = sentPacketState(iota) // sent but neither acked nor lost
|
|
sentPacketAcked // acked
|
|
sentPacketLost // declared lost
|
|
sentPacketUnsent // never sent
|
|
)
|
|
|
|
var sentPool = sync.Pool{
|
|
New: func() any {
|
|
return &sentPacket{}
|
|
},
|
|
}
|
|
|
|
func newSentPacket() *sentPacket {
|
|
sent := sentPool.Get().(*sentPacket)
|
|
sent.reset()
|
|
return sent
|
|
}
|
|
|
|
// recycle returns a sentPacket to the pool.
|
|
func (sent *sentPacket) recycle() {
|
|
sentPool.Put(sent)
|
|
}
|
|
|
|
func (sent *sentPacket) reset() {
|
|
*sent = sentPacket{
|
|
b: sent.b[:0],
|
|
}
|
|
}
|
|
|
|
// markAckEliciting marks the packet as containing an ack-eliciting frame.
|
|
func (sent *sentPacket) markAckEliciting() {
|
|
sent.ackEliciting = true
|
|
sent.inFlight = true
|
|
}
|
|
|
|
// The append* methods record information about frames in the packet.
|
|
|
|
func (sent *sentPacket) appendNonAckElicitingFrame(frameType byte) {
|
|
sent.b = append(sent.b, frameType)
|
|
}
|
|
|
|
func (sent *sentPacket) appendAckElicitingFrame(frameType byte) {
|
|
sent.ackEliciting = true
|
|
sent.inFlight = true
|
|
sent.b = append(sent.b, frameType)
|
|
}
|
|
|
|
func (sent *sentPacket) appendInt(v uint64) {
|
|
sent.b = quicwire.AppendVarint(sent.b, v)
|
|
}
|
|
|
|
func (sent *sentPacket) appendOffAndSize(start int64, size int) {
|
|
sent.b = quicwire.AppendVarint(sent.b, uint64(start))
|
|
sent.b = quicwire.AppendVarint(sent.b, uint64(size))
|
|
}
|
|
|
|
// The next* methods read back information about frames in the packet.
|
|
|
|
func (sent *sentPacket) next() (frameType byte) {
|
|
f := sent.b[sent.n]
|
|
sent.n++
|
|
return f
|
|
}
|
|
|
|
func (sent *sentPacket) nextInt() uint64 {
|
|
v, n := quicwire.ConsumeVarint(sent.b[sent.n:])
|
|
sent.n += n
|
|
return v
|
|
}
|
|
|
|
func (sent *sentPacket) nextRange() (start, end int64) {
|
|
start = int64(sent.nextInt())
|
|
end = start + int64(sent.nextInt())
|
|
return start, end
|
|
}
|
|
|
|
func (sent *sentPacket) done() bool {
|
|
return sent.n == len(sent.b)
|
|
}
|