mirror of
https://github.com/slackhq/nebula.git
synced 2026-08-16 15:57:03 +02:00
fix it!
This commit is contained in:
@@ -1,62 +0,0 @@
|
|||||||
//go:build !android && !e2e_testing
|
|
||||||
// +build !android,!e2e_testing
|
|
||||||
|
|
||||||
package udp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
// rawSendmmsg performs sendmmsg(2) over a syscall.RawConn without
|
|
||||||
// allocating a closure per call. The struct holds preallocated in/out
|
|
||||||
// scratch (chunk/sent/errno) and a method-value bound at construction so
|
|
||||||
// rawConn.Write receives a stable function pointer instead of a fresh
|
|
||||||
// closure on every send.
|
|
||||||
type rawSendmmsg struct {
|
|
||||||
msgs []rawMessage
|
|
||||||
chunk int
|
|
||||||
sent int
|
|
||||||
errno syscall.Errno
|
|
||||||
callback func(fd uintptr) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// bind wires r.callback to r.run. Must be called once after r.msgs is set;
|
|
||||||
// subsequent send calls invoke r.callback without rebinding.
|
|
||||||
func (r *rawSendmmsg) bind() { r.callback = r.run }
|
|
||||||
|
|
||||||
// run is the preallocated callback rawConn.Write invokes. It reads its
|
|
||||||
// input (r.chunk) and writes its outputs (r.sent, r.errno) through the
|
|
||||||
// rawSendmmsg fields so the method value does not capture per-call locals
|
|
||||||
// and therefore does not heap-allocate.
|
|
||||||
func (r *rawSendmmsg) run(fd uintptr) bool {
|
|
||||||
r1, _, errno := unix.Syscall6(unix.SYS_SENDMMSG, fd,
|
|
||||||
uintptr(unsafe.Pointer(&r.msgs[0])), uintptr(r.chunk),
|
|
||||||
0, 0, 0,
|
|
||||||
)
|
|
||||||
if errno == syscall.EAGAIN || errno == syscall.EWOULDBLOCK {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
r.sent = int(r1)
|
|
||||||
r.errno = errno
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// send issues sendmmsg over rc against the first n entries of r.msgs.
|
|
||||||
// Returns the number of entries the kernel processed and any error;
|
|
||||||
// matches the original sendmmsg helper's contract.
|
|
||||||
func (r *rawSendmmsg) send(rc syscall.RawConn, n int) (int, error) {
|
|
||||||
r.chunk = n
|
|
||||||
r.sent = 0
|
|
||||||
r.errno = 0
|
|
||||||
if err := rc.Write(r.callback); err != nil {
|
|
||||||
return r.sent, err
|
|
||||||
}
|
|
||||||
if r.errno != 0 {
|
|
||||||
return r.sent, &net.OpError{Op: "sendmmsg", Err: r.errno}
|
|
||||||
}
|
|
||||||
return r.sent, nil
|
|
||||||
}
|
|
||||||
+19
-46
@@ -50,10 +50,6 @@ type StdConn struct {
|
|||||||
// into mmsghdr entry e. Used to rewind `i` on partial sendmmsg success.
|
// into mmsghdr entry e. Used to rewind `i` on partial sendmmsg success.
|
||||||
writeEntryEnd []int
|
writeEntryEnd []int
|
||||||
|
|
||||||
// rawSend wraps the sendmmsg(2) callback in a closure-free helper so
|
|
||||||
// the hot path doesn't heap-allocate a fresh closure per call.
|
|
||||||
rawSend rawSendmmsg
|
|
||||||
|
|
||||||
// UDP GSO (sendmsg with UDP_SEGMENT cmsg) support. gsoSupported is
|
// UDP GSO (sendmsg with UDP_SEGMENT cmsg) support. gsoSupported is
|
||||||
// probed once at socket creation. When true, WriteBatch packs same-
|
// probed once at socket creation. When true, WriteBatch packs same-
|
||||||
// destination consecutive packets into a single sendmmsg entry with a
|
// destination consecutive packets into a single sendmmsg entry with a
|
||||||
@@ -115,8 +111,6 @@ func NewListener(l *slog.Logger, ip netip.Addr, port int, multi bool, batch int)
|
|||||||
out := &StdConn{sysFd: fd, isV4: ip.Is4(), l: l, batch: batch}
|
out := &StdConn{sysFd: fd, isV4: ip.Is4(), l: l, batch: batch}
|
||||||
|
|
||||||
out.prepareWriteMessages(MaxWriteBatch)
|
out.prepareWriteMessages(MaxWriteBatch)
|
||||||
out.rawSend.msgs = out.writeMsgs
|
|
||||||
out.rawSend.bind()
|
|
||||||
|
|
||||||
out.prepareGSO()
|
out.prepareGSO()
|
||||||
// GRO delivers coalesced superpackets that need a cmsg to split back
|
// GRO delivers coalesced superpackets that need a cmsg to split back
|
||||||
@@ -203,19 +197,11 @@ const maxGSOBytes = 65000
|
|||||||
func (u *StdConn) prepareGSO() {
|
func (u *StdConn) prepareGSO() {
|
||||||
u.maxGSOSegments = 63 //gotta be one less than the max so we can still attach a header
|
u.maxGSOSegments = 63 //gotta be one less than the max so we can still attach a header
|
||||||
|
|
||||||
var probeErr error
|
if err := unix.SetsockoptInt(u.sysFd, unix.IPPROTO_UDP, unix.UDP_SEGMENT, 0); err != nil {
|
||||||
if err := u.rawConn.Control(func(fd uintptr) {
|
|
||||||
probeErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_SEGMENT, 0)
|
|
||||||
}); err != nil {
|
|
||||||
u.l.Info("udp: GSO disabled", "reason", "rawconn control failed", "error", err)
|
u.l.Info("udp: GSO disabled", "reason", "rawconn control failed", "error", err)
|
||||||
recordCapability("udp.gso.enabled", false)
|
recordCapability("udp.gso.enabled", false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if probeErr != nil {
|
|
||||||
u.l.Info("udp: GSO disabled", "reason", "kernel rejected probe", "error", probeErr)
|
|
||||||
recordCapability("udp.gso.enabled", false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var un unix.Utsname
|
var un unix.Utsname
|
||||||
if err := unix.Uname(&un); err != nil {
|
if err := unix.Uname(&un); err != nil {
|
||||||
@@ -262,16 +248,9 @@ const udpGROCmsgPayload = 4
|
|||||||
// datagrams into one recvmmsg entry, with a cmsg carrying the gso_size used
|
// datagrams into one recvmmsg entry, with a cmsg carrying the gso_size used
|
||||||
// to split them back apart on the application side.
|
// to split them back apart on the application side.
|
||||||
func (u *StdConn) prepareGRO() {
|
func (u *StdConn) prepareGRO() {
|
||||||
var probeErr error
|
err := unix.SetsockoptInt(u.sysFd, unix.IPPROTO_UDP, unix.UDP_GRO, 1)
|
||||||
if err := u.rawConn.Control(func(fd uintptr) {
|
if err != nil {
|
||||||
probeErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_GRO, 1)
|
u.l.Info("udp: GRO disabled", "reason", "kernel rejected probe", "error", err)
|
||||||
}); err != nil {
|
|
||||||
u.l.Info("udp: GRO disabled", "reason", "rawconn control failed", "error", err)
|
|
||||||
recordCapability("udp.gro.enabled", false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if probeErr != nil {
|
|
||||||
u.l.Info("udp: GRO disabled", "reason", "kernel rejected probe", "error", probeErr)
|
|
||||||
recordCapability("udp.gro.enabled", false)
|
recordCapability("udp.gro.enabled", false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -287,15 +266,10 @@ func (u *StdConn) prepareGRO() {
|
|||||||
// Best-effort: we keep going on failure.
|
// Best-effort: we keep going on failure.
|
||||||
func (u *StdConn) prepareECNRecv() {
|
func (u *StdConn) prepareECNRecv() {
|
||||||
var v4err, v6err error
|
var v4err, v6err error
|
||||||
if err := u.rawConn.Control(func(fd uintptr) {
|
|
||||||
v4err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_RECVTOS, 1)
|
v4err = unix.SetsockoptInt(u.sysFd, unix.IPPROTO_IP, unix.IP_RECVTOS, 1)
|
||||||
if !u.isV4 {
|
if !u.isV4 {
|
||||||
v6err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_RECVTCLASS, 1)
|
v6err = unix.SetsockoptInt(u.sysFd, unix.IPPROTO_IPV6, unix.IPV6_RECVTCLASS, 1)
|
||||||
}
|
|
||||||
}); err != nil {
|
|
||||||
u.l.Info("udp: outer-ECN RX disabled", "reason", "rawconn control failed", "error", err)
|
|
||||||
recordCapability("udp.ecn_rx.enabled", false)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if u.isV4 { //only check the V4 attempt
|
if u.isV4 { //only check the V4 attempt
|
||||||
if v4err != nil {
|
if v4err != nil {
|
||||||
@@ -507,15 +481,6 @@ func (u *StdConn) ListenOut(r EncReader, flush func()) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// headerCounter returns the big-endian uint64 message counter at bytes
|
|
||||||
// [8:16] of a nebula packet, or 0 if the buffer is too short.
|
|
||||||
func headerCounter(buf []byte) uint64 {
|
|
||||||
if len(buf) < 16 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return binary.BigEndian.Uint64(buf[8:16])
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseRecvCmsg walks the per-slot ancillary buffer once and extracts up to
|
// parseRecvCmsg walks the per-slot ancillary buffer once and extracts up to
|
||||||
// two values of interest in a single pass: the UDP_GRO gso_size (when
|
// two values of interest in a single pass: the UDP_GRO gso_size (when
|
||||||
// wantGRO is true) and the outer IP-level ECN codepoint stamped on the
|
// wantGRO is true) and the outer IP-level ECN codepoint stamped on the
|
||||||
@@ -873,10 +838,18 @@ func (u *StdConn) writeEntryCmsg(entry, runLen, segSize int, ecn byte, dstIsV4 b
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sendmmsg issues sendmmsg(2) over u.rawConn against the first n entries
|
// sendmmsg issues sendmmsg(2) over u.rawConn against the first n entries
|
||||||
// of u.writeMsgs. Routes through u.rawSend so the per-call kernel callback
|
// of u.writeMsgs.
|
||||||
// stays alloc-free.
|
|
||||||
func (u *StdConn) sendmmsg(n int) (int, error) {
|
func (u *StdConn) sendmmsg(n int) (int, error) {
|
||||||
return u.rawSend.send(u.rawConn, n)
|
r1, _, errno := unix.Syscall6(unix.SYS_SENDMMSG, uintptr(u.sysFd),
|
||||||
|
uintptr(unsafe.Pointer(&u.writeMsgs[0])), uintptr(n),
|
||||||
|
0, 0, 0,
|
||||||
|
)
|
||||||
|
sent := int(r1)
|
||||||
|
|
||||||
|
if errno != 0 {
|
||||||
|
return sent, &net.OpError{Op: "sendmmsg", Err: errno}
|
||||||
|
}
|
||||||
|
return sent, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeSockaddr encodes addr into buf (which must be at least
|
// writeSockaddr encodes addr into buf (which must be at least
|
||||||
|
|||||||
Reference in New Issue
Block a user