Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46adeb9b5d | ||
|
|
f6ea97c5af | ||
|
|
3fa4ee409a | ||
|
|
958d6a25a4 | ||
|
|
86322a3fe1 | ||
|
|
78e0dfa18f | ||
|
|
ce9c864d89 | ||
|
|
da350ecc72 | ||
|
|
1a00992d06 | ||
|
|
150b116231 | ||
|
|
b93db9639d | ||
|
|
efd9884154 | ||
|
|
1a85bd3ef4 | ||
|
|
56a9b85cf5 | ||
|
|
660222a0dd | ||
|
|
fee2614ae3 | ||
|
|
2b625a47c0 | ||
|
|
dcf7d50379 | ||
|
|
4979f75513 | ||
|
|
2a0a0ab228 | ||
|
|
8adce0ea02 | ||
|
|
b6d323004e | ||
|
|
e212724bac | ||
|
|
9c933ea553 | ||
|
|
7545dc2d56 | ||
|
|
db70908d61 | ||
|
|
824b903ebd | ||
|
|
10d98f2679 | ||
|
|
aa8760b454 | ||
|
|
0a68b9f1d8 | ||
|
|
59b86002c4 | ||
|
|
688d4da4b7 |
15
go.mod
15
go.mod
@@ -3,19 +3,20 @@ module github.com/sagernet/sing-tun
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/go-ole/go-ole v1.2.6
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/go-ole/go-ole v1.3.0
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61
|
||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2
|
||||
github.com/sagernet/gvisor v0.0.0-20231119034329-07cfb6aaf930
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
|
||||
github.com/sagernet/sing v0.2.9
|
||||
github.com/sagernet/sing v0.2.18
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9
|
||||
golang.org/x/net v0.12.0
|
||||
golang.org/x/sys v0.10.0
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/net v0.19.0
|
||||
golang.org/x/sys v0.15.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/time v0.4.0 // indirect
|
||||
)
|
||||
|
||||
33
go.sum
33
go.sum
@@ -1,29 +1,30 @@
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2 h1:dnkKrzapqtAwjTSWt6hdPrARORfoYvuUczynvRLrueo=
|
||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2/go.mod h1:1JUiV7nGuf++YFm9eWZ8q2lrwHmhcUGzptMl/vL1+LA=
|
||||
github.com/sagernet/gvisor v0.0.0-20231119034329-07cfb6aaf930 h1:dSPgjIw0CT6ISLeEh8Q20dZMBMFCcEceo23+LncRcNQ=
|
||||
github.com/sagernet/gvisor v0.0.0-20231119034329-07cfb6aaf930/go.mod h1:JpKHkOYgh4wLwrX2BhH3ZIvCvazCkTnPeEcmigZJfHY=
|
||||
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.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.2.9 h1:3wsTz+JG5Wzy65eZnh6AuCrD2QqcRF6Iq6f7ttmJsAo=
|
||||
github.com/sagernet/sing v0.2.9/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
github.com/sagernet/sing v0.2.18 h1:2Ce4dl0pkWft+4914NGXPb8OiQpgA8UHQ9xFOmgvKuY=
|
||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||
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/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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.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.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY=
|
||||
golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
|
||||
@@ -385,3 +385,25 @@ func (luid LUID) SetDNS(family AddressFamily, servers []netip.Addr, domains []st
|
||||
func (luid LUID) FlushDNS(family AddressFamily) error {
|
||||
return luid.SetDNS(family, nil, nil)
|
||||
}
|
||||
|
||||
func (luid LUID) DisableDNSRegistration() error {
|
||||
guid, err := luid.GUID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dnsInterfaceSettings := &DnsInterfaceSettings{
|
||||
Version: DnsInterfaceSettingsVersion1,
|
||||
Flags: DnsInterfaceSettingsFlagRegistrationEnabled,
|
||||
RegistrationEnabled: 0,
|
||||
}
|
||||
|
||||
// For >= Windows 10 1809
|
||||
err = SetInterfaceDnsSettings(*guid, dnsInterfaceSettings)
|
||||
if err == nil || !errors.Is(err, windows.ERROR_PROC_NOT_FOUND) {
|
||||
return err
|
||||
}
|
||||
|
||||
// For < Windows 10 1809
|
||||
return luid.fallbackDisableDNSRegistration()
|
||||
}
|
||||
|
||||
@@ -51,10 +51,11 @@ func runNetsh(cmds []string) error {
|
||||
}
|
||||
|
||||
const (
|
||||
netshCmdTemplateFlush4 = "interface ipv4 set dnsservers name=%d source=static address=none validate=no register=both"
|
||||
netshCmdTemplateFlush6 = "interface ipv6 set dnsservers name=%d source=static address=none validate=no register=both"
|
||||
netshCmdTemplateAdd4 = "interface ipv4 add dnsservers name=%d address=%s validate=no"
|
||||
netshCmdTemplateAdd6 = "interface ipv6 add dnsservers name=%d address=%s validate=no"
|
||||
netshCmdTemplateFlush4 = "interface ipv4 set dnsservers name=%d source=static address=none validate=no"
|
||||
netshCmdTemplateFlush6 = "interface ipv6 set dnsservers name=%d source=static address=none validate=no"
|
||||
netshCmdTemplateAdd4 = "interface ipv4 add dnsservers name=%d address=%s validate=no"
|
||||
netshCmdTemplateAdd6 = "interface ipv6 add dnsservers name=%d address=%s validate=no"
|
||||
netshCmdTemplateDisableRegistration = "interface ipv6 set dnsservers name=%d register=none"
|
||||
)
|
||||
|
||||
func (luid LUID) fallbackSetDNSForFamily(family AddressFamily, dnses []netip.Addr) error {
|
||||
@@ -106,3 +107,13 @@ func (luid LUID) fallbackSetDNSDomain(domain string) error {
|
||||
key.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
func (luid LUID) fallbackDisableDNSRegistration() error {
|
||||
// the DNS registration setting is shared for both IPv4 and IPv6
|
||||
ipif, err := luid.IPInterface(windows.AF_INET)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := fmt.Sprintf(netshCmdTemplateDisableRegistration, ipif.InterfaceIndex)
|
||||
return runNetsh([]string{cmd})
|
||||
}
|
||||
|
||||
@@ -10,13 +10,14 @@ import (
|
||||
var ErrNoRoute = E.New("no route to internet")
|
||||
|
||||
type (
|
||||
NetworkUpdateCallback = func() error
|
||||
DefaultInterfaceUpdateCallback = func(event int) error
|
||||
NetworkUpdateCallback = func()
|
||||
DefaultInterfaceUpdateCallback = func(event int)
|
||||
)
|
||||
|
||||
const (
|
||||
EventInterfaceUpdate = 1
|
||||
EventAndroidVPNUpdate = 2
|
||||
EventNoRoute = 4
|
||||
)
|
||||
|
||||
type NetworkUpdateMonitor interface {
|
||||
@@ -24,7 +25,6 @@ type NetworkUpdateMonitor interface {
|
||||
Close() error
|
||||
RegisterCallback(callback NetworkUpdateCallback) *list.Element[NetworkUpdateCallback]
|
||||
UnregisterCallback(element *list.Element[NetworkUpdateCallback])
|
||||
E.Handler
|
||||
}
|
||||
|
||||
type DefaultInterfaceMonitor interface {
|
||||
@@ -32,6 +32,7 @@ type DefaultInterfaceMonitor interface {
|
||||
Close() error
|
||||
DefaultInterfaceName(destination netip.Addr) string
|
||||
DefaultInterfaceIndex(destination netip.Addr) int
|
||||
DefaultInterface(destination netip.Addr) (string, int)
|
||||
OverrideAndroidVPN() bool
|
||||
AndroidVPNEnabled() bool
|
||||
RegisterCallback(callback DefaultInterfaceUpdateCallback) *list.Element[DefaultInterfaceUpdateCallback]
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
|
||||
"golang.org/x/net/route"
|
||||
@@ -18,61 +17,78 @@ import (
|
||||
)
|
||||
|
||||
type networkUpdateMonitor struct {
|
||||
errorHandler E.Handler
|
||||
|
||||
access sync.Mutex
|
||||
callbacks list.List[NetworkUpdateCallback]
|
||||
routeSocket *os.File
|
||||
access sync.Mutex
|
||||
callbacks list.List[NetworkUpdateCallback]
|
||||
routeSocketFile *os.File
|
||||
closeOnce sync.Once
|
||||
done chan struct{}
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
func NewNetworkUpdateMonitor(errorHandler E.Handler) (NetworkUpdateMonitor, error) {
|
||||
func NewNetworkUpdateMonitor(logger logger.Logger) (NetworkUpdateMonitor, error) {
|
||||
return &networkUpdateMonitor{
|
||||
errorHandler: errorHandler,
|
||||
logger: logger,
|
||||
done: make(chan struct{}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *networkUpdateMonitor) Start() error {
|
||||
routeSocket, err := unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = unix.SetNonblock(routeSocket, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.routeSocket = os.NewFile(uintptr(routeSocket), "route")
|
||||
go m.loopUpdate()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *networkUpdateMonitor) loopUpdate() {
|
||||
rawConn, err := m.routeSocket.SyscallConn()
|
||||
for {
|
||||
select {
|
||||
case <-m.done:
|
||||
return
|
||||
case <-time.After(time.Second):
|
||||
}
|
||||
err := m.loopUpdate0()
|
||||
if err != nil {
|
||||
m.logger.Error("listen network update: ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *networkUpdateMonitor) loopUpdate0() error {
|
||||
routeSocket, err := unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
routeSocketFile := os.NewFile(uintptr(routeSocket), "route")
|
||||
m.routeSocketFile = routeSocketFile
|
||||
m.loopUpdate1(routeSocketFile)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *networkUpdateMonitor) loopUpdate1(routeSocketFile *os.File) {
|
||||
defer routeSocketFile.Close()
|
||||
buffer := buf.NewPacket()
|
||||
defer buffer.Release()
|
||||
n, err := routeSocketFile.Read(buffer.FreeBytes())
|
||||
if err != nil {
|
||||
m.errorHandler.NewError(context.Background(), E.Cause(err, "create raw route connection"))
|
||||
return
|
||||
}
|
||||
for {
|
||||
var innerErr error
|
||||
err = rawConn.Read(func(fd uintptr) (done bool) {
|
||||
var msg [2048]byte
|
||||
_, innerErr = unix.Read(int(fd), msg[:])
|
||||
return innerErr != unix.EWOULDBLOCK
|
||||
})
|
||||
if innerErr != nil {
|
||||
err = innerErr
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
m.emit()
|
||||
buffer.Truncate(n)
|
||||
messages, err := route.ParseRIB(route.RIBTypeRoute, buffer.Bytes())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err != syscall.EAGAIN {
|
||||
m.errorHandler.NewError(context.Background(), E.Cause(err, "read route message"))
|
||||
for _, message := range messages {
|
||||
if _, isRouteMessage := message.(*route.RouteMessage); isRouteMessage {
|
||||
m.emit()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *networkUpdateMonitor) Close() error {
|
||||
return common.Close(common.PtrOrNil(m.routeSocket))
|
||||
m.closeOnce.Do(func() {
|
||||
close(m.done)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *defaultInterfaceMonitor) checkUpdate() error {
|
||||
@@ -116,7 +132,7 @@ func (m *defaultInterfaceMonitor) checkUpdate() error {
|
||||
continue
|
||||
}
|
||||
if routeMessage.Flags&unix.RTF_IFSCOPE != 0 {
|
||||
continue
|
||||
// continue
|
||||
}
|
||||
defaultInterface = routeInterface
|
||||
break
|
||||
|
||||
@@ -2,30 +2,48 @@ package tun
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/netlink"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type networkUpdateMonitor struct {
|
||||
routeUpdate chan netlink.RouteUpdate
|
||||
linkUpdate chan netlink.LinkUpdate
|
||||
close chan struct{}
|
||||
errorHandler E.Handler
|
||||
routeUpdate chan netlink.RouteUpdate
|
||||
linkUpdate chan netlink.LinkUpdate
|
||||
close chan struct{}
|
||||
|
||||
access sync.Mutex
|
||||
callbacks list.List[NetworkUpdateCallback]
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
func NewNetworkUpdateMonitor(errorHandler E.Handler) (NetworkUpdateMonitor, error) {
|
||||
return &networkUpdateMonitor{
|
||||
routeUpdate: make(chan netlink.RouteUpdate, 2),
|
||||
linkUpdate: make(chan netlink.LinkUpdate, 2),
|
||||
close: make(chan struct{}),
|
||||
errorHandler: errorHandler,
|
||||
}, nil
|
||||
func NewNetworkUpdateMonitor(logger logger.Logger) (NetworkUpdateMonitor, error) {
|
||||
monitor := &networkUpdateMonitor{
|
||||
routeUpdate: make(chan netlink.RouteUpdate, 2),
|
||||
linkUpdate: make(chan netlink.LinkUpdate, 2),
|
||||
close: make(chan struct{}),
|
||||
logger: logger,
|
||||
}
|
||||
// check is netlink banned by google
|
||||
if runtime.GOOS == "android" {
|
||||
netlinkSocket, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_DGRAM, unix.NETLINK_ROUTE)
|
||||
if err != nil {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
err = unix.Bind(netlinkSocket, &unix.SockaddrNetlink{
|
||||
Family: unix.AF_NETLINK,
|
||||
})
|
||||
unix.Close(netlinkSocket)
|
||||
if err != nil {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
}
|
||||
return monitor, nil
|
||||
}
|
||||
|
||||
func (m *networkUpdateMonitor) Start() error {
|
||||
|
||||
@@ -4,7 +4,6 @@ package tun
|
||||
|
||||
import (
|
||||
"github.com/sagernet/netlink"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
@@ -37,5 +36,5 @@ func (m *defaultInterfaceMonitor) checkUpdate() error {
|
||||
m.emit(EventInterfaceUpdate)
|
||||
return nil
|
||||
}
|
||||
return E.New("no route to internet")
|
||||
return ErrNoRoute
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ package tun
|
||||
import (
|
||||
"os"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
func NewNetworkUpdateMonitor(errorHandler E.Handler) (NetworkUpdateMonitor, error) {
|
||||
func NewNetworkUpdateMonitor(logger logger.Logger) (NetworkUpdateMonitor, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func NewDefaultInterfaceMonitor(networkMonitor NetworkUpdateMonitor, options DefaultInterfaceMonitorOptions) (DefaultInterfaceMonitor, error) {
|
||||
func NewDefaultInterfaceMonitor(networkMonitor NetworkUpdateMonitor, logger logger.Logger, options DefaultInterfaceMonitorOptions) (DefaultInterfaceMonitor, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
@@ -32,17 +32,10 @@ func (m *networkUpdateMonitor) emit() {
|
||||
callbacks := m.callbacks.Array()
|
||||
m.access.Unlock()
|
||||
for _, callback := range callbacks {
|
||||
err := callback()
|
||||
if err != nil {
|
||||
m.NewError(context.Background(), err)
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *networkUpdateMonitor) NewError(ctx context.Context, err error) {
|
||||
m.errorHandler.NewError(ctx, err)
|
||||
}
|
||||
|
||||
type defaultInterfaceMonitor struct {
|
||||
options DefaultInterfaceMonitorOptions
|
||||
networkAddresses []networkAddress
|
||||
@@ -53,6 +46,7 @@ type defaultInterfaceMonitor struct {
|
||||
element *list.Element[NetworkUpdateCallback]
|
||||
access sync.Mutex
|
||||
callbacks list.List[DefaultInterfaceUpdateCallback]
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
type networkAddress struct {
|
||||
@@ -61,30 +55,33 @@ type networkAddress struct {
|
||||
addresses []netip.Prefix
|
||||
}
|
||||
|
||||
func NewDefaultInterfaceMonitor(networkMonitor NetworkUpdateMonitor, options DefaultInterfaceMonitorOptions) (DefaultInterfaceMonitor, error) {
|
||||
func NewDefaultInterfaceMonitor(networkMonitor NetworkUpdateMonitor, logger logger.Logger, options DefaultInterfaceMonitorOptions) (DefaultInterfaceMonitor, error) {
|
||||
return &defaultInterfaceMonitor{
|
||||
options: options,
|
||||
networkMonitor: networkMonitor,
|
||||
defaultInterfaceIndex: -1,
|
||||
logger: logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *defaultInterfaceMonitor) Start() error {
|
||||
err := m.checkUpdate()
|
||||
if err != nil {
|
||||
m.networkMonitor.NewError(context.Background(), err)
|
||||
}
|
||||
_ = m.checkUpdate()
|
||||
m.element = m.networkMonitor.RegisterCallback(m.delayCheckUpdate)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *defaultInterfaceMonitor) delayCheckUpdate() error {
|
||||
func (m *defaultInterfaceMonitor) delayCheckUpdate() {
|
||||
time.Sleep(time.Second)
|
||||
err := m.updateInterfaces()
|
||||
if err != nil {
|
||||
m.networkMonitor.NewError(context.Background(), E.Cause(err, "update interfaces"))
|
||||
m.logger.Error("update interfaces: ", err)
|
||||
}
|
||||
err = m.checkUpdate()
|
||||
if errors.Is(err, ErrNoRoute) {
|
||||
m.defaultInterfaceName = ""
|
||||
m.defaultInterfaceIndex = -1
|
||||
m.emit(EventNoRoute)
|
||||
}
|
||||
return m.checkUpdate()
|
||||
}
|
||||
|
||||
func (m *defaultInterfaceMonitor) updateInterfaces() error {
|
||||
@@ -150,6 +147,20 @@ func (m *defaultInterfaceMonitor) DefaultInterfaceIndex(destination netip.Addr)
|
||||
return m.defaultInterfaceIndex
|
||||
}
|
||||
|
||||
func (m *defaultInterfaceMonitor) DefaultInterface(destination netip.Addr) (string, int) {
|
||||
for _, address := range m.networkAddresses {
|
||||
for _, prefix := range address.addresses {
|
||||
if prefix.Contains(destination) {
|
||||
return address.interfaceName, address.interfaceIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
if m.defaultInterfaceIndex == -1 {
|
||||
m.checkUpdate()
|
||||
}
|
||||
return m.defaultInterfaceName, m.defaultInterfaceIndex
|
||||
}
|
||||
|
||||
func (m *defaultInterfaceMonitor) OverrideAndroidVPN() bool {
|
||||
return m.options.OverrideAndroidVPN
|
||||
}
|
||||
@@ -175,9 +186,6 @@ func (m *defaultInterfaceMonitor) emit(event int) {
|
||||
callbacks := m.callbacks.Array()
|
||||
m.access.Unlock()
|
||||
for _, callback := range callbacks {
|
||||
err := callback(event)
|
||||
if err != nil {
|
||||
m.networkMonitor.NewError(context.Background(), err)
|
||||
}
|
||||
callback(event)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-tun/internal/winipcfg"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
@@ -17,11 +18,12 @@ type networkUpdateMonitor struct {
|
||||
|
||||
access sync.Mutex
|
||||
callbacks list.List[NetworkUpdateCallback]
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
func NewNetworkUpdateMonitor(errorHandler E.Handler) (NetworkUpdateMonitor, error) {
|
||||
func NewNetworkUpdateMonitor(logger logger.Logger) (NetworkUpdateMonitor, error) {
|
||||
return &networkUpdateMonitor{
|
||||
errorHandler: errorHandler,
|
||||
logger: logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
20
stack.go
20
stack.go
@@ -2,6 +2,8 @@ package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing/common/control"
|
||||
@@ -35,9 +37,15 @@ func NewStack(
|
||||
) (Stack, error) {
|
||||
switch stack {
|
||||
case "":
|
||||
return NewSystem(options)
|
||||
if WithGVisor {
|
||||
return NewMixed(options)
|
||||
} else {
|
||||
return NewSystem(options)
|
||||
}
|
||||
case "gvisor":
|
||||
return NewGVisor(options)
|
||||
case "mixed":
|
||||
return NewMixed(options)
|
||||
case "system":
|
||||
return NewSystem(options)
|
||||
case "lwip":
|
||||
@@ -46,3 +54,13 @@ func NewStack(
|
||||
return nil, E.New("unknown stack: ", stack)
|
||||
}
|
||||
}
|
||||
|
||||
func BroadcastAddr(inet4Address []netip.Prefix) netip.Addr {
|
||||
if len(inet4Address) == 0 {
|
||||
return netip.Addr{}
|
||||
}
|
||||
prefix := inet4Address[0]
|
||||
var broadcastAddr [4]byte
|
||||
binary.BigEndian.PutUint32(broadcastAddr[:], binary.BigEndian.Uint32(prefix.Masked().Addr().AsSlice())|^binary.BigEndian.Uint32(net.CIDRMask(prefix.Bits(), 32)))
|
||||
return netip.AddrFrom4(broadcastAddr)
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ type GVisor struct {
|
||||
tunMtu uint32
|
||||
endpointIndependentNat bool
|
||||
udpTimeout int64
|
||||
broadcastAddr netip.Addr
|
||||
handler Handler
|
||||
logger logger.Logger
|
||||
stack *stack.Stack
|
||||
@@ -59,6 +60,7 @@ func NewGVisor(
|
||||
tunMtu: options.MTU,
|
||||
endpointIndependentNat: options.EndpointIndependentNat,
|
||||
udpTimeout: options.UDPTimeout,
|
||||
broadcastAddr: BroadcastAddr(options.Inet4Address),
|
||||
handler: options.Handler,
|
||||
logger: options.Logger,
|
||||
}
|
||||
@@ -70,44 +72,11 @@ func (t *GVisor) Start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ipStack := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{
|
||||
ipv4.NewProtocol,
|
||||
ipv6.NewProtocol,
|
||||
},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{
|
||||
tcp.NewProtocol,
|
||||
udp.NewProtocol,
|
||||
icmp.NewProtocol4,
|
||||
icmp.NewProtocol6,
|
||||
},
|
||||
})
|
||||
tErr := ipStack.CreateNIC(defaultNIC, linkEndpoint)
|
||||
if tErr != nil {
|
||||
return E.New("create nic: ", wrapStackError(tErr))
|
||||
linkEndpoint = &LinkEndpointFilter{linkEndpoint, t.broadcastAddr, t.tun.CreateVectorisedWriter()}
|
||||
ipStack, err := newGVisorStack(linkEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ipStack.SetRouteTable([]tcpip.Route{
|
||||
{Destination: header.IPv4EmptySubnet, NIC: defaultNIC},
|
||||
{Destination: header.IPv6EmptySubnet, NIC: defaultNIC},
|
||||
})
|
||||
ipStack.SetSpoofing(defaultNIC, true)
|
||||
ipStack.SetPromiscuousMode(defaultNIC, true)
|
||||
bufSize := 20 * 1024
|
||||
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &tcpip.TCPReceiveBufferSizeRangeOption{
|
||||
Min: 1,
|
||||
Default: bufSize,
|
||||
Max: bufSize,
|
||||
})
|
||||
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &tcpip.TCPSendBufferSizeRangeOption{
|
||||
Min: 1,
|
||||
Default: bufSize,
|
||||
Max: bufSize,
|
||||
})
|
||||
sOpt := tcpip.TCPSACKEnabled(true)
|
||||
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &sOpt)
|
||||
mOpt := tcpip.TCPModerateReceiveBufferOption(true)
|
||||
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &mOpt)
|
||||
|
||||
tcpForwarder := tcp.NewForwarder(ipStack, 0, 1024, func(r *tcp.ForwarderRequest) {
|
||||
var wq waiter.Queue
|
||||
handshakeCtx, cancel := context.WithCancel(context.Background())
|
||||
@@ -162,11 +131,12 @@ func (t *GVisor) Start() error {
|
||||
endpoint.Abort()
|
||||
return
|
||||
}
|
||||
gConn := &gUDPConn{UDPConn: udpConn}
|
||||
go func() {
|
||||
var metadata M.Metadata
|
||||
metadata.Source = M.SocksaddrFromNet(lAddr)
|
||||
metadata.Destination = M.SocksaddrFromNet(rAddr)
|
||||
ctx, conn := canceler.NewPacketConn(t.ctx, bufio.NewPacketConn(&bufio.UnbindPacketConn{ExtendedConn: bufio.NewExtendedConn(&gUDPConn{udpConn}), Addr: M.SocksaddrFromNet(rAddr)}), time.Duration(t.udpTimeout)*time.Second)
|
||||
ctx, conn := canceler.NewPacketConn(t.ctx, bufio.NewPacketConn(&bufio.UnbindPacketConn{ExtendedConn: bufio.NewExtendedConn(gConn), Addr: M.SocksaddrFromNet(rAddr)}), time.Duration(t.udpTimeout)*time.Second)
|
||||
hErr := t.handler.NewPacketConnection(ctx, conn, metadata)
|
||||
if hErr != nil {
|
||||
endpoint.Abort()
|
||||
@@ -207,3 +177,44 @@ func AddrFromAddress(address tcpip.Address) netip.Addr {
|
||||
return netip.AddrFrom4(address.As4())
|
||||
}
|
||||
}
|
||||
|
||||
func newGVisorStack(ep stack.LinkEndpoint) (*stack.Stack, error) {
|
||||
ipStack := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{
|
||||
ipv4.NewProtocol,
|
||||
ipv6.NewProtocol,
|
||||
},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{
|
||||
tcp.NewProtocol,
|
||||
udp.NewProtocol,
|
||||
icmp.NewProtocol4,
|
||||
icmp.NewProtocol6,
|
||||
},
|
||||
})
|
||||
tErr := ipStack.CreateNIC(defaultNIC, ep)
|
||||
if tErr != nil {
|
||||
return nil, E.New("create nic: ", wrapStackError(tErr))
|
||||
}
|
||||
ipStack.SetRouteTable([]tcpip.Route{
|
||||
{Destination: header.IPv4EmptySubnet, NIC: defaultNIC},
|
||||
{Destination: header.IPv6EmptySubnet, NIC: defaultNIC},
|
||||
})
|
||||
ipStack.SetSpoofing(defaultNIC, true)
|
||||
ipStack.SetPromiscuousMode(defaultNIC, true)
|
||||
bufSize := 20 * 1024
|
||||
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &tcpip.TCPReceiveBufferSizeRangeOption{
|
||||
Min: 1,
|
||||
Default: bufSize,
|
||||
Max: bufSize,
|
||||
})
|
||||
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &tcpip.TCPSendBufferSizeRangeOption{
|
||||
Min: 1,
|
||||
Default: bufSize,
|
||||
Max: bufSize,
|
||||
})
|
||||
sOpt := tcpip.TCPSACKEnabled(true)
|
||||
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &sOpt)
|
||||
mOpt := tcpip.TCPModerateReceiveBufferOption(true)
|
||||
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &mOpt)
|
||||
return ipStack, nil
|
||||
}
|
||||
@@ -27,28 +27,6 @@ func (c *gTCPConn) Write(b []byte) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
type gUDPConn struct {
|
||||
*gonet.UDPConn
|
||||
}
|
||||
|
||||
func (c *gUDPConn) Read(b []byte) (n int, err error) {
|
||||
n, err = c.UDPConn.Read(b)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
err = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *gUDPConn) Write(b []byte) (n int, err error) {
|
||||
n, err = c.UDPConn.Write(b)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
err = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
func wrapStackError(err tcpip.Error) error {
|
||||
switch err.(type) {
|
||||
case *tcpip.ErrClosedForSend,
|
||||
56
stack_gvisor_filter.go
Normal file
56
stack_gvisor_filter.go
Normal file
@@ -0,0 +1,56 @@
|
||||
//go:build with_gvisor
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"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)
|
||||
|
||||
type LinkEndpointFilter struct {
|
||||
stack.LinkEndpoint
|
||||
BroadcastAddress netip.Addr
|
||||
Writer N.VectorisedWriter
|
||||
}
|
||||
|
||||
func (w *LinkEndpointFilter) Attach(dispatcher stack.NetworkDispatcher) {
|
||||
w.LinkEndpoint.Attach(&networkDispatcherFilter{dispatcher, w.BroadcastAddress, w.Writer})
|
||||
}
|
||||
|
||||
var _ stack.NetworkDispatcher = (*networkDispatcherFilter)(nil)
|
||||
|
||||
type networkDispatcherFilter struct {
|
||||
stack.NetworkDispatcher
|
||||
broadcastAddress netip.Addr
|
||||
writer N.VectorisedWriter
|
||||
}
|
||||
|
||||
func (w *networkDispatcherFilter) DeliverNetworkPacket(protocol tcpip.NetworkProtocolNumber, pkt stack.PacketBufferPtr) {
|
||||
var network header.Network
|
||||
if protocol == header.IPv4ProtocolNumber {
|
||||
if headerPackets, loaded := pkt.Data().PullUp(header.IPv4MinimumSize); loaded {
|
||||
network = header.IPv4(headerPackets)
|
||||
}
|
||||
} else {
|
||||
if headerPackets, loaded := pkt.Data().PullUp(header.IPv6MinimumSize); loaded {
|
||||
network = header.IPv6(headerPackets)
|
||||
}
|
||||
}
|
||||
if network == nil {
|
||||
w.NetworkDispatcher.DeliverNetworkPacket(protocol, pkt)
|
||||
return
|
||||
}
|
||||
destination := AddrFromAddress(network.DestinationAddress())
|
||||
if destination == w.broadcastAddress || !destination.IsGlobalUnicast() {
|
||||
_, _ = bufio.WriteVectorised(w.writer, pkt.AsSlices())
|
||||
return
|
||||
}
|
||||
w.NetworkDispatcher.DeliverNetworkPacket(protocol, pkt)
|
||||
}
|
||||
@@ -13,3 +13,9 @@ func NewGVisor(
|
||||
) (Stack, error) {
|
||||
return nil, ErrGVisorNotIncluded
|
||||
}
|
||||
|
||||
func NewMixed(
|
||||
options StackOptions,
|
||||
) (Stack, error) {
|
||||
return nil, ErrGVisorNotIncluded
|
||||
}
|
||||
@@ -4,11 +4,16 @@ package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math"
|
||||
"net/netip"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/checksum"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
@@ -25,9 +30,8 @@ type UDPForwarder struct {
|
||||
udpNat *udpnat.Service[netip.AddrPort]
|
||||
|
||||
// cache
|
||||
cacheProto tcpip.NetworkProtocolNumber
|
||||
cacheID stack.TransportEndpointID
|
||||
cachePacket stack.PacketBufferPtr
|
||||
cacheProto tcpip.NetworkProtocolNumber
|
||||
cacheID stack.TransportEndpointID
|
||||
}
|
||||
|
||||
func NewUDPForwarder(ctx context.Context, stack *stack.Stack, handler Handler, udpTimeout int64) *UDPForwarder {
|
||||
@@ -47,13 +51,12 @@ func (f *UDPForwarder) HandlePacket(id stack.TransportEndpointID, pkt stack.Pack
|
||||
} else {
|
||||
f.cacheProto = header.IPv6ProtocolNumber
|
||||
}
|
||||
gBuffer := pkt.ToBuffer()
|
||||
gBuffer := pkt.Data().ToBuffer()
|
||||
sBuffer := buf.NewSize(int(gBuffer.Size()))
|
||||
gBuffer.Apply(func(view *buffer.View) {
|
||||
sBuffer.Write(view.AsSlice())
|
||||
})
|
||||
f.cacheID = id
|
||||
f.cachePacket = pkt
|
||||
f.udpNat.NewPacket(
|
||||
f.ctx,
|
||||
upstreamMetadata.Source.AddrPort(),
|
||||
@@ -74,16 +77,20 @@ func (f *UDPForwarder) newUDPConn(natConn N.PacketConn) N.PacketWriter {
|
||||
}
|
||||
|
||||
type UDPBackWriter struct {
|
||||
access sync.Mutex
|
||||
stack *stack.Stack
|
||||
source tcpip.Address
|
||||
sourcePort uint16
|
||||
sourceNetwork tcpip.NetworkProtocolNumber
|
||||
packet stack.PacketBufferPtr
|
||||
}
|
||||
|
||||
func (w *UDPBackWriter) WritePacket(packetBuffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
if destination.IsIPv4() && w.sourceNetwork == header.IPv6ProtocolNumber {
|
||||
if !destination.IsIP() {
|
||||
return E.Cause(os.ErrInvalid, "invalid destination")
|
||||
} else if destination.IsIPv4() && w.sourceNetwork == header.IPv6ProtocolNumber {
|
||||
destination = M.SocksaddrFrom(netip.AddrFrom16(destination.Addr.As16()), destination.Port)
|
||||
} else if destination.IsIPv6() && (w.sourceNetwork == header.IPv4AddressSizeBits) {
|
||||
} else if destination.IsIPv6() && (w.sourceNetwork == header.IPv4ProtocolNumber) {
|
||||
return E.New("send IPv6 packet to IPv4 connection")
|
||||
}
|
||||
|
||||
@@ -141,3 +148,74 @@ func (w *UDPBackWriter) WritePacket(packetBuffer *buf.Buffer, destination M.Sock
|
||||
route.Stats().UDP.PacketsSent.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
type gRequest struct {
|
||||
stack *stack.Stack
|
||||
id stack.TransportEndpointID
|
||||
pkt stack.PacketBufferPtr
|
||||
}
|
||||
|
||||
type gUDPConn struct {
|
||||
*gonet.UDPConn
|
||||
}
|
||||
|
||||
func (c *gUDPConn) Read(b []byte) (n int, err error) {
|
||||
n, err = c.UDPConn.Read(b)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
err = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *gUDPConn) Write(b []byte) (n int, err error) {
|
||||
n, err = c.UDPConn.Write(b)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
err = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *gUDPConn) Close() error {
|
||||
return c.UDPConn.Close()
|
||||
}
|
||||
|
||||
func gWriteUnreachable(gStack *stack.Stack, packet stack.PacketBufferPtr, err error) (retErr error) {
|
||||
if errors.Is(err, syscall.ENETUNREACH) {
|
||||
if packet.NetworkProtocolNumber == header.IPv4ProtocolNumber {
|
||||
return gWriteUnreachable4(gStack, packet, stack.RejectIPv4WithICMPNetUnreachable)
|
||||
} else {
|
||||
return gWriteUnreachable6(gStack, packet, stack.RejectIPv6WithICMPNoRoute)
|
||||
}
|
||||
} else if errors.Is(err, syscall.EHOSTUNREACH) {
|
||||
if packet.NetworkProtocolNumber == header.IPv4ProtocolNumber {
|
||||
return gWriteUnreachable4(gStack, packet, stack.RejectIPv4WithICMPHostUnreachable)
|
||||
} else {
|
||||
return gWriteUnreachable6(gStack, packet, stack.RejectIPv6WithICMPNoRoute)
|
||||
}
|
||||
} else if errors.Is(err, syscall.ECONNREFUSED) {
|
||||
if packet.NetworkProtocolNumber == header.IPv4ProtocolNumber {
|
||||
return gWriteUnreachable4(gStack, packet, stack.RejectIPv4WithICMPPortUnreachable)
|
||||
} else {
|
||||
return gWriteUnreachable6(gStack, packet, stack.RejectIPv6WithICMPPortUnreachable)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func gWriteUnreachable4(gStack *stack.Stack, packet stack.PacketBufferPtr, icmpCode stack.RejectIPv4WithICMPType) error {
|
||||
err := gStack.NetworkProtocolInstance(header.IPv4ProtocolNumber).(stack.RejectIPv4WithHandler).SendRejectionError(packet, icmpCode, true)
|
||||
if err != nil {
|
||||
return wrapStackError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func gWriteUnreachable6(gStack *stack.Stack, packet stack.PacketBufferPtr, icmpCode stack.RejectIPv6WithICMPType) error {
|
||||
err := gStack.NetworkProtocolInstance(header.IPv6ProtocolNumber).(stack.RejectIPv6WithHandler).SendRejectionError(packet, icmpCode, true)
|
||||
if err != nil {
|
||||
return wrapStackError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
208
stack_mixed.go
Normal file
208
stack_mixed.go
Normal file
@@ -0,0 +1,208 @@
|
||||
//go:build with_gvisor
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/link/channel"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
"github.com/sagernet/sing-tun/internal/clashtcpip"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/canceler"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
type Mixed struct {
|
||||
*System
|
||||
writer N.VectorisedWriter
|
||||
endpointIndependentNat bool
|
||||
stack *stack.Stack
|
||||
endpoint *channel.Endpoint
|
||||
}
|
||||
|
||||
func NewMixed(
|
||||
options StackOptions,
|
||||
) (Stack, error) {
|
||||
system, err := NewSystem(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Mixed{
|
||||
System: system.(*System),
|
||||
writer: options.Tun.CreateVectorisedWriter(),
|
||||
endpointIndependentNat: options.EndpointIndependentNat,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *Mixed) Start() error {
|
||||
err := m.System.start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
endpoint := channel.New(1024, m.mtu, "")
|
||||
ipStack, err := newGVisorStack(endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !m.endpointIndependentNat {
|
||||
udpForwarder := udp.NewForwarder(ipStack, func(request *udp.ForwarderRequest) {
|
||||
var wq waiter.Queue
|
||||
endpoint, err := request.CreateEndpoint(&wq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
udpConn := gonet.NewUDPConn(ipStack, &wq, endpoint)
|
||||
lAddr := udpConn.RemoteAddr()
|
||||
rAddr := udpConn.LocalAddr()
|
||||
if lAddr == nil || rAddr == nil {
|
||||
endpoint.Abort()
|
||||
return
|
||||
}
|
||||
gConn := &gUDPConn{UDPConn: udpConn}
|
||||
go func() {
|
||||
var metadata M.Metadata
|
||||
metadata.Source = M.SocksaddrFromNet(lAddr)
|
||||
metadata.Destination = M.SocksaddrFromNet(rAddr)
|
||||
ctx, conn := canceler.NewPacketConn(m.ctx, bufio.NewPacketConn(&bufio.UnbindPacketConn{ExtendedConn: bufio.NewExtendedConn(gConn), Addr: M.SocksaddrFromNet(rAddr)}), time.Duration(m.udpTimeout)*time.Second)
|
||||
hErr := m.handler.NewPacketConnection(ctx, conn, metadata)
|
||||
if hErr != nil {
|
||||
endpoint.Abort()
|
||||
}
|
||||
}()
|
||||
})
|
||||
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
|
||||
} else {
|
||||
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, NewUDPForwarder(m.ctx, ipStack, m.handler, m.udpTimeout).HandlePacket)
|
||||
}
|
||||
m.stack = ipStack
|
||||
m.endpoint = endpoint
|
||||
go m.tunLoop()
|
||||
go m.packetLoop()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Mixed) tunLoop() {
|
||||
if winTun, isWinTun := m.tun.(WinTun); isWinTun {
|
||||
m.wintunLoop(winTun)
|
||||
return
|
||||
}
|
||||
packetBuffer := make([]byte, m.mtu+PacketOffset)
|
||||
for {
|
||||
n, err := m.tun.Read(packetBuffer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if n < clashtcpip.IPv4PacketMinLength {
|
||||
continue
|
||||
}
|
||||
packet := packetBuffer[PacketOffset:n]
|
||||
switch ipVersion := packet[0] >> 4; ipVersion {
|
||||
case 4:
|
||||
err = m.processIPv4(packet)
|
||||
case 6:
|
||||
err = m.processIPv6(packet)
|
||||
default:
|
||||
err = E.New("ip: unknown version: ", ipVersion)
|
||||
}
|
||||
if err != nil {
|
||||
m.logger.Trace(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Mixed) wintunLoop(winTun WinTun) {
|
||||
for {
|
||||
packet, release, err := winTun.ReadPacket()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(packet) < clashtcpip.IPv4PacketMinLength {
|
||||
release()
|
||||
continue
|
||||
}
|
||||
switch ipVersion := packet[0] >> 4; ipVersion {
|
||||
case 4:
|
||||
err = m.processIPv4(packet)
|
||||
case 6:
|
||||
err = m.processIPv6(packet)
|
||||
default:
|
||||
err = E.New("ip: unknown version: ", ipVersion)
|
||||
}
|
||||
if err != nil {
|
||||
m.logger.Trace(err)
|
||||
}
|
||||
release()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Mixed) processIPv4(packet clashtcpip.IPv4Packet) error {
|
||||
destination := packet.DestinationIP()
|
||||
if destination == m.broadcastAddr || !destination.IsGlobalUnicast() {
|
||||
return common.Error(m.tun.Write(packet))
|
||||
}
|
||||
switch packet.Protocol() {
|
||||
case clashtcpip.TCP:
|
||||
return m.processIPv4TCP(packet, packet.Payload())
|
||||
case clashtcpip.UDP:
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Payload: buffer.MakeWithData(packet),
|
||||
})
|
||||
m.endpoint.InjectInbound(header.IPv4ProtocolNumber, pkt)
|
||||
pkt.DecRef()
|
||||
return nil
|
||||
case clashtcpip.ICMP:
|
||||
return m.processIPv4ICMP(packet, packet.Payload())
|
||||
default:
|
||||
return common.Error(m.tun.Write(packet))
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Mixed) processIPv6(packet clashtcpip.IPv6Packet) error {
|
||||
if !packet.DestinationIP().IsGlobalUnicast() {
|
||||
return common.Error(m.tun.Write(packet))
|
||||
}
|
||||
switch packet.Protocol() {
|
||||
case clashtcpip.TCP:
|
||||
return m.processIPv6TCP(packet, packet.Payload())
|
||||
case clashtcpip.UDP:
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Payload: buffer.MakeWithData(packet),
|
||||
})
|
||||
m.endpoint.InjectInbound(header.IPv6ProtocolNumber, pkt)
|
||||
pkt.DecRef()
|
||||
return nil
|
||||
case clashtcpip.ICMPv6:
|
||||
return m.processIPv6ICMP(packet, packet.Payload())
|
||||
default:
|
||||
return common.Error(m.tun.Write(packet))
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Mixed) packetLoop() {
|
||||
for {
|
||||
packet := m.endpoint.ReadContext(m.ctx)
|
||||
if packet == nil {
|
||||
break
|
||||
}
|
||||
bufio.WriteVectorised(m.writer, packet.AsSlices())
|
||||
packet.DecRef()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Mixed) Close() error {
|
||||
m.endpoint.Attach(nil)
|
||||
m.stack.Close()
|
||||
for _, endpoint := range m.stack.CleanupEndpoints() {
|
||||
endpoint.Abort()
|
||||
}
|
||||
return m.System.Close()
|
||||
}
|
||||
@@ -31,6 +31,7 @@ type System struct {
|
||||
inet4Address netip.Addr
|
||||
inet6ServerAddress netip.Addr
|
||||
inet6Address netip.Addr
|
||||
broadcastAddr netip.Addr
|
||||
udpTimeout int64
|
||||
tcpListener net.Listener
|
||||
tcpListener6 net.Listener
|
||||
@@ -60,6 +61,7 @@ func NewSystem(options StackOptions) (Stack, error) {
|
||||
logger: options.Logger,
|
||||
inet4Prefixes: options.Inet4Address,
|
||||
inet6Prefixes: options.Inet6Address,
|
||||
broadcastAddr: BroadcastAddr(options.Inet4Address),
|
||||
bindInterface: options.ForwarderBindInterface,
|
||||
interfaceFinder: options.InterfaceFinder,
|
||||
}
|
||||
@@ -91,6 +93,15 @@ func (s *System) Close() error {
|
||||
}
|
||||
|
||||
func (s *System) Start() error {
|
||||
err := s.start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go s.tunLoop()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *System) start() error {
|
||||
err := fixWindowsFirewall()
|
||||
if err != nil {
|
||||
return E.Cause(err, "fix windows firewall for system stack")
|
||||
@@ -125,7 +136,6 @@ func (s *System) Start() error {
|
||||
}
|
||||
s.tcpNat = NewNat(s.ctx, time.Second*time.Duration(s.udpTimeout))
|
||||
s.udpNat = udpnat.New[netip.AddrPort](s.udpTimeout, s.handler)
|
||||
go s.tunLoop()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -225,6 +235,10 @@ func (s *System) acceptLoop(listener net.Listener) {
|
||||
}
|
||||
|
||||
func (s *System) processIPv4(packet clashtcpip.IPv4Packet) error {
|
||||
destination := packet.DestinationIP()
|
||||
if destination == s.broadcastAddr || !destination.IsGlobalUnicast() {
|
||||
return common.Error(s.tun.Write(packet))
|
||||
}
|
||||
switch packet.Protocol() {
|
||||
case clashtcpip.TCP:
|
||||
return s.processIPv4TCP(packet, packet.Payload())
|
||||
@@ -238,6 +252,9 @@ func (s *System) processIPv4(packet clashtcpip.IPv4Packet) error {
|
||||
}
|
||||
|
||||
func (s *System) processIPv6(packet clashtcpip.IPv6Packet) error {
|
||||
if !packet.DestinationIP().IsGlobalUnicast() {
|
||||
return common.Error(s.tun.Write(packet))
|
||||
}
|
||||
switch packet.Protocol() {
|
||||
case clashtcpip.TCP:
|
||||
return s.processIPv6TCP(packet, packet.Payload())
|
||||
@@ -309,6 +326,9 @@ func (s *System) processIPv4UDP(packet clashtcpip.IPv4Packet, header clashtcpip.
|
||||
if packet.FragmentOffset() != 0 {
|
||||
return E.New("ipv4: udp: fragment dropped")
|
||||
}
|
||||
if !header.Valid() {
|
||||
return E.New("ipv4: udp: invalid packet")
|
||||
}
|
||||
source := netip.AddrPortFrom(packet.SourceIP(), header.SourcePort())
|
||||
destination := netip.AddrPortFrom(packet.DestinationIP(), header.DestinationPort())
|
||||
if !destination.Addr().IsGlobalUnicast() {
|
||||
@@ -332,6 +352,9 @@ func (s *System) processIPv4UDP(packet clashtcpip.IPv4Packet, header clashtcpip.
|
||||
}
|
||||
|
||||
func (s *System) processIPv6UDP(packet clashtcpip.IPv6Packet, header clashtcpip.UDPPacket) error {
|
||||
if !header.Valid() {
|
||||
return E.New("ipv6: udp: invalid packet")
|
||||
}
|
||||
source := netip.AddrPortFrom(packet.SourceIP(), header.SourcePort())
|
||||
destination := netip.AddrPortFrom(packet.DestinationIP(), header.DestinationPort())
|
||||
if !destination.Addr().IsGlobalUnicast() {
|
||||
39
tun.go
39
tun.go
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/ranges"
|
||||
)
|
||||
@@ -22,6 +23,7 @@ type Handler interface {
|
||||
|
||||
type Tun interface {
|
||||
io.ReadWriter
|
||||
CreateVectorisedWriter() N.VectorisedWriter
|
||||
Close() error
|
||||
}
|
||||
|
||||
@@ -31,22 +33,27 @@ type WinTun interface {
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Name string
|
||||
Inet4Address []netip.Prefix
|
||||
Inet6Address []netip.Prefix
|
||||
MTU uint32
|
||||
AutoRoute bool
|
||||
StrictRoute bool
|
||||
Inet4RouteAddress []netip.Prefix
|
||||
Inet6RouteAddress []netip.Prefix
|
||||
IncludeUID []ranges.Range[uint32]
|
||||
ExcludeUID []ranges.Range[uint32]
|
||||
IncludeAndroidUser []int
|
||||
IncludePackage []string
|
||||
ExcludePackage []string
|
||||
InterfaceMonitor DefaultInterfaceMonitor
|
||||
TableIndex int
|
||||
FileDescriptor int
|
||||
Name string
|
||||
Inet4Address []netip.Prefix
|
||||
Inet6Address []netip.Prefix
|
||||
MTU uint32
|
||||
AutoRoute bool
|
||||
StrictRoute bool
|
||||
Inet4RouteAddress []netip.Prefix
|
||||
Inet6RouteAddress []netip.Prefix
|
||||
Inet4RouteExcludeAddress []netip.Prefix
|
||||
Inet6RouteExcludeAddress []netip.Prefix
|
||||
IncludeInterface []string
|
||||
ExcludeInterface []string
|
||||
IncludeUID []ranges.Range[uint32]
|
||||
ExcludeUID []ranges.Range[uint32]
|
||||
IncludeAndroidUser []int
|
||||
IncludePackage []string
|
||||
ExcludePackage []string
|
||||
InterfaceMonitor DefaultInterfaceMonitor
|
||||
TableIndex int
|
||||
FileDescriptor int
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
func CalculateInterfaceName(name string) (tunName string) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"unsafe"
|
||||
|
||||
"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"
|
||||
@@ -101,6 +102,20 @@ func (t *NativeTun) Write(p []byte) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (t *NativeTun) CreateVectorisedWriter() N.VectorisedWriter {
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *NativeTun) WriteVectorised(buffers []*buf.Buffer) error {
|
||||
var packetHeader []byte
|
||||
if buffers[0].Byte(0)>>4 == 4 {
|
||||
packetHeader = packetHeader4[:]
|
||||
} else {
|
||||
packetHeader = packetHeader6[:]
|
||||
}
|
||||
return t.tunWriter.WriteVectorised(append([]*buf.Buffer{buf.As(packetHeader)}, buffers...))
|
||||
}
|
||||
|
||||
func (t *NativeTun) Close() error {
|
||||
flushDNSCache()
|
||||
return t.tunFile.Close()
|
||||
@@ -248,43 +263,16 @@ func configure(tunFd int, ifIndex int, name string, options Options) error {
|
||||
}
|
||||
}
|
||||
if options.AutoRoute {
|
||||
if len(options.Inet4Address) > 0 {
|
||||
var routes []netip.Prefix
|
||||
if len(options.Inet4RouteAddress) > 0 {
|
||||
routes = append(options.Inet4RouteAddress, netip.PrefixFrom(options.Inet4Address[0].Addr().Next(), 32))
|
||||
var routeRanges []netip.Prefix
|
||||
routeRanges, err = options.BuildAutoRouteRanges(false)
|
||||
for _, routeRange := range routeRanges {
|
||||
if routeRange.Addr().Is4() {
|
||||
err = addRoute(routeRange, options.Inet4Address[0].Addr())
|
||||
} else {
|
||||
routes = []netip.Prefix{
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{1, 0, 0, 0}), 8),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{2, 0, 0, 0}), 7),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{4, 0, 0, 0}), 6),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{8, 0, 0, 0}), 5),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{16, 0, 0, 0}), 4),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{32, 0, 0, 0}), 3),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{64, 0, 0, 0}), 2),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{128, 0, 0, 0}), 1),
|
||||
}
|
||||
err = addRoute(routeRange, options.Inet6Address[0].Addr())
|
||||
}
|
||||
for _, subnet := range routes {
|
||||
err = addRoute(subnet, options.Inet4Address[0].Addr())
|
||||
if err != nil {
|
||||
return E.Cause(err, "add ipv4 route ", subnet)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(options.Inet6Address) > 0 {
|
||||
var routes []netip.Prefix
|
||||
if len(options.Inet6RouteAddress) > 0 {
|
||||
routes = append(options.Inet6RouteAddress, netip.PrefixFrom(options.Inet6Address[0].Addr().Next(), 128))
|
||||
} else {
|
||||
routes = []netip.Prefix{
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}), 3),
|
||||
}
|
||||
}
|
||||
for _, subnet := range routes {
|
||||
err = addRoute(subnet, options.Inet6Address[0].Addr())
|
||||
if err != nil {
|
||||
return E.Cause(err, "add ipv6 route ", subnet)
|
||||
}
|
||||
if err != nil {
|
||||
return E.Cause(err, "add route: ", routeRange)
|
||||
}
|
||||
}
|
||||
flushDNSCache()
|
||||
|
||||
@@ -105,6 +105,10 @@ func (e *DarwinEndpoint) ARPHardwareType() header.ARPHardwareType {
|
||||
func (e *DarwinEndpoint) AddHeader(buffer stack.PacketBufferPtr) {
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) ParseHeader(ptr stack.PacketBufferPtr) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) WritePackets(packetBufferList stack.PacketBufferList) (int, tcpip.Error) {
|
||||
var n int
|
||||
for _, packet := range packetBufferList.AsSlice() {
|
||||
|
||||
253
tun_linux.go
253
tun_linux.go
@@ -12,7 +12,9 @@ import (
|
||||
|
||||
"github.com/sagernet/netlink"
|
||||
"github.com/sagernet/sing/common"
|
||||
"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/rw"
|
||||
"github.com/sagernet/sing/common/shell"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
@@ -68,6 +70,10 @@ func (t *NativeTun) Write(p []byte) (n int, err error) {
|
||||
return t.tunFile.Write(p)
|
||||
}
|
||||
|
||||
func (t *NativeTun) CreateVectorisedWriter() N.VectorisedWriter {
|
||||
return bufio.NewVectorisedWriter(t.tunFile)
|
||||
}
|
||||
|
||||
var controlPath string
|
||||
|
||||
func init() {
|
||||
@@ -182,57 +188,25 @@ func (t *NativeTun) Close() error {
|
||||
return E.Errors(t.unsetRoute(), t.unsetRules(), common.Close(common.PtrOrNil(t.tunFile)))
|
||||
}
|
||||
|
||||
func (t *NativeTun) routes(tunLink netlink.Link) []netlink.Route {
|
||||
var routes []netlink.Route
|
||||
if len(t.options.Inet4Address) > 0 {
|
||||
if t.options.AutoRoute {
|
||||
if len(t.options.Inet4RouteAddress) > 0 {
|
||||
for _, addr := range t.options.Inet4RouteAddress {
|
||||
routes = append(routes, netlink.Route{
|
||||
Dst: &net.IPNet{
|
||||
IP: addr.Addr().AsSlice(),
|
||||
Mask: net.CIDRMask(addr.Bits(), 32),
|
||||
},
|
||||
LinkIndex: tunLink.Attrs().Index,
|
||||
Table: t.options.TableIndex,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
routes = append(routes, netlink.Route{
|
||||
Dst: &net.IPNet{
|
||||
IP: net.IPv4zero,
|
||||
Mask: net.CIDRMask(0, 32),
|
||||
},
|
||||
LinkIndex: tunLink.Attrs().Index,
|
||||
Table: t.options.TableIndex,
|
||||
})
|
||||
}
|
||||
}
|
||||
func prefixToIPNet(prefix netip.Prefix) *net.IPNet {
|
||||
return &net.IPNet{
|
||||
IP: prefix.Addr().AsSlice(),
|
||||
Mask: net.CIDRMask(prefix.Bits(), prefix.Addr().BitLen()),
|
||||
}
|
||||
if len(t.options.Inet6Address) > 0 {
|
||||
if len(t.options.Inet6RouteAddress) > 0 {
|
||||
for _, addr := range t.options.Inet6RouteAddress {
|
||||
routes = append(routes, netlink.Route{
|
||||
Dst: &net.IPNet{
|
||||
IP: addr.Addr().AsSlice(),
|
||||
Mask: net.CIDRMask(addr.Bits(), 128),
|
||||
},
|
||||
LinkIndex: tunLink.Attrs().Index,
|
||||
Table: t.options.TableIndex,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
routes = append(routes, netlink.Route{
|
||||
Dst: &net.IPNet{
|
||||
IP: net.IPv6zero,
|
||||
Mask: net.CIDRMask(0, 128),
|
||||
},
|
||||
LinkIndex: tunLink.Attrs().Index,
|
||||
Table: t.options.TableIndex,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (t *NativeTun) routes(tunLink netlink.Link) ([]netlink.Route, error) {
|
||||
routeRanges, err := t.options.BuildAutoRouteRanges(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return routes
|
||||
return common.Map(routeRanges, func(it netip.Prefix) netlink.Route {
|
||||
return netlink.Route{
|
||||
Dst: prefixToIPNet(it),
|
||||
LinkIndex: tunLink.Attrs().Index,
|
||||
Table: t.options.TableIndex,
|
||||
}
|
||||
}), nil
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -317,6 +291,110 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
priority6++
|
||||
}
|
||||
}
|
||||
if len(t.options.IncludeInterface) > 0 {
|
||||
matchPriority := priority + 2*len(t.options.IncludeInterface) + 1
|
||||
for _, includeInterface := range t.options.IncludeInterface {
|
||||
if p4 {
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority
|
||||
it.IifName = includeInterface
|
||||
it.Goto = matchPriority
|
||||
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()
|
||||
it.Priority = priority6
|
||||
it.IifName = includeInterface
|
||||
it.Goto = matchPriority
|
||||
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 {
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority
|
||||
it.Family = unix.AF_INET
|
||||
it.Goto = nopPriority
|
||||
rules = append(rules, it)
|
||||
priority++
|
||||
|
||||
it = netlink.NewRule()
|
||||
it.Priority = matchPriority
|
||||
it.Family = unix.AF_INET
|
||||
rules = append(rules, it)
|
||||
priority++
|
||||
}
|
||||
if p6 {
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority6
|
||||
it.Family = unix.AF_INET6
|
||||
it.Goto = nopPriority
|
||||
rules = append(rules, it)
|
||||
priority6++
|
||||
|
||||
it = netlink.NewRule()
|
||||
it.Priority = matchPriority
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
priority6++
|
||||
}
|
||||
} else if len(t.options.ExcludeInterface) > 0 {
|
||||
for _, excludeInterface := range t.options.ExcludeInterface {
|
||||
if p4 {
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority
|
||||
it.IifName = excludeInterface
|
||||
it.Goto = nopPriority
|
||||
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()
|
||||
it.Priority = priority6
|
||||
it.IifName = excludeInterface
|
||||
it.Goto = nopPriority
|
||||
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++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS == "android" && t.options.InterfaceMonitor.AndroidVPNEnabled() {
|
||||
const protectedFromVPN = 0x20000
|
||||
@@ -462,36 +540,42 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
priority++
|
||||
}
|
||||
if p6 {
|
||||
// FIXME: this match connections from public address
|
||||
if !t.options.StrictRoute {
|
||||
for _, address := range t.options.Inet6Address {
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority6
|
||||
it.IifName = "lo"
|
||||
it.Src = address.Masked()
|
||||
it.Table = t.options.TableIndex
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
}
|
||||
priority6++
|
||||
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority6
|
||||
it.IifName = "lo"
|
||||
it.Src = netip.PrefixFrom(netip.IPv6Unspecified(), 1)
|
||||
it.Goto = nopPriority
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority6
|
||||
it.IifName = "lo"
|
||||
it.Src = netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 128}), 1)
|
||||
it.Goto = nopPriority
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
|
||||
priority6++
|
||||
}
|
||||
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority6
|
||||
it.Table = t.options.TableIndex
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
|
||||
/*it = netlink.NewRule()
|
||||
it.Priority = priority
|
||||
it.Invert = true
|
||||
it.IifName = "lo"
|
||||
it.Table = tunTableIndex
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority
|
||||
it.IifName = "lo"
|
||||
it.Src = netip.PrefixFrom(netip.IPv6Unspecified(), 128) // not working
|
||||
it.Table = tunTableIndex
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority
|
||||
it.IifName = "lo"
|
||||
it.Src = t.options.Inet6Address.Masked()
|
||||
it.Table = tunTableIndex
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)*/
|
||||
priority6++
|
||||
}
|
||||
if p4 {
|
||||
@@ -510,7 +594,11 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
}
|
||||
|
||||
func (t *NativeTun) setRoute(tunLink netlink.Link) error {
|
||||
for i, route := range t.routes(tunLink) {
|
||||
routes, err := t.routes(tunLink)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i, route := range routes {
|
||||
err := netlink.RouteAdd(&route)
|
||||
if err != nil {
|
||||
return E.Cause(err, "add route ", i)
|
||||
@@ -541,8 +629,10 @@ func (t *NativeTun) unsetRoute() error {
|
||||
}
|
||||
|
||||
func (t *NativeTun) unsetRoute0(tunLink netlink.Link) error {
|
||||
for _, route := range t.routes(tunLink) {
|
||||
_ = netlink.RouteDel(&route)
|
||||
if routes, err := t.routes(tunLink); err == nil {
|
||||
for _, route := range routes {
|
||||
_ = netlink.RouteDel(&route)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -588,15 +678,16 @@ func (t *NativeTun) resetRules() error {
|
||||
return t.setRules()
|
||||
}
|
||||
|
||||
func (t *NativeTun) routeUpdate(event int) error {
|
||||
func (t *NativeTun) routeUpdate(event int) {
|
||||
if event&EventAndroidVPNUpdate == 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
err := t.resetRules()
|
||||
if err != nil {
|
||||
return E.Cause(err, "reset route")
|
||||
if t.options.Logger != nil {
|
||||
t.options.Logger.Error(E.Cause(err, "reset route"))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *NativeTun) setSearchDomainForSystemdResolved() {
|
||||
|
||||
75
tun_rules.go
75
tun_rules.go
@@ -2,13 +2,17 @@ package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/ranges"
|
||||
|
||||
"go4.org/netipx"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -96,3 +100,74 @@ func buildExcludedRanges(includeRanges []ranges.Range[uint32], excludeRanges []r
|
||||
}
|
||||
return ranges.Merge(uidRanges)
|
||||
}
|
||||
|
||||
const autoRouteUseSubRanges = runtime.GOOS == "darwin"
|
||||
|
||||
func (o *Options) BuildAutoRouteRanges(underNetworkExtension bool) ([]netip.Prefix, error) {
|
||||
var routeRanges []netip.Prefix
|
||||
if o.AutoRoute && len(o.Inet4Address) > 0 {
|
||||
var inet4Ranges []netip.Prefix
|
||||
if len(o.Inet4RouteAddress) > 0 {
|
||||
inet4Ranges = o.Inet4RouteAddress
|
||||
} else if autoRouteUseSubRanges && !underNetworkExtension {
|
||||
inet4Ranges = []netip.Prefix{
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{1, 0, 0, 0}), 8),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{2, 0, 0, 0}), 7),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{4, 0, 0, 0}), 6),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{8, 0, 0, 0}), 5),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{16, 0, 0, 0}), 4),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{32, 0, 0, 0}), 3),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{64, 0, 0, 0}), 2),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{128, 0, 0, 0}), 1),
|
||||
}
|
||||
} else {
|
||||
inet4Ranges = []netip.Prefix{netip.PrefixFrom(netip.IPv4Unspecified(), 0)}
|
||||
}
|
||||
if len(o.Inet4RouteExcludeAddress) == 0 {
|
||||
routeRanges = append(routeRanges, inet4Ranges...)
|
||||
} else {
|
||||
var builder netipx.IPSetBuilder
|
||||
for _, inet4Range := range inet4Ranges {
|
||||
builder.AddPrefix(inet4Range)
|
||||
}
|
||||
for _, prefix := range o.Inet4RouteExcludeAddress {
|
||||
builder.RemovePrefix(prefix)
|
||||
}
|
||||
resultSet, err := builder.IPSet()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "build IPv4 route address")
|
||||
}
|
||||
routeRanges = append(routeRanges, resultSet.Prefixes()...)
|
||||
}
|
||||
}
|
||||
if len(o.Inet6Address) > 0 {
|
||||
var inet6Ranges []netip.Prefix
|
||||
if len(o.Inet6RouteAddress) > 0 {
|
||||
inet6Ranges = o.Inet6RouteAddress
|
||||
} else if autoRouteUseSubRanges && !underNetworkExtension {
|
||||
inet6Ranges = []netip.Prefix{
|
||||
netip.PrefixFrom(netip.IPv6Unspecified(), 1),
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 128}), 1),
|
||||
}
|
||||
} else {
|
||||
inet6Ranges = []netip.Prefix{netip.PrefixFrom(netip.IPv6Unspecified(), 0)}
|
||||
}
|
||||
if len(o.Inet6RouteExcludeAddress) == 0 {
|
||||
routeRanges = append(routeRanges, inet6Ranges...)
|
||||
} else {
|
||||
var builder netipx.IPSetBuilder
|
||||
for _, inet6Range := range inet6Ranges {
|
||||
builder.AddPrefix(inet6Range)
|
||||
}
|
||||
for _, prefix := range o.Inet6RouteExcludeAddress {
|
||||
builder.RemovePrefix(prefix)
|
||||
}
|
||||
resultSet, err := builder.IPSet()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "build IPv6 route address")
|
||||
}
|
||||
routeRanges = append(routeRanges, resultSet.Prefixes()...)
|
||||
}
|
||||
}
|
||||
return routeRanges, nil
|
||||
}
|
||||
|
||||
@@ -16,7 +16,10 @@ import (
|
||||
"github.com/sagernet/sing-tun/internal/winipcfg"
|
||||
"github.com/sagernet/sing-tun/internal/winsys"
|
||||
"github.com/sagernet/sing-tun/internal/wintun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/windnsapi"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
@@ -85,38 +88,22 @@ func (t *NativeTun) configure() error {
|
||||
return E.Cause(err, "set ipv6 dns")
|
||||
}
|
||||
}
|
||||
if len(t.options.Inet4Address) > 0 || len(t.options.Inet6Address) > 0 {
|
||||
_ = luid.DisableDNSRegistration()
|
||||
}
|
||||
if t.options.AutoRoute {
|
||||
if len(t.options.Inet4Address) > 0 {
|
||||
if len(t.options.Inet4RouteAddress) > 0 {
|
||||
for _, addr := range t.options.Inet4RouteAddress {
|
||||
err := luid.AddRoute(addr, netip.IPv4Unspecified(), 0)
|
||||
if err != nil {
|
||||
return E.Cause(err, "add ipv4 route: ", addr)
|
||||
}
|
||||
}
|
||||
routeRanges, err := t.options.BuildAutoRouteRanges(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, routeRange := range routeRanges {
|
||||
if routeRange.Addr().Is4() {
|
||||
err = luid.AddRoute(routeRange, netip.IPv4Unspecified(), 0)
|
||||
} else {
|
||||
err := luid.AddRoute(netip.PrefixFrom(netip.IPv4Unspecified(), 0), netip.IPv4Unspecified(), 0)
|
||||
if err != nil {
|
||||
return E.Cause(err, "set ipv4 route")
|
||||
}
|
||||
err = luid.AddRoute(routeRange, netip.IPv6Unspecified(), 0)
|
||||
}
|
||||
}
|
||||
if len(t.options.Inet6Address) > 0 {
|
||||
if len(t.options.Inet6RouteAddress) > 0 {
|
||||
for _, addr := range t.options.Inet6RouteAddress {
|
||||
err := luid.AddRoute(addr, netip.IPv6Unspecified(), 0)
|
||||
if err != nil {
|
||||
return E.Cause(err, "add ipv6 route: ", addr)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err := luid.AddRoute(netip.PrefixFrom(netip.IPv6Unspecified(), 0), netip.IPv6Unspecified(), 0)
|
||||
if err != nil {
|
||||
return E.Cause(err, "set ipv6 route")
|
||||
}
|
||||
}
|
||||
}
|
||||
err := windnsapi.FlushResolverCache()
|
||||
err = windnsapi.FlushResolverCache()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -467,6 +454,15 @@ func (t *NativeTun) write(packetElementList [][]byte) (n int, err error) {
|
||||
return 0, fmt.Errorf("write failed: %w", err)
|
||||
}
|
||||
|
||||
func (t *NativeTun) CreateVectorisedWriter() N.VectorisedWriter {
|
||||
return t
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
@@ -102,6 +102,10 @@ func (e *WintunEndpoint) ARPHardwareType() header.ARPHardwareType {
|
||||
func (e *WintunEndpoint) AddHeader(buffer stack.PacketBufferPtr) {
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) ParseHeader(ptr stack.PacketBufferPtr) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) WritePackets(packetBufferList stack.PacketBufferList) (int, tcpip.Error) {
|
||||
var n int
|
||||
for _, packet := range packetBufferList.AsSlice() {
|
||||
|
||||
Reference in New Issue
Block a user