Skip to content

Commit

Permalink
disable GSO and ECN on kernels older than version 5 (#4456)
Browse files Browse the repository at this point in the history
  • Loading branch information
marten-seemann committed Apr 24, 2024
1 parent 394aa56 commit 12aa638
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 8 deletions.
12 changes: 12 additions & 0 deletions quic_suite_linux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//go:build linux

package quic

import (
"fmt"
)

func init() {
major, minor := kernelVersion()
fmt.Printf("Kernel Version: %d.%d\n\n", major, minor)
}
4 changes: 3 additions & 1 deletion sys_conn_helper_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@ func parseIPv4PktInfo(body []byte) (ip netip.Addr, ifIndex uint32, ok bool) {
return netip.AddrFrom4(*(*[4]byte)(body[8:12])), binary.LittleEndian.Uint32(body), true
}

func isGSOSupported(syscall.RawConn) bool { return false }
func isGSOEnabled(syscall.RawConn) bool { return false }

func isECNEnabled() bool { return !isECNDisabledUsingEnv() }
4 changes: 3 additions & 1 deletion sys_conn_helper_freebsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ func parseIPv4PktInfo(body []byte) (ip netip.Addr, _ uint32, ok bool) {
return netip.AddrFrom4(*(*[4]byte)(body)), 0, true
}

func isGSOSupported(syscall.RawConn) bool { return false }
func isGSOEnabled(syscall.RawConn) bool { return false }

func isECNEnabled() bool { return !isECNDisabledUsingEnv() }
50 changes: 48 additions & 2 deletions sys_conn_helper_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ 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(c syscall.RawConn, bytes int) error {
var serr error
if err := c.Control(func(fd uintptr) {
Expand Down Expand Up @@ -55,9 +61,12 @@ func parseIPv4PktInfo(body []byte) (ip netip.Addr, ifIndex uint32, ok bool) {
return netip.AddrFrom4(*(*[4]byte)(body[8:12])), binary.LittleEndian.Uint32(body), true
}

// isGSOSupported tests if the kernel supports GSO.
// 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 isGSOSupported(conn syscall.RawConn) bool {
func isGSOEnabled(conn syscall.RawConn) bool {
if kernelVersionMajor < 5 {
return false
}
disabled, err := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_GSO"))
if err == nil && disabled {
return false
Expand Down Expand Up @@ -108,3 +117,40 @@ func isPermissionError(err error) bool {
}
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() (major, minor int) {
var uname syscall.Utsname
if err := syscall.Uname(&uname); err != nil {
return
}

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

return values[0], values[1]
}
8 changes: 4 additions & 4 deletions sys_conn_oob.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func inspectWriteBuffer(c syscall.RawConn) (int, error) {
return size, serr
}

func isECNDisabled() bool {
func isECNDisabledUsingEnv() bool {
disabled, err := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_ECN"))
return err == nil && disabled
}
Expand Down Expand Up @@ -147,8 +147,8 @@ func newConn(c OOBCapablePacketConn, supportsDF bool) (*oobConn, error) {
readPos: batchSize,
cap: connCapabilities{
DF: supportsDF,
GSO: isGSOSupported(rawConn),
ECN: !isECNDisabled(),
GSO: isGSOEnabled(rawConn),
ECN: isECNEnabled(),
},
}
for i := 0; i < batchSize; i++ {
Expand Down Expand Up @@ -247,7 +247,7 @@ func (c *oobConn) WritePacket(b []byte, addr net.Addr, packetInfoOOB []byte, gso
}
if ecn != protocol.ECNUnsupported {
if !c.capabilities().ECN {
panic("tried to send a ECN-marked packet although ECN is disabled")
panic("tried to send an ECN-marked packet although ECN is disabled")
}
if remoteUDPAddr, ok := addr.(*net.UDPAddr); ok {
if remoteUDPAddr.IP.To4() != nil {
Expand Down

0 comments on commit 12aa638

Please sign in to comment.