aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/netlink/netlink.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/netlink/netlink.go')
-rw-r--r--internal/netlink/netlink.go51
1 files changed, 31 insertions, 20 deletions
diff --git a/internal/netlink/netlink.go b/internal/netlink/netlink.go
index 89f692a5..857259f1 100644
--- a/internal/netlink/netlink.go
+++ b/internal/netlink/netlink.go
@@ -5,33 +5,29 @@ import (
"context"
"fmt"
"os"
- "sync"
"syscall"
"time"
"unsafe"
)
-// AF_NETLINK socket is never shared
-var (
- nlPid uint32
- nlPidOnce sync.Once
-)
-
-// getpid returns a cached pid value.
-func getpid() uint32 {
- nlPidOnce.Do(func() { nlPid = uint32(os.Getpid()) })
- return nlPid
-}
-
// net/netlink/af_netlink.c
const maxRecvmsgLen = 32768
+const (
+ // stateOpen denotes an open conn.
+ stateOpen uint32 = 1 << iota
+)
+
// A conn represents resources associated to a netlink socket.
type conn struct {
// AF_NETLINK socket.
f *os.File
// For using runtime polling via f.
raw syscall.RawConn
+ // Port ID assigned by the kernel.
+ port uint32
+ // Internal connection status.
+ state uint32
// Kernel module or netlink group to communicate with.
family int
// Message sequence number.
@@ -49,7 +45,7 @@ type conn struct {
}
// dial returns the address of a newly connected conn of specified family.
-func dial(family int) (*conn, error) {
+func dial(family int, groups uint32) (*conn, error) {
var c conn
if fd, err := syscall.Socket(
syscall.AF_NETLINK,
@@ -59,17 +55,32 @@ func dial(family int) (*conn, error) {
return nil, os.NewSyscallError("socket", err)
} else if err = syscall.Bind(fd, &syscall.SockaddrNetlink{
Family: syscall.AF_NETLINK,
- Pid: getpid(),
+ Groups: groups,
}); err != nil {
_ = syscall.Close(fd)
return nil, os.NewSyscallError("bind", err)
} else {
+ var addr syscall.Sockaddr
+ if addr, err = syscall.Getsockname(fd); err != nil {
+ _ = syscall.Close(fd)
+ return nil, os.NewSyscallError("getsockname", err)
+ }
+ switch a := addr.(type) {
+ case *syscall.SockaddrNetlink:
+ c.port = a.Pid
+
+ default: // unreachable
+ _ = syscall.Close(fd)
+ return nil, syscall.ENOTRECOVERABLE
+ }
+
c.family = family
c.f = os.NewFile(uintptr(fd), "netlink")
if c.raw, err = c.f.SyscallConn(); err != nil {
_ = c.f.Close()
return nil, err
}
+ c.state |= stateOpen
}
c.pos = syscall.NLMSG_HDRLEN
@@ -78,14 +89,14 @@ func dial(family int) (*conn, error) {
}
// ok returns whether conn is still open.
-func (c *conn) ok() bool { return c.family >= 0 }
+func (c *conn) ok() bool { return c.state&stateOpen != 0 }
// Close closes the underlying socket.
func (c *conn) Close() error {
if !c.ok() {
return syscall.EINVAL
}
- c.family = -1
+ c.state &= ^stateOpen
return c.f.Close()
}
@@ -231,7 +242,7 @@ func (c *conn) pending() []byte {
Type: c.typ,
Flags: c.flags,
Seq: c.seq,
- Pid: getpid(),
+ Pid: c.port,
}
return buf
}
@@ -266,8 +277,8 @@ func (c *conn) receive(ctx context.Context, f HandlerFunc, flags int) error {
for i := range resp {
header := &resp[i].Header
- if header.Seq != c.seq || header.Pid != getpid() {
- return &InconsistentError{*header, c.seq, getpid()}
+ if header.Seq != c.seq || header.Pid != c.port {
+ return &InconsistentError{*header, c.seq, c.port}
}
}
if err = f(resp); err != nil {