forked from quic-go/quic-go
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:
@@ -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 {
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
Reference in New Issue
Block a user