Compare commits
9 Commits
v0.4.0-bet
...
v0.4.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49977c4d84 | ||
|
|
db923a2c7c | ||
|
|
aaaacba916 | ||
|
|
2f8200267b | ||
|
|
5d440412ba | ||
|
|
60a8276dc4 | ||
|
|
7c4975c412 | ||
|
|
c01b403a44 | ||
|
|
625ac412bb |
10
.github/workflows/lint.yml
vendored
10
.github/workflows/lint.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Lint
|
||||
name: lint
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -24,16 +24,16 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.22
|
||||
go-version: ^1.23
|
||||
- name: Cache go module
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
key: go-${{ hashFiles('**/go.sum') }}
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: latest
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Debug build
|
||||
name: test
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -16,7 +16,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Linux Debug build
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -24,14 +24,14 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.22
|
||||
go-version: ^1.23
|
||||
- name: Build
|
||||
run: |
|
||||
make test
|
||||
build_go120:
|
||||
name: Linux Debug build (Go 1.20)
|
||||
name: Linux (Go 1.20)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ~1.20
|
||||
continue-on-error: true
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
run: |
|
||||
make test
|
||||
build_go121:
|
||||
name: Linux Debug build (Go 1.21)
|
||||
name: Linux (Go 1.21)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -55,15 +55,31 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ~1.21
|
||||
continue-on-error: true
|
||||
- name: Build
|
||||
run: |
|
||||
make test
|
||||
build__windows:
|
||||
name: Windows Debug build
|
||||
build_go122:
|
||||
name: Linux (Go 1.22)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ~1.22
|
||||
continue-on-error: true
|
||||
- name: Build
|
||||
run: |
|
||||
make test
|
||||
build_windows:
|
||||
name: Windows
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -71,15 +87,15 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.22
|
||||
go-version: ^1.23
|
||||
continue-on-error: true
|
||||
- name: Build
|
||||
run: |
|
||||
make test
|
||||
build_darwin:
|
||||
name: macOS Debug build
|
||||
name: macOS
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -87,9 +103,9 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.22
|
||||
go-version: ^1.23
|
||||
continue-on-error: true
|
||||
- name: Build
|
||||
run: |
|
||||
@@ -3,18 +3,22 @@ linters:
|
||||
enable:
|
||||
- gofumpt
|
||||
- govet
|
||||
# - gci
|
||||
- gci
|
||||
- staticcheck
|
||||
- paralleltest
|
||||
- ineffassign
|
||||
|
||||
issues:
|
||||
exclude-dirs:
|
||||
- internal
|
||||
|
||||
linters-settings:
|
||||
# gci:
|
||||
# sections:
|
||||
# - standard
|
||||
# - prefix(github.com/sagernet/sing)
|
||||
# - default
|
||||
staticcheck:
|
||||
go: '1.19'
|
||||
gci:
|
||||
custom-order: true
|
||||
sections:
|
||||
- standard
|
||||
- prefix(github.com/sagernet/)
|
||||
- default
|
||||
|
||||
run:
|
||||
go: "1.23"
|
||||
3
Makefile
3
Makefile
@@ -27,4 +27,5 @@ lint_install:
|
||||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
|
||||
test:
|
||||
go test -v .
|
||||
go build -v .
|
||||
#go test -v .
|
||||
|
||||
5
go.mod
5
go.mod
@@ -3,12 +3,12 @@ module github.com/sagernet/sing-tun
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/go-ole/go-ole v1.3.0
|
||||
github.com/sagernet/fswatch v0.1.1
|
||||
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a
|
||||
github.com/sagernet/nftables v0.3.0-beta.4
|
||||
github.com/sagernet/sing v0.5.0-alpha.10
|
||||
github.com/sagernet/sing v0.5.0-alpha.11.0.20240625144910-6bd878184516
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
|
||||
golang.org/x/net v0.26.0
|
||||
@@ -16,6 +16,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
|
||||
6
go.sum
6
go.sum
@@ -14,14 +14,16 @@ github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU
|
||||
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
|
||||
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs=
|
||||
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
|
||||
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f h1:NkhuupzH5ch7b/Y/6ZHJWrnNLoiNnSJaow6DPb8VW2I=
|
||||
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f/go.mod h1:KXmw+ouSJNOsuRpg4wgwwCQuunrGz4yoAqQjsLjc6N0=
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
||||
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
||||
github.com/sagernet/sing v0.5.0-alpha.10 h1:kuHl10gpjbKQAdQfyogQU3u0CVnpqC3wrAHe/+BFaXc=
|
||||
github.com/sagernet/sing v0.5.0-alpha.10/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing v0.5.0-alpha.11.0.20240625144910-6bd878184516 h1:C5NYqSEQC2CcILDFhT31iZe5Kp5hFNEtdS9mnNWyW5c=
|
||||
github.com/sagernet/sing v0.5.0-alpha.11.0.20240625144910-6bd878184516/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
|
||||
@@ -78,12 +78,16 @@ func (m *defaultInterfaceMonitor) checkUpdate() error {
|
||||
continue
|
||||
}
|
||||
|
||||
if ifrow.Type == winipcfg.IfTypePropVirtual || ifrow.Type == winipcfg.IfTypeSoftwareLoopback {
|
||||
continue
|
||||
}
|
||||
|
||||
iface, err := row.InterfaceLUID.IPInterface(windows.AF_INET)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if ifrow.Type == winipcfg.IfTypePropVirtual || ifrow.Type == winipcfg.IfTypeSoftwareLoopback {
|
||||
if !iface.Connected {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
11
packages.go
11
packages.go
@@ -1,6 +1,6 @@
|
||||
package tun
|
||||
|
||||
import E "github.com/sagernet/sing/common/exceptions"
|
||||
import "github.com/sagernet/sing/common/logger"
|
||||
|
||||
type PackageManager interface {
|
||||
Start() error
|
||||
@@ -11,7 +11,14 @@ type PackageManager interface {
|
||||
SharedPackageByID(id uint32) (string, bool)
|
||||
}
|
||||
|
||||
type PackageManagerOptions struct {
|
||||
Callback PackageManagerCallback
|
||||
|
||||
// Logger is the logger to log errors
|
||||
// optional
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
type PackageManagerCallback interface {
|
||||
OnPackagesUpdated(packages int, sharedUsers int)
|
||||
E.Handler
|
||||
}
|
||||
|
||||
@@ -2,30 +2,33 @@ package tun
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/sagernet/fswatch"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/abx"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
type packageManager struct {
|
||||
callback PackageManagerCallback
|
||||
watcher *fsnotify.Watcher
|
||||
logger logger.Logger
|
||||
watcher *fswatch.Watcher
|
||||
idByPackage map[string]uint32
|
||||
sharedByPackage map[string]uint32
|
||||
packageById map[uint32]string
|
||||
sharedById map[uint32]string
|
||||
}
|
||||
|
||||
func NewPackageManager(callback PackageManagerCallback) (PackageManager, error) {
|
||||
return &packageManager{callback: callback}, nil
|
||||
func NewPackageManager(options PackageManagerOptions) (PackageManager, error) {
|
||||
return &packageManager{
|
||||
callback: options.Callback,
|
||||
logger: options.Logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *packageManager) Start() error {
|
||||
@@ -35,42 +38,33 @@ func (m *packageManager) Start() error {
|
||||
}
|
||||
err = m.startWatcher()
|
||||
if err != nil {
|
||||
m.callback.NewError(context.Background(), E.Cause(err, "create fsnotify watcher"))
|
||||
m.logger.Error(E.Cause(err, "create watcher for packages list"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *packageManager) startWatcher() error {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
watcher, err := fswatch.NewWatcher(fswatch.Options{
|
||||
Path: []string{"/data/system/packages.xml"},
|
||||
Direct: true,
|
||||
Callback: m.packagesUpdated,
|
||||
Logger: m.logger,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = watcher.Add("/data/system/packages.xml")
|
||||
err = watcher.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.watcher = watcher
|
||||
go m.loopUpdate()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *packageManager) loopUpdate() {
|
||||
for {
|
||||
select {
|
||||
case _, ok := <-m.watcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
err := m.updatePackages()
|
||||
if err != nil {
|
||||
m.callback.NewError(context.Background(), E.Cause(err, "update packages"))
|
||||
}
|
||||
case err, ok := <-m.watcher.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
m.callback.NewError(context.Background(), E.Cause(err, "fsnotify error"))
|
||||
}
|
||||
func (m *packageManager) packagesUpdated(path string) {
|
||||
err := m.updatePackages()
|
||||
if err != nil {
|
||||
m.logger.Error(E.Cause(err, "update packages"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,6 @@ package tun
|
||||
|
||||
import "os"
|
||||
|
||||
func NewPackageManager(callback PackageManagerCallback) (PackageManager, error) {
|
||||
func NewPackageManager(options PackageManagerOptions) (PackageManager, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func (r *autoRedirect) setupIPTablesForFamily(iptablesPath string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-I FORWARD -j", tableNameInput)
|
||||
err = r.runShell(iptablesPath, "-I INPUT -j", tableNameInput)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -235,18 +235,26 @@ func (r *autoRedirect) cleanupIPTables() {
|
||||
}
|
||||
|
||||
func (r *autoRedirect) cleanupIPTablesForFamily(iptablesPath string) {
|
||||
tableNameInput := r.tableName + "-input"
|
||||
tableNameOutput := r.tableName + "-output"
|
||||
tableNameForward := r.tableName + "-forward"
|
||||
tableNamePreRouteing := r.tableName + "-prerouting"
|
||||
|
||||
_ = r.runShell(iptablesPath, "-t nat -D OUTPUT -j", tableNameOutput)
|
||||
_ = r.runShell(iptablesPath, "-t nat -F", tableNameOutput)
|
||||
_ = r.runShell(iptablesPath, "-t nat -X", tableNameOutput)
|
||||
if runtime.GOOS == "android" {
|
||||
return
|
||||
}
|
||||
|
||||
_ = r.runShell(iptablesPath, "-D INPUT -j", tableNameInput)
|
||||
_ = r.runShell(iptablesPath, "-F", tableNameInput)
|
||||
_ = r.runShell(iptablesPath, "-X", tableNameInput)
|
||||
|
||||
_ = r.runShell(iptablesPath, "-D FORWARD -j", tableNameForward)
|
||||
_ = r.runShell(iptablesPath, "-F", tableNameForward)
|
||||
_ = r.runShell(iptablesPath, "-X", tableNameForward)
|
||||
|
||||
_ = r.runShell(iptablesPath, "-t nat -D PREROUTING -j", tableNamePreRouteing)
|
||||
_ = r.runShell(iptablesPath, "-t nat -F", tableNamePreRouteing)
|
||||
_ = r.runShell(iptablesPath, "-t nat -X", tableNamePreRouteing)
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/nftables"
|
||||
"github.com/sagernet/sing/common"
|
||||
@@ -81,7 +80,8 @@ func NewAutoRedirect(options AutoRedirectOptions) (AutoRedirect, error) {
|
||||
if r.useNFTables {
|
||||
err = r.initializeNFTables()
|
||||
if err != nil && err != os.ErrInvalid {
|
||||
r.logger.Debug("device has no nftables support: ", err)
|
||||
r.useNFTables = false
|
||||
r.logger.Debug("missing nftables support: ", err)
|
||||
}
|
||||
}
|
||||
if len(r.tunOptions.Inet4Address) > 0 {
|
||||
@@ -131,7 +131,6 @@ func (r *autoRedirect) Start() error {
|
||||
}
|
||||
r.redirectServer = server
|
||||
}
|
||||
startAt := time.Now()
|
||||
var err error
|
||||
if r.useNFTables {
|
||||
r.cleanupNFTables()
|
||||
@@ -140,11 +139,7 @@ func (r *autoRedirect) Start() error {
|
||||
r.cleanupIPTables()
|
||||
err = r.setupIPTables()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.logger.Debug("auto-redirect configured in ", time.Since(startAt))
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *autoRedirect) Close() error {
|
||||
|
||||
@@ -4,7 +4,6 @@ package tun
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sagernet/nftables"
|
||||
"github.com/sagernet/nftables/expr"
|
||||
@@ -77,42 +76,34 @@ func nftablesCreateIPSet(
|
||||
id uint32, name string, family nftables.TableFamily,
|
||||
setList []*netipx.IPSet, prefixList []netip.Prefix, appendDefault bool, update bool,
|
||||
) (*nftables.Set, error) {
|
||||
if len(prefixList) > 0 {
|
||||
var builder netipx.IPSetBuilder
|
||||
for _, prefix := range prefixList {
|
||||
builder.AddPrefix(prefix)
|
||||
}
|
||||
ipSet, err := builder.IPSet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setList = append(setList, ipSet)
|
||||
var builder netipx.IPSetBuilder
|
||||
for _, prefix := range prefixList {
|
||||
builder.AddPrefix(prefix)
|
||||
}
|
||||
ipSets := make([]*myIPSet, 0, len(setList))
|
||||
var rangeLen int
|
||||
for _, set := range setList {
|
||||
mySet := (*myIPSet)(unsafe.Pointer(set))
|
||||
ipSets = append(ipSets, mySet)
|
||||
rangeLen += len(mySet.rr)
|
||||
builder.AddSet(set)
|
||||
}
|
||||
setElements := make([]nftables.SetElement, 0, rangeLen)
|
||||
for _, mySet := range ipSets {
|
||||
for _, rr := range mySet.rr {
|
||||
if (family == nftables.TableFamilyIPv4) != rr.from.Is4() {
|
||||
continue
|
||||
}
|
||||
endAddr := rr.to.Next()
|
||||
if !endAddr.IsValid() {
|
||||
endAddr = rr.from
|
||||
}
|
||||
setElements = append(setElements, nftables.SetElement{
|
||||
Key: rr.from.AsSlice(),
|
||||
})
|
||||
setElements = append(setElements, nftables.SetElement{
|
||||
Key: endAddr.AsSlice(),
|
||||
IntervalEnd: true,
|
||||
})
|
||||
ipSet, err := builder.IPSet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ipRanges := ipSet.Ranges()
|
||||
setElements := make([]nftables.SetElement, 0, len(ipRanges))
|
||||
for _, rr := range ipRanges {
|
||||
if (family == nftables.TableFamilyIPv4) != rr.From().Is4() {
|
||||
continue
|
||||
}
|
||||
endAddr := rr.To().Next()
|
||||
if !endAddr.IsValid() {
|
||||
endAddr = rr.From()
|
||||
}
|
||||
setElements = append(setElements, nftables.SetElement{
|
||||
Key: rr.From().AsSlice(),
|
||||
})
|
||||
setElements = append(setElements, nftables.SetElement{
|
||||
Key: endAddr.AsSlice(),
|
||||
IntervalEnd: true,
|
||||
})
|
||||
}
|
||||
if len(prefixList) == 0 && appendDefault {
|
||||
if family == nftables.TableFamilyIPv4 {
|
||||
@@ -179,12 +170,3 @@ func nftablesCreateIPSet(
|
||||
}
|
||||
return mySet, nil
|
||||
}
|
||||
|
||||
type myIPSet struct {
|
||||
rr []myIPRange
|
||||
}
|
||||
|
||||
type myIPRange struct {
|
||||
from netip.Addr
|
||||
to netip.Addr
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/sagernet/nftables"
|
||||
"github.com/sagernet/nftables/binaryutil"
|
||||
"github.com/sagernet/nftables/expr"
|
||||
"github.com/sagernet/nftables/userdata"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/ranges"
|
||||
|
||||
@@ -245,84 +246,124 @@ func (r *autoRedirect) nftablesCreateExcludeRules(nft *nftables.Conn, table *nft
|
||||
}
|
||||
} else {
|
||||
if len(r.tunOptions.IncludeUID) > 0 {
|
||||
includeUID := &nftables.Set{
|
||||
Table: table,
|
||||
Anonymous: true,
|
||||
Constant: true,
|
||||
Interval: true,
|
||||
KeyType: nftables.TypeUID,
|
||||
}
|
||||
err := nft.AddSet(includeUID, common.FlatMap(r.tunOptions.IncludeUID, func(it ranges.Range[uint32]) []nftables.SetElement {
|
||||
return []nftables.SetElement{
|
||||
{
|
||||
Key: binaryutil.BigEndian.PutUint32(it.Start),
|
||||
},
|
||||
{
|
||||
Key: binaryutil.BigEndian.PutUint32(it.End + 1),
|
||||
IntervalEnd: true,
|
||||
},
|
||||
if len(r.tunOptions.IncludeUID) > 1 || r.tunOptions.IncludeUID[0].Start != r.tunOptions.IncludeUID[0].End {
|
||||
includeUID := &nftables.Set{
|
||||
Table: table,
|
||||
Anonymous: true,
|
||||
Constant: true,
|
||||
Interval: true,
|
||||
KeyType: nftables.TypeUID,
|
||||
}
|
||||
}))
|
||||
if err != nil {
|
||||
return err
|
||||
err := nft.AddSet(includeUID, common.FlatMap(r.tunOptions.IncludeUID, func(it ranges.Range[uint32]) []nftables.SetElement {
|
||||
return []nftables.SetElement{
|
||||
{
|
||||
Key: binaryutil.NativeEndian.PutUint32(it.Start),
|
||||
},
|
||||
{
|
||||
Key: binaryutil.NativeEndian.PutUint32(it.End + 1),
|
||||
IntervalEnd: true,
|
||||
},
|
||||
}
|
||||
}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chain,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Meta{Key: expr.MetaKeySKUID, Register: 1},
|
||||
&expr.Lookup{
|
||||
SourceRegister: 1,
|
||||
SetID: includeUID.ID,
|
||||
SetName: includeUID.Name,
|
||||
Invert: true,
|
||||
},
|
||||
&expr.Counter{},
|
||||
&expr.Verdict{
|
||||
Kind: expr.VerdictReturn,
|
||||
},
|
||||
},
|
||||
UserData: userdata.AppendString(nil, userdata.TypeComment, "not a bug :("),
|
||||
})
|
||||
} else {
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chain,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Meta{Key: expr.MetaKeySKUID, Register: 1},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpNeq,
|
||||
Register: 1,
|
||||
Data: binaryutil.BigEndian.PutUint32(r.tunOptions.IncludeUID[0].Start),
|
||||
},
|
||||
&expr.Counter{},
|
||||
&expr.Verdict{
|
||||
Kind: expr.VerdictReturn,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chain,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Meta{Key: expr.MetaKeySKUID, Register: 1},
|
||||
&expr.Lookup{
|
||||
SourceRegister: 1,
|
||||
SetID: includeUID.ID,
|
||||
SetName: includeUID.Name,
|
||||
Invert: true,
|
||||
},
|
||||
&expr.Counter{},
|
||||
&expr.Verdict{
|
||||
Kind: expr.VerdictReturn,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if len(r.tunOptions.ExcludeUID) > 0 {
|
||||
excludeUID := &nftables.Set{
|
||||
Table: table,
|
||||
Anonymous: true,
|
||||
Constant: true,
|
||||
Interval: true,
|
||||
KeyType: nftables.TypeUID,
|
||||
}
|
||||
err := nft.AddSet(excludeUID, common.FlatMap(r.tunOptions.ExcludeUID, func(it ranges.Range[uint32]) []nftables.SetElement {
|
||||
return []nftables.SetElement{
|
||||
{
|
||||
Key: binaryutil.BigEndian.PutUint32(it.Start),
|
||||
},
|
||||
{
|
||||
Key: binaryutil.BigEndian.PutUint32(it.End + 1),
|
||||
IntervalEnd: true,
|
||||
},
|
||||
if len(r.tunOptions.ExcludeUID) > 1 || r.tunOptions.ExcludeUID[0].Start != r.tunOptions.ExcludeUID[0].End {
|
||||
excludeUID := &nftables.Set{
|
||||
Table: table,
|
||||
Anonymous: true,
|
||||
Constant: true,
|
||||
Interval: true,
|
||||
KeyType: nftables.TypeUID,
|
||||
}
|
||||
}))
|
||||
if err != nil {
|
||||
return err
|
||||
err := nft.AddSet(excludeUID, common.FlatMap(r.tunOptions.ExcludeUID, func(it ranges.Range[uint32]) []nftables.SetElement {
|
||||
return []nftables.SetElement{
|
||||
{
|
||||
Key: binaryutil.NativeEndian.PutUint32(it.Start),
|
||||
},
|
||||
{
|
||||
Key: binaryutil.NativeEndian.PutUint32(it.End + 1),
|
||||
IntervalEnd: true,
|
||||
},
|
||||
}
|
||||
}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chain,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Meta{Key: expr.MetaKeySKUID, Register: 1},
|
||||
&expr.Lookup{
|
||||
SourceRegister: 1,
|
||||
SetID: excludeUID.ID,
|
||||
SetName: excludeUID.Name,
|
||||
},
|
||||
&expr.Counter{},
|
||||
&expr.Verdict{
|
||||
Kind: expr.VerdictReturn,
|
||||
},
|
||||
},
|
||||
UserData: userdata.AppendString(nil, userdata.TypeComment, "not a bug :("),
|
||||
})
|
||||
} else {
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chain,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Meta{Key: expr.MetaKeySKUID, Register: 1},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpEq,
|
||||
Register: 1,
|
||||
Data: binaryutil.NativeEndian.PutUint32(r.tunOptions.ExcludeUID[0].Start),
|
||||
},
|
||||
&expr.Counter{},
|
||||
&expr.Verdict{
|
||||
Kind: expr.VerdictReturn,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chain,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Meta{Key: expr.MetaKeySKUID, Register: 1},
|
||||
&expr.Lookup{
|
||||
SourceRegister: 1,
|
||||
SetID: excludeUID.ID,
|
||||
SetName: excludeUID.Name,
|
||||
},
|
||||
&expr.Counter{},
|
||||
&expr.Verdict{
|
||||
Kind: expr.VerdictReturn,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,51 +578,70 @@ func (r *autoRedirect) nftablesCreateDNSHijackRulesForFamily(
|
||||
dnsServer = r.tunOptions.Inet6Address[0].Addr().Next()
|
||||
}
|
||||
}
|
||||
exprs := []expr.Any{
|
||||
&expr.Meta{
|
||||
Key: expr.MetaKeyNFPROTO,
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpEq,
|
||||
Register: 1,
|
||||
Data: []byte{uint8(family)},
|
||||
},
|
||||
}
|
||||
if chain.Hooknum == nftables.ChainHookOutput {
|
||||
// It looks like we can't hijack DNS requests sent to loopback.
|
||||
// https://serverfault.com/questions/363899/iptables-dnat-from-loopback
|
||||
// and tproxy is not available in output
|
||||
exprs = append(exprs,
|
||||
&expr.Meta{
|
||||
Key: expr.MetaKeyOIFNAME,
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpNeq,
|
||||
Register: 1,
|
||||
Data: nftablesIfname("lo"),
|
||||
},
|
||||
)
|
||||
}
|
||||
exprs = append(exprs,
|
||||
&expr.Meta{
|
||||
Key: expr.MetaKeyL4PROTO,
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Lookup{
|
||||
SourceRegister: 1,
|
||||
SetID: ipProto.ID,
|
||||
SetName: ipProto.Name,
|
||||
},
|
||||
&expr.Payload{
|
||||
OperationType: expr.PayloadLoad,
|
||||
DestRegister: 1,
|
||||
Base: expr.PayloadBaseTransportHeader,
|
||||
Offset: 2,
|
||||
Len: 2,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpEq,
|
||||
Register: 1,
|
||||
Data: binaryutil.BigEndian.PutUint16(53),
|
||||
},
|
||||
&expr.Immediate{
|
||||
Register: 1,
|
||||
Data: dnsServer.AsSlice(),
|
||||
},
|
||||
&expr.NAT{
|
||||
Type: expr.NATTypeDestNAT,
|
||||
Family: uint32(family),
|
||||
RegAddrMin: 1,
|
||||
},
|
||||
&expr.Counter{},
|
||||
)
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chain,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Meta{
|
||||
Key: expr.MetaKeyNFPROTO,
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpEq,
|
||||
Register: 1,
|
||||
Data: []byte{uint8(family)},
|
||||
},
|
||||
&expr.Meta{
|
||||
Key: expr.MetaKeyL4PROTO,
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Lookup{
|
||||
SourceRegister: 1,
|
||||
SetID: ipProto.ID,
|
||||
SetName: ipProto.Name,
|
||||
},
|
||||
&expr.Payload{
|
||||
OperationType: expr.PayloadLoad,
|
||||
DestRegister: 1,
|
||||
Base: expr.PayloadBaseTransportHeader,
|
||||
Offset: 2,
|
||||
Len: 2,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpEq,
|
||||
Register: 1,
|
||||
Data: binaryutil.BigEndian.PutUint16(53),
|
||||
},
|
||||
&expr.Counter{},
|
||||
&expr.Immediate{
|
||||
Register: 1,
|
||||
Data: dnsServer.AsSlice(),
|
||||
},
|
||||
&expr.NAT{
|
||||
Type: expr.NATTypeDestNAT,
|
||||
Family: uint32(family),
|
||||
RegAddrMin: 1,
|
||||
},
|
||||
},
|
||||
Exprs: exprs,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ var controlPath string
|
||||
func init() {
|
||||
const defaultTunPath = "/dev/net/tun"
|
||||
const androidTunPath = "/dev/tun"
|
||||
if rw.FileExists(androidTunPath) {
|
||||
if rw.IsFile(androidTunPath) {
|
||||
controlPath = androidTunPath
|
||||
} else {
|
||||
controlPath = defaultTunPath
|
||||
@@ -712,7 +712,7 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
it.Family = unix.AF_INET
|
||||
rules = append(rules, it)
|
||||
}
|
||||
priority++
|
||||
// priority++
|
||||
}
|
||||
if p6 {
|
||||
it = netlink.NewRule()
|
||||
@@ -755,7 +755,7 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
it.Table = t.options.IPRoute2TableIndex
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
priority6++
|
||||
// priority6++
|
||||
}
|
||||
if p4 {
|
||||
it = netlink.NewRule()
|
||||
|
||||
Reference in New Issue
Block a user