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} }