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 package quic
import ( import (
"crypto/rand"
"encoding/binary"
"fmt" "fmt"
mrand "math/rand"
"github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/utils"
@@ -15,7 +18,12 @@ type connIDManager struct {
activeConnectionID protocol.ConnectionID activeConnectionID protocol.ConnectionID
activeStatelessResetToken *[16]byte 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 packetsSinceLastChange uint64
rand *mrand.Rand
packetsPerConnectionID uint64
addStatelessResetToken func([16]byte) addStatelessResetToken func([16]byte)
removeStatelessResetToken func([16]byte) removeStatelessResetToken func([16]byte)
@@ -30,12 +38,16 @@ func newConnIDManager(
retireStatelessResetToken func([16]byte), retireStatelessResetToken func([16]byte),
queueControlFrame func(wire.Frame), queueControlFrame func(wire.Frame),
) *connIDManager { ) *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{ return &connIDManager{
activeConnectionID: initialDestConnID, activeConnectionID: initialDestConnID,
addStatelessResetToken: addStatelessResetToken, addStatelessResetToken: addStatelessResetToken,
removeStatelessResetToken: removeStatelessResetToken, removeStatelessResetToken: removeStatelessResetToken,
retireStatelessResetToken: retireStatelessResetToken, retireStatelessResetToken: retireStatelessResetToken,
queueControlFrame: queueControlFrame, queueControlFrame: queueControlFrame,
rand: mrand.New(mrand.NewSource(seed)),
} }
} }
@@ -114,6 +126,7 @@ func (h *connIDManager) updateConnectionID() {
h.activeConnectionID = front.ConnectionID h.activeConnectionID = front.ConnectionID
h.activeStatelessResetToken = front.StatelessResetToken h.activeStatelessResetToken = front.StatelessResetToken
h.packetsSinceLastChange = 0 h.packetsSinceLastChange = 0
h.packetsPerConnectionID = protocol.PacketsPerConnectionID/2 + uint64(h.rand.Int63n(protocol.PacketsPerConnectionID))
h.addStatelessResetToken(*h.activeStatelessResetToken) h.addStatelessResetToken(*h.activeStatelessResetToken)
} }
@@ -154,7 +167,7 @@ func (h *connIDManager) shouldUpdateConnID() bool {
// 1. The queue of connection IDs is filled more than 50%. // 1. The queue of connection IDs is filled more than 50%.
// 2. We sent at least PacketsPerConnectionID packets // 2. We sent at least PacketsPerConnectionID packets
return 2*h.queue.Len() >= protocol.MaxActiveConnectionIDs && return 2*h.queue.Len() >= protocol.MaxActiveConnectionIDs &&
h.packetsSinceLastChange >= protocol.PacketsPerConnectionID h.packetsSinceLastChange >= h.packetsPerConnectionID
} }
func (h *connIDManager) Get() protocol.ConnectionID { 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() { 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{ Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: uint64(i), SequenceNumber: uint64(s),
ConnectionID: protocol.ConnectionID{i, i, i, i}, ConnectionID: protocol.ConnectionID{s, s, s, s},
StatelessResetToken: [16]byte{i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i}, StatelessResetToken: [16]byte{s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s},
})).To(Succeed()) })).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() 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(counter).To(BeNumerically("~", 50, 10))
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}))
}) })
It("only initiates subsequent updates when enough if enough connection IDs are queued", func() { It("only initiates subsequent updates when enough if enough connection IDs are queued", func() {