diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-03-25 14:06:59 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-03-25 14:06:59 +0900 |
| commit | b98c5f2e219c142ab89c09445b6c014120f0760f (patch) | |
| tree | c002a0213e567c60a9d97a874056ce3e6283be2a /internal/netlink | |
| parent | d972cffe5a221cc585e2edfdd1d3514eab51271c (diff) | |
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>
Diffstat (limited to 'internal/netlink')
| -rw-r--r-- | internal/netlink/netlink.go | 59 |
1 files changed, 51 insertions, 8 deletions
diff --git a/internal/netlink/netlink.go b/internal/netlink/netlink.go index 5df7b390..df6af840 100644 --- a/internal/netlink/netlink.go +++ b/internal/netlink/netlink.go @@ -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) |
