github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/conn_id_manager.go (about)

     1  package quic
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/metacubex/quic-go/internal/protocol"
     7  	"github.com/metacubex/quic-go/internal/qerr"
     8  	"github.com/metacubex/quic-go/internal/utils"
     9  	list "github.com/metacubex/quic-go/internal/utils/linkedlist"
    10  	"github.com/metacubex/quic-go/internal/wire"
    11  )
    12  
    13  type newConnID struct {
    14  	SequenceNumber      uint64
    15  	ConnectionID        protocol.ConnectionID
    16  	StatelessResetToken protocol.StatelessResetToken
    17  }
    18  
    19  type connIDManager struct {
    20  	queue list.List[newConnID]
    21  
    22  	handshakeComplete         bool
    23  	activeSequenceNumber      uint64
    24  	highestRetired            uint64
    25  	activeConnectionID        protocol.ConnectionID
    26  	activeStatelessResetToken *protocol.StatelessResetToken
    27  
    28  	// We change the connection ID after sending on average
    29  	// protocol.PacketsPerConnectionID packets. The actual value is randomized
    30  	// hide the packet loss rate from on-path observers.
    31  	rand                   utils.Rand
    32  	packetsSinceLastChange uint32
    33  	packetsPerConnectionID uint32
    34  
    35  	addStatelessResetToken    func(protocol.StatelessResetToken)
    36  	removeStatelessResetToken func(protocol.StatelessResetToken)
    37  	queueControlFrame         func(wire.Frame)
    38  }
    39  
    40  func newConnIDManager(
    41  	initialDestConnID protocol.ConnectionID,
    42  	addStatelessResetToken func(protocol.StatelessResetToken),
    43  	removeStatelessResetToken func(protocol.StatelessResetToken),
    44  	queueControlFrame func(wire.Frame),
    45  ) *connIDManager {
    46  	return &connIDManager{
    47  		activeConnectionID:        initialDestConnID,
    48  		addStatelessResetToken:    addStatelessResetToken,
    49  		removeStatelessResetToken: removeStatelessResetToken,
    50  		queueControlFrame:         queueControlFrame,
    51  	}
    52  }
    53  
    54  func (h *connIDManager) AddFromPreferredAddress(connID protocol.ConnectionID, resetToken protocol.StatelessResetToken) error {
    55  	return h.addConnectionID(1, connID, resetToken)
    56  }
    57  
    58  func (h *connIDManager) Add(f *wire.NewConnectionIDFrame) error {
    59  	if err := h.add(f); err != nil {
    60  		return err
    61  	}
    62  	if h.queue.Len() >= protocol.MaxActiveConnectionIDs {
    63  		return &qerr.TransportError{ErrorCode: qerr.ConnectionIDLimitError}
    64  	}
    65  	return nil
    66  }
    67  
    68  func (h *connIDManager) add(f *wire.NewConnectionIDFrame) error {
    69  	// If the NEW_CONNECTION_ID frame is reordered, such that its sequence number is smaller than the currently active
    70  	// connection ID or if it was already retired, send the RETIRE_CONNECTION_ID frame immediately.
    71  	if f.SequenceNumber < h.activeSequenceNumber || f.SequenceNumber < h.highestRetired {
    72  		h.queueControlFrame(&wire.RetireConnectionIDFrame{
    73  			SequenceNumber: f.SequenceNumber,
    74  		})
    75  		return nil
    76  	}
    77  
    78  	// Retire elements in the queue.
    79  	// Doesn't retire the active connection ID.
    80  	if f.RetirePriorTo > h.highestRetired {
    81  		var next *list.Element[newConnID]
    82  		for el := h.queue.Front(); el != nil; el = next {
    83  			if el.Value.SequenceNumber >= f.RetirePriorTo {
    84  				break
    85  			}
    86  			next = el.Next()
    87  			h.queueControlFrame(&wire.RetireConnectionIDFrame{
    88  				SequenceNumber: el.Value.SequenceNumber,
    89  			})
    90  			h.queue.Remove(el)
    91  		}
    92  		h.highestRetired = f.RetirePriorTo
    93  	}
    94  
    95  	if f.SequenceNumber == h.activeSequenceNumber {
    96  		return nil
    97  	}
    98  
    99  	if err := h.addConnectionID(f.SequenceNumber, f.ConnectionID, f.StatelessResetToken); err != nil {
   100  		return err
   101  	}
   102  
   103  	// Retire the active connection ID, if necessary.
   104  	if h.activeSequenceNumber < f.RetirePriorTo {
   105  		// The queue is guaranteed to have at least one element at this point.
   106  		h.updateConnectionID()
   107  	}
   108  	return nil
   109  }
   110  
   111  func (h *connIDManager) addConnectionID(seq uint64, connID protocol.ConnectionID, resetToken protocol.StatelessResetToken) error {
   112  	// insert a new element at the end
   113  	if h.queue.Len() == 0 || h.queue.Back().Value.SequenceNumber < seq {
   114  		h.queue.PushBack(newConnID{
   115  			SequenceNumber:      seq,
   116  			ConnectionID:        connID,
   117  			StatelessResetToken: resetToken,
   118  		})
   119  		return nil
   120  	}
   121  	// insert a new element somewhere in the middle
   122  	for el := h.queue.Front(); el != nil; el = el.Next() {
   123  		if el.Value.SequenceNumber == seq {
   124  			if el.Value.ConnectionID != connID {
   125  				return fmt.Errorf("received conflicting connection IDs for sequence number %d", seq)
   126  			}
   127  			if el.Value.StatelessResetToken != resetToken {
   128  				return fmt.Errorf("received conflicting stateless reset tokens for sequence number %d", seq)
   129  			}
   130  			break
   131  		}
   132  		if el.Value.SequenceNumber > seq {
   133  			h.queue.InsertBefore(newConnID{
   134  				SequenceNumber:      seq,
   135  				ConnectionID:        connID,
   136  				StatelessResetToken: resetToken,
   137  			}, el)
   138  			break
   139  		}
   140  	}
   141  	return nil
   142  }
   143  
   144  func (h *connIDManager) updateConnectionID() {
   145  	h.queueControlFrame(&wire.RetireConnectionIDFrame{
   146  		SequenceNumber: h.activeSequenceNumber,
   147  	})
   148  	h.highestRetired = utils.Max(h.highestRetired, h.activeSequenceNumber)
   149  	if h.activeStatelessResetToken != nil {
   150  		h.removeStatelessResetToken(*h.activeStatelessResetToken)
   151  	}
   152  
   153  	front := h.queue.Remove(h.queue.Front())
   154  	h.activeSequenceNumber = front.SequenceNumber
   155  	h.activeConnectionID = front.ConnectionID
   156  	h.activeStatelessResetToken = &front.StatelessResetToken
   157  	h.packetsSinceLastChange = 0
   158  	h.packetsPerConnectionID = protocol.PacketsPerConnectionID/2 + uint32(h.rand.Int31n(protocol.PacketsPerConnectionID))
   159  	h.addStatelessResetToken(*h.activeStatelessResetToken)
   160  }
   161  
   162  func (h *connIDManager) Close() {
   163  	if h.activeStatelessResetToken != nil {
   164  		h.removeStatelessResetToken(*h.activeStatelessResetToken)
   165  	}
   166  }
   167  
   168  // is called when the server performs a Retry
   169  // and when the server changes the connection ID in the first Initial sent
   170  func (h *connIDManager) ChangeInitialConnID(newConnID protocol.ConnectionID) {
   171  	if h.activeSequenceNumber != 0 {
   172  		panic("expected first connection ID to have sequence number 0")
   173  	}
   174  	h.activeConnectionID = newConnID
   175  }
   176  
   177  // is called when the server provides a stateless reset token in the transport parameters
   178  func (h *connIDManager) SetStatelessResetToken(token protocol.StatelessResetToken) {
   179  	if h.activeSequenceNumber != 0 {
   180  		panic("expected first connection ID to have sequence number 0")
   181  	}
   182  	h.activeStatelessResetToken = &token
   183  	h.addStatelessResetToken(token)
   184  }
   185  
   186  func (h *connIDManager) SentPacket() {
   187  	h.packetsSinceLastChange++
   188  }
   189  
   190  func (h *connIDManager) shouldUpdateConnID() bool {
   191  	if !h.handshakeComplete {
   192  		return false
   193  	}
   194  	// initiate the first change as early as possible (after handshake completion)
   195  	if h.queue.Len() > 0 && h.activeSequenceNumber == 0 {
   196  		return true
   197  	}
   198  	// For later changes, only change if
   199  	// 1. The queue of connection IDs is filled more than 50%.
   200  	// 2. We sent at least PacketsPerConnectionID packets
   201  	return 2*h.queue.Len() >= protocol.MaxActiveConnectionIDs &&
   202  		h.packetsSinceLastChange >= h.packetsPerConnectionID
   203  }
   204  
   205  func (h *connIDManager) Get() protocol.ConnectionID {
   206  	if h.shouldUpdateConnID() {
   207  		h.updateConnectionID()
   208  	}
   209  	return h.activeConnectionID
   210  }
   211  
   212  func (h *connIDManager) SetHandshakeComplete() {
   213  	h.handshakeComplete = true
   214  }