//go:build linux package quic import ( "encoding/binary" "errors" "net/netip" "os" "strconv" "syscall" "unsafe" "golang.org/x/sys/unix" ) const ( msgTypeIPTOS = unix.IP_TOS ipv4PKTINFO = unix.IP_PKTINFO ) const ecnIPv4DataLen = 1 const batchSize = 8 // needs to smaller than MaxUint8 (otherwise the type of oobConn.readPos has to be changed) func forceSetReceiveBuffer(c syscall.RawConn, bytes int) error { var serr error if err := c.Control(func(fd uintptr) { serr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUFFORCE, bytes) }); err != nil { return err } return serr } func forceSetSendBuffer(c syscall.RawConn, bytes int) error { var serr error if err := c.Control(func(fd uintptr) { serr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUFFORCE, bytes) }); err != nil { return err } return serr } func parseIPv4PktInfo(body []byte) (ip netip.Addr, ifIndex uint32, ok bool) { // struct in_pktinfo { // unsigned int ipi_ifindex; /* Interface index */ // struct in_addr ipi_spec_dst; /* Local address */ // struct in_addr ipi_addr; /* Header Destination address */ // }; if len(body) != 12 { return netip.Addr{}, 0, false } return netip.AddrFrom4(*(*[4]byte)(body[8:12])), binary.LittleEndian.Uint32(body), true } // isGSOSupported tests if the kernel supports GSO. // Sending with GSO might still fail later on, if the interface doesn't support it (see isGSOError). func isGSOSupported(conn syscall.RawConn) bool { disabled, err := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_GSO")) if err == nil && disabled { return false } var serr error if err := conn.Control(func(fd uintptr) { _, serr = unix.GetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_SEGMENT) }); err != nil { return false } return serr == nil } func appendUDPSegmentSizeMsg(b []byte, size uint16) []byte { startLen := len(b) const dataLen = 2 // payload is a uint16 b = append(b, make([]byte, unix.CmsgSpace(dataLen))...) h := (*unix.Cmsghdr)(unsafe.Pointer(&b[startLen])) h.Level = syscall.IPPROTO_UDP h.Type = unix.UDP_SEGMENT h.SetLen(unix.CmsgLen(dataLen)) // UnixRights uses the private `data` method, but I *think* this achieves the same goal. offset := startLen + unix.CmsgSpace(0) *(*uint16)(unsafe.Pointer(&b[offset])) = size return b } func isGSOError(err error) bool { var serr *os.SyscallError if errors.As(err, &serr) { // EIO is returned by udp_send_skb() if the device driver does not have tx checksums enabled, // which is a hard requirement of UDP_SEGMENT. See: // https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man7/udp.7?id=806eabd74910447f21005160e90957bde4db0183#n228 // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/udp.c?h=v6.2&id=c9c3395d5e3dcc6daee66c6908354d47bf98cb0c#n942 return serr.Err == unix.EIO } return false } // The first sendmsg call on a new UDP socket sometimes errors on Linux. // It's not clear why this happens. // See https://github.com/golang/go/issues/63322. func isPermissionError(err error) bool { var serr *os.SyscallError if errors.As(err, &serr) { return serr.Syscall == "sendmsg" && serr.Err == unix.EPERM } return false }