//go:build linux

package quic

import (
	
	
	
	
	
	
	

	
)

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)

var kernelVersionMajor int

func init() {
	kernelVersionMajor, _ = kernelVersion()
}

func forceSetReceiveBuffer( syscall.RawConn,  int) error {
	var  error
	if  := .Control(func( uintptr) {
		 = unix.SetsockoptInt(int(), unix.SOL_SOCKET, unix.SO_RCVBUFFORCE, )
	});  != nil {
		return 
	}
	return 
}

func forceSetSendBuffer( syscall.RawConn,  int) error {
	var  error
	if  := .Control(func( uintptr) {
		 = unix.SetsockoptInt(int(), unix.SOL_SOCKET, unix.SO_SNDBUFFORCE, )
	});  != nil {
		return 
	}
	return 
}

func parseIPv4PktInfo( []byte) ( netip.Addr,  uint32,  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() != 12 {
		return netip.Addr{}, 0, false
	}
	return netip.AddrFrom4(*(*[4]byte)([8:12])), binary.NativeEndian.Uint32(), true
}

// isGSOEnabled tests if the kernel supports GSO.
// Sending with GSO might still fail later on, if the interface doesn't support it (see isGSOError).
func isGSOEnabled( syscall.RawConn) bool {
	if kernelVersionMajor < 5 {
		return false
	}
	,  := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_GSO"))
	if  == nil &&  {
		return false
	}
	var  error
	if  := .Control(func( uintptr) {
		_,  = unix.GetsockoptInt(int(), unix.IPPROTO_UDP, unix.UDP_SEGMENT)
	});  != nil {
		return false
	}
	return  == nil
}

func appendUDPSegmentSizeMsg( []byte,  uint16) []byte {
	 := len()
	const  = 2 // payload is a uint16
	 = append(, make([]byte, unix.CmsgSpace())...)
	 := (*unix.Cmsghdr)(unsafe.Pointer(&[]))
	.Level = syscall.IPPROTO_UDP
	.Type = unix.UDP_SEGMENT
	.SetLen(unix.CmsgLen())

	// UnixRights uses the private `data` method, but I *think* this achieves the same goal.
	 :=  + unix.CmsgSpace(0)
	*(*uint16)(unsafe.Pointer(&[])) = 
	return 
}

func isGSOError( error) bool {
	var  *os.SyscallError
	if errors.As(, &) {
		// 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 .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( error) bool {
	var  *os.SyscallError
	if errors.As(, &) {
		return .Syscall == "sendmsg" && .Err == unix.EPERM
	}
	return false
}

func isECNEnabled() bool {
	return kernelVersionMajor >= 5 && !isECNDisabledUsingEnv()
}

// kernelVersion returns major and minor kernel version numbers, parsed from
// the syscall.Uname's Release field, or 0, 0 if the version can't be obtained
// or parsed.
//
// copied from the standard library's internal/syscall/unix/kernel_version_linux.go
func kernelVersion() (,  int) {
	var  syscall.Utsname
	if  := syscall.Uname(&);  != nil {
		return
	}

	var (
		    [2]int
		,  int
	)
	for ,  := range .Release {
		if '0' <=  &&  <= '9' {
			 = ( * 10) + int(-'0')
		} else {
			// Note that we're assuming N.N.N here.
			// If we see anything else, we are likely to mis-parse it.
			[] = 
			++
			if  >= len() {
				break
			}
			 = 0
		}
	}

	return [0], [1]
}