add a method to pack a retransmission in the packetPacker

This commit is contained in:
Marten Seemann
2017-02-25 15:47:54 +07:00
parent 7d2922d7ab
commit 5e91f139ce
2 changed files with 138 additions and 7 deletions

View File

@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"github.com/lucas-clemente/quic-go/ackhandler"
"github.com/lucas-clemente/quic-go/frames"
"github.com/lucas-clemente/quic-go/handshake"
"github.com/lucas-clemente/quic-go/protocol"
@@ -51,7 +52,19 @@ func (p *packetPacker) PackConnectionClose(ccf *frames.ConnectionCloseFrame, lea
// in case the connection is closed, all queued control frames aren't of any use anymore
// discard them and queue the ConnectionCloseFrame
p.controlFrames = []frames.Frame{ccf}
return p.packPacket(nil, leastUnacked)
return p.packPacket(nil, leastUnacked, nil)
}
// RetransmitNonForwardSecurePacket retransmits a handshake packet, that was sent with less than forward-secure encryption
func (p *packetPacker) RetransmitNonForwardSecurePacket(stopWaitingFrame *frames.StopWaitingFrame, packet *ackhandler.Packet) (*packedPacket, error) {
if packet.EncryptionLevel == protocol.EncryptionForwardSecure {
return nil, errors.New("PacketPacker BUG: forward-secure encrypted handshake packets don't need special treatment")
}
if stopWaitingFrame == nil {
return nil, errors.New("PacketPacker BUG: Handshake retransmissions must contain a StopWaitingFrame")
}
return p.packPacket(stopWaitingFrame, 0, packet)
}
// PackPacket packs a new packet
@@ -59,10 +72,12 @@ func (p *packetPacker) PackConnectionClose(ccf *frames.ConnectionCloseFrame, lea
// the other controlFrames are sent in the next packet, but might be queued and sent in the next packet if the packet would overflow MaxPacketSize otherwise
func (p *packetPacker) PackPacket(stopWaitingFrame *frames.StopWaitingFrame, controlFrames []frames.Frame, leastUnacked protocol.PacketNumber) (*packedPacket, error) {
p.controlFrames = append(p.controlFrames, controlFrames...)
return p.packPacket(stopWaitingFrame, leastUnacked)
return p.packPacket(stopWaitingFrame, leastUnacked, nil)
}
func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, leastUnacked protocol.PacketNumber) (*packedPacket, error) {
func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, leastUnacked protocol.PacketNumber, packetToRetransmit *ackhandler.Packet) (*packedPacket, error) {
// packetToRetransmit is only set for handshake retransmissions
isHandshakeRetransmission := (packetToRetransmit != nil)
// cryptoSetup needs to be locked here, so that the AEADs are not changed between
// calling DiversificationNonce() and Seal().
p.cryptoSetup.LockForSealing()
@@ -103,7 +118,19 @@ func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, lea
}
var payloadFrames []frames.Frame
if isConnectionClose {
if isHandshakeRetransmission {
payloadFrames = append(payloadFrames, stopWaitingFrame)
// don't retransmit Acks and StopWaitings
for _, f := range packetToRetransmit.Frames {
switch f.(type) {
case *frames.AckFrame:
continue
case *frames.StopWaitingFrame:
continue
}
payloadFrames = append(payloadFrames, f)
}
} else if isConnectionClose {
payloadFrames = []frames.Frame{p.controlFrames[0]}
} else {
maxSize := protocol.MaxFrameAndPublicHeaderSize - publicHeaderLength
@@ -139,7 +166,7 @@ func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, lea
if sf, ok := frame.(*frames.StreamFrame); ok && sf.StreamID != 1 {
hasNonCryptoStreamData = true
}
err := frame.Write(buffer, p.version)
err = frame.Write(buffer, p.version)
if err != nil {
return nil, err
}
@@ -150,7 +177,16 @@ func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, lea
}
raw = raw[0:buffer.Len()]
_, encryptionLevel := p.cryptoSetup.Seal(raw[payloadStartIndex:payloadStartIndex], raw[payloadStartIndex:], currentPacketNumber, raw[:payloadStartIndex])
var encryptionLevel protocol.EncryptionLevel
if isHandshakeRetransmission {
var err error
_, encryptionLevel, err = p.cryptoSetup.SealWith(raw[payloadStartIndex:payloadStartIndex], raw[payloadStartIndex:], currentPacketNumber, raw[:payloadStartIndex], packetToRetransmit.EncryptionLevel)
if err != nil {
return nil, err
}
} else {
_, encryptionLevel = p.cryptoSetup.Seal(raw[payloadStartIndex:payloadStartIndex], raw[payloadStartIndex:], currentPacketNumber, raw[:payloadStartIndex])
}
raw = raw[0 : buffer.Len()+12]
if hasNonCryptoStreamData && encryptionLevel <= protocol.EncryptionUnencrypted {

View File

@@ -3,6 +3,7 @@ package quic
import (
"bytes"
"github.com/lucas-clemente/quic-go/ackhandler"
"github.com/lucas-clemente/quic-go/frames"
"github.com/lucas-clemente/quic-go/protocol"
"github.com/lucas-clemente/quic-go/qerr"
@@ -25,7 +26,7 @@ func (m *mockCryptoSetup) Seal(dst, src []byte, packetNumber protocol.PacketNumb
return append(src, bytes.Repeat([]byte{0}, 12)...), m.encLevelSeal
}
func (m *mockCryptoSetup) SealWith(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte, encLevel protocol.EncryptionLevel) ([]byte, protocol.EncryptionLevel, error) {
panic("not implemented")
return append(src, bytes.Repeat([]byte{0}, 12)...), encLevel, nil
}
func (m *mockCryptoSetup) LockForSealing() {}
func (m *mockCryptoSetup) UnlockForSealing() {}
@@ -569,4 +570,98 @@ var _ = Describe("Packet packer", func() {
Expect(p.frames).To(HaveLen(1))
Expect(p.frames[0]).To(Equal(wuf))
})
Context("retransmitting of handshake packets", func() {
swf := &frames.StopWaitingFrame{LeastUnacked: 1}
sf := &frames.StreamFrame{
StreamID: 1,
Data: []byte("foobar"),
}
It("packs a retransmission for a packet sent with no encryption", func() {
packet := &ackhandler.Packet{
EncryptionLevel: protocol.EncryptionUnencrypted,
Frames: []frames.Frame{sf},
}
p, err := packer.RetransmitNonForwardSecurePacket(swf, packet)
Expect(err).ToNot(HaveOccurred())
Expect(p.frames).To(ContainElement(sf))
Expect(p.frames).To(ContainElement(swf))
Expect(p.encryptionLevel).To(Equal(protocol.EncryptionUnencrypted))
})
It("packs a retransmission for a packet sent with initial encryption", func() {
packet := &ackhandler.Packet{
EncryptionLevel: protocol.EncryptionSecure,
Frames: []frames.Frame{sf},
}
p, err := packer.RetransmitNonForwardSecurePacket(swf, packet)
Expect(err).ToNot(HaveOccurred())
Expect(p.frames).To(ContainElement(sf))
Expect(p.frames).To(ContainElement(swf))
Expect(p.encryptionLevel).To(Equal(protocol.EncryptionSecure))
})
It("removes non-retransmittable frames", func() {
wuf := &frames.WindowUpdateFrame{StreamID: 5, ByteOffset: 10}
packet := &ackhandler.Packet{
EncryptionLevel: protocol.EncryptionSecure,
Frames: []frames.Frame{
sf,
&frames.StopWaitingFrame{},
wuf,
&frames.AckFrame{},
},
}
p, err := packer.RetransmitNonForwardSecurePacket(swf, packet)
Expect(err).ToNot(HaveOccurred())
Expect(p.frames).To(HaveLen(3))
Expect(p.frames).To(ContainElement(sf))
Expect(p.frames).To(ContainElement(swf))
Expect(p.frames).To(ContainElement(wuf))
Expect(p.encryptionLevel).To(Equal(protocol.EncryptionSecure))
})
It("doesn't pack a packet for a non-retransmittable packet", func() {
packet := &ackhandler.Packet{
EncryptionLevel: protocol.EncryptionSecure,
Frames: []frames.Frame{&frames.AckFrame{}, &frames.StopWaitingFrame{}},
}
p, err := packer.RetransmitNonForwardSecurePacket(swf, packet)
Expect(err).ToNot(HaveOccurred())
Expect(p).To(BeNil())
})
// this should never happen, since non forward-secure packets are limited to a size smaller than MaxPacketSize, such that it is always possible to retransmit them without splitting the StreamFrame
// (note that the retransmitted packet needs to have enough space for the StopWaitingFrame)
It("refuses to send a packet larger than MaxPacketSize", func() {
packet := &ackhandler.Packet{
EncryptionLevel: protocol.EncryptionSecure,
Frames: []frames.Frame{
&frames.StreamFrame{
StreamID: 1,
Data: bytes.Repeat([]byte{'f'}, int(protocol.MaxPacketSize-5)),
},
},
}
_, err := packer.RetransmitNonForwardSecurePacket(swf, packet)
Expect(err).To(MatchError("PacketPacker BUG: packet too large"))
})
It("refuses to retransmit packets that were sent with forward-secure encryption", func() {
p := &ackhandler.Packet{
EncryptionLevel: protocol.EncryptionForwardSecure,
}
_, err := packer.RetransmitNonForwardSecurePacket(nil, p)
Expect(err).To(MatchError("PacketPacker BUG: forward-secure encrypted handshake packets don't need special treatment"))
})
It("refuses to retransmit packets without a StopWaitingFrame", func() {
p := &ackhandler.Packet{
EncryptionLevel: protocol.EncryptionSecure,
}
_, err := packer.RetransmitNonForwardSecurePacket(nil, p)
Expect(err).To(MatchError("PacketPacker BUG: Handshake retransmissions must contain a StopWaitingFrame"))
})
})
})