From 963517e7be3ac257a310a92a8f5b288ffc28e382 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Mon, 20 Jun 2016 11:31:23 +0700 Subject: [PATCH] implement basic ACK block parsing for QUIC 34 ACK frames ref #182 --- frames/ack_frame_new.go | 58 ++++++++++++++++++++++++++++---- frames/ack_frame_new_test.go | 64 +++++++++++++++++++++++++++++++++--- frames/ack_range.go | 9 +++++ 3 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 frames/ack_range.go diff --git a/frames/ack_frame_new.go b/frames/ack_frame_new.go index c52cf542..7a4ef3c6 100644 --- a/frames/ack_frame_new.go +++ b/frames/ack_frame_new.go @@ -2,17 +2,22 @@ package frames import ( "bytes" + "errors" "time" "github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/utils" ) +var errInvalidAckRanges = errors.New("AckFrame: ACK frame contains invalid ACK ranges") + // An AckFrameNew is a ACK frame in QUIC c34 type AckFrameNew struct { // TODO: rename to LargestAcked LargestObserved protocol.PacketNumber NackRanges []NackRange // has to be ordered. The NACK range with the highest FirstPacketNumber goes first, the NACK range with the lowest FirstPacketNumber goes last + LowestAcked protocol.PacketNumber + AckRanges []AckRange DelayTime time.Duration PacketReceivedTime time.Time // only for received packets. Will not be modified for received ACKs frames @@ -32,10 +37,6 @@ func ParseAckFrameNew(r *bytes.Reader, version protocol.VersionNumber) (*AckFram hasNACK = true } - if hasNACK { - panic("NACKs not yet implemented") - } - largestObservedLen := 2 * ((typeByte & 0x0C) >> 2) if largestObservedLen == 0 { largestObservedLen = 1 @@ -58,15 +59,58 @@ func ParseAckFrameNew(r *bytes.Reader, version protocol.VersionNumber) (*AckFram } frame.DelayTime = time.Duration(delay) * time.Microsecond - // TODO: read number of ACK blocks if n flag is set + var numAckBlocks uint8 + if hasNACK { + numAckBlocks, err = r.ReadByte() + if err != nil { + return nil, err + } + } ackBlockLength, err := utils.ReadUintN(r, missingSequenceNumberDeltaLen) if err != nil { return nil, err } - utils.Debugf("ackBlockLength: %d", ackBlockLength) - // TODO: read ACK blocks + if ackBlockLength > largestObserved { + return nil, errInvalidAckRanges + } + + if hasNACK { + ackRange := AckRange{ + FirstPacketNumber: protocol.PacketNumber(largestObserved-ackBlockLength) + 1, + LastPacketNumber: frame.LargestObserved, + } + frame.AckRanges = append(frame.AckRanges, ackRange) + + for i := uint8(0); i < numAckBlocks; i++ { + var gap uint8 + gap, err = r.ReadByte() + if err != nil { + return nil, err + } + + ackBlockLength, err = utils.ReadUintN(r, missingSequenceNumberDeltaLen) + if err != nil { + return nil, err + } + + ackRange := AckRange{ + LastPacketNumber: frame.AckRanges[i].FirstPacketNumber - protocol.PacketNumber(gap) - 1, + } + ackRange.FirstPacketNumber = ackRange.LastPacketNumber - protocol.PacketNumber(ackBlockLength) + 1 + frame.AckRanges = append(frame.AckRanges, ackRange) + } + frame.LowestAcked = frame.AckRanges[numAckBlocks].FirstPacketNumber + } else { + // make sure that LowestAcked is not 0. 0 is not a valid PacketNumber + // TODO: is this really the right behavior? + if largestObserved == ackBlockLength { + frame.LowestAcked = 1 + } else { + frame.LowestAcked = protocol.PacketNumber(largestObserved - ackBlockLength) + } + } var numTimestampByte byte numTimestampByte, err = r.ReadByte() diff --git a/frames/ack_frame_new_test.go b/frames/ack_frame_new_test.go index 98d0d2a9..083a4469 100644 --- a/frames/ack_frame_new_test.go +++ b/frames/ack_frame_new_test.go @@ -12,10 +12,11 @@ import ( var _ = Describe("AckFrame", func() { Context("when parsing", func() { It("accepts a sample frame", func() { - b := bytes.NewReader([]byte{0x40, 0x1a, 0x8e, 0x0, 0x1a, 0x1, 0x1, 0x6b, 0x26, 0x3, 0x0}) - frame, err := ParseAckFrameNew(b, protocol.Version34) + b := bytes.NewReader([]byte{0x40, 0x1c, 0x8e, 0x0, 0x1c, 0x1, 0x1, 0x6b, 0x26, 0x3, 0x0}) + frame, err := ParseAckFrameNew(b, 0) Expect(err).ToNot(HaveOccurred()) - Expect(frame.LargestObserved).To(Equal(protocol.PacketNumber(0x1a))) + Expect(frame.LargestObserved).To(Equal(protocol.PacketNumber(0x1c))) + Expect(frame.LowestAcked).To(Equal(protocol.PacketNumber(1))) Expect(frame.DelayTime).To(Equal(142 * time.Microsecond)) Expect(frame.HasNACK()).To(Equal(false)) Expect(b.Len()).To(BeZero()) @@ -23,7 +24,7 @@ var _ = Describe("AckFrame", func() { It("parses a frame with a 48 bit packet number", func() { b := bytes.NewReader([]byte{0x4c, 0x37, 0x13, 0xad, 0xfb, 0xca, 0xde, 0x0, 0x0, 0x0, 0x1, 0, 0, 0, 0, 0}) - frame, err := ParseAckFrameNew(b, protocol.Version32) + frame, err := ParseAckFrameNew(b, protocol.Version34) Expect(err).ToNot(HaveOccurred()) Expect(frame.LargestObserved).To(Equal(protocol.PacketNumber(0xdecafbad1337))) Expect(b.Len()).To(BeZero()) @@ -31,10 +32,63 @@ var _ = Describe("AckFrame", func() { It("parses a frame with multiple timestamps", func() { b := bytes.NewReader([]byte{0x40, 0x10, 0x0, 0x0, 0x10, 0x4, 0x1, 0x6b, 0x26, 0x4, 0x0, 0x3, 0, 0, 0x2, 0, 0, 0x1, 0, 0}) - _, err := ParseAckFrameNew(b, protocol.Version34) + _, err := ParseAckFrameNew(b, 0) Expect(err).ToNot(HaveOccurred()) Expect(b.Len()).To(BeZero()) }) + + It("errors when the ACK range is too large", func() { + // LargestObserved: 0x1c + // Length: 0x1d => LowestAcked would be -1 + b := bytes.NewReader([]byte{0x40, 0x1c, 0x8e, 0x0, 0x1d, 0x1, 0x1, 0x6b, 0x26, 0x3, 0x0}) + _, err := ParseAckFrameNew(b, 0) + Expect(err).To(MatchError(errInvalidAckRanges)) + }) + + Context("ACK blocks", func() { + It("parses a frame with one ACK block", func() { + b := bytes.NewReader([]byte{0x60, 0x18, 0x94, 0x1, 0x1, 0x3, 0x2, 0x13, 0x2, 0x1, 0x5c, 0xd5, 0x0, 0x0, 0x0, 0x95, 0x0}) + frame, err := ParseAckFrameNew(b, 0) + Expect(err).ToNot(HaveOccurred()) + Expect(frame.LargestObserved).To(Equal(protocol.PacketNumber(24))) + Expect(frame.AckRanges).To(HaveLen(2)) + Expect(frame.AckRanges[0]).To(Equal(AckRange{FirstPacketNumber: 22, LastPacketNumber: 24})) + Expect(frame.AckRanges[1]).To(Equal(AckRange{FirstPacketNumber: 1, LastPacketNumber: 19})) + Expect(frame.LowestAcked).To(Equal(protocol.PacketNumber(1))) + Expect(b.Len()).To(BeZero()) + }) + + It("parses a frame with multiple single packets missing", func() { + b := bytes.NewReader([]byte{0x60, 0x27, 0xda, 0x0, 0x6, 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x13, 0x2, 0x1, 0x71, 0x12, 0x3, 0x0, 0x0, 0x47, 0x2}) + frame, err := ParseAckFrameNew(b, 0) + Expect(err).ToNot(HaveOccurred()) + Expect(frame.LargestObserved).To(Equal(protocol.PacketNumber(0x27))) + Expect(frame.AckRanges).To(HaveLen(7)) + Expect(frame.AckRanges[0]).To(Equal(AckRange{FirstPacketNumber: 31, LastPacketNumber: 0x27})) + Expect(frame.AckRanges[1]).To(Equal(AckRange{FirstPacketNumber: 29, LastPacketNumber: 29})) + Expect(frame.AckRanges[2]).To(Equal(AckRange{FirstPacketNumber: 27, LastPacketNumber: 27})) + Expect(frame.AckRanges[3]).To(Equal(AckRange{FirstPacketNumber: 25, LastPacketNumber: 25})) + Expect(frame.AckRanges[4]).To(Equal(AckRange{FirstPacketNumber: 23, LastPacketNumber: 23})) + Expect(frame.AckRanges[5]).To(Equal(AckRange{FirstPacketNumber: 21, LastPacketNumber: 21})) + Expect(frame.AckRanges[6]).To(Equal(AckRange{FirstPacketNumber: 1, LastPacketNumber: 19})) + Expect(frame.LowestAcked).To(Equal(protocol.PacketNumber(1))) + Expect(b.Len()).To(BeZero()) + }) + + It("parses a frame with multiple longer ACK blocks", func() { + b := bytes.NewReader([]byte{0x60, 0x52, 0xd1, 0x0, 0x3, 0x17, 0xa, 0x10, 0x4, 0x8, 0x2, 0x13, 0x2, 0x1, 0x6c, 0xc8, 0x2, 0x0, 0x0, 0x7e, 0x1}) + frame, err := ParseAckFrameNew(b, 0) + Expect(err).ToNot(HaveOccurred()) + Expect(frame.LargestObserved).To(Equal(protocol.PacketNumber(0x52))) + Expect(frame.AckRanges).To(HaveLen(4)) + Expect(frame.AckRanges[0]).To(Equal(AckRange{FirstPacketNumber: 60, LastPacketNumber: 0x52})) + Expect(frame.AckRanges[1]).To(Equal(AckRange{FirstPacketNumber: 34, LastPacketNumber: 49})) + Expect(frame.AckRanges[2]).To(Equal(AckRange{FirstPacketNumber: 22, LastPacketNumber: 29})) + Expect(frame.AckRanges[3]).To(Equal(AckRange{FirstPacketNumber: 1, LastPacketNumber: 19})) + Expect(frame.LowestAcked).To(Equal(protocol.PacketNumber(1))) + Expect(b.Len()).To(BeZero()) + }) + }) }) Context("when writing", func() { diff --git a/frames/ack_range.go b/frames/ack_range.go new file mode 100644 index 00000000..ac65d33e --- /dev/null +++ b/frames/ack_range.go @@ -0,0 +1,9 @@ +package frames + +import "github.com/lucas-clemente/quic-go/protocol" + +// AckRange is an ACK range +type AckRange struct { + FirstPacketNumber protocol.PacketNumber + LastPacketNumber protocol.PacketNumber +}