208 lines
6.5 KiB
Go
208 lines
6.5 KiB
Go
|
package quic
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
|
||
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||
|
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||
|
"github.com/lucas-clemente/quic-go/internal/wire"
|
||
|
)
|
||
|
|
||
|
type connIDManager struct {
|
||
|
queue utils.NewConnectionIDList
|
||
|
|
||
|
handshakeComplete bool
|
||
|
activeSequenceNumber uint64
|
||
|
highestRetired uint64
|
||
|
activeConnectionID protocol.ConnectionID
|
||
|
activeStatelessResetToken *protocol.StatelessResetToken
|
||
|
|
||
|
// 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.
|
||
|
rand utils.Rand
|
||
|
packetsSinceLastChange uint32
|
||
|
packetsPerConnectionID uint32
|
||
|
|
||
|
addStatelessResetToken func(protocol.StatelessResetToken)
|
||
|
removeStatelessResetToken func(protocol.StatelessResetToken)
|
||
|
queueControlFrame func(wire.Frame)
|
||
|
}
|
||
|
|
||
|
func newConnIDManager(
|
||
|
initialDestConnID protocol.ConnectionID,
|
||
|
addStatelessResetToken func(protocol.StatelessResetToken),
|
||
|
removeStatelessResetToken func(protocol.StatelessResetToken),
|
||
|
queueControlFrame func(wire.Frame),
|
||
|
) *connIDManager {
|
||
|
return &connIDManager{
|
||
|
activeConnectionID: initialDestConnID,
|
||
|
addStatelessResetToken: addStatelessResetToken,
|
||
|
removeStatelessResetToken: removeStatelessResetToken,
|
||
|
queueControlFrame: queueControlFrame,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (h *connIDManager) AddFromPreferredAddress(connID protocol.ConnectionID, resetToken protocol.StatelessResetToken) error {
|
||
|
return h.addConnectionID(1, connID, resetToken)
|
||
|
}
|
||
|
|
||
|
func (h *connIDManager) Add(f *wire.NewConnectionIDFrame) error {
|
||
|
if err := h.add(f); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if h.queue.Len() >= protocol.MaxActiveConnectionIDs {
|
||
|
return &qerr.TransportError{ErrorCode: qerr.ConnectionIDLimitError}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (h *connIDManager) add(f *wire.NewConnectionIDFrame) error {
|
||
|
// If the NEW_CONNECTION_ID frame is reordered, such that its sequence number is smaller than the currently active
|
||
|
// connection ID or if it was already retired, send the RETIRE_CONNECTION_ID frame immediately.
|
||
|
if f.SequenceNumber < h.activeSequenceNumber || f.SequenceNumber < h.highestRetired {
|
||
|
h.queueControlFrame(&wire.RetireConnectionIDFrame{
|
||
|
SequenceNumber: f.SequenceNumber,
|
||
|
})
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Retire elements in the queue.
|
||
|
// Doesn't retire the active connection ID.
|
||
|
if f.RetirePriorTo > h.highestRetired {
|
||
|
var next *utils.NewConnectionIDElement
|
||
|
for el := h.queue.Front(); el != nil; el = next {
|
||
|
if el.Value.SequenceNumber >= f.RetirePriorTo {
|
||
|
break
|
||
|
}
|
||
|
next = el.Next()
|
||
|
h.queueControlFrame(&wire.RetireConnectionIDFrame{
|
||
|
SequenceNumber: el.Value.SequenceNumber,
|
||
|
})
|
||
|
h.queue.Remove(el)
|
||
|
}
|
||
|
h.highestRetired = f.RetirePriorTo
|
||
|
}
|
||
|
|
||
|
if f.SequenceNumber == h.activeSequenceNumber {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if err := h.addConnectionID(f.SequenceNumber, f.ConnectionID, f.StatelessResetToken); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Retire the active connection ID, if necessary.
|
||
|
if h.activeSequenceNumber < f.RetirePriorTo {
|
||
|
// The queue is guaranteed to have at least one element at this point.
|
||
|
h.updateConnectionID()
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (h *connIDManager) addConnectionID(seq uint64, connID protocol.ConnectionID, resetToken protocol.StatelessResetToken) error {
|
||
|
// insert a new element at the end
|
||
|
if h.queue.Len() == 0 || h.queue.Back().Value.SequenceNumber < seq {
|
||
|
h.queue.PushBack(utils.NewConnectionID{
|
||
|
SequenceNumber: seq,
|
||
|
ConnectionID: connID,
|
||
|
StatelessResetToken: resetToken,
|
||
|
})
|
||
|
return nil
|
||
|
}
|
||
|
// insert a new element somewhere in the middle
|
||
|
for el := h.queue.Front(); el != nil; el = el.Next() {
|
||
|
if el.Value.SequenceNumber == seq {
|
||
|
if !el.Value.ConnectionID.Equal(connID) {
|
||
|
return fmt.Errorf("received conflicting connection IDs for sequence number %d", seq)
|
||
|
}
|
||
|
if el.Value.StatelessResetToken != resetToken {
|
||
|
return fmt.Errorf("received conflicting stateless reset tokens for sequence number %d", seq)
|
||
|
}
|
||
|
break
|
||
|
}
|
||
|
if el.Value.SequenceNumber > seq {
|
||
|
h.queue.InsertBefore(utils.NewConnectionID{
|
||
|
SequenceNumber: seq,
|
||
|
ConnectionID: connID,
|
||
|
StatelessResetToken: resetToken,
|
||
|
}, el)
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (h *connIDManager) updateConnectionID() {
|
||
|
h.queueControlFrame(&wire.RetireConnectionIDFrame{
|
||
|
SequenceNumber: h.activeSequenceNumber,
|
||
|
})
|
||
|
h.highestRetired = utils.MaxUint64(h.highestRetired, h.activeSequenceNumber)
|
||
|
if h.activeStatelessResetToken != nil {
|
||
|
h.removeStatelessResetToken(*h.activeStatelessResetToken)
|
||
|
}
|
||
|
|
||
|
front := h.queue.Remove(h.queue.Front())
|
||
|
h.activeSequenceNumber = front.SequenceNumber
|
||
|
h.activeConnectionID = front.ConnectionID
|
||
|
h.activeStatelessResetToken = &front.StatelessResetToken
|
||
|
h.packetsSinceLastChange = 0
|
||
|
h.packetsPerConnectionID = protocol.PacketsPerConnectionID/2 + uint32(h.rand.Int31n(protocol.PacketsPerConnectionID))
|
||
|
h.addStatelessResetToken(*h.activeStatelessResetToken)
|
||
|
}
|
||
|
|
||
|
func (h *connIDManager) Close() {
|
||
|
if h.activeStatelessResetToken != nil {
|
||
|
h.removeStatelessResetToken(*h.activeStatelessResetToken)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// is called when the server performs a Retry
|
||
|
// and when the server changes the connection ID in the first Initial sent
|
||
|
func (h *connIDManager) ChangeInitialConnID(newConnID protocol.ConnectionID) {
|
||
|
if h.activeSequenceNumber != 0 {
|
||
|
panic("expected first connection ID to have sequence number 0")
|
||
|
}
|
||
|
h.activeConnectionID = newConnID
|
||
|
}
|
||
|
|
||
|
// is called when the server provides a stateless reset token in the transport parameters
|
||
|
func (h *connIDManager) SetStatelessResetToken(token protocol.StatelessResetToken) {
|
||
|
if h.activeSequenceNumber != 0 {
|
||
|
panic("expected first connection ID to have sequence number 0")
|
||
|
}
|
||
|
h.activeStatelessResetToken = &token
|
||
|
h.addStatelessResetToken(token)
|
||
|
}
|
||
|
|
||
|
func (h *connIDManager) SentPacket() {
|
||
|
h.packetsSinceLastChange++
|
||
|
}
|
||
|
|
||
|
func (h *connIDManager) shouldUpdateConnID() bool {
|
||
|
if !h.handshakeComplete {
|
||
|
return false
|
||
|
}
|
||
|
// initiate the first change as early as possible (after handshake completion)
|
||
|
if h.queue.Len() > 0 && h.activeSequenceNumber == 0 {
|
||
|
return true
|
||
|
}
|
||
|
// For later changes, only change if
|
||
|
// 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 >= h.packetsPerConnectionID
|
||
|
}
|
||
|
|
||
|
func (h *connIDManager) Get() protocol.ConnectionID {
|
||
|
if h.shouldUpdateConnID() {
|
||
|
h.updateConnectionID()
|
||
|
}
|
||
|
return h.activeConnectionID
|
||
|
}
|
||
|
|
||
|
func (h *connIDManager) SetHandshakeComplete() {
|
||
|
h.handshakeComplete = true
|
||
|
}
|