use SO_RCVBUFFORCE to force receive buffer increase on Linux (#3804)

* Add ability to force change the receive buffer size using SO_RCVBUFFORCE in Linux

* Fix imports

* Update test

* Add sys_conn_helper_not_linux

* Rename file

* ignore the error on SetReadBuffer

* also run unit tests as root

---------

Co-authored-by: Marten Seemann <martenseemann@gmail.com>
This commit is contained in:
Marco Munizaga 2023-05-08 02:40:47 -07:00 committed by GitHub
parent da198b710b
commit 843b633434
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 90 additions and 5 deletions

View file

@ -20,6 +20,16 @@ jobs:
env:
TIMESCALE_FACTOR: 10
run: go run github.com/onsi/ginkgo/v2/ginkgo -r -v -cover -randomize-all -randomize-suites -trace -skip-package integrationtests
- name: Run tests as root
if: ${{ matrix.os == 'ubuntu' }}
env:
TIMESCALE_FACTOR: 10
FILE: sys_conn_helper_linux_test.go
run: |
test -f $FILE # make sure the file actually exists
go run github.com/onsi/ginkgo/v2/ginkgo build -cover -tags root .
sudo ./quic-go.test -ginkgo.v -ginkgo.trace -ginkgo.randomize-all -ginkgo.focus-file=$FILE -test.coverprofile coverage-root.txt
rm quic-go.test
- name: Run tests (32 bit)
if: ${{ matrix.os != 'macos' }} # can't run 32 bit tests on OSX.
env:
@ -34,5 +44,5 @@ jobs:
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: coverage.txt
files: coverage.txt coverage-root.txt
env_vars: OS=${{ matrix.os }}, GO=${{ matrix.go }}

View file

@ -2,7 +2,13 @@
package quic
import "golang.org/x/sys/unix"
import (
"errors"
"fmt"
"syscall"
"golang.org/x/sys/unix"
)
const msgTypeIPTOS = unix.IP_TOS
@ -17,3 +23,23 @@ const (
)
const batchSize = 8 // needs to smaller than MaxUint8 (otherwise the type of oobConn.readPos has to be changed)
func forceSetReceiveBuffer(c interface{}, bytes int) error {
conn, ok := c.(interface {
SyscallConn() (syscall.RawConn, error)
})
if !ok {
return errors.New("doesn't have a SyscallConn")
}
rawConn, err := conn.SyscallConn()
if err != nil {
return fmt.Errorf("couldn't get syscall.RawConn: %w", err)
}
var serr error
if err := rawConn.Control(func(fd uintptr) {
serr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUFFORCE, bytes)
}); err != nil {
return err
}
return serr
}

View file

@ -0,0 +1,37 @@
// We need root permissions to use RCVBUFFORCE.
// This test is therefore only compiled when the root build flag is set.
// It can only succeed if the tests are then also run with root permissions.
//go:build linux && root
package quic
import (
"net"
"os"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Can change the receive buffer size", func() {
It("Force a change (if we have CAP_NET_ADMIN)", func() {
if os.Getuid() != 0 {
Fail("Must be root to force change the receive buffer size")
}
c, err := net.ListenPacket("udp", "127.0.0.1:0")
Expect(err).ToNot(HaveOccurred())
forceSetReceiveBuffer(c, 256<<10)
size, err := inspectReadBuffer(c)
Expect(err).ToNot(HaveOccurred())
// The kernel doubles this value (to allow space for bookkeeping overhead)
Expect(size).To(Equal(512 << 10))
forceSetReceiveBuffer(c, 512<<10)
size, err = inspectReadBuffer(c)
Expect(err).ToNot(HaveOccurred())
// The kernel doubles this value (to allow space for bookkeeping overhead)
Expect(size).To(Equal(1024 << 10))
})
})

View file

@ -0,0 +1,5 @@
//go:build !linux
package quic
func forceSetReceiveBuffer(c interface{}, bytes int) error { return nil }

View file

@ -180,10 +180,17 @@ func setReceiveBuffer(c net.PacketConn, logger utils.Logger) error {
logger.Debugf("Conn has receive buffer of %d kiB (wanted: at least %d kiB)", size/1024, protocol.DesiredReceiveBufferSize/1024)
return nil
}
if err := conn.SetReadBuffer(protocol.DesiredReceiveBufferSize); err != nil {
return fmt.Errorf("failed to increase receive buffer size: %w", err)
}
// Ignore the error. We check if we succeeded by querying the buffer size afterward.
_ = conn.SetReadBuffer(protocol.DesiredReceiveBufferSize)
newSize, err := inspectReadBuffer(c)
if newSize < protocol.DesiredReceiveBufferSize {
// Try again with RCVBUFFORCE on Linux
_ = forceSetReceiveBuffer(c, protocol.DesiredReceiveBufferSize)
newSize, err = inspectReadBuffer(c)
if err != nil {
return fmt.Errorf("failed to determine receive buffer size: %w", err)
}
}
if err != nil {
return fmt.Errorf("failed to determine receive buffer size: %w", err)
}