mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-02 19:57:35 +03:00
uTLS is not yet bumped to the new version, so this commit breaks the dependencies relationship by getting rid of the local replace.
224 lines
6.9 KiB
Go
224 lines
6.9 KiB
Go
package quic
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/refraction-networking/uquic/internal/protocol"
|
|
"github.com/refraction-networking/uquic/internal/qerr"
|
|
"github.com/refraction-networking/uquic/internal/utils"
|
|
list "github.com/refraction-networking/uquic/internal/utils/linkedlist"
|
|
"github.com/refraction-networking/uquic/internal/wire"
|
|
)
|
|
|
|
type newConnID struct {
|
|
SequenceNumber uint64
|
|
ConnectionID protocol.ConnectionID
|
|
StatelessResetToken protocol.StatelessResetToken
|
|
}
|
|
|
|
type connIDManager struct {
|
|
queue list.List[newConnID]
|
|
|
|
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)
|
|
|
|
connectionIDLimit uint64 // [UQUIC] custom Connection ID limit
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// [UQUIC]
|
|
connIDLimit := h.connectionIDLimit
|
|
if connIDLimit == 0 {
|
|
connIDLimit = protocol.MaxActiveConnectionIDs
|
|
}
|
|
// [/UQUIC]
|
|
|
|
if uint64(h.queue.Len()) >= connIDLimit {
|
|
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 *list.Element[newConnID]
|
|
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(newConnID{
|
|
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 != 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(newConnID{
|
|
SequenceNumber: seq,
|
|
ConnectionID: connID,
|
|
StatelessResetToken: resetToken,
|
|
}, el)
|
|
break
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *connIDManager) updateConnectionID() {
|
|
h.queueControlFrame(&wire.RetireConnectionIDFrame{
|
|
SequenceNumber: h.activeSequenceNumber,
|
|
})
|
|
h.highestRetired = utils.Max(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
|
|
}
|