mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-04 20:57:36 +03:00
296 lines
8 KiB
Go
296 lines
8 KiB
Go
package frames
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/lucas-clemente/quic-go/protocol"
|
|
"github.com/lucas-clemente/quic-go/utils"
|
|
)
|
|
|
|
var errInvalidNackRanges = errors.New("AckFrame: ACK frame contains invalid NACK ranges")
|
|
|
|
// An AckFrame in QUIC
|
|
type AckFrame struct {
|
|
LargestObserved protocol.PacketNumber
|
|
Entropy byte
|
|
NackRanges []NackRange // has to be ordered. The NACK range with the highest FirstPacketNumber goes first, the NACK range with the lowest FirstPacketNumber goes last
|
|
Truncated bool
|
|
|
|
DelayTime time.Duration
|
|
PacketReceivedTime time.Time // only for received packets. Will not be modified for received ACKs frames
|
|
}
|
|
|
|
// Write writes an ACK frame.
|
|
func (f *AckFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
|
|
typeByte := uint8(0x40 | 0x0C)
|
|
|
|
if f.HasNACK() {
|
|
typeByte |= (0x20 | 0x03)
|
|
}
|
|
|
|
f.DelayTime = time.Now().Sub(f.PacketReceivedTime)
|
|
|
|
b.WriteByte(typeByte)
|
|
b.WriteByte(f.Entropy)
|
|
utils.WriteUint48(b, uint64(f.LargestObserved)) // TODO: send the correct length
|
|
utils.WriteUfloat16(b, uint64(f.DelayTime/time.Microsecond))
|
|
b.WriteByte(0x01) // Just one timestamp
|
|
b.WriteByte(0x00) // Delta Largest observed
|
|
utils.WriteUint32(b, 0) // First timestamp
|
|
|
|
if f.HasNACK() {
|
|
numRanges := uint64(0)
|
|
// calculate the number of NackRanges that are about to be written
|
|
// this number is different from len(f.NackRanges) for the case of contiguous NACK ranges
|
|
for _, nackRange := range f.NackRanges {
|
|
rangeLength := nackRange.Len()
|
|
numRanges += rangeLength/0xFF + 1
|
|
if rangeLength > 0 && rangeLength%0xFF == 0 {
|
|
numRanges--
|
|
}
|
|
}
|
|
if numRanges > 0xFF {
|
|
panic("Too many NACK ranges. Truncating not yet implemented.")
|
|
}
|
|
|
|
b.WriteByte(uint8(numRanges))
|
|
|
|
rangeCounter := uint8(0)
|
|
for i, nackRange := range f.NackRanges {
|
|
var missingPacketSequenceNumberDelta uint64
|
|
if i == 0 {
|
|
if nackRange.LastPacketNumber > f.LargestObserved {
|
|
return errors.New("AckFrame: Invalid NACK ranges")
|
|
}
|
|
missingPacketSequenceNumberDelta = uint64(f.LargestObserved) - uint64(nackRange.LastPacketNumber)
|
|
} else {
|
|
lastNackRange := f.NackRanges[i-1]
|
|
missingPacketSequenceNumberDelta = uint64(lastNackRange.FirstPacketNumber) - uint64(nackRange.LastPacketNumber) - 1
|
|
}
|
|
rangeLength := nackRange.Len()
|
|
|
|
utils.WriteUint48(b, missingPacketSequenceNumberDelta)
|
|
b.WriteByte(uint8(rangeLength % 0x100))
|
|
rangeCounter++
|
|
|
|
rangeLength = rangeLength - (rangeLength % 0x100)
|
|
for rangeLength > 0 {
|
|
rangeCounter++
|
|
utils.WriteUint48(b, 0)
|
|
b.WriteByte(uint8(0xFF))
|
|
rangeLength -= 0x100
|
|
}
|
|
}
|
|
|
|
if rangeCounter != uint8(numRanges) {
|
|
panic("Inconsistent number of NACK ranges written.")
|
|
}
|
|
|
|
// TODO: Remove once we drop support for <32
|
|
if version < protocol.VersionNumber(32) {
|
|
b.WriteByte(0)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// MinLength of a written frame
|
|
func (f *AckFrame) MinLength() (protocol.ByteCount, error) {
|
|
l := 1 + 1 + 6 + 2 + 1 + 1 + 4
|
|
l += (1 + 2) * 0 /* TODO: num_timestamps */
|
|
if f.HasNACK() {
|
|
l += 1 + (6+1)*len(f.NackRanges)
|
|
l++ // TODO: Remove once we drop support for <32
|
|
}
|
|
return protocol.ByteCount(l), nil
|
|
}
|
|
|
|
// HasNACK returns if the frame has NACK ranges
|
|
func (f *AckFrame) HasNACK() bool {
|
|
if len(f.NackRanges) > 0 {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// GetHighestInOrderPacketNumber gets the highest in order packet number that is confirmed by this ACK
|
|
func (f *AckFrame) GetHighestInOrderPacketNumber() protocol.PacketNumber {
|
|
if f.HasNACK() {
|
|
return (f.NackRanges[len(f.NackRanges)-1].FirstPacketNumber - 1)
|
|
}
|
|
return f.LargestObserved
|
|
}
|
|
|
|
// ParseAckFrame reads an ACK frame
|
|
func ParseAckFrame(r *bytes.Reader, version protocol.VersionNumber) (*AckFrame, error) {
|
|
frame := &AckFrame{}
|
|
|
|
typeByte, err := r.ReadByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
hasNACK := false
|
|
if typeByte&0x20 == 0x20 {
|
|
hasNACK = true
|
|
}
|
|
frame.Truncated = typeByte&0x10 > 0
|
|
|
|
largestObservedLen := 2 * ((typeByte & 0x0C) >> 2)
|
|
if largestObservedLen == 0 {
|
|
largestObservedLen = 1
|
|
}
|
|
|
|
missingSequenceNumberDeltaLen := 2 * (typeByte & 0x03)
|
|
if missingSequenceNumberDeltaLen == 0 {
|
|
missingSequenceNumberDeltaLen = 1
|
|
}
|
|
|
|
frame.Entropy, err = r.ReadByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
largestObserved, err := utils.ReadUintN(r, largestObservedLen)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
frame.LargestObserved = protocol.PacketNumber(largestObserved)
|
|
|
|
delay, err := utils.ReadUfloat16(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
frame.DelayTime = time.Duration(delay) * time.Microsecond
|
|
|
|
if !frame.Truncated {
|
|
var err error
|
|
numTimestampByte, err := r.ReadByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
numTimestamp := uint8(numTimestampByte)
|
|
|
|
// Delta Largest observed
|
|
_, err = r.ReadByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// First Timestamp
|
|
_, err = utils.ReadUint32(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for i := 0; i < int(numTimestamp)-1; i++ {
|
|
// Delta Largest observed
|
|
_, err = r.ReadByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Time Since Previous Timestamp
|
|
_, err = utils.ReadUint16(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
|
|
// Invalid NACK Handling:
|
|
// NACKs contain a lot of offsets that require subtractions of PacketNumbers. If an ACK contains invalid data, it is possible to underflow the uint64 used to store the PacketNumber
|
|
// TODO: handle uint64 overflows
|
|
if hasNACK {
|
|
var numRanges uint8
|
|
numRanges, err = r.ReadByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for i := uint8(0); i < numRanges; i++ {
|
|
missingPacketSequenceNumberDelta, err := utils.ReadUintN(r, missingSequenceNumberDeltaLen)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rangeLengthByte, err := r.ReadByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rangeLength := uint8(rangeLengthByte)
|
|
|
|
if i == 0 && missingPacketSequenceNumberDelta == 0 {
|
|
return nil, errors.New("ACK frame: largest observed missing not yet implemented")
|
|
}
|
|
|
|
// contiguous NACK range
|
|
if missingPacketSequenceNumberDelta == 0 {
|
|
nackRange := &frame.NackRanges[len(frame.NackRanges)-1]
|
|
if uint64(nackRange.FirstPacketNumber) <= uint64(rangeLength)+1 {
|
|
return nil, errInvalidNackRanges
|
|
}
|
|
nackRange.FirstPacketNumber = protocol.PacketNumber(uint64(nackRange.FirstPacketNumber) - uint64(rangeLength) - 1)
|
|
} else {
|
|
nackRange := NackRange{}
|
|
if i == 0 {
|
|
if uint64(frame.LargestObserved) < missingPacketSequenceNumberDelta+uint64(rangeLength) {
|
|
return nil, errInvalidNackRanges
|
|
}
|
|
nackRange.FirstPacketNumber = frame.LargestObserved - protocol.PacketNumber(missingPacketSequenceNumberDelta+uint64(rangeLength))
|
|
} else {
|
|
lastNackRange := frame.NackRanges[len(frame.NackRanges)-1]
|
|
if uint64(lastNackRange.FirstPacketNumber) <= missingPacketSequenceNumberDelta+uint64(rangeLength) {
|
|
return nil, errInvalidNackRanges
|
|
}
|
|
nackRange.FirstPacketNumber = lastNackRange.FirstPacketNumber - protocol.PacketNumber(missingPacketSequenceNumberDelta+uint64(rangeLength)) - 1
|
|
}
|
|
nackRange.LastPacketNumber = protocol.PacketNumber(uint64(nackRange.FirstPacketNumber) + uint64(rangeLength))
|
|
frame.NackRanges = append(frame.NackRanges, nackRange)
|
|
}
|
|
|
|
// TODO: Remove once we drop support for versions <32
|
|
if version < protocol.VersionNumber(32) {
|
|
_, err = r.ReadByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if !frame.validateNackRanges() {
|
|
return nil, errInvalidNackRanges
|
|
}
|
|
|
|
return frame, nil
|
|
}
|
|
|
|
func (f *AckFrame) validateNackRanges() bool {
|
|
// check the validity of every single NACK range
|
|
for _, nackRange := range f.NackRanges {
|
|
if nackRange.FirstPacketNumber > nackRange.LastPacketNumber {
|
|
return false
|
|
}
|
|
if nackRange.LastPacketNumber >= f.LargestObserved {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// check the consistency for ACK with multiple NACK ranges
|
|
for i, nackRange := range f.NackRanges {
|
|
if i == 0 {
|
|
continue
|
|
}
|
|
lastNackRange := f.NackRanges[i-1]
|
|
if lastNackRange.FirstPacketNumber <= nackRange.FirstPacketNumber {
|
|
return false
|
|
}
|
|
if lastNackRange.FirstPacketNumber <= nackRange.LastPacketNumber {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|