github.com/sagernet/quic-go@v0.43.1-beta.1/ech/mtu_discoverer.go (about)

     1  package quic
     2  
     3  import (
     4  	"net"
     5  	"time"
     6  
     7  	"github.com/sagernet/quic-go/internal/ackhandler"
     8  	"github.com/sagernet/quic-go/internal/protocol"
     9  	"github.com/sagernet/quic-go/internal/utils"
    10  	"github.com/sagernet/quic-go/internal/wire"
    11  )
    12  
    13  type mtuDiscoverer interface {
    14  	// Start starts the MTU discovery process.
    15  	// It's unnecessary to call ShouldSendProbe before that.
    16  	Start(maxPacketSize protocol.ByteCount)
    17  	ShouldSendProbe(now time.Time) bool
    18  	CurrentSize() protocol.ByteCount
    19  	GetPing() (ping ackhandler.Frame, datagramSize protocol.ByteCount)
    20  }
    21  
    22  const (
    23  	// At some point, we have to stop searching for a higher MTU.
    24  	// We're happy to send a packet that's 10 bytes smaller than the actual MTU.
    25  	maxMTUDiff = 20
    26  	// send a probe packet every mtuProbeDelay RTTs
    27  	mtuProbeDelay = 5
    28  )
    29  
    30  func getMaxPacketSize(addr net.Addr) protocol.ByteCount {
    31  	maxSize := protocol.ByteCount(protocol.MinInitialPacketSize)
    32  	// If this is not a UDP address, we don't know anything about the MTU.
    33  	// Use the minimum size of an Initial packet as the max packet size.
    34  	if udpAddr, ok := addr.(*net.UDPAddr); ok {
    35  		if utils.IsIPv4(udpAddr.IP) {
    36  			maxSize = protocol.InitialPacketSizeIPv4
    37  		} else {
    38  			maxSize = protocol.InitialPacketSizeIPv6
    39  		}
    40  	}
    41  	return maxSize
    42  }
    43  
    44  type mtuFinder struct {
    45  	lastProbeTime time.Time
    46  	mtuIncreased  func(protocol.ByteCount)
    47  
    48  	rttStats *utils.RTTStats
    49  	inFlight protocol.ByteCount // the size of the probe packet currently in flight. InvalidByteCount if none is in flight
    50  	current  protocol.ByteCount
    51  	max      protocol.ByteCount // the maximum value, as advertised by the peer (or our maximum size buffer)
    52  }
    53  
    54  var _ mtuDiscoverer = &mtuFinder{}
    55  
    56  func newMTUDiscoverer(rttStats *utils.RTTStats, start protocol.ByteCount, mtuIncreased func(protocol.ByteCount)) *mtuFinder {
    57  	return &mtuFinder{
    58  		inFlight:     protocol.InvalidByteCount,
    59  		current:      start,
    60  		rttStats:     rttStats,
    61  		mtuIncreased: mtuIncreased,
    62  	}
    63  }
    64  
    65  func (f *mtuFinder) done() bool {
    66  	return f.max-f.current <= maxMTUDiff+1
    67  }
    68  
    69  func (f *mtuFinder) Start(maxPacketSize protocol.ByteCount) {
    70  	f.lastProbeTime = time.Now() // makes sure the first probe packet is not sent immediately
    71  	f.max = maxPacketSize
    72  }
    73  
    74  func (f *mtuFinder) ShouldSendProbe(now time.Time) bool {
    75  	if f.max == 0 || f.lastProbeTime.IsZero() {
    76  		return false
    77  	}
    78  	if f.inFlight != protocol.InvalidByteCount || f.done() {
    79  		return false
    80  	}
    81  	return !now.Before(f.lastProbeTime.Add(mtuProbeDelay * f.rttStats.SmoothedRTT()))
    82  }
    83  
    84  func (f *mtuFinder) GetPing() (ackhandler.Frame, protocol.ByteCount) {
    85  	size := (f.max + f.current) / 2
    86  	f.lastProbeTime = time.Now()
    87  	f.inFlight = size
    88  	return ackhandler.Frame{
    89  		Frame:   &wire.PingFrame{},
    90  		Handler: (*mtuFinderAckHandler)(f),
    91  	}, size
    92  }
    93  
    94  func (f *mtuFinder) CurrentSize() protocol.ByteCount {
    95  	return f.current
    96  }
    97  
    98  type mtuFinderAckHandler mtuFinder
    99  
   100  var _ ackhandler.FrameHandler = &mtuFinderAckHandler{}
   101  
   102  func (h *mtuFinderAckHandler) OnAcked(wire.Frame) {
   103  	size := h.inFlight
   104  	if size == protocol.InvalidByteCount {
   105  		panic("OnAcked callback called although there's no MTU probe packet in flight")
   106  	}
   107  	h.inFlight = protocol.InvalidByteCount
   108  	h.current = size
   109  	h.mtuIncreased(size)
   110  }
   111  
   112  func (h *mtuFinderAckHandler) OnLost(wire.Frame) {
   113  	size := h.inFlight
   114  	if size == protocol.InvalidByteCount {
   115  		panic("OnLost callback called although there's no MTU probe packet in flight")
   116  	}
   117  	h.max = size
   118  	h.inFlight = protocol.InvalidByteCount
   119  }