mirror of
https://github.com/slackhq/nebula.git
synced 2026-11-15 17:26:52 +01:00
udp/darwin: batch receives and connected-socket sends with recvmsg_x/sendmsg_x
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>
This commit is contained in:
1 parent
37032a22bf
commit
21075ba32e
7 files changed
+808
-125
No files matched your search
@@ -0,0 +1,58 @@
|
||||
// Package msgx calls xnu's private batched socket calls, sendmsg_x and recvmsg_x, which move many datagrams per
|
||||
// syscall. They are not public API, so a kernel or sandbox may refuse them; callers fall back to one datagram per
|
||||
// syscall when Unsupported reports that.
|
||||
package msgx
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Batch is the most messages the callers hand one sendmsg_x or recvmsg_x call.
|
||||
const Batch = 64
|
||||
|
||||
// Hdr mirrors xnu's struct msghdr_x (bsd/sys/socket_private.h), which the SDK doesn't ship. recvmsg_x reports each
|
||||
// message's length in Datalen rather than in its return value.
|
||||
type Hdr struct {
|
||||
Name *byte
|
||||
Namelen uint32
|
||||
Iov *unix.Iovec
|
||||
Iovlen int32
|
||||
Control *byte
|
||||
Controllen uint32
|
||||
Flags int32
|
||||
Datalen uint64
|
||||
}
|
||||
|
||||
// Send passes hdrs to sendmsg_x on fd and returns how many messages the kernel took. EINTR is retried.
|
||||
func Send(fd uintptr, hdrs []Hdr) (int, syscall.Errno) {
|
||||
return call(unix.SYS_SENDMSG_X, fd, hdrs)
|
||||
}
|
||||
|
||||
// Recv passes hdrs to recvmsg_x on fd and returns how many messages it filled. EINTR is retried.
|
||||
func Recv(fd uintptr, hdrs []Hdr) (int, syscall.Errno) {
|
||||
return call(unix.SYS_RECVMSG_X, fd, hdrs)
|
||||
}
|
||||
|
||||
func call(trap, fd uintptr, hdrs []Hdr) (int, syscall.Errno) {
|
||||
if len(hdrs) == 0 {
|
||||
return 0, syscall.EINVAL
|
||||
}
|
||||
for {
|
||||
r, _, errno := unix.Syscall6(trap, fd, uintptr(unsafe.Pointer(&hdrs[0])), uintptr(len(hdrs)), 0, 0, 0)
|
||||
switch errno {
|
||||
case 0:
|
||||
return int(r), 0
|
||||
case syscall.EINTR:
|
||||
default:
|
||||
return 0, errno
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unsupported reports whether errno means the kernel or a sandbox refuses the private calls.
|
||||
func Unsupported(errno syscall.Errno) bool {
|
||||
return errno == syscall.ENOSYS || errno == syscall.EPERM || errno == syscall.EOPNOTSUPP
|
||||
}
|
||||
Reference in new issue
Block a user