mirror of
https://github.com/slackhq/nebula.git
synced 2026-11-15 13:46:52 +01:00
internal/msgx is the one copy of xnu's private batched socket calls: the msghdr_x layout, Send/Recv through the raw syscall (EINTR retried) and Unsupported for the errnos a kernel or sandbox refuses them with. Every socket the listener owns reads with recvmsg_x: the listener and each connected socket run their own reader goroutine, and hand whole batches to ListenOut over a channel. ListenOut runs the EncReader over a batch and flushes once, so the reader stays single-threaded without a lock that readers contend on, and the next recvmsg_x overlaps the decrypt of the last batch. Batch buffers (64 x MTU) come from a per-listener free list that keeps up to 11; a reader takes one only while its socket is readable, so idle connected sockets hold none. A batch with a header intact msghdr_x entries can't produce, or ENOSYS/EPERM/EOPNOTSUPP, switches every reader to one datagram per read for good. Connected sockets send runs with sendmsg_x, which xnu batches through the stack on a connected socket. EAGAIN/ENOBUFS keep #1901's single-datagram stall handling; a refused sendmsg_x retries the datagram with write and turns sendmsg_x off once that goes through. The connected-socket knobs lose their NEBULA_CONNSOCK* environment overrides and keep #1901's defaults. Based on Caleb Jasik's #1895 (recvmsg_x) and #1897 (sendmsg_x), and Jack Doan's internal/msgx shape from #1898. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
80 lines
2.3 KiB
Go
80 lines
2.3 KiB
Go
package msgx
|
|
|
|
import (
|
|
"bytes"
|
|
"syscall"
|
|
"testing"
|
|
"unsafe"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// TestHdrLayout pins Hdr to struct msghdr_x's LP64 layout: 56 bytes, Datalen last at 48.
|
|
func TestHdrLayout(t *testing.T) {
|
|
var h Hdr
|
|
assert.EqualValues(t, 56, unsafe.Sizeof(h))
|
|
assert.EqualValues(t, 48, unsafe.Offsetof(h.Datalen))
|
|
}
|
|
|
|
// TestSendRecvRoundTrip sends a batch through sendmsg_x in one call and reads it back through recvmsg_x in one
|
|
// call, each message whole, in order, with Datalen set.
|
|
func TestSendRecvRoundTrip(t *testing.T) {
|
|
fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_DGRAM, 0)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
_ = unix.Close(fds[0])
|
|
_ = unix.Close(fds[1])
|
|
})
|
|
// A dropped datagram fails the receive instead of hanging it.
|
|
require.NoError(t, unix.SetsockoptTimeval(fds[1], unix.SOL_SOCKET, unix.SO_RCVTIMEO, &unix.Timeval{Sec: 2}))
|
|
|
|
const n = 5
|
|
var want [n][]byte
|
|
var iovs [n]unix.Iovec
|
|
hdrs := make([]Hdr, n)
|
|
for i := range n {
|
|
want[i] = bytes.Repeat([]byte{byte(i + 1)}, 10+i)
|
|
iovs[i] = unix.Iovec{Base: &want[i][0]}
|
|
iovs[i].SetLen(len(want[i]))
|
|
hdrs[i] = Hdr{Iov: &iovs[i], Iovlen: 1}
|
|
}
|
|
sent, errno := Send(uintptr(fds[0]), hdrs)
|
|
require.Zero(t, errno)
|
|
require.Equal(t, n, sent)
|
|
|
|
var bufs [n + 1][64]byte
|
|
var riovs [n + 1]unix.Iovec
|
|
rhdrs := make([]Hdr, n+1)
|
|
for i := range rhdrs {
|
|
riovs[i] = unix.Iovec{Base: &bufs[i][0]}
|
|
riovs[i].SetLen(len(bufs[i]))
|
|
rhdrs[i] = Hdr{Iov: &riovs[i], Iovlen: 1}
|
|
}
|
|
got, errno := Recv(uintptr(fds[1]), rhdrs)
|
|
require.Zero(t, errno)
|
|
require.Equal(t, n, got)
|
|
for i := range n {
|
|
l := int(rhdrs[i].Datalen)
|
|
assert.Equal(t, want[i], bufs[i][:l], "message %d", i)
|
|
}
|
|
}
|
|
|
|
// TestEmptyBatch pins that an empty batch is refused before it reaches the kernel.
|
|
func TestEmptyBatch(t *testing.T) {
|
|
_, errno := Send(0, nil)
|
|
assert.Equal(t, syscall.EINVAL, errno)
|
|
_, errno = Recv(0, nil)
|
|
assert.Equal(t, syscall.EINVAL, errno)
|
|
}
|
|
|
|
func TestUnsupported(t *testing.T) {
|
|
for _, e := range []syscall.Errno{syscall.ENOSYS, syscall.EPERM, syscall.EOPNOTSUPP} {
|
|
assert.True(t, Unsupported(e), "%v", e)
|
|
}
|
|
for _, e := range []syscall.Errno{0, syscall.EAGAIN, syscall.ENOBUFS, syscall.EINVAL} {
|
|
assert.False(t, Unsupported(e), "%v", e)
|
|
}
|
|
}
|