uquic/conn_id_manager_test.go
Gaukas Wang 95575f5fe7
break: update repo url [ci skip]
uTLS is not yet bumped to the new version, so this commit breaks the dependencies relationship by getting rid of the local replace.
2023-08-03 18:58:52 -06:00

364 lines
15 KiB
Go

package quic
import (
"github.com/refraction-networking/uquic/internal/protocol"
"github.com/refraction-networking/uquic/internal/qerr"
"github.com/refraction-networking/uquic/internal/wire"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Connection ID Manager", func() {
var (
m *connIDManager
frameQueue []wire.Frame
tokenAdded *protocol.StatelessResetToken
removedTokens []protocol.StatelessResetToken
)
initialConnID := protocol.ParseConnectionID([]byte{0, 0, 0, 0})
BeforeEach(func() {
frameQueue = nil
tokenAdded = nil
removedTokens = nil
m = newConnIDManager(
initialConnID,
func(token protocol.StatelessResetToken) { tokenAdded = &token },
func(token protocol.StatelessResetToken) { removedTokens = append(removedTokens, token) },
func(f wire.Frame,
) {
frameQueue = append(frameQueue, f)
})
})
get := func() (protocol.ConnectionID, protocol.StatelessResetToken) {
if m.queue.Len() == 0 {
return protocol.ConnectionID{}, protocol.StatelessResetToken{}
}
val := m.queue.Remove(m.queue.Front())
return val.ConnectionID, val.StatelessResetToken
}
It("returns the initial connection ID", func() {
Expect(m.Get()).To(Equal(initialConnID))
})
It("changes the initial connection ID", func() {
m.ChangeInitialConnID(protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5}))
Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5})))
})
It("sets the token for the first connection ID", func() {
token := protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
m.SetStatelessResetToken(token)
Expect(*m.activeStatelessResetToken).To(Equal(token))
Expect(*tokenAdded).To(Equal(token))
})
It("adds and gets connection IDs", func() {
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 10,
ConnectionID: protocol.ParseConnectionID([]byte{2, 3, 4, 5}),
StatelessResetToken: protocol.StatelessResetToken{0xe, 0xd, 0xc, 0xb, 0xa, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
})).To(Succeed())
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 4,
ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
StatelessResetToken: protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe},
})).To(Succeed())
c1, rt1 := get()
Expect(c1).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4})))
Expect(rt1).To(Equal(protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe}))
c2, rt2 := get()
Expect(c2).To(Equal(protocol.ParseConnectionID([]byte{2, 3, 4, 5})))
Expect(rt2).To(Equal(protocol.StatelessResetToken{0xe, 0xd, 0xc, 0xb, 0xa, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}))
c3, _ := get()
Expect(c3).To(BeZero())
})
It("accepts duplicates", func() {
f1 := &wire.NewConnectionIDFrame{
SequenceNumber: 1,
ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
StatelessResetToken: protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe},
}
f2 := &wire.NewConnectionIDFrame{
SequenceNumber: 1,
ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
StatelessResetToken: protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe},
}
Expect(m.Add(f1)).To(Succeed())
Expect(m.Add(f2)).To(Succeed())
c1, rt1 := get()
Expect(c1).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4})))
Expect(rt1).To(Equal(protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe}))
c2, _ := get()
Expect(c2).To(BeZero())
})
It("ignores duplicates for the currently used connection ID", func() {
f := &wire.NewConnectionIDFrame{
SequenceNumber: 1,
ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
StatelessResetToken: protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe},
}
m.SetHandshakeComplete()
Expect(m.Add(f)).To(Succeed())
Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4})))
c, _ := get()
Expect(c).To(BeZero())
// Now send the same connection ID again. It should not be queued.
Expect(m.Add(f)).To(Succeed())
c, _ = get()
Expect(c).To(BeZero())
})
It("rejects duplicates with different connection IDs", func() {
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 42,
ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
})).To(Succeed())
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 42,
ConnectionID: protocol.ParseConnectionID([]byte{2, 3, 4, 5}),
})).To(MatchError("received conflicting connection IDs for sequence number 42"))
})
It("rejects duplicates with different connection IDs", func() {
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 42,
ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
StatelessResetToken: protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe},
})).To(Succeed())
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 42,
ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
StatelessResetToken: protocol.StatelessResetToken{0xe, 0xd, 0xc, 0xb, 0xa, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
})).To(MatchError("received conflicting stateless reset tokens for sequence number 42"))
})
It("retires connection IDs", func() {
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 10,
ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
})).To(Succeed())
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 13,
ConnectionID: protocol.ParseConnectionID([]byte{2, 3, 4, 5}),
})).To(Succeed())
Expect(frameQueue).To(BeEmpty())
Expect(m.Add(&wire.NewConnectionIDFrame{
RetirePriorTo: 14,
SequenceNumber: 17,
ConnectionID: protocol.ParseConnectionID([]byte{3, 4, 5, 6}),
})).To(Succeed())
Expect(frameQueue).To(HaveLen(3))
Expect(frameQueue[0].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeEquivalentTo(10))
Expect(frameQueue[1].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeEquivalentTo(13))
Expect(frameQueue[2].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeZero())
Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{3, 4, 5, 6})))
})
It("ignores reordered connection IDs, if their sequence number was already retired", func() {
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 10,
ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
RetirePriorTo: 5,
})).To(Succeed())
Expect(frameQueue).To(HaveLen(1))
Expect(frameQueue[0].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeZero())
frameQueue = nil
// If this NEW_CONNECTION_ID frame hadn't been reordered, we would have retired it before.
// Make sure it gets retired immediately now.
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 4,
ConnectionID: protocol.ParseConnectionID([]byte{4, 3, 2, 1}),
})).To(Succeed())
Expect(frameQueue).To(HaveLen(1))
Expect(frameQueue[0].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeEquivalentTo(4))
})
It("ignores reordered connection IDs, if their sequence number was already retired or less than active", func() {
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 10,
ConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}),
RetirePriorTo: 5,
})).To(Succeed())
Expect(frameQueue).To(HaveLen(1))
Expect(frameQueue[0].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeZero())
frameQueue = nil
Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef})))
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 9,
ConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}),
RetirePriorTo: 5,
})).To(Succeed())
Expect(frameQueue).To(HaveLen(1))
Expect(frameQueue[0].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeEquivalentTo(9))
})
It("accepts retransmissions for the connection ID that is in use", func() {
connID := protocol.ParseConnectionID([]byte{1, 2, 3, 4})
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 1,
ConnectionID: connID,
})).To(Succeed())
m.SetHandshakeComplete()
Expect(frameQueue).To(BeEmpty())
Expect(m.Get()).To(Equal(connID))
Expect(frameQueue).To(HaveLen(1))
Expect(frameQueue[0]).To(BeAssignableToTypeOf(&wire.RetireConnectionIDFrame{}))
Expect(frameQueue[0].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeZero())
frameQueue = nil
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 1,
ConnectionID: connID,
})).To(Succeed())
Expect(frameQueue).To(BeEmpty())
})
It("errors when the peer sends too connection IDs", func() {
for i := uint8(1); i < protocol.MaxActiveConnectionIDs; i++ {
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: uint64(i),
ConnectionID: protocol.ParseConnectionID([]byte{i, i, i, i}),
StatelessResetToken: protocol.StatelessResetToken{i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i},
})).To(Succeed())
}
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: uint64(9999),
ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
StatelessResetToken: protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
})).To(MatchError(&qerr.TransportError{ErrorCode: qerr.ConnectionIDLimitError}))
})
It("initiates the first connection ID update as soon as possible", func() {
Expect(m.Get()).To(Equal(initialConnID))
m.SetHandshakeComplete()
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 1,
ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
StatelessResetToken: protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
})).To(Succeed())
Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4})))
})
It("waits until handshake completion before initiating a connection ID update", func() {
Expect(m.Get()).To(Equal(initialConnID))
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 1,
ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
StatelessResetToken: protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
})).To(Succeed())
Expect(m.Get()).To(Equal(initialConnID))
m.SetHandshakeComplete()
Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4})))
})
It("initiates subsequent updates when enough packets are sent", func() {
var s uint8
for s = uint8(1); s < protocol.MaxActiveConnectionIDs; s++ {
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: uint64(s),
ConnectionID: protocol.ParseConnectionID([]byte{s, s, s, s}),
StatelessResetToken: protocol.StatelessResetToken{s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s},
})).To(Succeed())
}
m.SetHandshakeComplete()
lastConnID := m.Get()
Expect(lastConnID).To(Equal(protocol.ParseConnectionID([]byte{1, 1, 1, 1})))
var counter int
for i := 0; i < 50*protocol.PacketsPerConnectionID; i++ {
m.SentPacket()
connID := m.Get()
if connID != lastConnID {
counter++
lastConnID = connID
Expect(removedTokens).To(HaveLen(1))
removedTokens = nil
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: uint64(s),
ConnectionID: protocol.ParseConnectionID([]byte{s, s, s, s}),
StatelessResetToken: protocol.StatelessResetToken{s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s},
})).To(Succeed())
s++
}
}
Expect(counter).To(BeNumerically("~", 50, 10))
})
It("retires delayed connection IDs that arrive after a higher connection ID was already retired", func() {
for s := uint8(10); s <= 10+protocol.MaxActiveConnectionIDs/2; s++ {
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: uint64(s),
ConnectionID: protocol.ParseConnectionID([]byte{s, s, s, s}),
StatelessResetToken: protocol.StatelessResetToken{s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s},
})).To(Succeed())
}
m.SetHandshakeComplete()
Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{10, 10, 10, 10})))
for {
m.SentPacket()
if m.Get() == protocol.ParseConnectionID([]byte{11, 11, 11, 11}) {
break
}
}
// The active conn ID is now {11, 11, 11, 11}
Expect(m.queue.Front().Value.ConnectionID).To(Equal(protocol.ParseConnectionID([]byte{12, 12, 12, 12})))
// Add a delayed connection ID. It should just be ignored now.
frameQueue = nil
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: uint64(5),
ConnectionID: protocol.ParseConnectionID([]byte{5, 5, 5, 5}),
StatelessResetToken: protocol.StatelessResetToken{5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5},
})).To(Succeed())
Expect(m.queue.Front().Value.ConnectionID).To(Equal(protocol.ParseConnectionID([]byte{12, 12, 12, 12})))
Expect(frameQueue).To(HaveLen(1))
Expect(frameQueue[0].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeEquivalentTo(5))
})
It("only initiates subsequent updates when enough if enough connection IDs are queued", func() {
for i := uint8(1); i <= protocol.MaxActiveConnectionIDs/2; i++ {
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: uint64(i),
ConnectionID: protocol.ParseConnectionID([]byte{i, i, i, i}),
StatelessResetToken: protocol.StatelessResetToken{i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i},
})).To(Succeed())
}
m.SetHandshakeComplete()
Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{1, 1, 1, 1})))
for i := 0; i < 2*protocol.PacketsPerConnectionID; i++ {
m.SentPacket()
}
Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{1, 1, 1, 1})))
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 1337,
ConnectionID: protocol.ParseConnectionID([]byte{1, 3, 3, 7}),
})).To(Succeed())
Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{2, 2, 2, 2})))
Expect(removedTokens).To(HaveLen(1))
Expect(removedTokens[0]).To(Equal(protocol.StatelessResetToken{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}))
})
It("removes the currently active stateless reset token when it is closed", func() {
m.Close()
Expect(removedTokens).To(BeEmpty())
Expect(m.Add(&wire.NewConnectionIDFrame{
SequenceNumber: 1,
ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
StatelessResetToken: protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
})).To(Succeed())
m.SetHandshakeComplete()
Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4})))
m.Close()
Expect(removedTokens).To(HaveLen(1))
Expect(removedTokens[0]).To(Equal(protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}))
})
})