Skip to content

Commit 4997ad5

Browse files
committed
auto-redirect: Add route address set support for nftables
1 parent 85fe25a commit 4997ad5

14 files changed

+1242
-410
lines changed

go.mod

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,22 @@ require (
66
github.com/fsnotify/fsnotify v1.7.0
77
github.com/go-ole/go-ole v1.3.0
88
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f
9-
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba
10-
github.com/sagernet/nftables v0.3.0-beta.2
11-
github.com/sagernet/sing v0.5.0-alpha.9
9+
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a
10+
github.com/sagernet/nftables v0.3.0-beta.4
11+
github.com/sagernet/sing v0.5.0-alpha.10
1212
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
13+
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
1314
golang.org/x/net v0.26.0
1415
golang.org/x/sys v0.21.0
1516
)
1617

1718
require (
1819
github.com/google/btree v1.1.2 // indirect
19-
github.com/google/go-cmp v0.5.9 // indirect
20+
github.com/google/go-cmp v0.6.0 // indirect
2021
github.com/josharian/native v1.1.0 // indirect
2122
github.com/mdlayher/netlink v1.7.2 // indirect
2223
github.com/mdlayher/socket v0.4.1 // indirect
2324
github.com/vishvananda/netns v0.0.4 // indirect
24-
golang.org/x/sync v0.1.0 // indirect
25+
golang.org/x/sync v0.7.0 // indirect
2526
golang.org/x/time v0.5.0 // indirect
2627
)

go.sum

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
55
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
66
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
77
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
8-
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
9-
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
8+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
9+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
1010
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
1111
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
1212
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
@@ -16,21 +16,23 @@ github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8Ku
1616
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1717
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f h1:NkhuupzH5ch7b/Y/6ZHJWrnNLoiNnSJaow6DPb8VW2I=
1818
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f/go.mod h1:KXmw+ouSJNOsuRpg4wgwwCQuunrGz4yoAqQjsLjc6N0=
19-
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba h1:EY5AS7CCtfmARNv2zXUOrsEMPFDGYxaw65JzA2p51Vk=
20-
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
21-
github.com/sagernet/nftables v0.3.0-beta.2 h1:yKqMl4Dpb6nKxAmlE6fXjJRlLO2c1f2wyNFBg4hBr8w=
22-
github.com/sagernet/nftables v0.3.0-beta.2/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
23-
github.com/sagernet/sing v0.5.0-alpha.9 h1:Mmg+LCbaKXBeQD/ttzi0/MQa3NcUyfadIgkGzhQW7o0=
24-
github.com/sagernet/sing v0.5.0-alpha.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
19+
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
20+
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
21+
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
22+
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
23+
github.com/sagernet/sing v0.5.0-alpha.10 h1:kuHl10gpjbKQAdQfyogQU3u0CVnpqC3wrAHe/+BFaXc=
24+
github.com/sagernet/sing v0.5.0-alpha.10/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
2525
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
2626
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
2727
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
2828
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
2929
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
30+
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
31+
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
3032
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
3133
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
32-
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
33-
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
34+
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
35+
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
3436
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
3537
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
3638
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

monitor_darwin.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,9 @@ func (m *defaultInterfaceMonitor) checkUpdate() error {
154154
if routeMessage.Flags&unix.RTF_GATEWAY == 0 {
155155
continue
156156
}
157-
if routeMessage.Flags&unix.RTF_IFSCOPE != 0 {
158-
// continue
159-
}
157+
// if routeMessage.Flags&unix.RTF_IFSCOPE != 0 {
158+
//continue
159+
//}
160160
defaultInterface = routeInterface
161161
break
162162
}

redirect.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,33 @@ package tun
33
import (
44
"context"
55

6+
"github.com/sagernet/sing/common/control"
67
"github.com/sagernet/sing/common/logger"
8+
9+
"go4.org/netipx"
10+
)
11+
12+
const (
13+
DefaultAutoRedirectInputMark = 0x2023
14+
DefaultAutoRedirectOutputMark = 0x2024
715
)
816

917
type AutoRedirect interface {
1018
Start() error
1119
Close() error
20+
UpdateRouteAddressSet()
1221
}
1322

1423
type AutoRedirectOptions struct {
15-
TunOptions *Options
16-
Context context.Context
17-
Handler Handler
18-
Logger logger.Logger
19-
TableName string
20-
DisableNFTables bool
21-
CustomRedirectPort func() int
24+
TunOptions *Options
25+
Context context.Context
26+
Handler Handler
27+
Logger logger.Logger
28+
NetworkMonitor NetworkUpdateMonitor
29+
InterfaceFinder control.InterfaceFinder
30+
TableName string
31+
DisableNFTables bool
32+
CustomRedirectPort func() int
33+
RouteAddressSet *[]*netipx.IPSet
34+
RouteExcludeAddressSet *[]*netipx.IPSet
2235
}

redirect_iptables.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ func (r *autoRedirect) setupIPTables() error {
2929
}
3030

3131
func (r *autoRedirect) setupIPTablesForFamily(iptablesPath string) error {
32-
tableNameOutput := r.tableName + "-output"
32+
tableNameInput := r.tableName + "-input"
3333
tableNameForward := r.tableName + "-forward"
34+
tableNameOutput := r.tableName + "-output"
3435
tableNamePreRouteing := r.tableName + "-prerouting"
3536
redirectPort := r.redirectPort()
3637
// OUTPUT
@@ -51,6 +52,25 @@ func (r *autoRedirect) setupIPTablesForFamily(iptablesPath string) error {
5152
if r.androidSu {
5253
return nil
5354
}
55+
// INPUT
56+
err = r.runShell(iptablesPath, "-N", tableNameInput)
57+
if err != nil {
58+
return err
59+
}
60+
err = r.runShell(iptablesPath, "-A", tableNameInput,
61+
"-i", r.tunOptions.Name, "-j", "ACCEPT")
62+
if err != nil {
63+
return err
64+
}
65+
err = r.runShell(iptablesPath, "-A", tableNameInput,
66+
"-o", r.tunOptions.Name, "-j", "ACCEPT")
67+
if err != nil {
68+
return err
69+
}
70+
err = r.runShell(iptablesPath, "-I FORWARD -j", tableNameInput)
71+
if err != nil {
72+
return err
73+
}
5474
// FORWARD
5575
err = r.runShell(iptablesPath, "-N", tableNameForward)
5676
if err != nil {

redirect_linux.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@ import (
66
"os"
77
"os/exec"
88
"runtime"
9+
"time"
910

1011
"github.com/sagernet/nftables"
1112
"github.com/sagernet/sing/common"
13+
"github.com/sagernet/sing/common/control"
1214
E "github.com/sagernet/sing/common/exceptions"
1315
"github.com/sagernet/sing/common/logger"
1416
M "github.com/sagernet/sing/common/metadata"
17+
"github.com/sagernet/sing/common/x/list"
18+
19+
"go4.org/netipx"
1520
)
1621

1722
type autoRedirect struct {
@@ -20,6 +25,10 @@ type autoRedirect struct {
2025
handler Handler
2126
logger logger.Logger
2227
tableName string
28+
networkMonitor NetworkUpdateMonitor
29+
networkListener *list.Element[NetworkUpdateCallback]
30+
interfaceFinder control.InterfaceFinder
31+
localAddresses []netip.Prefix
2332
customRedirectPortFunc func() int
2433
customRedirectPort int
2534
redirectServer *redirectServer
@@ -30,6 +39,8 @@ type autoRedirect struct {
3039
useNFTables bool
3140
androidSu bool
3241
suPath string
42+
routeAddressSet *[]*netipx.IPSet
43+
routeExcludeAddressSet *[]*netipx.IPSet
3344
}
3445

3546
func NewAutoRedirect(options AutoRedirectOptions) (AutoRedirect, error) {
@@ -38,9 +49,13 @@ func NewAutoRedirect(options AutoRedirectOptions) (AutoRedirect, error) {
3849
ctx: options.Context,
3950
handler: options.Handler,
4051
logger: options.Logger,
52+
networkMonitor: options.NetworkMonitor,
53+
interfaceFinder: options.InterfaceFinder,
4154
tableName: options.TableName,
4255
useNFTables: runtime.GOOS != "android" && !options.DisableNFTables,
4356
customRedirectPortFunc: options.CustomRedirectPort,
57+
routeAddressSet: options.RouteAddressSet,
58+
routeExcludeAddressSet: options.RouteExcludeAddressSet,
4459
}
4560
var err error
4661
if runtime.GOOS == "android" {
@@ -116,11 +131,18 @@ func (r *autoRedirect) Start() error {
116131
}
117132
r.redirectServer = server
118133
}
134+
startAt := time.Now()
135+
var err error
119136
if r.useNFTables {
120-
return r.setupNFTables()
137+
err = r.setupNFTables()
121138
} else {
122-
return r.setupIPTables()
139+
err = r.setupIPTables()
140+
}
141+
if err != nil {
142+
return err
123143
}
144+
r.logger.Debug("auto-redirect configured in ", time.Since(startAt))
145+
return nil
124146
}
125147

126148
func (r *autoRedirect) Close() error {
@@ -134,6 +156,15 @@ func (r *autoRedirect) Close() error {
134156
)
135157
}
136158

159+
func (r *autoRedirect) UpdateRouteAddressSet() {
160+
if r.useNFTables {
161+
err := r.nftablesUpdateRouteAddressSet()
162+
if err != nil {
163+
r.logger.Error("update route address set: ", err)
164+
}
165+
}
166+
}
167+
137168
func (r *autoRedirect) initializeNFTables() error {
138169
nft, err := nftables.New()
139170
if err != nil {

0 commit comments

Comments
 (0)