Compare commits

..

5 Commits

Author SHA1 Message Date
世界
c7b084b174 Add checksum offload stub 2023-12-16 01:42:41 +08:00
世界
3195f6f4a2 Update BatchTUN API for WireGuard 2023-12-15 20:01:58 +08:00
世界
0e138754d5 Remove GSOMaxSize 2023-12-15 11:52:27 +08:00
世界
b967c6f837 Fix GSO write 2023-12-14 14:34:37 +08:00
世界
6a1419aeae Add GSO support 2023-12-12 14:09:35 +08:00
12 changed files with 133 additions and 137 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

1
.gitignore vendored
View File

@@ -1,3 +1,2 @@
/.idea/
/vendor/
.DS_Store

7
go.mod
View File

@@ -7,10 +7,11 @@ require (
github.com/go-ole/go-ole v1.3.0
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
github.com/sagernet/sing v0.3.8
github.com/sagernet/sing v0.3.0-beta.3
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/net v0.24.0
golang.org/x/sys v0.19.0
golang.org/x/net v0.19.0
golang.org/x/sys v0.15.0
)
require (

16
go.sum
View File

@@ -10,19 +10,21 @@ github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e h1:DOkjByVeAR56dks
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e/go.mod h1:fLxq/gtp0qzkaEwywlRRiGmjOK5ES/xUzyIKIFP2Asw=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/sing v0.3.8 h1:gm4JKalPhydMYX2zFOTnnd4TXtM/16WFRqSjMepYQQk=
github.com/sagernet/sing v0.3.8/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/sagernet/sing v0.3.0-beta.3 h1:E2xBoJUducK/FE6EwMk95Rt2bkXeht9l1BTYRui+DXs=
github.com/sagernet/sing v0.3.0-beta.3/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

BIN
internal/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -12,6 +12,7 @@ import (
"github.com/go-ole/go-ole"
"github.com/go-ole/go-ole/oleutil"
"github.com/scjalliance/comshim"
)
// Firewall related API constants.
@@ -249,10 +250,7 @@ func FirewallRuleExistsByName(rules *ole.IDispatch, name string) (bool, error) {
// then:
// dispatch firewallAPIRelease(u, fwp)
func firewallAPIInit() (*ole.IUnknown, *ole.IDispatch, error) {
err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)
if err != nil {
return nil, nil, fmt.Errorf("Failed to initialize COM: %s", err)
}
comshim.Add(1)
unknown, err := oleutil.CreateObject("HNetCfg.FwPolicy2")
if err != nil {
@@ -272,5 +270,5 @@ func firewallAPIInit() (*ole.IUnknown, *ole.IDispatch, error) {
func firewallAPIRelease(u *ole.IUnknown, fwp *ole.IDispatch) {
fwp.Release()
u.Release()
ole.CoUninitialize()
comshim.Done()
}

View File

@@ -42,7 +42,7 @@ func (m *networkUpdateMonitor) loopUpdate() {
select {
case <-m.done:
return
default:
case <-time.After(time.Second):
}
err := m.loopUpdate0()
if err != nil {
@@ -57,31 +57,17 @@ func (m *networkUpdateMonitor) loopUpdate0() error {
if err != nil {
return err
}
err = unix.SetNonblock(routeSocket, true)
if err != nil {
unix.Close(routeSocket)
return err
}
routeSocketFile := os.NewFile(uintptr(routeSocket), "route")
defer routeSocketFile.Close()
m.routeSocketFile = routeSocketFile
m.loopUpdate1(routeSocketFile)
return nil
}
func (m *networkUpdateMonitor) loopUpdate1(routeSocketFile *os.File) {
defer routeSocketFile.Close()
buffer := buf.NewPacket()
defer buffer.Release()
done := make(chan struct{})
go func() {
select {
case <-m.done:
routeSocketFile.Close()
case <-done:
}
}()
n, err := routeSocketFile.Read(buffer.FreeBytes())
close(done)
if err != nil {
return
}
@@ -106,59 +92,57 @@ func (m *networkUpdateMonitor) Close() error {
}
func (m *defaultInterfaceMonitor) checkUpdate() error {
var (
defaultInterface *net.Interface
err error
)
if m.options.UnderNetworkExtension {
defaultInterface, err = getDefaultInterfaceBySocket()
ribMessage, err := route.FetchRIB(unix.AF_UNSPEC, route.RIBTypeRoute, 0)
if err != nil {
return err
}
routeMessages, err := route.ParseRIB(route.RIBTypeRoute, ribMessage)
if err != nil {
return err
}
var defaultInterface *net.Interface
for _, rawRouteMessage := range routeMessages {
routeMessage := rawRouteMessage.(*route.RouteMessage)
if len(routeMessage.Addrs) <= unix.RTAX_NETMASK {
continue
}
destination, isIPv4Destination := routeMessage.Addrs[unix.RTAX_DST].(*route.Inet4Addr)
if !isIPv4Destination {
continue
}
if destination.IP != netip.IPv4Unspecified().As4() {
continue
}
mask, isIPv4Mask := routeMessage.Addrs[unix.RTAX_NETMASK].(*route.Inet4Addr)
if !isIPv4Mask {
continue
}
ones, _ := net.IPMask(mask.IP[:]).Size()
if ones != 0 {
continue
}
routeInterface, err := net.InterfaceByIndex(routeMessage.Index)
if err != nil {
return err
}
} else {
ribMessage, err := route.FetchRIB(unix.AF_UNSPEC, route.RIBTypeRoute, 0)
if err != nil {
return err
if routeMessage.Flags&unix.RTF_UP == 0 {
continue
}
routeMessages, err := route.ParseRIB(route.RIBTypeRoute, ribMessage)
if err != nil {
return err
if routeMessage.Flags&unix.RTF_GATEWAY == 0 {
continue
}
for _, rawRouteMessage := range routeMessages {
routeMessage := rawRouteMessage.(*route.RouteMessage)
if len(routeMessage.Addrs) <= unix.RTAX_NETMASK {
continue
}
destination, isIPv4Destination := routeMessage.Addrs[unix.RTAX_DST].(*route.Inet4Addr)
if !isIPv4Destination {
continue
}
if destination.IP != netip.IPv4Unspecified().As4() {
continue
}
mask, isIPv4Mask := routeMessage.Addrs[unix.RTAX_NETMASK].(*route.Inet4Addr)
if !isIPv4Mask {
continue
}
ones, _ := net.IPMask(mask.IP[:]).Size()
if ones != 0 {
continue
}
routeInterface, err := net.InterfaceByIndex(routeMessage.Index)
if routeMessage.Flags&unix.RTF_IFSCOPE != 0 {
// continue
}
defaultInterface = routeInterface
break
}
if defaultInterface == nil {
if m.options.UnderNetworkExtension {
defaultInterface, err = getDefaultInterfaceBySocket()
if err != nil {
return err
}
if routeMessage.Flags&unix.RTF_UP == 0 {
continue
}
if routeMessage.Flags&unix.RTF_GATEWAY == 0 {
continue
}
if routeMessage.Flags&unix.RTF_IFSCOPE != 0 {
// continue
}
defaultInterface = routeInterface
break
}
}
if defaultInterface == nil {
@@ -186,8 +170,6 @@ func getDefaultInterfaceBySocket() (*net.Interface, error) {
Port: 80,
})
result := make(chan netip.Addr, 1)
done := make(chan struct{})
defer close(done)
go func() {
for {
sockname, sockErr := unix.Getsockname(socketFd)
@@ -200,13 +182,8 @@ func getDefaultInterfaceBySocket() (*net.Interface, error) {
}
addr := netip.AddrFrom4(sockaddr.Addr)
if addr.IsUnspecified() {
select {
case <-done:
break
default:
time.Sleep(10 * time.Millisecond)
continue
}
time.Sleep(time.Millisecond)
continue
}
result <- addr
break
@@ -216,7 +193,7 @@ func getDefaultInterfaceBySocket() (*net.Interface, error) {
select {
case selectedAddr = <-result:
case <-time.After(time.Second):
return nil, nil
return nil, os.ErrDeadlineExceeded
}
interfaces, err := net.Interfaces()
if err != nil {

View File

@@ -4,7 +4,6 @@ import (
"os"
"runtime"
"sync"
"time"
"github.com/sagernet/netlink"
E "github.com/sagernet/sing/common/exceptions"
@@ -68,9 +67,6 @@ func (m *networkUpdateMonitor) Start() error {
}
func (m *networkUpdateMonitor) loopUpdate() {
const minDuration = time.Second
timer := time.NewTimer(minDuration)
defer timer.Stop()
for {
select {
case <-m.close:
@@ -79,12 +75,6 @@ func (m *networkUpdateMonitor) loopUpdate() {
case <-m.linkUpdate:
}
m.emit()
select {
case <-m.close:
return
case <-timer.C:
timer.Reset(minDuration)
}
}
}

View File

@@ -43,7 +43,6 @@ type defaultInterfaceMonitor struct {
defaultInterfaceIndex int
androidVPNEnabled bool
networkMonitor NetworkUpdateMonitor
checkUpdateTimer *time.Timer
element *list.Element[NetworkUpdateCallback]
access sync.Mutex
callbacks list.List[DefaultInterfaceUpdateCallback]
@@ -72,14 +71,7 @@ func (m *defaultInterfaceMonitor) Start() error {
}
func (m *defaultInterfaceMonitor) delayCheckUpdate() {
if m.checkUpdateTimer == nil {
m.checkUpdateTimer = time.AfterFunc(time.Second, m.postCheckUpdate)
} else {
m.checkUpdateTimer.Reset(time.Second)
}
}
func (m *defaultInterfaceMonitor) postCheckUpdate() {
time.Sleep(time.Second)
err := m.updateInterfaces()
if err != nil {
m.logger.Error("update interfaces: ", err)
@@ -89,8 +81,6 @@ func (m *defaultInterfaceMonitor) postCheckUpdate() {
m.defaultInterfaceName = ""
m.defaultInterfaceIndex = -1
m.emit(EventNoRoute)
} else if err != nil {
m.logger.Error("check interface: ", err)
}
}
@@ -137,6 +127,9 @@ func (m *defaultInterfaceMonitor) DefaultInterfaceName(destination netip.Addr) s
}
}
}
if m.defaultInterfaceIndex == -1 {
m.checkUpdate()
}
return m.defaultInterfaceName
}
@@ -148,6 +141,9 @@ func (m *defaultInterfaceMonitor) DefaultInterfaceIndex(destination netip.Addr)
}
}
}
if m.defaultInterfaceIndex == -1 {
m.checkUpdate()
}
return m.defaultInterfaceIndex
}
@@ -159,6 +155,9 @@ func (m *defaultInterfaceMonitor) DefaultInterface(destination netip.Addr) (stri
}
}
}
if m.defaultInterfaceIndex == -1 {
m.checkUpdate()
}
return m.defaultInterfaceName, m.defaultInterfaceIndex
}

View File

@@ -111,9 +111,9 @@ func (s *System) start() error {
var listener net.ListenConfig
if s.bindInterface {
listener.Control = control.Append(listener.Control, func(network, address string, conn syscall.RawConn) error {
bindErr := control.BindToInterface0(s.interfaceFinder, conn, network, address, s.tunName, -1, true)
if bindErr != nil {
s.logger.Warn("bind forwarder to interface: ", bindErr)
err := control.BindToInterface(s.interfaceFinder, s.tunName, -1)(network, address, conn)
if err != nil {
s.logger.Warn("bind forwarder to interface: ", err)
}
return nil
})

View File

@@ -136,13 +136,11 @@ func (t *NativeTun) BatchSize() int {
if !t.gsoEnabled {
return 1
}
/* // Not works on some devices: https://github.com/SagerNet/sing-box/issues/1605
batchSize := int(gsoMaxSize/t.options.MTU) * 2
if batchSize > idealBatchSize {
batchSize = idealBatchSize
}
return batchSize*/
return idealBatchSize
return batchSize
}
func (t *NativeTun) BatchRead(buffers [][]byte, offset int, readN []int) (n int, err error) {
@@ -273,7 +271,7 @@ func (t *NativeTun) configure(tunLink netlink.Link) error {
_ = setChecksumOffload(t.options.Name, unix.ETHTOOL_SRXCSUM)
}
if t.options._TXChecksumOffload {
if !t.options._TXChecksumOffload {
var txChecksumOffload bool
txChecksumOffload, err = checkChecksumOffload(t.options.Name, unix.ETHTOOL_GTXCSUM)
if err != nil {
@@ -452,6 +450,14 @@ func (t *NativeTun) rules() []*netlink.Rule {
it.Family = unix.AF_INET
rules = append(rules, it)
priority++
it = netlink.NewRule()
it.Priority = priority
it.OifName = includeInterface
it.Goto = matchPriority
it.Family = unix.AF_INET
rules = append(rules, it)
priority++
}
if p6 {
it = netlink.NewRule()
@@ -461,6 +467,14 @@ func (t *NativeTun) rules() []*netlink.Rule {
it.Family = unix.AF_INET6
rules = append(rules, it)
priority6++
it = netlink.NewRule()
it.Priority = priority6
it.OifName = includeInterface
it.Goto = matchPriority
it.Family = unix.AF_INET6
rules = append(rules, it)
priority6++
}
}
if p4 {
@@ -501,6 +515,14 @@ func (t *NativeTun) rules() []*netlink.Rule {
it.Family = unix.AF_INET
rules = append(rules, it)
priority++
it = netlink.NewRule()
it.Priority = priority
it.OifName = excludeInterface
it.Goto = nopPriority
it.Family = unix.AF_INET
rules = append(rules, it)
priority++
}
if p6 {
it = netlink.NewRule()
@@ -510,6 +532,14 @@ func (t *NativeTun) rules() []*netlink.Rule {
it.Family = unix.AF_INET6
rules = append(rules, it)
priority6++
it = netlink.NewRule()
it.Priority = priority6
it.OifName = excludeInterface
it.Goto = nopPriority
it.Family = unix.AF_INET6
rules = append(rules, it)
priority6++
}
}
}

View File

@@ -9,6 +9,7 @@ import (
"net/netip"
"os"
"sync"
"sync/atomic"
"time"
"unsafe"
@@ -16,7 +17,6 @@ import (
"github.com/sagernet/sing-tun/internal/winsys"
"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"
@@ -34,7 +34,7 @@ type NativeTun struct {
rate rateJuggler
running sync.WaitGroup
closeOnce sync.Once
close atomic.Int32
close int32
fwpmSession uintptr
}
@@ -334,13 +334,13 @@ func (t *NativeTun) ReadPacket() ([]byte, func(), error) {
t.running.Add(1)
defer t.running.Done()
retry:
if t.close.Load() == 1 {
if atomic.LoadInt32(&t.close) == 1 {
return nil, nil, os.ErrClosed
}
start := nanotime()
shouldSpin := t.rate.current.Load() >= spinloopRateThreshold && uint64(start-t.rate.nextStartTime.Load()) <= rateMeasurementGranularity*2
shouldSpin := atomic.LoadUint64(&t.rate.current) >= spinloopRateThreshold && uint64(start-atomic.LoadInt64(&t.rate.nextStartTime)) <= rateMeasurementGranularity*2
for {
if t.close.Load() == 1 {
if atomic.LoadInt32(&t.close) == 1 {
return nil, nil, os.ErrClosed
}
packet, err := t.session.ReceivePacket()
@@ -369,13 +369,13 @@ func (t *NativeTun) ReadFunc(block func(b []byte)) error {
t.running.Add(1)
defer t.running.Done()
retry:
if t.close.Load() == 1 {
if atomic.LoadInt32(&t.close) == 1 {
return os.ErrClosed
}
start := nanotime()
shouldSpin := t.rate.current.Load() >= spinloopRateThreshold && uint64(start-t.rate.nextStartTime.Load()) <= rateMeasurementGranularity*2
shouldSpin := atomic.LoadUint64(&t.rate.current) >= spinloopRateThreshold && uint64(start-atomic.LoadInt64(&t.rate.nextStartTime)) <= rateMeasurementGranularity*2
for {
if t.close.Load() == 1 {
if atomic.LoadInt32(&t.close) == 1 {
return os.ErrClosed
}
packet, err := t.session.ReceivePacket()
@@ -405,7 +405,7 @@ retry:
func (t *NativeTun) Write(p []byte) (n int, err error) {
t.running.Add(1)
defer t.running.Done()
if t.close.Load() == 1 {
if atomic.LoadInt32(&t.close) == 1 {
return 0, os.ErrClosed
}
t.rate.update(uint64(len(p)))
@@ -427,7 +427,7 @@ func (t *NativeTun) Write(p []byte) (n int, err error) {
func (t *NativeTun) write(packetElementList [][]byte) (n int, err error) {
t.running.Add(1)
defer t.running.Done()
if t.close.Load() == 1 {
if atomic.LoadInt32(&t.close) == 1 {
return 0, os.ErrClosed
}
var packetSize int
@@ -461,7 +461,7 @@ func (t *NativeTun) WriteVectorised(buffers []*buf.Buffer) error {
func (t *NativeTun) Close() error {
var err error
t.closeOnce.Do(func() {
t.close.Store(1)
atomic.StoreInt32(&t.close, 1)
windows.SetEvent(t.readWait)
t.running.Wait()
t.session.End()
@@ -491,24 +491,24 @@ func procyield(cycles uint32)
func nanotime() int64
type rateJuggler struct {
current atomic.Uint64
nextByteCount atomic.Uint64
nextStartTime atomic.Int64
changing atomic.Int32
current uint64
nextByteCount uint64
nextStartTime int64
changing int32
}
func (rate *rateJuggler) update(packetLen uint64) {
now := nanotime()
total := rate.nextByteCount.Add(packetLen)
period := uint64(now - rate.nextStartTime.Load())
total := atomic.AddUint64(&rate.nextByteCount, packetLen)
period := uint64(now - atomic.LoadInt64(&rate.nextStartTime))
if period >= rateMeasurementGranularity {
if !rate.changing.CompareAndSwap(0, 1) {
if !atomic.CompareAndSwapInt32(&rate.changing, 0, 1) {
return
}
rate.nextStartTime.Store(now)
rate.current.Store(total * uint64(time.Second/time.Nanosecond) / period)
rate.nextByteCount.Store(0)
rate.changing.Store(0)
atomic.StoreInt64(&rate.nextStartTime, now)
atomic.StoreUint64(&rate.current, total*uint64(time.Second/time.Nanosecond)/period)
atomic.StoreUint64(&rate.nextByteCount, 0)
atomic.StoreInt32(&rate.changing, 0)
}
}