From 64b9c1f5c7c1044b4d658783764056e695bbbd26 Mon Sep 17 00:00:00 2001 From: Markus Bauer Date: Tue, 24 Mar 2020 17:33:26 +0100 Subject: [PATCH 1/4] Use SO_RCVBUFFORCE to set read buffer size (see issue #164) --- conn_linux.go | 28 ++++++++++++++++++++++++---- conn_linux_test.go | 4 ++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/conn_linux.go b/conn_linux.go index 61af3a2..9c8c39b 100644 --- a/conn_linux.go +++ b/conn_linux.go @@ -276,21 +276,41 @@ func (c *conn) SetWriteDeadline(t time.Time) error { // SetReadBuffer sets the size of the operating system's receive buffer // associated with the Conn. func (c *conn) SetReadBuffer(bytes int) error { - return os.NewSyscallError("setsockopt", c.s.SetSockoptInt( + // First try SO_RCVBUFFORCE. Given necessary permissions this syscall ignores limits. + err := os.NewSyscallError("setsockopt", c.s.SetSockoptInt( unix.SOL_SOCKET, - unix.SO_RCVBUF, + unix.SO_RCVBUFFORCE, bytes, )) + if err != nil { + // If SO_SNDBUFFORCE fails, try SO_RCVBUF + err = os.NewSyscallError("setsockopt", c.s.SetSockoptInt( + unix.SOL_SOCKET, + unix.SO_RCVBUF, + bytes, + )) + } + return nil } // SetReadBuffer sets the size of the operating system's transmit buffer // associated with the Conn. func (c *conn) SetWriteBuffer(bytes int) error { - return os.NewSyscallError("setsockopt", c.s.SetSockoptInt( + // First try SO_SNDBUFFORCE. Given necessary permissions this syscall ignores limits. + err := os.NewSyscallError("setsockopt", c.s.SetSockoptInt( unix.SOL_SOCKET, - unix.SO_SNDBUF, + unix.SO_SNDBUFFORCE, bytes, )) + if err != nil { + // If SO_SNDBUFFORCE fails, try SO_SNDBUF + err = os.NewSyscallError("setsockopt", c.s.SetSockoptInt( + unix.SOL_SOCKET, + unix.SO_SNDBUF, + bytes, + )) + } + return err } // linuxOption converts a ConnOption to its Linux value. diff --git a/conn_linux_test.go b/conn_linux_test.go index 17d1ed9..5bb9be4 100644 --- a/conn_linux_test.go +++ b/conn_linux_test.go @@ -449,12 +449,12 @@ func TestLinuxConnSetBuffers(t *testing.T) { want := []setSockopt{ { level: unix.SOL_SOCKET, - opt: unix.SO_RCVBUF, + opt: unix.SO_RCVBUFFORCE, value: n, }, { level: unix.SOL_SOCKET, - opt: unix.SO_SNDBUF, + opt: unix.SO_SNDBUFFORCE, value: n, }, } From 74e9b55e8212e680db923cf19aeefee9a6f472c6 Mon Sep 17 00:00:00 2001 From: Markus Bauer Date: Tue, 24 Mar 2020 17:48:25 +0100 Subject: [PATCH 2/4] Add methods to read back the actual read / send buffer size --- conn_linux.go | 40 ++++++++++++++++++++++++++++++++++++++++ conn_linux_test.go | 10 ++++++++++ 2 files changed, 50 insertions(+) diff --git a/conn_linux.go b/conn_linux.go index 9c8c39b..55aaf21 100644 --- a/conn_linux.go +++ b/conn_linux.go @@ -43,6 +43,7 @@ type socket interface { SetWriteDeadline(t time.Time) error SetSockoptSockFprog(level, opt int, fprog *unix.SockFprog) error SetSockoptInt(level, opt, value int) error + GetSockoptInt(level, opt int) (int, error) } // dial is the entry point for Dial. dial opens a netlink socket using @@ -313,6 +314,32 @@ func (c *conn) SetWriteBuffer(bytes int) error { return err } +// GetReadBuffer retrieves the size of the operating system's receive buffer +// associated with the Conn. +func (c *conn) GetReadBuffer() (int, error) { + value, err := c.s.GetSockoptInt( + unix.SOL_SOCKET, + unix.SO_RCVBUF, + ) + if err != nil { + return value, os.NewSyscallError("getsockopt", err) + } + return value, err +} + +// GetWriteBuffer retrieves the size of the operating system's transmit buffer +// associated with the Conn. +func (c *conn) GetWriteBuffer() (int, error) { + value, err := c.s.GetSockoptInt( + unix.SOL_SOCKET, + unix.SO_SNDBUF, + ) + if err != nil { + return value, os.NewSyscallError("getsockopt", err) + } + return value, err +} + // linuxOption converts a ConnOption to its Linux value. func linuxOption(o ConnOption) (int, bool) { switch o { @@ -617,6 +644,19 @@ func (s *sysSocket) SetSockoptInt(level, opt, value int) error { return err } +func (s *sysSocket) GetSockoptInt(level, opt int) (int, error) { + var value int + var err error + doErr := s.control(func(fd int) { + value, err = unix.GetsockoptInt(fd, level, opt) + }) + if doErr != nil { + return value, doErr + } + + return value, err +} + func (s *sysSocket) SetSockoptSockFprog(level, opt int, fprog *unix.SockFprog) error { var err error doErr := s.control(func(fd int) { diff --git a/conn_linux_test.go b/conn_linux_test.go index 5bb9be4..b3a2264 100644 --- a/conn_linux_test.go +++ b/conn_linux_test.go @@ -679,6 +679,16 @@ func (s *testSocket) SetSockoptInt(level, opt, value int) error { return nil } +func (s *testSocket) GetSockoptInt(level, opt int) (int, error) { + for i := len(s.setSockopt)-1; i >= 0; i-- { + if s.setSockopt[i].level == level && s.setSockopt[i].opt == opt { + return s.setSockopt[i].value, nil + } + } + + return 0, errors.New("getsockopt without preceding setsockopt") +} + func (s *testSocket) SetSockoptSockFprog(_, _ int, _ *unix.SockFprog) error { panic("netlink: testSocket.SetSockoptSockFprog not currently implemented") } From 4e92b431b7c63084fdfcec8f573a0fb9be79e499 Mon Sep 17 00:00:00 2001 From: Markus Bauer Date: Tue, 24 Mar 2020 17:56:15 +0100 Subject: [PATCH 3/4] (fix return value of SetReadBuffer) --- conn_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conn_linux.go b/conn_linux.go index 55aaf21..6fac3b8 100644 --- a/conn_linux.go +++ b/conn_linux.go @@ -291,7 +291,7 @@ func (c *conn) SetReadBuffer(bytes int) error { bytes, )) } - return nil + return err } // SetReadBuffer sets the size of the operating system's transmit buffer From e4e678731839443c403ac812b02b3bcc9bf09f90 Mon Sep 17 00:00:00 2001 From: Markus Bauer Date: Mon, 11 May 2020 15:57:42 +0200 Subject: [PATCH 4/4] Comments from code review --- conn_linux.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/conn_linux.go b/conn_linux.go index 6fac3b8..61aa592 100644 --- a/conn_linux.go +++ b/conn_linux.go @@ -322,9 +322,9 @@ func (c *conn) GetReadBuffer() (int, error) { unix.SO_RCVBUF, ) if err != nil { - return value, os.NewSyscallError("getsockopt", err) + return 0, os.NewSyscallError("getsockopt", err) } - return value, err + return value, nil } // GetWriteBuffer retrieves the size of the operating system's transmit buffer @@ -335,9 +335,9 @@ func (c *conn) GetWriteBuffer() (int, error) { unix.SO_SNDBUF, ) if err != nil { - return value, os.NewSyscallError("getsockopt", err) + return 0, os.NewSyscallError("getsockopt", err) } - return value, err + return value, nil } // linuxOption converts a ConnOption to its Linux value. @@ -645,13 +645,15 @@ func (s *sysSocket) SetSockoptInt(level, opt, value int) error { } func (s *sysSocket) GetSockoptInt(level, opt int) (int, error) { - var value int - var err error + var ( + value int + err error + ) doErr := s.control(func(fd int) { value, err = unix.GetsockoptInt(fd, level, opt) }) if doErr != nil { - return value, doErr + return 0, doErr } return value, err