github.com/mikelsr/quic-go@v0.36.1-0.20230701132136-1d9415b66898/sys_conn.go (about) 1 package quic 2 3 import ( 4 "fmt" 5 "net" 6 "syscall" 7 "time" 8 9 "github.com/mikelsr/quic-go/internal/protocol" 10 "github.com/mikelsr/quic-go/internal/utils" 11 ) 12 13 // OOBCapablePacketConn is a connection that allows the reading of ECN bits from the IP header. 14 // If the PacketConn passed to Dial or Listen satisfies this interface, quic-go will use it. 15 // In this case, ReadMsgUDP() will be used instead of ReadFrom() to read packets. 16 type OOBCapablePacketConn interface { 17 net.PacketConn 18 SyscallConn() (syscall.RawConn, error) 19 SetReadBuffer(int) error 20 ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) 21 WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) 22 } 23 24 var _ OOBCapablePacketConn = &net.UDPConn{} 25 26 // OptimizeConn takes a net.PacketConn and attempts to enable various optimizations that will improve QUIC performance: 27 // 1. It enables the Don't Fragment (DF) bit on the IP header. 28 // This is required to run DPLPMTUD (Path MTU Discovery, RFC 8899). 29 // 2. It enables reading of the ECN bits from the IP header. 30 // This allows the remote node to speed up its loss detection and recovery. 31 // 3. It uses batched syscalls (recvmmsg) to more efficiently receive packets from the socket. 32 // 4. It uses Generic Segmentation Offload (GSO) to efficiently send batches of packets (on Linux). 33 // 34 // In order for this to work, the connection needs to implement the OOBCapablePacketConn interface (as a *net.UDPConn does). 35 // 36 // It's only necessary to call this function explicitly if the application calls WriteTo 37 // after passing the connection to the Transport. 38 func OptimizeConn(c net.PacketConn) (net.PacketConn, error) { 39 return wrapConn(c) 40 } 41 42 func wrapConn(pc net.PacketConn) (interface { 43 net.PacketConn 44 rawConn 45 }, error, 46 ) { 47 conn, ok := pc.(interface { 48 SyscallConn() (syscall.RawConn, error) 49 }) 50 var supportsDF bool 51 if ok { 52 rawConn, err := conn.SyscallConn() 53 if err != nil { 54 return nil, err 55 } 56 57 if _, ok := pc.LocalAddr().(*net.UDPAddr); ok { 58 // Only set DF on sockets that we expect to be able to handle that configuration. 59 var err error 60 supportsDF, err = setDF(rawConn) 61 if err != nil { 62 return nil, err 63 } 64 } 65 } 66 c, ok := pc.(OOBCapablePacketConn) 67 if !ok { 68 utils.DefaultLogger.Infof("PacketConn is not a net.UDPConn. Disabling optimizations possible on UDP connections.") 69 return &basicConn{PacketConn: pc, supportsDF: supportsDF}, nil 70 } 71 return newConn(c, supportsDF) 72 } 73 74 // The basicConn is the most trivial implementation of a rawConn. 75 // It reads a single packet from the underlying net.PacketConn. 76 // It is used when 77 // * the net.PacketConn is not a OOBCapablePacketConn, and 78 // * when the OS doesn't support OOB. 79 type basicConn struct { 80 net.PacketConn 81 supportsDF bool 82 } 83 84 var _ rawConn = &basicConn{} 85 86 func (c *basicConn) ReadPacket() (receivedPacket, error) { 87 buffer := getPacketBuffer() 88 // The packet size should not exceed protocol.MaxPacketBufferSize bytes 89 // If it does, we only read a truncated packet, which will then end up undecryptable 90 buffer.Data = buffer.Data[:protocol.MaxPacketBufferSize] 91 n, addr, err := c.PacketConn.ReadFrom(buffer.Data) 92 if err != nil { 93 return receivedPacket{}, err 94 } 95 return receivedPacket{ 96 remoteAddr: addr, 97 rcvTime: time.Now(), 98 data: buffer.Data[:n], 99 buffer: buffer, 100 }, nil 101 } 102 103 func (c *basicConn) WritePacket(b []byte, packetSize uint16, addr net.Addr, _ []byte) (n int, err error) { 104 if uint16(len(b)) != packetSize { 105 panic(fmt.Sprintf("inconsistent length. got: %d. expected %d", packetSize, len(b))) 106 } 107 return c.PacketConn.WriteTo(b, addr) 108 } 109 110 func (c *basicConn) capabilities() connCapabilities { return connCapabilities{DF: c.supportsDF} }