github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/mtu_discoverer.go (about) 1 package quic 2 3 import ( 4 "time" 5 6 "github.com/metacubex/quic-go/internal/ackhandler" 7 "github.com/metacubex/quic-go/internal/protocol" 8 "github.com/metacubex/quic-go/internal/utils" 9 "github.com/metacubex/quic-go/internal/wire" 10 "github.com/metacubex/quic-go/logging" 11 ) 12 13 type mtuDiscoverer interface { 14 // Start starts the MTU discovery process. 15 // It's unnecessary to call ShouldSendProbe before that. 16 Start() 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 type mtuFinder struct { 31 lastProbeTime time.Time 32 mtuIncreased func(protocol.ByteCount) 33 34 rttStats *utils.RTTStats 35 inFlight protocol.ByteCount // the size of the probe packet currently in flight. InvalidByteCount if none is in flight 36 current protocol.ByteCount 37 max protocol.ByteCount // the maximum value, as advertised by the peer (or our maximum size buffer) 38 39 tracer *logging.ConnectionTracer 40 } 41 42 var _ mtuDiscoverer = &mtuFinder{} 43 44 func newMTUDiscoverer( 45 rttStats *utils.RTTStats, 46 start, max protocol.ByteCount, 47 mtuIncreased func(protocol.ByteCount), 48 tracer *logging.ConnectionTracer, 49 ) *mtuFinder { 50 return &mtuFinder{ 51 inFlight: protocol.InvalidByteCount, 52 current: start, 53 max: max, 54 rttStats: rttStats, 55 mtuIncreased: mtuIncreased, 56 tracer: tracer, 57 } 58 } 59 60 func (f *mtuFinder) done() bool { 61 return f.max-f.current <= maxMTUDiff+1 62 } 63 64 func (f *mtuFinder) SetMax(max protocol.ByteCount) { 65 f.max = max 66 } 67 68 func (f *mtuFinder) Start() { 69 if f.max == protocol.InvalidByteCount { 70 panic("invalid") 71 } 72 f.lastProbeTime = time.Now() // makes sure the first probe packet is not sent immediately 73 } 74 75 func (f *mtuFinder) ShouldSendProbe(now time.Time) bool { 76 if f.max == 0 || f.lastProbeTime.IsZero() { 77 return false 78 } 79 if f.inFlight != protocol.InvalidByteCount || f.done() { 80 return false 81 } 82 return !now.Before(f.lastProbeTime.Add(mtuProbeDelay * f.rttStats.SmoothedRTT())) 83 } 84 85 func (f *mtuFinder) GetPing() (ackhandler.Frame, protocol.ByteCount) { 86 size := (f.max + f.current) / 2 87 f.lastProbeTime = time.Now() 88 f.inFlight = size 89 return ackhandler.Frame{ 90 Frame: &wire.PingFrame{}, 91 Handler: &mtuFinderAckHandler{f}, 92 }, size 93 } 94 95 func (f *mtuFinder) CurrentSize() protocol.ByteCount { 96 return f.current 97 } 98 99 type mtuFinderAckHandler struct { 100 *mtuFinder 101 } 102 103 var _ ackhandler.FrameHandler = &mtuFinderAckHandler{} 104 105 func (h *mtuFinderAckHandler) OnAcked(wire.Frame) { 106 size := h.inFlight 107 if size == protocol.InvalidByteCount { 108 panic("OnAcked callback called although there's no MTU probe packet in flight") 109 } 110 h.inFlight = protocol.InvalidByteCount 111 h.current = size 112 if h.tracer != nil && h.tracer.UpdatedMTU != nil { 113 h.tracer.UpdatedMTU(size, h.done()) 114 } 115 h.mtuIncreased(size) 116 } 117 118 func (h *mtuFinderAckHandler) OnLost(wire.Frame) { 119 size := h.inFlight 120 if size == protocol.InvalidByteCount { 121 panic("OnLost callback called although there's no MTU probe packet in flight") 122 } 123 h.max = size 124 h.inFlight = protocol.InvalidByteCount 125 }