Merge pull request #2203 from lucas-clemente/randomize-conn-id-change

randomize the number of packets sent with the same connection ID
This commit is contained in:
Marten Seemann
2019-11-06 15:46:47 +07:00
committed by GitHub
2 changed files with 40 additions and 10 deletions

View File

@@ -1,7 +1,10 @@
package quic
import (
"crypto/rand"
"encoding/binary"
"fmt"
mrand "math/rand"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
@@ -15,7 +18,12 @@ type connIDManager struct {
activeConnectionID protocol.ConnectionID
activeStatelessResetToken *[16]byte
// We change the connection ID after sending on average
// protocol.PacketsPerConnectionID packets. The actual value is randomized
// hide the packet loss rate from on-path observers.
packetsSinceLastChange uint64
rand *mrand.Rand
packetsPerConnectionID uint64
addStatelessResetToken func([16]byte)
removeStatelessResetToken func([16]byte)
@@ -30,12 +38,16 @@ func newConnIDManager(
retireStatelessResetToken func([16]byte),
queueControlFrame func(wire.Frame),
) *connIDManager {
b := make([]byte, 8)
_, _ = rand.Read(b) // ignore the error here. Nothing bad will happen if the seed is not perfectly random.
seed := int64(binary.BigEndian.Uint64(b))
return &connIDManager{
activeConnectionID: initialDestConnID,
addStatelessResetToken: addStatelessResetToken,
removeStatelessResetToken: removeStatelessResetToken,
retireStatelessResetToken: retireStatelessResetToken,
queueControlFrame: queueControlFrame,
rand: mrand.New(mrand.NewSource(seed)),
}
}
@@ -114,6 +126,7 @@ func (h *connIDManager) updateConnectionID() {
h.activeConnectionID = front.ConnectionID
h.activeStatelessResetToken = front.StatelessResetToken
h.packetsSinceLastChange = 0
h.packetsPerConnectionID = protocol.PacketsPerConnectionID/2 + uint64(h.rand.Int63n(protocol.PacketsPerConnectionID))
h.addStatelessResetToken(*h.activeStatelessResetToken)
}
@@ -154,7 +167,7 @@ func (h *connIDManager) shouldUpdateConnID() bool {
// 1. The queue of connection IDs is filled more than 50%.
// 2. We sent at least PacketsPerConnectionID packets
return 2*h.queue.Len() >= protocol.MaxActiveConnectionIDs &&
h.packetsSinceLastChange >= protocol.PacketsPerConnectionID
h.packetsSinceLastChange >= h.packetsPerConnectionID
}
func (h *connIDManager) Get() protocol.ConnectionID {

View File

@@ -175,20 +175,37 @@ var _ = Describe("Connection ID Manager", func() {
})
It("initiates subsequent updates when enough packets are sent", func() {
for i := uint8(1); i <= protocol.MaxActiveConnectionIDs; i++ {
var s uint8
for s = uint8(1); s <= protocol.MaxActiveConnectionIDs; s++ {
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: uint64(i),
ConnectionID: protocol.ConnectionID{i, i, i, i},
StatelessResetToken: [16]byte{i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i},
SequenceNumber: uint64(s),
ConnectionID: protocol.ConnectionID{s, s, s, s},
StatelessResetToken: [16]byte{s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s},
})).To(Succeed())
}
Expect(m.Get()).To(Equal(protocol.ConnectionID{1, 1, 1, 1}))
for i := 0; i < protocol.PacketsPerConnectionID; i++ {
lastConnID := m.Get()
Expect(lastConnID).To(Equal(protocol.ConnectionID{1, 1, 1, 1}))
var counter int
for i := 0; i < 50*protocol.PacketsPerConnectionID; i++ {
m.SentPacket()
connID := m.Get()
if !connID.Equal(lastConnID) {
counter++
lastConnID = connID
Expect(retiredTokens).To(HaveLen(1))
retiredTokens = nil
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: uint64(s),
ConnectionID: protocol.ConnectionID{s, s, s, s},
StatelessResetToken: [16]byte{s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s},
})).To(Succeed())
s++
}
}
Expect(m.Get()).To(Equal(protocol.ConnectionID{2, 2, 2, 2}))
Expect(retiredTokens).To(HaveLen(1))
Expect(retiredTokens[0]).To(Equal([16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}))
Expect(counter).To(BeNumerically("~", 50, 10))
})
It("only initiates subsequent updates when enough if enough connection IDs are queued", func() {