diff --git a/.gitignore b/.gitignore index efc8a0a..df6e9d3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ internal/integration/integration.test netlink.test netlink-fuzz.zip testdata/ +go.work +go.work.sum diff --git a/conn.go b/conn.go index a88efee..2bc6023 100644 --- a/conn.go +++ b/conn.go @@ -52,6 +52,7 @@ type Socket interface { Close() error Send(m Message) error SendMessages(m []Message) error + SendMessagesScatter(m []Message) error Receive() ([]Message, error) } @@ -176,6 +177,13 @@ func (c *Conn) SendMessages(msgs []Message) ([]Message, error) { return msgs, nil } +// SendMessagesScatter sends multiple Messages to netlink using scatter-gather I/O. +// Each message is marshaled to its own buffer, avoiding a single large +// contiguous allocation. All messages are sent as one datagram. +func (c *Conn) SendMessagesScatter(msgs []Message) ([]Message, error) { + return c.SendMessages(msgs) +} + // Send sends a single Message to netlink. In most cases, a Header's Length, // Sequence, and PID fields should be set to 0, so they can be populated // automatically before the Message is sent. On success, Send returns a copy diff --git a/conn_linux.go b/conn_linux.go index 1eab74a..be6bed5 100644 --- a/conn_linux.go +++ b/conn_linux.go @@ -107,6 +107,23 @@ func (c *conn) SendMessages(messages []Message) error { return err } +// SendMessagesScatter sends multiple messages using scatter-gather I/O. +// Each message is marshaled to its own buffer, avoiding a single large +// contiguous allocation. All messages are sent as one datagram. +func (c *conn) SendMessagesScatter(messages []Message) error { + buffers := make([][]byte, len(messages)) + for i, m := range messages { + b, err := m.MarshalBinary() + if err != nil { + return err + } + buffers[i] = b + } + sa := &unix.SockaddrNetlink{Family: unix.AF_NETLINK} + _, err := c.s.SendmsgBuffers(context.Background(), buffers, nil, sa, 0) + return err +} + // Send sends a single Message to netlink. func (c *conn) Send(m Message) error { b, err := m.MarshalBinary() @@ -208,7 +225,7 @@ func (c *conn) SetWriteDeadline(t time.Time) error { return c.s.SetWriteDeadline // associated with the Conn. func (c *conn) SetReadBuffer(bytes int) error { return c.s.SetReadBuffer(bytes) } -// SetReadBuffer sets the size of the operating system's transmit buffer +// SetWriteBuffer sets the size of the operating system's transmit buffer // associated with the Conn. func (c *conn) SetWriteBuffer(bytes int) error { return c.s.SetWriteBuffer(bytes) } diff --git a/conn_others.go b/conn_others.go index 4c5e739..069d7f0 100644 --- a/conn_others.go +++ b/conn_others.go @@ -24,7 +24,8 @@ type conn struct{} func dial(_ int, _ *Config) (*conn, uint32, error) { return nil, 0, errUnimplemented } func newError(_ int) error { return errUnimplemented } -func (c *conn) Send(_ Message) error { return errUnimplemented } -func (c *conn) SendMessages(_ []Message) error { return errUnimplemented } -func (c *conn) Receive() ([]Message, error) { return nil, errUnimplemented } -func (c *conn) Close() error { return errUnimplemented } +func (c *conn) Send(_ Message) error { return errUnimplemented } +func (c *conn) SendMessages(_ []Message) error { return errUnimplemented } +func (c *conn) SendMessagesScatter(_ []Message) error { return errUnimplemented } +func (c *conn) Receive() ([]Message, error) { return nil, errUnimplemented } +func (c *conn) Close() error { return errUnimplemented } diff --git a/conn_others_test.go b/conn_others_test.go index e11b1b5..76dd6a0 100644 --- a/conn_others_test.go +++ b/conn_others_test.go @@ -28,6 +28,10 @@ func TestOthersConnUnimplemented(t *testing.T) { t.Fatalf("unexpected error during c.SendMessages:\n- want: %v\n- got: %v", want, got) } + if got := c.SendMessagesScatter(nil); want != got { + t.Fatalf("unexpected error during c.SendMessagesScatter:\n- want: %v\n- got: %v", + want, got) + } if _, got := c.Receive(); want != got { t.Fatalf("unexpected error during c.Receive:\n- want: %v\n- got: %v",