mirror of
https://github.com/SagerNet/sing-tun.git
synced 2025-09-26 20:51:13 +08:00
Improve darwin tun performance
This commit is contained in:
@@ -168,6 +168,7 @@ type endpoint struct {
|
||||
mtu uint32
|
||||
|
||||
batchSize int
|
||||
sendMsgX bool
|
||||
}
|
||||
|
||||
// Options specify the details about the fd-based endpoint to be created.
|
||||
@@ -223,6 +224,9 @@ type Options struct {
|
||||
// ProcessorsPerChannel is the number of goroutines used to handle packets
|
||||
// from each FD.
|
||||
ProcessorsPerChannel int
|
||||
|
||||
MultiPendingPackets bool
|
||||
SendMsgX bool
|
||||
}
|
||||
|
||||
// New creates a new fd-based endpoint.
|
||||
@@ -261,6 +265,12 @@ func New(opts *Options) (stack.LinkEndpoint, error) {
|
||||
if opts.MaxSyscallHeaderBytes < 0 {
|
||||
return nil, fmt.Errorf("opts.MaxSyscallHeaderBytes is negative")
|
||||
}
|
||||
var batchSize int
|
||||
if opts.MultiPendingPackets {
|
||||
batchSize = int((512*1024)/(opts.MTU)) + 1
|
||||
} else {
|
||||
batchSize = 1
|
||||
}
|
||||
|
||||
e := &endpoint{
|
||||
mtu: opts.MTU,
|
||||
@@ -271,7 +281,8 @@ func New(opts *Options) (stack.LinkEndpoint, error) {
|
||||
packetDispatchMode: opts.PacketDispatchMode,
|
||||
maxSyscallHeaderBytes: uintptr(opts.MaxSyscallHeaderBytes),
|
||||
writevMaxIovs: rawfile.MaxIovs,
|
||||
batchSize: int((512*1024)/(opts.MTU)) + 1,
|
||||
batchSize: batchSize,
|
||||
sendMsgX: opts.SendMsgX,
|
||||
}
|
||||
if e.maxSyscallHeaderBytes != 0 {
|
||||
if max := int(e.maxSyscallHeaderBytes / rawfile.SizeofIovec); max < e.writevMaxIovs {
|
||||
@@ -478,7 +489,7 @@ func (e *endpoint) writePacket(pkt *stack.PacketBuffer) tcpip.Error {
|
||||
|
||||
func (e *endpoint) sendBatch(batchFDInfo fdInfo, pkts []*stack.PacketBuffer) (int, tcpip.Error) {
|
||||
// Degrade to writePacket if underlying fd is not a socket.
|
||||
if !batchFDInfo.isSocket {
|
||||
if !batchFDInfo.isSocket || !e.sendMsgX {
|
||||
var written int
|
||||
var err tcpip.Error
|
||||
for written < len(pkts) {
|
||||
|
@@ -116,7 +116,7 @@ func newRecvMMsgDispatcher(fd int, e *endpoint, opts *Options) (linkDispatcher,
|
||||
return nil, err
|
||||
}
|
||||
var batchSize int
|
||||
if opts.MTU < 49152 {
|
||||
if opts.MultiPendingPackets {
|
||||
batchSize = int((512*1024)/(opts.MTU)) + 1
|
||||
} else {
|
||||
batchSize = 1
|
||||
|
@@ -40,6 +40,7 @@ type GVisor struct {
|
||||
|
||||
type GVisorTun interface {
|
||||
Tun
|
||||
WritePacket(pkt *stack.PacketBuffer) (int, error)
|
||||
NewEndpoint() (stack.LinkEndpoint, stack.NICOptions, error)
|
||||
}
|
||||
|
||||
|
@@ -8,8 +8,6 @@ import (
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
var _ stack.LinkEndpoint = (*LinkEndpointFilter)(nil)
|
||||
@@ -17,7 +15,7 @@ var _ stack.LinkEndpoint = (*LinkEndpointFilter)(nil)
|
||||
type LinkEndpointFilter struct {
|
||||
stack.LinkEndpoint
|
||||
BroadcastAddress netip.Addr
|
||||
Writer N.VectorisedWriter
|
||||
Writer GVisorTun
|
||||
}
|
||||
|
||||
func (w *LinkEndpointFilter) Attach(dispatcher stack.NetworkDispatcher) {
|
||||
@@ -29,7 +27,7 @@ var _ stack.NetworkDispatcher = (*networkDispatcherFilter)(nil)
|
||||
type networkDispatcherFilter struct {
|
||||
stack.NetworkDispatcher
|
||||
broadcastAddress netip.Addr
|
||||
writer N.VectorisedWriter
|
||||
writer GVisorTun
|
||||
}
|
||||
|
||||
func (w *networkDispatcherFilter) DeliverNetworkPacket(protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
|
||||
@@ -49,7 +47,7 @@ func (w *networkDispatcherFilter) DeliverNetworkPacket(protocol tcpip.NetworkPro
|
||||
}
|
||||
destination := AddrFromAddress(network.DestinationAddress())
|
||||
if destination == w.broadcastAddress || !destination.IsGlobalUnicast() {
|
||||
_, _ = bufio.WriteVectorised(w.writer, pkt.AsSlices())
|
||||
w.writer.WritePacket(pkt)
|
||||
return
|
||||
}
|
||||
w.NetworkDispatcher.DeliverNetworkPacket(protocol, pkt)
|
||||
|
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip/checksum"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
@@ -56,7 +55,7 @@ func (f *TCPForwarder) HandlePacket(id stack.TransportEndpointID, pkt *stack.Pac
|
||||
tcpHdr.SetChecksum(^checksum.Checksum(tcpHdr.Payload(), tcpHdr.CalculateChecksum(
|
||||
header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddress(), ipHdr.DestinationAddress(), ipHdr.PayloadLength()),
|
||||
)))
|
||||
bufio.WriteVectorised(f.tun, pkt.AsSlices())
|
||||
f.tun.WritePacket(pkt)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -70,7 +69,7 @@ func (f *TCPForwarder) HandlePacket(id stack.TransportEndpointID, pkt *stack.Pac
|
||||
tcpHdr.SetChecksum(^checksum.Checksum(tcpHdr.Payload(), tcpHdr.CalculateChecksum(
|
||||
header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddress(), ipHdr.DestinationAddress(), ipHdr.PayloadLength()),
|
||||
)))
|
||||
bufio.WriteVectorised(f.tun, pkt.AsSlices())
|
||||
f.tun.WritePacket(pkt)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@@ -11,12 +11,12 @@ import (
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip/header"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type Mixed struct {
|
||||
*System
|
||||
tun GVisorTun
|
||||
stack *stack.Stack
|
||||
endpoint *channel.Endpoint
|
||||
}
|
||||
@@ -30,6 +30,7 @@ func NewMixed(
|
||||
}
|
||||
return &Mixed{
|
||||
System: system.(*System),
|
||||
tun: system.(*System).tun.(GVisorTun),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -77,7 +78,7 @@ func (m *Mixed) tunLoop() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if darwinTUN, isDarwinTUN := m.tun.(DarwinTUN); isDarwinTUN && m.mtu < 49152 {
|
||||
if darwinTUN, isDarwinTUN := m.tun.(DarwinTUN); isDarwinTUN && m.multiPendingPackets {
|
||||
m.batchLoopDarwin(darwinTUN)
|
||||
return
|
||||
}
|
||||
@@ -265,11 +266,11 @@ func (m *Mixed) processIPv6(ipHdr header.IPv6) (writeBack bool, err error) {
|
||||
|
||||
func (m *Mixed) packetLoop() {
|
||||
for {
|
||||
packet := m.endpoint.ReadContext(m.ctx)
|
||||
if packet == nil {
|
||||
pkt := m.endpoint.ReadContext(m.ctx)
|
||||
if pkt == nil {
|
||||
break
|
||||
}
|
||||
bufio.WriteVectorised(m.tun, packet.AsSlices())
|
||||
packet.DecRef()
|
||||
m.tun.WritePacket(pkt)
|
||||
pkt.DecRef()
|
||||
}
|
||||
}
|
||||
|
@@ -49,6 +49,7 @@ type System struct {
|
||||
interfaceFinder control.InterfaceFinder
|
||||
frontHeadroom int
|
||||
txChecksumOffload bool
|
||||
multiPendingPackets bool
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
@@ -74,6 +75,7 @@ func NewSystem(options StackOptions) (Stack, error) {
|
||||
broadcastAddr: BroadcastAddr(options.TunOptions.Inet4Address),
|
||||
bindInterface: options.ForwarderBindInterface,
|
||||
interfaceFinder: options.InterfaceFinder,
|
||||
multiPendingPackets: options.TunOptions.EXP_MultiPendingPackets,
|
||||
}
|
||||
if len(options.TunOptions.Inet4Address) > 0 {
|
||||
if !HasNextAddress(options.TunOptions.Inet4Address[0], 1) {
|
||||
@@ -174,7 +176,7 @@ func (s *System) tunLoop() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if darwinTUN, isDarwinTUN := s.tun.(DarwinTUN); isDarwinTUN && s.mtu < 49152 {
|
||||
if darwinTUN, isDarwinTUN := s.tun.(DarwinTUN); isDarwinTUN && s.multiPendingPackets {
|
||||
s.batchLoopDarwin(darwinTUN)
|
||||
return
|
||||
}
|
||||
@@ -320,6 +322,13 @@ func (s *System) acceptLoop(listener net.Listener) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = acceptConn(conn)
|
||||
if err != nil {
|
||||
s.logger.Error("set buffer for conn: ", err)
|
||||
_ = conn.Close()
|
||||
listener.Close()
|
||||
return
|
||||
}
|
||||
connPort := M.SocksaddrFromNet(conn.RemoteAddr()).Port
|
||||
session := s.tcpNat.LookupBack(connPort)
|
||||
if session == nil {
|
||||
|
26
stack_system_unix.go
Normal file
26
stack_system_unix.go
Normal file
@@ -0,0 +1,26 @@
|
||||
//go:build !windows
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing/common/control"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func acceptConn(conn net.Conn) error {
|
||||
return control.Conn(conn.(*net.TCPConn), func(fd uintptr) error {
|
||||
const bufferSize = 1024 * 1024
|
||||
oErr := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUF, bufferSize)
|
||||
if oErr != nil {
|
||||
return oErr
|
||||
}
|
||||
oErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUF, bufferSize)
|
||||
if oErr != nil {
|
||||
return oErr
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
@@ -2,6 +2,7 @@ package tun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -31,3 +32,7 @@ func fixWindowsFirewall() error {
|
||||
func retryableListenError(err error) bool {
|
||||
return errors.Is(err, windows.WSAEADDRNOTAVAIL)
|
||||
}
|
||||
|
||||
func acceptConn(conn net.Conn) error {
|
||||
return nil
|
||||
}
|
||||
|
8
tun.go
8
tun.go
@@ -25,7 +25,6 @@ type Handler interface {
|
||||
|
||||
type Tun interface {
|
||||
io.ReadWriter
|
||||
N.VectorisedWriter
|
||||
Name() (string, error)
|
||||
Start() error
|
||||
Close() error
|
||||
@@ -97,6 +96,13 @@ type Options struct {
|
||||
|
||||
// For library usages.
|
||||
EXP_DisableDNSHijack bool
|
||||
|
||||
// For gvisor stack, it should be enabled when MTU is less than 32768; otherwise it should be less than or equal to 8192.
|
||||
// The above condition is just an estimate and not exact, calculated on M4 pro.
|
||||
EXP_MultiPendingPackets bool
|
||||
|
||||
// Will cause the darwin network to die, do not use.
|
||||
EXP_SendMsgX bool
|
||||
}
|
||||
|
||||
func (o *Options) Inet4GatewayAddr() netip.Addr {
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -6,16 +6,43 @@ import (
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/link/qdisc/fifo"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/sing-tun/internal/fdbased_darwin"
|
||||
"github.com/sagernet/sing-tun/internal/rawfile_darwin"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var _ GVisorTun = (*NativeTun)(nil)
|
||||
|
||||
func (t *NativeTun) WritePacket(pkt *stack.PacketBuffer) (int, error) {
|
||||
iovecs := t.iovecsOutputDefault
|
||||
var dataLen int
|
||||
for _, packetSlice := range pkt.AsSlices() {
|
||||
dataLen += len(packetSlice)
|
||||
iovec := unix.Iovec{
|
||||
Base: &packetSlice[0],
|
||||
}
|
||||
iovec.SetLen(len(packetSlice))
|
||||
iovecs = append(iovecs, iovec)
|
||||
}
|
||||
if cap(iovecs) > cap(t.iovecsOutputDefault) {
|
||||
t.iovecsOutputDefault = iovecs[:0]
|
||||
}
|
||||
errno := rawfile.NonBlockingWriteIovec(t.tunFd, iovecs)
|
||||
if errno == 0 {
|
||||
return dataLen, nil
|
||||
} else {
|
||||
return 0, errno
|
||||
}
|
||||
}
|
||||
|
||||
func (t *NativeTun) NewEndpoint() (stack.LinkEndpoint, stack.NICOptions, error) {
|
||||
ep, err := fdbased.New(&fdbased.Options{
|
||||
FDs: []int{t.tunFd},
|
||||
MTU: t.options.MTU,
|
||||
RXChecksumOffload: true,
|
||||
PacketDispatchMode: fdbased.RecvMMsg,
|
||||
FDs: []int{t.tunFd},
|
||||
MTU: t.options.MTU,
|
||||
RXChecksumOffload: true,
|
||||
PacketDispatchMode: fdbased.RecvMMsg,
|
||||
MultiPendingPackets: t.options.EXP_MultiPendingPackets,
|
||||
SendMsgX: t.options.EXP_SendMsgX,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, stack.NICOptions{}, err
|
||||
|
53
tun_linux.go
53
tun_linux.go
@@ -18,10 +18,8 @@ import (
|
||||
"github.com/sagernet/sing-tun/internal/gtcpip/header"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/common/shell"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
@@ -32,22 +30,22 @@ import (
|
||||
var _ LinuxTUN = (*NativeTun)(nil)
|
||||
|
||||
type NativeTun struct {
|
||||
tunFd int
|
||||
tunFile *os.File
|
||||
tunWriter N.VectorisedWriter
|
||||
interfaceCallback *list.Element[DefaultInterfaceUpdateCallback]
|
||||
options Options
|
||||
ruleIndex6 []int
|
||||
readAccess sync.Mutex
|
||||
writeAccess sync.Mutex
|
||||
vnetHdr bool
|
||||
writeBuffer []byte
|
||||
gsoToWrite []int
|
||||
tcpGROTable *tcpGROTable
|
||||
udpGroAccess sync.Mutex
|
||||
udpGROTable *udpGROTable
|
||||
gro groDisablementFlags
|
||||
txChecksumOffload bool
|
||||
tunFd int
|
||||
tunFile *os.File
|
||||
iovecsOutputDefault []unix.Iovec
|
||||
interfaceCallback *list.Element[DefaultInterfaceUpdateCallback]
|
||||
options Options
|
||||
ruleIndex6 []int
|
||||
readAccess sync.Mutex
|
||||
writeAccess sync.Mutex
|
||||
vnetHdr bool
|
||||
writeBuffer []byte
|
||||
gsoToWrite []int
|
||||
tcpGROTable *tcpGROTable
|
||||
udpGroAccess sync.Mutex
|
||||
udpGROTable *udpGROTable
|
||||
gro groDisablementFlags
|
||||
txChecksumOffload bool
|
||||
}
|
||||
|
||||
func New(options Options) (Tun, error) {
|
||||
@@ -77,11 +75,6 @@ func New(options Options) (Tun, error) {
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
var ok bool
|
||||
nativeTun.tunWriter, ok = bufio.CreateVectorisedWriter(nativeTun.tunFile)
|
||||
if !ok {
|
||||
panic("create vectorised writer")
|
||||
}
|
||||
return nativeTun, nil
|
||||
}
|
||||
|
||||
@@ -402,20 +395,6 @@ func (t *NativeTun) Write(p []byte) (n int, err error) {
|
||||
return t.tunFile.Write(p)
|
||||
}
|
||||
|
||||
func (t *NativeTun) WriteVectorised(buffers []*buf.Buffer) error {
|
||||
if t.vnetHdr {
|
||||
n := buf.LenMulti(buffers)
|
||||
buffer := buf.NewSize(virtioNetHdrLen + n)
|
||||
buffer.Truncate(virtioNetHdrLen)
|
||||
buf.CopyMulti(buffer.Extend(n), buffers)
|
||||
_, err := t.tunFile.Write(buffer.Bytes())
|
||||
buffer.Release()
|
||||
return err
|
||||
} else {
|
||||
return t.tunWriter.WriteVectorised(buffers)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *NativeTun) FrontHeadroom() int {
|
||||
if t.vnetHdr {
|
||||
return virtioNetHdrLen
|
||||
|
@@ -3,8 +3,11 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"github.com/sagernet/gvisor/pkg/rawfile"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/link/fdbased"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -13,6 +16,28 @@ func init() {
|
||||
|
||||
var _ GVisorTun = (*NativeTun)(nil)
|
||||
|
||||
func (t *NativeTun) WritePacket(pkt *stack.PacketBuffer) (int, error) {
|
||||
iovecs := t.iovecsOutputDefault
|
||||
var dataLen int
|
||||
for _, packetSlice := range pkt.AsSlices() {
|
||||
dataLen += len(packetSlice)
|
||||
iovec := unix.Iovec{
|
||||
Base: &packetSlice[0],
|
||||
}
|
||||
iovec.SetLen(len(packetSlice))
|
||||
iovecs = append(iovecs, iovec)
|
||||
}
|
||||
if cap(iovecs) > cap(t.iovecsOutputDefault) {
|
||||
t.iovecsOutputDefault = iovecs[:0]
|
||||
}
|
||||
errno := rawfile.NonBlockingWriteIovec(t.tunFd, iovecs)
|
||||
if errno == 0 {
|
||||
return dataLen, nil
|
||||
} else {
|
||||
return 0, errno
|
||||
}
|
||||
}
|
||||
|
||||
func (t *NativeTun) NewEndpoint() (stack.LinkEndpoint, stack.NICOptions, error) {
|
||||
if t.vnetHdr {
|
||||
ep, err := fdbased.New(&fdbased.Options{
|
||||
|
@@ -17,7 +17,6 @@ import (
|
||||
"github.com/sagernet/sing-tun/internal/wintun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/windnsapi"
|
||||
|
||||
@@ -517,11 +516,6 @@ func (t *NativeTun) write(packetElementList [][]byte) (n int, err error) {
|
||||
return 0, fmt.Errorf("write failed: %w", err)
|
||||
}
|
||||
|
||||
func (t *NativeTun) WriteVectorised(buffers []*buf.Buffer) error {
|
||||
defer buf.ReleaseMulti(buffers)
|
||||
return common.Error(t.write(buf.ToSliceMulti(buffers)))
|
||||
}
|
||||
|
||||
func (t *NativeTun) Close() error {
|
||||
var err error
|
||||
t.closeOnce.Do(func() {
|
||||
|
@@ -11,6 +11,10 @@ import (
|
||||
|
||||
var _ GVisorTun = (*NativeTun)(nil)
|
||||
|
||||
func (t *NativeTun) WritePacket(pkt *stack.PacketBuffer) (int, error) {
|
||||
return t.write(pkt.AsSlices())
|
||||
}
|
||||
|
||||
func (t *NativeTun) NewEndpoint() (stack.LinkEndpoint, stack.NICOptions, error) {
|
||||
return &WintunEndpoint{tun: t}, stack.NICOptions{}, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user