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.go69
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