mirror of
https://github.com/SagerNet/sing-tun.git
synced 2025-09-27 04:55:53 +08:00
Improve darwin tun performance
This commit is contained in:
@@ -168,6 +168,7 @@ type endpoint struct {
|
|||||||
mtu uint32
|
mtu uint32
|
||||||
|
|
||||||
batchSize int
|
batchSize int
|
||||||
|
sendMsgX bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options specify the details about the fd-based endpoint to be created.
|
// 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
|
// ProcessorsPerChannel is the number of goroutines used to handle packets
|
||||||
// from each FD.
|
// from each FD.
|
||||||
ProcessorsPerChannel int
|
ProcessorsPerChannel int
|
||||||
|
|
||||||
|
MultiPendingPackets bool
|
||||||
|
SendMsgX bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new fd-based endpoint.
|
// New creates a new fd-based endpoint.
|
||||||
@@ -261,6 +265,12 @@ func New(opts *Options) (stack.LinkEndpoint, error) {
|
|||||||
if opts.MaxSyscallHeaderBytes < 0 {
|
if opts.MaxSyscallHeaderBytes < 0 {
|
||||||
return nil, fmt.Errorf("opts.MaxSyscallHeaderBytes is negative")
|
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{
|
e := &endpoint{
|
||||||
mtu: opts.MTU,
|
mtu: opts.MTU,
|
||||||
@@ -271,7 +281,8 @@ func New(opts *Options) (stack.LinkEndpoint, error) {
|
|||||||
packetDispatchMode: opts.PacketDispatchMode,
|
packetDispatchMode: opts.PacketDispatchMode,
|
||||||
maxSyscallHeaderBytes: uintptr(opts.MaxSyscallHeaderBytes),
|
maxSyscallHeaderBytes: uintptr(opts.MaxSyscallHeaderBytes),
|
||||||
writevMaxIovs: rawfile.MaxIovs,
|
writevMaxIovs: rawfile.MaxIovs,
|
||||||
batchSize: int((512*1024)/(opts.MTU)) + 1,
|
batchSize: batchSize,
|
||||||
|
sendMsgX: opts.SendMsgX,
|
||||||
}
|
}
|
||||||
if e.maxSyscallHeaderBytes != 0 {
|
if e.maxSyscallHeaderBytes != 0 {
|
||||||
if max := int(e.maxSyscallHeaderBytes / rawfile.SizeofIovec); max < e.writevMaxIovs {
|
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) {
|
func (e *endpoint) sendBatch(batchFDInfo fdInfo, pkts []*stack.PacketBuffer) (int, tcpip.Error) {
|
||||||
// Degrade to writePacket if underlying fd is not a socket.
|
// Degrade to writePacket if underlying fd is not a socket.
|
||||||
if !batchFDInfo.isSocket {
|
if !batchFDInfo.isSocket || !e.sendMsgX {
|
||||||
var written int
|
var written int
|
||||||
var err tcpip.Error
|
var err tcpip.Error
|
||||||
for written < len(pkts) {
|
for written < len(pkts) {
|
||||||
|
@@ -116,7 +116,7 @@ func newRecvMMsgDispatcher(fd int, e *endpoint, opts *Options) (linkDispatcher,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var batchSize int
|
var batchSize int
|
||||||
if opts.MTU < 49152 {
|
if opts.MultiPendingPackets {
|
||||||
batchSize = int((512*1024)/(opts.MTU)) + 1
|
batchSize = int((512*1024)/(opts.MTU)) + 1
|
||||||
} else {
|
} else {
|
||||||
batchSize = 1
|
batchSize = 1
|
||||||
|
@@ -40,6 +40,7 @@ type GVisor struct {
|
|||||||
|
|
||||||
type GVisorTun interface {
|
type GVisorTun interface {
|
||||||
Tun
|
Tun
|
||||||
|
WritePacket(pkt *stack.PacketBuffer) (int, error)
|
||||||
NewEndpoint() (stack.LinkEndpoint, stack.NICOptions, error)
|
NewEndpoint() (stack.LinkEndpoint, stack.NICOptions, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,8 +8,6 @@ import (
|
|||||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
"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)
|
var _ stack.LinkEndpoint = (*LinkEndpointFilter)(nil)
|
||||||
@@ -17,7 +15,7 @@ var _ stack.LinkEndpoint = (*LinkEndpointFilter)(nil)
|
|||||||
type LinkEndpointFilter struct {
|
type LinkEndpointFilter struct {
|
||||||
stack.LinkEndpoint
|
stack.LinkEndpoint
|
||||||
BroadcastAddress netip.Addr
|
BroadcastAddress netip.Addr
|
||||||
Writer N.VectorisedWriter
|
Writer GVisorTun
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *LinkEndpointFilter) Attach(dispatcher stack.NetworkDispatcher) {
|
func (w *LinkEndpointFilter) Attach(dispatcher stack.NetworkDispatcher) {
|
||||||
@@ -29,7 +27,7 @@ var _ stack.NetworkDispatcher = (*networkDispatcherFilter)(nil)
|
|||||||
type networkDispatcherFilter struct {
|
type networkDispatcherFilter struct {
|
||||||
stack.NetworkDispatcher
|
stack.NetworkDispatcher
|
||||||
broadcastAddress netip.Addr
|
broadcastAddress netip.Addr
|
||||||
writer N.VectorisedWriter
|
writer GVisorTun
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *networkDispatcherFilter) DeliverNetworkPacket(protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
|
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())
|
destination := AddrFromAddress(network.DestinationAddress())
|
||||||
if destination == w.broadcastAddress || !destination.IsGlobalUnicast() {
|
if destination == w.broadcastAddress || !destination.IsGlobalUnicast() {
|
||||||
_, _ = bufio.WriteVectorised(w.writer, pkt.AsSlices())
|
w.writer.WritePacket(pkt)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.NetworkDispatcher.DeliverNetworkPacket(protocol, pkt)
|
w.NetworkDispatcher.DeliverNetworkPacket(protocol, pkt)
|
||||||
|
@@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
|
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
|
||||||
"github.com/sagernet/sing-tun/internal/gtcpip/checksum"
|
"github.com/sagernet/sing-tun/internal/gtcpip/checksum"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
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(
|
tcpHdr.SetChecksum(^checksum.Checksum(tcpHdr.Payload(), tcpHdr.CalculateChecksum(
|
||||||
header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddress(), ipHdr.DestinationAddress(), ipHdr.PayloadLength()),
|
header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddress(), ipHdr.DestinationAddress(), ipHdr.PayloadLength()),
|
||||||
)))
|
)))
|
||||||
bufio.WriteVectorised(f.tun, pkt.AsSlices())
|
f.tun.WritePacket(pkt)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,7 +69,7 @@ func (f *TCPForwarder) HandlePacket(id stack.TransportEndpointID, pkt *stack.Pac
|
|||||||
tcpHdr.SetChecksum(^checksum.Checksum(tcpHdr.Payload(), tcpHdr.CalculateChecksum(
|
tcpHdr.SetChecksum(^checksum.Checksum(tcpHdr.Payload(), tcpHdr.CalculateChecksum(
|
||||||
header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddress(), ipHdr.DestinationAddress(), ipHdr.PayloadLength()),
|
header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddress(), ipHdr.DestinationAddress(), ipHdr.PayloadLength()),
|
||||||
)))
|
)))
|
||||||
bufio.WriteVectorised(f.tun, pkt.AsSlices())
|
f.tun.WritePacket(pkt)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,12 +11,12 @@ import (
|
|||||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
|
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
|
||||||
"github.com/sagernet/sing-tun/internal/gtcpip/header"
|
"github.com/sagernet/sing-tun/internal/gtcpip/header"
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mixed struct {
|
type Mixed struct {
|
||||||
*System
|
*System
|
||||||
|
tun GVisorTun
|
||||||
stack *stack.Stack
|
stack *stack.Stack
|
||||||
endpoint *channel.Endpoint
|
endpoint *channel.Endpoint
|
||||||
}
|
}
|
||||||
@@ -30,6 +30,7 @@ func NewMixed(
|
|||||||
}
|
}
|
||||||
return &Mixed{
|
return &Mixed{
|
||||||
System: system.(*System),
|
System: system.(*System),
|
||||||
|
tun: system.(*System).tun.(GVisorTun),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +78,7 @@ func (m *Mixed) tunLoop() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if darwinTUN, isDarwinTUN := m.tun.(DarwinTUN); isDarwinTUN && m.mtu < 49152 {
|
if darwinTUN, isDarwinTUN := m.tun.(DarwinTUN); isDarwinTUN && m.multiPendingPackets {
|
||||||
m.batchLoopDarwin(darwinTUN)
|
m.batchLoopDarwin(darwinTUN)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -265,11 +266,11 @@ func (m *Mixed) processIPv6(ipHdr header.IPv6) (writeBack bool, err error) {
|
|||||||
|
|
||||||
func (m *Mixed) packetLoop() {
|
func (m *Mixed) packetLoop() {
|
||||||
for {
|
for {
|
||||||
packet := m.endpoint.ReadContext(m.ctx)
|
pkt := m.endpoint.ReadContext(m.ctx)
|
||||||
if packet == nil {
|
if pkt == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
bufio.WriteVectorised(m.tun, packet.AsSlices())
|
m.tun.WritePacket(pkt)
|
||||||
packet.DecRef()
|
pkt.DecRef()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -49,6 +49,7 @@ type System struct {
|
|||||||
interfaceFinder control.InterfaceFinder
|
interfaceFinder control.InterfaceFinder
|
||||||
frontHeadroom int
|
frontHeadroom int
|
||||||
txChecksumOffload bool
|
txChecksumOffload bool
|
||||||
|
multiPendingPackets bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
@@ -74,6 +75,7 @@ func NewSystem(options StackOptions) (Stack, error) {
|
|||||||
broadcastAddr: BroadcastAddr(options.TunOptions.Inet4Address),
|
broadcastAddr: BroadcastAddr(options.TunOptions.Inet4Address),
|
||||||
bindInterface: options.ForwarderBindInterface,
|
bindInterface: options.ForwarderBindInterface,
|
||||||
interfaceFinder: options.InterfaceFinder,
|
interfaceFinder: options.InterfaceFinder,
|
||||||
|
multiPendingPackets: options.TunOptions.EXP_MultiPendingPackets,
|
||||||
}
|
}
|
||||||
if len(options.TunOptions.Inet4Address) > 0 {
|
if len(options.TunOptions.Inet4Address) > 0 {
|
||||||
if !HasNextAddress(options.TunOptions.Inet4Address[0], 1) {
|
if !HasNextAddress(options.TunOptions.Inet4Address[0], 1) {
|
||||||
@@ -174,7 +176,7 @@ func (s *System) tunLoop() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if darwinTUN, isDarwinTUN := s.tun.(DarwinTUN); isDarwinTUN && s.mtu < 49152 {
|
if darwinTUN, isDarwinTUN := s.tun.(DarwinTUN); isDarwinTUN && s.multiPendingPackets {
|
||||||
s.batchLoopDarwin(darwinTUN)
|
s.batchLoopDarwin(darwinTUN)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -320,6 +322,13 @@ func (s *System) acceptLoop(listener net.Listener) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
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
|
connPort := M.SocksaddrFromNet(conn.RemoteAddr()).Port
|
||||||
session := s.tcpNat.LookupBack(connPort)
|
session := s.tcpNat.LookupBack(connPort)
|
||||||
if session == nil {
|
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 (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
@@ -31,3 +32,7 @@ func fixWindowsFirewall() error {
|
|||||||
func retryableListenError(err error) bool {
|
func retryableListenError(err error) bool {
|
||||||
return errors.Is(err, windows.WSAEADDRNOTAVAIL)
|
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 {
|
type Tun interface {
|
||||||
io.ReadWriter
|
io.ReadWriter
|
||||||
N.VectorisedWriter
|
|
||||||
Name() (string, error)
|
Name() (string, error)
|
||||||
Start() error
|
Start() error
|
||||||
Close() error
|
Close() error
|
||||||
@@ -97,6 +96,13 @@ type Options struct {
|
|||||||
|
|
||||||
// For library usages.
|
// For library usages.
|
||||||
EXP_DisableDNSHijack bool
|
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 {
|
func (o *Options) Inet4GatewayAddr() netip.Addr {
|
||||||
|
@@ -14,9 +14,7 @@ import (
|
|||||||
"github.com/sagernet/sing-tun/internal/stopfd_darwin"
|
"github.com/sagernet/sing-tun/internal/stopfd_darwin"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
"github.com/sagernet/sing/common/shell"
|
"github.com/sagernet/sing/common/shell"
|
||||||
|
|
||||||
"golang.org/x/net/route"
|
"golang.org/x/net/route"
|
||||||
@@ -33,15 +31,16 @@ type NativeTun struct {
|
|||||||
batchSize int
|
batchSize int
|
||||||
iovecs []iovecBuffer
|
iovecs []iovecBuffer
|
||||||
iovecsOutput []iovecBuffer
|
iovecsOutput []iovecBuffer
|
||||||
|
iovecsOutputDefault []unix.Iovec
|
||||||
msgHdrs []rawfile.MsgHdrX
|
msgHdrs []rawfile.MsgHdrX
|
||||||
msgHdrsOutput []rawfile.MsgHdrX
|
msgHdrsOutput []rawfile.MsgHdrX
|
||||||
buffers []*buf.Buffer
|
buffers []*buf.Buffer
|
||||||
stopFd stopfd.StopFD
|
stopFd stopfd.StopFD
|
||||||
tunWriter N.VectorisedWriter
|
|
||||||
options Options
|
options Options
|
||||||
inet4Address [4]byte
|
inet4Address [4]byte
|
||||||
inet6Address [16]byte
|
inet6Address [16]byte
|
||||||
routeSet bool
|
routeSet bool
|
||||||
|
writeMsgX bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type iovecBuffer struct {
|
type iovecBuffer struct {
|
||||||
@@ -111,14 +110,14 @@ func New(options Options) (Tun, error) {
|
|||||||
unix.Close(tunFd)
|
unix.Close(tunFd)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = configure(tunFd, options.MTU, batchSize)
|
err = configure(tunFd, options.EXP_MultiPendingPackets, batchSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
unix.Close(tunFd)
|
unix.Close(tunFd)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tunFd = options.FileDescriptor
|
tunFd = options.FileDescriptor
|
||||||
err := configure(tunFd, options.MTU, batchSize)
|
err := configure(tunFd, options.EXP_MultiPendingPackets, batchSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -133,6 +132,7 @@ func New(options Options) (Tun, error) {
|
|||||||
msgHdrs: make([]rawfile.MsgHdrX, batchSize),
|
msgHdrs: make([]rawfile.MsgHdrX, batchSize),
|
||||||
msgHdrsOutput: make([]rawfile.MsgHdrX, batchSize),
|
msgHdrsOutput: make([]rawfile.MsgHdrX, batchSize),
|
||||||
stopFd: common.Must1(stopfd.New()),
|
stopFd: common.Must1(stopfd.New()),
|
||||||
|
writeMsgX: options.EXP_SendMsgX,
|
||||||
}
|
}
|
||||||
for i := 0; i < batchSize; i++ {
|
for i := 0; i < batchSize; i++ {
|
||||||
nativeTun.iovecs[i] = newIovecBuffer(int(options.MTU))
|
nativeTun.iovecs[i] = newIovecBuffer(int(options.MTU))
|
||||||
@@ -144,11 +144,6 @@ func New(options Options) (Tun, error) {
|
|||||||
if len(options.Inet6Address) > 0 {
|
if len(options.Inet6Address) > 0 {
|
||||||
nativeTun.inet6Address = options.Inet6Address[0].Addr().As16()
|
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
|
return nativeTun, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,17 +177,6 @@ func init() {
|
|||||||
packetHeaderVec6.SetLen(4)
|
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 utunControlName = "com.apple.net.utun_control"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -332,12 +316,12 @@ func create(tunFd int, ifIndex int, name string, options Options) error {
|
|||||||
return nil
|
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)
|
err := unix.SetNonblock(tunFd, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return os.NewSyscallError("SetNonblock", err)
|
return os.NewSyscallError("SetNonblock", err)
|
||||||
}
|
}
|
||||||
if tunMTU < 49152 {
|
if multiPendingPackets {
|
||||||
const UTUN_OPT_MAX_PENDING_PACKETS = 16
|
const UTUN_OPT_MAX_PENDING_PACKETS = 16
|
||||||
err = unix.SetsockoptInt(tunFd, 2, UTUN_OPT_MAX_PENDING_PACKETS, batchSize)
|
err = unix.SetsockoptInt(tunFd, 2, UTUN_OPT_MAX_PENDING_PACKETS, batchSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -347,10 +331,6 @@ func configure(tunFd int, tunMTU uint32, batchSize int) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *NativeTun) BatchSize() int {
|
|
||||||
return t.batchSize
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *NativeTun) BatchRead() ([]*buf.Buffer, error) {
|
func (t *NativeTun) BatchRead() ([]*buf.Buffer, error) {
|
||||||
for i := 0; i < t.batchSize; i++ {
|
for i := 0; i < t.batchSize; i++ {
|
||||||
iovecs := t.iovecs[i].nextIovecs()
|
iovecs := t.iovecs[i].nextIovecs()
|
||||||
@@ -384,6 +364,17 @@ func (t *NativeTun) BatchRead() ([]*buf.Buffer, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *NativeTun) BatchWrite(buffers []*buf.Buffer) error {
|
func (t *NativeTun) BatchWrite(buffers []*buf.Buffer) error {
|
||||||
|
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 {
|
for i, buffer := range buffers {
|
||||||
iovecs := t.iovecsOutput[i].nextIovecsOutput(buffer)
|
iovecs := t.iovecsOutput[i].nextIovecsOutput(buffer)
|
||||||
t.msgHdrsOutput[i] = rawfile.MsgHdrX{}
|
t.msgHdrsOutput[i] = rawfile.MsgHdrX{}
|
||||||
@@ -394,6 +385,7 @@ func (t *NativeTun) BatchWrite(buffers []*buf.Buffer) error {
|
|||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return errno
|
return errno
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,16 +6,43 @@ import (
|
|||||||
"github.com/sagernet/gvisor/pkg/tcpip/link/qdisc/fifo"
|
"github.com/sagernet/gvisor/pkg/tcpip/link/qdisc/fifo"
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||||
"github.com/sagernet/sing-tun/internal/fdbased_darwin"
|
"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)
|
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) {
|
func (t *NativeTun) NewEndpoint() (stack.LinkEndpoint, stack.NICOptions, error) {
|
||||||
ep, err := fdbased.New(&fdbased.Options{
|
ep, err := fdbased.New(&fdbased.Options{
|
||||||
FDs: []int{t.tunFd},
|
FDs: []int{t.tunFd},
|
||||||
MTU: t.options.MTU,
|
MTU: t.options.MTU,
|
||||||
RXChecksumOffload: true,
|
RXChecksumOffload: true,
|
||||||
PacketDispatchMode: fdbased.RecvMMsg,
|
PacketDispatchMode: fdbased.RecvMMsg,
|
||||||
|
MultiPendingPackets: t.options.EXP_MultiPendingPackets,
|
||||||
|
SendMsgX: t.options.EXP_SendMsgX,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, stack.NICOptions{}, err
|
return nil, stack.NICOptions{}, err
|
||||||
|
23
tun_linux.go
23
tun_linux.go
@@ -18,10 +18,8 @@ import (
|
|||||||
"github.com/sagernet/sing-tun/internal/gtcpip/header"
|
"github.com/sagernet/sing-tun/internal/gtcpip/header"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
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/rw"
|
||||||
"github.com/sagernet/sing/common/shell"
|
"github.com/sagernet/sing/common/shell"
|
||||||
"github.com/sagernet/sing/common/x/list"
|
"github.com/sagernet/sing/common/x/list"
|
||||||
@@ -34,7 +32,7 @@ var _ LinuxTUN = (*NativeTun)(nil)
|
|||||||
type NativeTun struct {
|
type NativeTun struct {
|
||||||
tunFd int
|
tunFd int
|
||||||
tunFile *os.File
|
tunFile *os.File
|
||||||
tunWriter N.VectorisedWriter
|
iovecsOutputDefault []unix.Iovec
|
||||||
interfaceCallback *list.Element[DefaultInterfaceUpdateCallback]
|
interfaceCallback *list.Element[DefaultInterfaceUpdateCallback]
|
||||||
options Options
|
options Options
|
||||||
ruleIndex6 []int
|
ruleIndex6 []int
|
||||||
@@ -77,11 +75,6 @@ func New(options Options) (Tun, error) {
|
|||||||
options: options,
|
options: options,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var ok bool
|
|
||||||
nativeTun.tunWriter, ok = bufio.CreateVectorisedWriter(nativeTun.tunFile)
|
|
||||||
if !ok {
|
|
||||||
panic("create vectorised writer")
|
|
||||||
}
|
|
||||||
return nativeTun, nil
|
return nativeTun, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,20 +395,6 @@ func (t *NativeTun) Write(p []byte) (n int, err error) {
|
|||||||
return t.tunFile.Write(p)
|
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 {
|
func (t *NativeTun) FrontHeadroom() int {
|
||||||
if t.vnetHdr {
|
if t.vnetHdr {
|
||||||
return virtioNetHdrLen
|
return virtioNetHdrLen
|
||||||
|
@@ -3,8 +3,11 @@
|
|||||||
package tun
|
package tun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/sagernet/gvisor/pkg/rawfile"
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/link/fdbased"
|
"github.com/sagernet/gvisor/pkg/tcpip/link/fdbased"
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -13,6 +16,28 @@ func init() {
|
|||||||
|
|
||||||
var _ GVisorTun = (*NativeTun)(nil)
|
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) {
|
func (t *NativeTun) NewEndpoint() (stack.LinkEndpoint, stack.NICOptions, error) {
|
||||||
if t.vnetHdr {
|
if t.vnetHdr {
|
||||||
ep, err := fdbased.New(&fdbased.Options{
|
ep, err := fdbased.New(&fdbased.Options{
|
||||||
|
@@ -17,7 +17,6 @@ import (
|
|||||||
"github.com/sagernet/sing-tun/internal/wintun"
|
"github.com/sagernet/sing-tun/internal/wintun"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/atomic"
|
"github.com/sagernet/sing/common/atomic"
|
||||||
"github.com/sagernet/sing/common/buf"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/windnsapi"
|
"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)
|
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 {
|
func (t *NativeTun) Close() error {
|
||||||
var err error
|
var err error
|
||||||
t.closeOnce.Do(func() {
|
t.closeOnce.Do(func() {
|
||||||
|
@@ -11,6 +11,10 @@ import (
|
|||||||
|
|
||||||
var _ GVisorTun = (*NativeTun)(nil)
|
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) {
|
func (t *NativeTun) NewEndpoint() (stack.LinkEndpoint, stack.NICOptions, error) {
|
||||||
return &WintunEndpoint{tun: t}, stack.NICOptions{}, nil
|
return &WintunEndpoint{tun: t}, stack.NICOptions{}, nil
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user