Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve behaviour of SetReadBuffer / SetWriteBuffer #165

Merged
merged 4 commits into from
May 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 66 additions & 4 deletions conn_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -276,21 +277,67 @@ 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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a specific syscall error we could inspect to determine if fallback is needed? Would something like os.IsPermission be sufficient?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The man page does not require a specific error for that case, but the kernel uses EPERM (which is the only logical one of course). Also the permission check is the first thing to happen. I don't think it will ever make a difference if we check for permission errors or not, libnfnetlink does not check for it. Your choice.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm good with leaving it be then, thanks for the info.

err = os.NewSyscallError("setsockopt", c.s.SetSockoptInt(
unix.SOL_SOCKET,
unix.SO_RCVBUF,
bytes,
))
}
return err
}

// 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
}

// 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 0, os.NewSyscallError("getsockopt", err)
}
return value, nil
}

// GetWriteBuffer retrieves the size of the operating system's transmit buffer
// associated with the Conn.
func (c *conn) GetWriteBuffer() (int, error) {
MarkusBauer marked this conversation as resolved.
Show resolved Hide resolved
value, err := c.s.GetSockoptInt(
unix.SOL_SOCKET,
unix.SO_SNDBUF,
)
if err != nil {
return 0, os.NewSyscallError("getsockopt", err)
}
return value, nil
}

// linuxOption converts a ConnOption to its Linux value.
Expand Down Expand Up @@ -597,6 +644,21 @@ func (s *sysSocket) SetSockoptInt(level, opt, value int) error {
return err
}

func (s *sysSocket) GetSockoptInt(level, opt int) (int, error) {
MarkusBauer marked this conversation as resolved.
Show resolved Hide resolved
var (
value int
err error
)
doErr := s.control(func(fd int) {
value, err = unix.GetsockoptInt(fd, level, opt)
})
if doErr != nil {
return 0, doErr
}

return value, err
}

func (s *sysSocket) SetSockoptSockFprog(level, opt int, fprog *unix.SockFprog) error {
var err error
doErr := s.control(func(fd int) {
Expand Down
14 changes: 12 additions & 2 deletions conn_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
}
Expand Down Expand Up @@ -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")
}