github.com/daeuniverse/quic-go@v0.0.0-20240413031024-943f218e0810/mtu_discoverer.go (about) 1 package quic 2 3 import ( 4 "net" 5 "time" 6 7 "github.com/daeuniverse/quic-go/internal/ackhandler" 8 "github.com/daeuniverse/quic-go/internal/protocol" 9 "github.com/daeuniverse/quic-go/internal/utils" 10 "github.com/daeuniverse/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 }