internal/netlink: nonblocking socket I/O
All checks were successful
Test / Create distribution (push) Successful in 1m13s
Test / Sandbox (push) Successful in 2m59s
Test / Hakurei (push) Successful in 4m0s
Test / ShareFS (push) Successful in 4m1s
Test / Sandbox (race detector) (push) Successful in 5m21s
Test / Hakurei (race detector) (push) Successful in 3m21s
Test / Flake checks (push) Successful in 1m18s

This enables use with blocking calls like when used with NETLINK_KOBJECT_UEVENT.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2026-03-25 14:06:59 +09:00
parent d972cffe5a
commit b98c5f2e21

View File

@@ -24,7 +24,9 @@ func getpid() uint32 {
// A conn represents resources associated to a netlink socket.
type conn struct {
// AF_NETLINK socket.
fd int
f *os.File
// For using runtime polling via f.
raw syscall.RawConn
// Kernel module or netlink group to communicate with.
family int
// Message sequence number.
@@ -42,7 +44,7 @@ func dial(family int) (*conn, error) {
var c conn
if fd, err := syscall.Socket(
syscall.AF_NETLINK,
syscall.SOCK_RAW|syscall.SOCK_CLOEXEC,
syscall.SOCK_RAW|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC,
family,
); err != nil {
return nil, os.NewSyscallError("socket", err)
@@ -53,8 +55,14 @@ func dial(family int) (*conn, error) {
_ = syscall.Close(fd)
return nil, os.NewSyscallError("bind", err)
} else {
c.fd, c.family = fd, family
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.pos = syscall.NLMSG_HDRLEN
c.buf = make([]byte, os.Getpagesize())
return &c, nil
@@ -66,7 +74,42 @@ func (c *conn) Close() error {
return syscall.EINVAL
}
c.buf = nil
return syscall.Close(c.fd)
return c.f.Close()
}
// recvfrom wraps recv(2) with nonblocking behaviour via the runtime network poller.
func (c *conn) recvfrom(
p []byte,
flags int,
) (n int, from syscall.Sockaddr, err error) {
rcErr := c.raw.Read(func(fd uintptr) (done bool) {
n, from, err = syscall.Recvfrom(int(fd), p, flags)
return err != syscall.EWOULDBLOCK
})
if err != nil {
err = os.NewSyscallError("recvfrom", err)
} else {
err = rcErr
}
return
}
// sendto wraps send(2) with nonblocking behaviour via the runtime network poller.
func (c *conn) sendto(
p []byte,
flags int,
to syscall.Sockaddr,
) (err error) {
rcErr := c.raw.Write(func(fd uintptr) (done bool) {
err = syscall.Sendto(int(fd), p, flags, to)
return err != syscall.EWOULDBLOCK
})
if err != nil {
err = os.NewSyscallError("sendto", err)
} else {
err = rcErr
}
return
}
// Msg is type constraint for types sent over the wire via netlink.
@@ -152,8 +195,8 @@ type HandlerFunc func(resp []syscall.NetlinkMessage) error
func (c *conn) receive(f HandlerFunc, flags int) error {
for {
buf := c.buf
if n, _, err := syscall.Recvfrom(c.fd, buf, flags); err != nil {
return os.NewSyscallError("recvfrom", err)
if n, _, err := c.recvfrom(buf, flags); err != nil {
return err
} else if n < syscall.NLMSG_HDRLEN {
return syscall.EBADE
} else {
@@ -187,10 +230,10 @@ func (c *conn) Roundtrip(f HandlerFunc) error {
}
defer func() { c.seq++ }()
if err := syscall.Sendto(c.fd, c.pending(), 0, &syscall.SockaddrNetlink{
if err := c.sendto(c.pending(), 0, &syscall.SockaddrNetlink{
Family: syscall.AF_NETLINK,
}); err != nil {
return os.NewSyscallError("sendto", err)
return err
}
return c.receive(f, 0)