diff options
Diffstat (limited to 'internal/netlink/netlink.go')
| -rw-r--r-- | internal/netlink/netlink.go | 69 |
1 files changed, 36 insertions, 33 deletions
diff --git a/internal/netlink/netlink.go b/internal/netlink/netlink.go index 0a3c6688..ca8d0916 100644 --- a/internal/netlink/netlink.go +++ b/internal/netlink/netlink.go @@ -18,8 +18,8 @@ const ( stateOpen uint32 = 1 << iota ) -// A conn represents resources associated to a netlink socket. -type conn struct { +// A Conn represents resources associated to a netlink socket. +type Conn struct { // AF_NETLINK socket. f *os.File // For using runtime polling via f. @@ -44,9 +44,10 @@ type conn struct { t time.Time } -// dial returns the address of a newly connected conn of specified family. -func dial(family int, groups uint32) (*conn, error) { - var c conn +// Dial returns the address of a newly connected generic netlink connection of +// specified family and groups. +func Dial(family int, groups uint32) (*Conn, error) { + var c Conn if fd, err := syscall.Socket( syscall.AF_NETLINK, syscall.SOCK_RAW|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, @@ -89,10 +90,10 @@ func dial(family int, groups uint32) (*conn, error) { } // ok returns whether conn is still open. -func (c *conn) ok() bool { return c.state&stateOpen != 0 } +func (c *Conn) ok() bool { return c.state&stateOpen != 0 } // Close closes the underlying socket. -func (c *conn) Close() error { +func (c *Conn) Close() error { if !c.ok() { return syscall.EINVAL } @@ -100,22 +101,29 @@ func (c *conn) Close() error { return c.f.Close() } -// recvfrom wraps recv(2) with nonblocking behaviour via the runtime network poller. -func (c *conn) recvfrom( +// Recvfrom wraps recv(2) with nonblocking behaviour via the runtime network poller. +// +// The returned slice is valid until the next call to Recvfrom. +func (c *Conn) Recvfrom( ctx context.Context, - p []byte, flags int, -) (n int, from syscall.Sockaddr, err error) { +) (data []byte, from syscall.Sockaddr, err error) { if err = c.f.SetReadDeadline(time.Time{}); err != nil { return } + var n int + data = c.buf[:] done := make(chan error, 1) go func() { - done <- c.raw.Read(func(fd uintptr) (done bool) { - n, from, err = syscall.Recvfrom(int(fd), p, flags) + rcErr := c.raw.Read(func(fd uintptr) (done bool) { + n, from, err = syscall.Recvfrom(int(fd), data, flags) return err != syscall.EWOULDBLOCK }) + if n >= 0 { + data = data[:n] + } + done <- rcErr }() select { @@ -125,6 +133,7 @@ func (c *conn) recvfrom( } else { err = rcErr } + return case <-ctx.Done(): cancelErr := c.f.SetReadDeadline(c.t) @@ -136,11 +145,10 @@ func (c *conn) recvfrom( } return } - return } -// sendto wraps send(2) with nonblocking behaviour via the runtime network poller. -func (c *conn) sendto( +// Sendto wraps send(2) with nonblocking behaviour via the runtime network poller. +func (c *Conn) Sendto( ctx context.Context, p []byte, flags int, @@ -165,6 +173,7 @@ func (c *conn) sendto( } else { err = rcErr } + return case <-ctx.Done(): cancelErr := c.f.SetWriteDeadline(c.t) @@ -176,7 +185,6 @@ func (c *conn) sendto( } return } - return } // Msg is type constraint for types sent over the wire via netlink. @@ -198,7 +206,7 @@ func As[M Msg](data []byte) *M { } // add queues a value to be sent by conn. -func add[M Msg](c *conn, p *M) bool { +func add[M Msg](c *Conn, p *M) bool { pos := c.pos c.pos += int(unsafe.Sizeof(*p)) if c.pos > len(c.buf) { @@ -233,7 +241,7 @@ func (e *InconsistentError) Error() string { } // checkReply checks the message header of a reply from the kernel. -func (c *conn) checkReply(header *syscall.NlMsghdr) error { +func (c *Conn) checkReply(header *syscall.NlMsghdr) error { if header.Seq != c.seq || header.Pid != c.port { return &InconsistentError{*header, c.seq, c.port} } @@ -241,7 +249,7 @@ func (c *conn) checkReply(header *syscall.NlMsghdr) error { } // pending returns the valid slice of buf and initialises pos. -func (c *conn) pending() []byte { +func (c *Conn) pending() []byte { buf := c.buf[:c.pos] c.pos = syscall.NLMSG_HDRLEN @@ -267,23 +275,18 @@ type HandlerFunc func(resp []syscall.NetlinkMessage) error // receive receives from a socket with specified flags until a non-nil error is // returned by f. An error of type [Complete] is returned as nil. -func (c *conn) receive(ctx context.Context, f HandlerFunc, flags int) error { +func (c *Conn) receive(ctx context.Context, f HandlerFunc, flags int) error { for { - buf := c.buf[:] - if n, _, err := c.recvfrom(ctx, buf, flags); err != nil { + var resp []syscall.NetlinkMessage + if data, _, err := c.Recvfrom(ctx, flags); err != nil { return err - } else if n < syscall.NLMSG_HDRLEN { + } else if len(data) < syscall.NLMSG_HDRLEN { return syscall.EBADE - } else { - buf = buf[:n] - } - - resp, err := syscall.ParseNetlinkMessage(buf) - if err != nil { + } else if resp, err = syscall.ParseNetlinkMessage(data); err != nil { return err } - if err = f(resp); err != nil { + if err := f(resp); err != nil { if err == (Complete{}) { return nil } @@ -293,13 +296,13 @@ func (c *conn) receive(ctx context.Context, f HandlerFunc, flags int) error { } // Roundtrip sends the pending message and handles the reply. -func (c *conn) Roundtrip(ctx context.Context, f HandlerFunc) error { +func (c *Conn) Roundtrip(ctx context.Context, f HandlerFunc) error { if !c.ok() { return syscall.EINVAL } defer func() { c.seq++ }() - if err := c.sendto(ctx, c.pending(), 0, &syscall.SockaddrNetlink{ + if err := c.Sendto(ctx, c.pending(), 0, &syscall.SockaddrNetlink{ Family: syscall.AF_NETLINK, }); err != nil { return err |
