From 2a8b038ca6d8a5715c329f1319ae68f84170d2c5 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Tue, 5 Nov 2019 16:16:58 +0700 Subject: [PATCH] randomize the number of packets sent with the same connection ID --- conn_id_manager.go | 15 ++++++++++++++- conn_id_manager_test.go | 35 ++++++++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/conn_id_manager.go b/conn_id_manager.go index 8fc537c30..bdc236eb6 100644 --- a/conn_id_manager.go +++ b/conn_id_manager.go @@ -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 { diff --git a/conn_id_manager_test.go b/conn_id_manager_test.go index 453daa8f6..2ea771866 100644 --- a/conn_id_manager_test.go +++ b/conn_id_manager_test.go @@ -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() {