Skip to content

Commit

Permalink
netlink: add Config.Strict option
Browse files Browse the repository at this point in the history
Signed-off-by: Matt Layher <mdlayher@gmail.com>
  • Loading branch information
mdlayher committed Dec 13, 2021
1 parent 60801e2 commit ad9e2c4
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 6 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
# CHANGELOG

## Unreleased

**This is the first release of package netlink that only supports Go 1.13+.
Users on older versions must use v1.5.0.**

- [New API] [commit TODO]: the `netlink.Config.Strict` field can be used to
apply a more strict default set of options to a `netlink.Conn`. This is
recommended for applications running on modern Linux kernels, but cannot be
enabled by default because the options may require a more recent kernel than
Go supports. See the documentation for details.

## v1.5.0

**This is the last release of package netlink that supports Go 1.12.**

- [New API] [commit](https://github.com/mdlayher/netlink/commit/53a1c10065e51077659ceedf921c8f0807abe8c0):
the `netlink.Config.PID` field can be used to specify an explicit port ID when
binding the netlink socket. This is intended for advanced use cases and most
Expand Down
41 changes: 39 additions & 2 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,37 @@ type Socket interface {
}

// Dial dials a connection to netlink, using the specified netlink family.
// Config specifies optional configuration for Conn. If config is nil, a default
// Config specifies optional configuration for Conn. If config is nil, a default
// configuration will be used.
func Dial(family int, config *Config) (*Conn, error) {
// Use OS-specific dial() to create Socket
if config == nil {
config = &Config{}
}

// TODO(mdlayher): plumb in netlink.OpError wrapping?

// Use OS-specific dial() to create Socket.
c, pid, err := dial(family, config)
if err != nil {
return nil, err
}

if config.Strict {
// The caller has requested the strict option set. Historically we have
// recommended checking for ENOPROTOOPT if the kernel does not support
// the option in question, but that may result in a silent failure and
// unexpected behavior for the user.
//
// Treat any error here as a fatal error, and require the caller to deal
// with it.
for _, o := range []ConnOption{ExtendedAcknowledge, GetStrictCheck} {
if err := c.SetOption(o, true); err != nil {
_ = c.Close()
return nil, err
}
}
}

return NewConn(c, pid), nil
}

Expand Down Expand Up @@ -569,4 +591,19 @@ type Config struct {
// for advanced use cases where the kernel expects a fixed unicast address
// destination for netlink messages.
PID uint32

// Strict applies a more strict default set of options to the Conn,
// including:
// - ExtendedAcknowledge: true
// - provides more useful error messages when supported by the kernel
// - GetStrictCheck: true
// - more strictly enforces request validation for some families such
// as rtnetlink which were historically misused
//
// If any of the options specified by Strict cannot be configured due to an
// outdated kernel or similar, an error will be returned.
//
// When possible, setting Strict to true is recommended for applications
// running on modern Linux kernels.
Strict bool
}
4 changes: 0 additions & 4 deletions conn_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ type conn struct {
// dial is the entry point for Dial. dial opens a netlink socket using
// system calls, and returns its PID.
func dial(family int, config *Config) (*conn, uint32, error) {
if config == nil {
config = &Config{}
}

// Prepare the netlink socket.
s, err := socket.Socket(
unix.AF_NETLINK,
Expand Down
43 changes: 43 additions & 0 deletions netlink_linux_gteq_1.13_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,46 @@ func TestIntegrationConnClosedConn(t *testing.T) {
})
}
}

func TestIntegrationConnStrict(t *testing.T) {
c, err := netlink.Dial(unix.NETLINK_GENERIC, &netlink.Config{Strict: true})
if err != nil {
if errors.Is(err, unix.ENOPROTOOPT) {
t.Skipf("skipping, strict options not supported by this kernel: %v", err)
}

t.Fatalf("failed to dial netlink: %v", err)
}
defer c.Close()

sc, err := c.SyscallConn()
if err != nil {
t.Fatalf("failed to open syscall conn: %v", err)
}

// Strict mode applies a series of socket options. Check each applied option
// and update the map to true if we found it set to true. Any options which
// were not applied as expected will result in the test failing.
opts := map[int]bool{
unix.NETLINK_EXT_ACK: false,
unix.NETLINK_GET_STRICT_CHK: false,
}

err = sc.Control(func(fd uintptr) {
for k := range opts {
// The kernel uses 0 for false and 1 for true.
if v, err := unix.GetsockoptInt(int(fd), unix.SOL_NETLINK, k); err == nil && v == 1 {
opts[k] = true
}
}
})
if err != nil {
t.Fatalf("failed to call control: %v", err)
}

for k, v := range opts {
if !v {
t.Errorf("socket option %d was not set to true", k)
}
}
}

0 comments on commit ad9e2c4

Please sign in to comment.