diff --git a/conn_generic.go b/conn_generic.go index a2a125c0..526778c1 100644 --- a/conn_generic.go +++ b/conn_generic.go @@ -5,6 +5,8 @@ package quic import "net" +const disablePathMTUDiscovery = false + func newConn(c net.PacketConn) (connection, error) { return &basicConn{PacketConn: c}, nil } diff --git a/conn_helper_darwin.go b/conn_helper_darwin.go index eabf489f..fdab73b6 100644 --- a/conn_helper_darwin.go +++ b/conn_helper_darwin.go @@ -5,7 +5,10 @@ package quic import "golang.org/x/sys/unix" -const msgTypeIPTOS = unix.IP_RECVTOS +const ( + msgTypeIPTOS = unix.IP_RECVTOS + disablePathMTUDiscovery = false +) const ( ipv4RECVPKTINFO = unix.IP_RECVPKTINFO diff --git a/conn_helper_freebsd.go b/conn_helper_freebsd.go index f30f8a27..e22f9861 100644 --- a/conn_helper_freebsd.go +++ b/conn_helper_freebsd.go @@ -5,7 +5,10 @@ package quic import "golang.org/x/sys/unix" -const msgTypeIPTOS = unix.IP_RECVTOS +const ( + msgTypeIPTOS = unix.IP_RECVTOS + disablePathMTUDiscovery = false +) const ( ipv4RECVPKTINFO = 0x7 diff --git a/conn_helper_linux.go b/conn_helper_linux.go index 51bec900..4aa04dc9 100644 --- a/conn_helper_linux.go +++ b/conn_helper_linux.go @@ -5,7 +5,10 @@ package quic import "golang.org/x/sys/unix" -const msgTypeIPTOS = unix.IP_TOS +const ( + msgTypeIPTOS = unix.IP_TOS + disablePathMTUDiscovery = false +) const ( ipv4RECVPKTINFO = unix.IP_PKTINFO diff --git a/conn_windows.go b/conn_windows.go index 77eac366..a6e591b6 100644 --- a/conn_windows.go +++ b/conn_windows.go @@ -12,7 +12,10 @@ import ( "golang.org/x/sys/windows" ) -const IP_DONTFRAGMENT = 14 +const ( + disablePathMTUDiscovery = true + IP_DONTFRAGMENT = 14 +) func newConn(c OOBCapablePacketConn) (connection, error) { rawConn, err := c.SyscallConn() diff --git a/interface.go b/interface.go index eb1faae5..36d8b9b3 100644 --- a/interface.go +++ b/interface.go @@ -283,6 +283,7 @@ type Config struct { KeepAlive bool // DisablePathMTUDiscovery disables Path MTU Discovery (RFC 8899). // Packets will then be at most 1252 (IPv4) / 1232 (IPv6) bytes in size. + // Note that Path MTU discovery is always disabled on Windows, see https://github.com/lucas-clemente/quic-go/issues/3273. DisablePathMTUDiscovery bool // DisableVersionNegotiationPackets disables the sending of Version Negotiation packets. // This can be useful if version information is exchanged out-of-band. diff --git a/session.go b/session.go index 24bde4c3..d2b3091c 100644 --- a/session.go +++ b/session.go @@ -130,6 +130,10 @@ func (e *errCloseForRecreating) Error() string { var sessionTracingID uint64 // to be accessed atomically func nextSessionTracingID() uint64 { return atomic.AddUint64(&sessionTracingID, 1) } +func pathMTUDiscoveryEnabled(config *Config) bool { + return !disablePathMTUDiscovery && !config.DisablePathMTUDiscovery +} + // A Session is a QUIC session type session struct { // Destination connection ID used during the handshake. @@ -743,7 +747,7 @@ func (s *session) maybeResetTimer() { deadline = s.idleTimeoutStartTime().Add(s.idleTimeout) } } - if s.handshakeConfirmed && !s.config.DisablePathMTUDiscovery { + if s.handshakeConfirmed && pathMTUDiscoveryEnabled(s.config) { if probeTime := s.mtuDiscoverer.NextProbeTime(); !probeTime.IsZero() { deadline = utils.MinTime(deadline, probeTime) } @@ -807,7 +811,7 @@ func (s *session) handleHandshakeConfirmed() { s.sentPacketHandler.SetHandshakeConfirmed() s.cryptoStreamHandler.SetHandshakeConfirmed() - if !s.config.DisablePathMTUDiscovery { + if pathMTUDiscoveryEnabled(s.config) { maxPacketSize := s.peerParams.MaxUDPPayloadSize if maxPacketSize == 0 { maxPacketSize = protocol.MaxByteCount @@ -1768,7 +1772,7 @@ func (s *session) sendPacket() (bool, error) { s.sendQueue.Send(packet.buffer) return true, nil } - if !s.config.DisablePathMTUDiscovery && s.mtuDiscoverer.ShouldSendProbe(now) { + if pathMTUDiscoveryEnabled(s.config) && s.mtuDiscoverer.ShouldSendProbe(now) { packet, err := s.packer.PackMTUProbePacket(s.mtuDiscoverer.GetPing()) if err != nil { return false, err diff --git a/session_test.go b/session_test.go index 531828a0..7ca60947 100644 --- a/session_test.go +++ b/session_test.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "net" + "runtime" "runtime/pprof" "strings" "time" @@ -1681,33 +1682,35 @@ var _ = Describe("Session", func() { time.Sleep(50 * time.Millisecond) }) - It("sends a Path MTU probe packet", func() { - mtuDiscoverer := NewMockMtuDiscoverer(mockCtrl) - sess.mtuDiscoverer = mtuDiscoverer - sess.config.DisablePathMTUDiscovery = false - sph.EXPECT().SentPacket(gomock.Any()) - sph.EXPECT().HasPacingBudget().Return(true).AnyTimes() - sph.EXPECT().SendMode().Return(ackhandler.SendAny) - sph.EXPECT().SendMode().Return(ackhandler.SendNone) - written := make(chan struct{}, 1) - sender.EXPECT().WouldBlock().AnyTimes() - sender.EXPECT().Send(gomock.Any()).DoAndReturn(func(p *packetBuffer) { written <- struct{}{} }) - gomock.InOrder( - mtuDiscoverer.EXPECT().NextProbeTime(), - mtuDiscoverer.EXPECT().ShouldSendProbe(gomock.Any()).Return(true), - mtuDiscoverer.EXPECT().NextProbeTime(), - ) - ping := ackhandler.Frame{Frame: &wire.PingFrame{}} - mtuDiscoverer.EXPECT().GetPing().Return(ping, protocol.ByteCount(1234)) - packer.EXPECT().PackMTUProbePacket(ping, protocol.ByteCount(1234)).Return(getPacket(1), nil) - go func() { - defer GinkgoRecover() - cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) - sess.run() - }() - sess.scheduleSending() - Eventually(written).Should(Receive()) - }) + if runtime.GOOS != "windows" { // Path MTU Discovery is disabled on Windows + It("sends a Path MTU probe packet", func() { + mtuDiscoverer := NewMockMtuDiscoverer(mockCtrl) + sess.mtuDiscoverer = mtuDiscoverer + sess.config.DisablePathMTUDiscovery = false + sph.EXPECT().SentPacket(gomock.Any()) + sph.EXPECT().HasPacingBudget().Return(true).AnyTimes() + sph.EXPECT().SendMode().Return(ackhandler.SendAny) + sph.EXPECT().SendMode().Return(ackhandler.SendNone) + written := make(chan struct{}, 1) + sender.EXPECT().WouldBlock().AnyTimes() + sender.EXPECT().Send(gomock.Any()).DoAndReturn(func(p *packetBuffer) { written <- struct{}{} }) + gomock.InOrder( + mtuDiscoverer.EXPECT().NextProbeTime(), + mtuDiscoverer.EXPECT().ShouldSendProbe(gomock.Any()).Return(true), + mtuDiscoverer.EXPECT().NextProbeTime(), + ) + ping := ackhandler.Frame{Frame: &wire.PingFrame{}} + mtuDiscoverer.EXPECT().GetPing().Return(ping, protocol.ByteCount(1234)) + packer.EXPECT().PackMTUProbePacket(ping, protocol.ByteCount(1234)).Return(getPacket(1), nil) + go func() { + defer GinkgoRecover() + cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) + sess.run() + }() + sess.scheduleSending() + Eventually(written).Should(Receive()) + }) + } }) Context("scheduling sending", func() {