Improve darwin tun performance

This commit is contained in:
世界 2025-07-17 17:33:41 +08:00
parent 7812930a48
commit aa1fd4d994
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
16 changed files with 192 additions and 115 deletions

View file

@ -14,9 +14,7 @@ import (
"github.com/sagernet/sing-tun/internal/stopfd_darwin"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/shell"
"golang.org/x/net/route"
@ -28,20 +26,21 @@ var _ DarwinTUN = (*NativeTun)(nil)
const PacketOffset = 4
type NativeTun struct {
tunFd int
tunFile *os.File
batchSize int
iovecs []iovecBuffer
iovecsOutput []iovecBuffer
msgHdrs []rawfile.MsgHdrX
msgHdrsOutput []rawfile.MsgHdrX
buffers []*buf.Buffer
stopFd stopfd.StopFD
tunWriter N.VectorisedWriter
options Options
inet4Address [4]byte
inet6Address [16]byte
routeSet bool
tunFd int
tunFile *os.File
batchSize int
iovecs []iovecBuffer
iovecsOutput []iovecBuffer
iovecsOutputDefault []unix.Iovec
msgHdrs []rawfile.MsgHdrX
msgHdrsOutput []rawfile.MsgHdrX
buffers []*buf.Buffer
stopFd stopfd.StopFD
options Options
inet4Address [4]byte
inet6Address [16]byte
routeSet bool
writeMsgX bool
}
type iovecBuffer struct {
@ -111,14 +110,14 @@ func New(options Options) (Tun, error) {
unix.Close(tunFd)
return nil, err
}
err = configure(tunFd, options.MTU, batchSize)
err = configure(tunFd, options.EXP_MultiPendingPackets, batchSize)
if err != nil {
unix.Close(tunFd)
return nil, err
}
} else {
tunFd = options.FileDescriptor
err := configure(tunFd, options.MTU, batchSize)
err := configure(tunFd, options.EXP_MultiPendingPackets, batchSize)
if err != nil {
return nil, err
}
@ -133,6 +132,7 @@ func New(options Options) (Tun, error) {
msgHdrs: make([]rawfile.MsgHdrX, batchSize),
msgHdrsOutput: make([]rawfile.MsgHdrX, batchSize),
stopFd: common.Must1(stopfd.New()),
writeMsgX: options.EXP_SendMsgX,
}
for i := 0; i < batchSize; i++ {
nativeTun.iovecs[i] = newIovecBuffer(int(options.MTU))
@ -144,11 +144,6 @@ func New(options Options) (Tun, error) {
if len(options.Inet6Address) > 0 {
nativeTun.inet6Address = options.Inet6Address[0].Addr().As16()
}
var ok bool
nativeTun.tunWriter, ok = bufio.CreateVectorisedWriter(nativeTun.tunFile)
if !ok {
panic("create vectorised writer")
}
return nativeTun, nil
}
@ -182,17 +177,6 @@ func init() {
packetHeaderVec6.SetLen(4)
}
func (t *NativeTun) WriteVectorised(buffers []*buf.Buffer) error {
var packetHeader []byte
switch header.IPVersion(buffers[0].Bytes()) {
case header.IPv4Version:
packetHeader = packetHeader4[:]
case header.IPv6Version:
packetHeader = packetHeader6[:]
}
return t.tunWriter.WriteVectorised(append([]*buf.Buffer{buf.As(packetHeader)}, buffers...))
}
const utunControlName = "com.apple.net.utun_control"
const (
@ -332,12 +316,12 @@ func create(tunFd int, ifIndex int, name string, options Options) error {
return nil
}
func configure(tunFd int, tunMTU uint32, batchSize int) error {
func configure(tunFd int, multiPendingPackets bool, batchSize int) error {
err := unix.SetNonblock(tunFd, true)
if err != nil {
return os.NewSyscallError("SetNonblock", err)
}
if tunMTU < 49152 {
if multiPendingPackets {
const UTUN_OPT_MAX_PENDING_PACKETS = 16
err = unix.SetsockoptInt(tunFd, 2, UTUN_OPT_MAX_PENDING_PACKETS, batchSize)
if err != nil {
@ -347,10 +331,6 @@ func configure(tunFd int, tunMTU uint32, batchSize int) error {
return nil
}
func (t *NativeTun) BatchSize() int {
return t.batchSize
}
func (t *NativeTun) BatchRead() ([]*buf.Buffer, error) {
for i := 0; i < t.batchSize; i++ {
iovecs := t.iovecs[i].nextIovecs()
@ -384,15 +364,27 @@ func (t *NativeTun) BatchRead() ([]*buf.Buffer, error) {
}
func (t *NativeTun) BatchWrite(buffers []*buf.Buffer) error {
for i, buffer := range buffers {
iovecs := t.iovecsOutput[i].nextIovecsOutput(buffer)
t.msgHdrsOutput[i] = rawfile.MsgHdrX{}
t.msgHdrsOutput[i].Msg.Iov = &iovecs[0]
t.msgHdrsOutput[i].Msg.Iovlen = 2
}
_, errno := rawfile.NonBlockingSendMMsg(t.tunFd, t.msgHdrsOutput[:len(buffers)])
if errno != 0 {
return errno
if !t.writeMsgX {
for i, buffer := range buffers {
t.iovecsOutput[i].nextIovecsOutput(buffer)
}
for i := range buffers {
errno := rawfile.NonBlockingWriteIovec(t.tunFd, t.iovecsOutput[i].iovecs)
if errno != 0 {
return errno
}
}
} else {
for i, buffer := range buffers {
iovecs := t.iovecsOutput[i].nextIovecsOutput(buffer)
t.msgHdrsOutput[i] = rawfile.MsgHdrX{}
t.msgHdrsOutput[i].Msg.Iov = &iovecs[0]
t.msgHdrsOutput[i].Msg.Iovlen = 2
}
_, errno := rawfile.NonBlockingSendMMsg(t.tunFd, t.msgHdrsOutput[:len(buffers)])
if errno != 0 {
return errno
}
}
return nil
}