Files
quic-go/integrationtests/self/mtu_test.go
2024-05-08 21:41:08 -07:00

150 lines
4.7 KiB
Go

package self_test
import (
"context"
"fmt"
"io"
"net"
"sync"
"time"
"github.com/quic-go/quic-go"
quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy"
"github.com/quic-go/quic-go/internal/protocol"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("DPLPMTUD", func() {
It("discovers the MTU", func() {
rtt := scaleDuration(10 * time.Millisecond)
const mtu = 1400
ln, err := quic.ListenAddr(
"localhost:0",
getTLSConfig(),
getQuicConfig(&quic.Config{
InitialPacketSize: 1234,
DisablePathMTUDiscovery: true,
EnableDatagrams: true,
}),
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
go func() {
defer GinkgoRecover()
conn, err := ln.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
str, err := conn.AcceptStream(context.Background())
Expect(err).ToNot(HaveOccurred())
_, err = io.Copy(str, str)
Expect(err).ToNot(HaveOccurred())
str.Close()
}()
var mx sync.Mutex
var maxPacketSizeClient, maxPacketSizeServer int
serverPort := ln.Addr().(*net.UDPAddr).Port
proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
RemoteAddr: fmt.Sprintf("localhost:%d", serverPort),
DelayPacket: func(quicproxy.Direction, []byte) time.Duration { return rtt / 2 },
DropPacket: func(dir quicproxy.Direction, packet []byte) bool {
if len(packet) > mtu {
return true
}
mx.Lock()
defer mx.Unlock()
switch dir {
case quicproxy.DirectionIncoming:
if len(packet) > maxPacketSizeClient {
maxPacketSizeClient = len(packet)
}
case quicproxy.DirectionOutgoing:
if len(packet) > maxPacketSizeServer {
maxPacketSizeServer = len(packet)
}
}
return false
},
})
Expect(err).ToNot(HaveOccurred())
defer proxy.Close()
// Make sure to use v4-only socket here.
// We can't reliably set the DF bit on dual-stack sockets on macOS.
udpConn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
Expect(err).ToNot(HaveOccurred())
defer udpConn.Close()
tr := &quic.Transport{Conn: udpConn}
defer tr.Close()
conn, err := tr.Dial(
context.Background(),
proxy.LocalAddr(),
getTLSClientConfig(),
getQuicConfig(&quic.Config{
InitialPacketSize: protocol.MinInitialPacketSize,
EnableDatagrams: true,
}),
)
Expect(err).ToNot(HaveOccurred())
defer conn.CloseWithError(0, "")
str, err := conn.OpenStream()
Expect(err).ToNot(HaveOccurred())
done := make(chan struct{})
go func() {
defer close(done)
defer GinkgoRecover()
data, err := io.ReadAll(str)
Expect(err).ToNot(HaveOccurred())
Expect(data).To(Equal(PRDataLong))
}()
err = conn.SendDatagram(make([]byte, 2000))
Expect(err).To(BeAssignableToTypeOf(&quic.DatagramTooLargeError{}))
initialMaxDatagramSize := err.(*quic.DatagramTooLargeError).MaxDatagramPayloadSize
_, err = str.Write(PRDataLong)
Expect(err).ToNot(HaveOccurred())
str.Close()
Eventually(done, 20*time.Second).Should(BeClosed())
err = conn.SendDatagram(make([]byte, 2000))
Expect(err).To(BeAssignableToTypeOf(&quic.DatagramTooLargeError{}))
finalMaxDatagramSize := err.(*quic.DatagramTooLargeError).MaxDatagramPayloadSize
mx.Lock()
defer mx.Unlock()
fmt.Fprintf(GinkgoWriter, "max client packet size: %d, MTU: %d\n", maxPacketSizeClient, mtu)
fmt.Fprintf(GinkgoWriter, "max datagram size: initial: %d, final: %d\n", initialMaxDatagramSize, finalMaxDatagramSize)
fmt.Fprintf(GinkgoWriter, "max server packet size: %d, MTU: %d\n", maxPacketSizeServer, mtu)
Expect(maxPacketSizeClient).To(BeNumerically(">=", mtu-25))
const maxDiff = 40 // this includes the 21 bytes for the short header, 16 bytes for the encryption tag, and framing overhead
Expect(initialMaxDatagramSize).To(BeNumerically(">=", protocol.MinInitialPacketSize-maxDiff))
Expect(finalMaxDatagramSize).To(BeNumerically(">=", maxPacketSizeClient-maxDiff))
// MTU discovery was disabled on the server side
Expect(maxPacketSizeServer).To(Equal(1234))
})
It("uses the initial packet size", func() {
c, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
Expect(err).ToNot(HaveOccurred())
defer c.Close()
cconn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
Expect(err).ToNot(HaveOccurred())
defer cconn.Close()
ctx, cancel := context.WithCancel(context.Background())
done := make(chan struct{})
go func() {
defer close(done)
quic.Dial(ctx, cconn, c.LocalAddr(), getTLSClientConfig(), getQuicConfig(&quic.Config{InitialPacketSize: 1337}))
}()
b := make([]byte, 2000)
n, _, err := c.ReadFrom(b)
Expect(err).ToNot(HaveOccurred())
Expect(n).To(Equal(1337))
cancel()
Eventually(done).Should(BeClosed())
})
})