1
0
forked from rosa/hakurei

internal/netlink: nonblocking socket I/O

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. // A conn represents resources associated to a netlink socket.
type conn struct { type conn struct {
// AF_NETLINK socket. // 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. // Kernel module or netlink group to communicate with.
family int family int
// Message sequence number. // Message sequence number.
@@ -42,7 +44,7 @@ func dial(family int) (*conn, error) {
var c conn var c conn
if fd, err := syscall.Socket( if fd, err := syscall.Socket(
syscall.AF_NETLINK, syscall.AF_NETLINK,
syscall.SOCK_RAW|syscall.SOCK_CLOEXEC, syscall.SOCK_RAW|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC,
family, family,
); err != nil { ); err != nil {
return nil, os.NewSyscallError("socket", err) return nil, os.NewSyscallError("socket", err)
@@ -53,8 +55,14 @@ func dial(family int) (*conn, error) {
_ = syscall.Close(fd) _ = syscall.Close(fd)
return nil, os.NewSyscallError("bind", err) return nil, os.NewSyscallError("bind", err)
} else { } 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.pos = syscall.NLMSG_HDRLEN
c.buf = make([]byte, os.Getpagesize()) c.buf = make([]byte, os.Getpagesize())
return &c, nil return &c, nil
@@ -66,7 +74,42 @@ func (c *conn) Close() error {
return syscall.EINVAL return syscall.EINVAL
} }
c.buf = nil 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. // 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 { func (c *conn) receive(f HandlerFunc, flags int) error {
for { for {
buf := c.buf buf := c.buf
if n, _, err := syscall.Recvfrom(c.fd, buf, flags); err != nil { if n, _, err := c.recvfrom(buf, flags); err != nil {
return os.NewSyscallError("recvfrom", err) return err
} else if n < syscall.NLMSG_HDRLEN { } else if n < syscall.NLMSG_HDRLEN {
return syscall.EBADE return syscall.EBADE
} else { } else {
@@ -187,10 +230,10 @@ func (c *conn) Roundtrip(f HandlerFunc) error {
} }
defer func() { c.seq++ }() 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, Family: syscall.AF_NETLINK,
}); err != nil { }); err != nil {
return os.NewSyscallError("sendto", err) return err
} }
return c.receive(f, 0) return c.receive(f, 0)