Skip to content

Commit 5077961

Browse files
committed
Add route exclude support
1 parent 86322a3 commit 5077961

File tree

7 files changed

+141
-133
lines changed

7 files changed

+141
-133
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
1111
github.com/sagernet/sing v0.2.17
1212
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9
13+
go4.org/netipx v0.0.0-20230824141953-6213f710f925
1314
golang.org/x/net v0.18.0
1415
golang.org/x/sys v0.14.0
1516
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh
1717
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
1818
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
1919
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
20+
go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ=
21+
go4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
2022
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
2123
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
2224
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

tun.go

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,27 @@ type WinTun interface {
3333
}
3434

3535
type Options struct {
36-
Name string
37-
Inet4Address []netip.Prefix
38-
Inet6Address []netip.Prefix
39-
MTU uint32
40-
AutoRoute bool
41-
StrictRoute bool
42-
Inet4RouteAddress []netip.Prefix
43-
Inet6RouteAddress []netip.Prefix
44-
IncludeInterface []string
45-
ExcludeInterface []string
46-
IncludeUID []ranges.Range[uint32]
47-
ExcludeUID []ranges.Range[uint32]
48-
IncludeAndroidUser []int
49-
IncludePackage []string
50-
ExcludePackage []string
51-
InterfaceMonitor DefaultInterfaceMonitor
52-
TableIndex int
53-
FileDescriptor int
54-
Logger logger.Logger
36+
Name string
37+
Inet4Address []netip.Prefix
38+
Inet6Address []netip.Prefix
39+
MTU uint32
40+
AutoRoute bool
41+
StrictRoute bool
42+
Inet4RouteAddress []netip.Prefix
43+
Inet6RouteAddress []netip.Prefix
44+
Inet4RouteExcludeAddress []netip.Prefix
45+
Inet6RouteExcludeAddress []netip.Prefix
46+
IncludeInterface []string
47+
ExcludeInterface []string
48+
IncludeUID []ranges.Range[uint32]
49+
ExcludeUID []ranges.Range[uint32]
50+
IncludeAndroidUser []int
51+
IncludePackage []string
52+
ExcludePackage []string
53+
InterfaceMonitor DefaultInterfaceMonitor
54+
TableIndex int
55+
FileDescriptor int
56+
Logger logger.Logger
5557
}
5658

5759
func CalculateInterfaceName(name string) (tunName string) {

tun_darwin.go

Lines changed: 8 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -263,43 +263,16 @@ func configure(tunFd int, ifIndex int, name string, options Options) error {
263263
}
264264
}
265265
if options.AutoRoute {
266-
if len(options.Inet4Address) > 0 {
267-
var routes []netip.Prefix
268-
if len(options.Inet4RouteAddress) > 0 {
269-
routes = append(options.Inet4RouteAddress, netip.PrefixFrom(options.Inet4Address[0].Addr().Next(), 32))
266+
var routeRanges []netip.Prefix
267+
routeRanges, err = options.BuildAutoRouteRanges(false)
268+
for _, routeRange := range routeRanges {
269+
if routeRange.Addr().Is4() {
270+
err = addRoute(routeRange, options.Inet4Address[0].Addr())
270271
} else {
271-
routes = []netip.Prefix{
272-
netip.PrefixFrom(netip.AddrFrom4([4]byte{1, 0, 0, 0}), 8),
273-
netip.PrefixFrom(netip.AddrFrom4([4]byte{2, 0, 0, 0}), 7),
274-
netip.PrefixFrom(netip.AddrFrom4([4]byte{4, 0, 0, 0}), 6),
275-
netip.PrefixFrom(netip.AddrFrom4([4]byte{8, 0, 0, 0}), 5),
276-
netip.PrefixFrom(netip.AddrFrom4([4]byte{16, 0, 0, 0}), 4),
277-
netip.PrefixFrom(netip.AddrFrom4([4]byte{32, 0, 0, 0}), 3),
278-
netip.PrefixFrom(netip.AddrFrom4([4]byte{64, 0, 0, 0}), 2),
279-
netip.PrefixFrom(netip.AddrFrom4([4]byte{128, 0, 0, 0}), 1),
280-
}
281-
}
282-
for _, subnet := range routes {
283-
err = addRoute(subnet, options.Inet4Address[0].Addr())
284-
if err != nil {
285-
return E.Cause(err, "add ipv4 route ", subnet)
286-
}
272+
err = addRoute(routeRange, options.Inet6Address[0].Addr())
287273
}
288-
}
289-
if len(options.Inet6Address) > 0 {
290-
var routes []netip.Prefix
291-
if len(options.Inet6RouteAddress) > 0 {
292-
routes = append(options.Inet6RouteAddress, netip.PrefixFrom(options.Inet6Address[0].Addr().Next(), 128))
293-
} else {
294-
routes = []netip.Prefix{
295-
netip.PrefixFrom(netip.AddrFrom16([16]byte{32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}), 3),
296-
}
297-
}
298-
for _, subnet := range routes {
299-
err = addRoute(subnet, options.Inet6Address[0].Addr())
300-
if err != nil {
301-
return E.Cause(err, "add ipv6 route ", subnet)
302-
}
274+
if err != nil {
275+
return E.Cause(err, "add route: ", routeRange)
303276
}
304277
}
305278
flushDNSCache()

tun_linux.go

Lines changed: 26 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -188,57 +188,25 @@ func (t *NativeTun) Close() error {
188188
return E.Errors(t.unsetRoute(), t.unsetRules(), common.Close(common.PtrOrNil(t.tunFile)))
189189
}
190190

191-
func (t *NativeTun) routes(tunLink netlink.Link) []netlink.Route {
192-
var routes []netlink.Route
193-
if len(t.options.Inet4Address) > 0 {
194-
if t.options.AutoRoute {
195-
if len(t.options.Inet4RouteAddress) > 0 {
196-
for _, addr := range t.options.Inet4RouteAddress {
197-
routes = append(routes, netlink.Route{
198-
Dst: &net.IPNet{
199-
IP: addr.Addr().AsSlice(),
200-
Mask: net.CIDRMask(addr.Bits(), 32),
201-
},
202-
LinkIndex: tunLink.Attrs().Index,
203-
Table: t.options.TableIndex,
204-
})
205-
}
206-
} else {
207-
routes = append(routes, netlink.Route{
208-
Dst: &net.IPNet{
209-
IP: net.IPv4zero,
210-
Mask: net.CIDRMask(0, 32),
211-
},
212-
LinkIndex: tunLink.Attrs().Index,
213-
Table: t.options.TableIndex,
214-
})
215-
}
216-
}
191+
func prefixToIPNet(prefix netip.Prefix) *net.IPNet {
192+
return &net.IPNet{
193+
IP: prefix.Addr().AsSlice(),
194+
Mask: net.CIDRMask(prefix.Bits(), prefix.Addr().BitLen()),
217195
}
218-
if len(t.options.Inet6Address) > 0 {
219-
if len(t.options.Inet6RouteAddress) > 0 {
220-
for _, addr := range t.options.Inet6RouteAddress {
221-
routes = append(routes, netlink.Route{
222-
Dst: &net.IPNet{
223-
IP: addr.Addr().AsSlice(),
224-
Mask: net.CIDRMask(addr.Bits(), 128),
225-
},
226-
LinkIndex: tunLink.Attrs().Index,
227-
Table: t.options.TableIndex,
228-
})
229-
}
230-
} else {
231-
routes = append(routes, netlink.Route{
232-
Dst: &net.IPNet{
233-
IP: net.IPv6zero,
234-
Mask: net.CIDRMask(0, 128),
235-
},
236-
LinkIndex: tunLink.Attrs().Index,
237-
Table: t.options.TableIndex,
238-
})
239-
}
196+
}
197+
198+
func (t *NativeTun) routes(tunLink netlink.Link) ([]netlink.Route, error) {
199+
routeRanges, err := t.options.BuildAutoRouteRanges(false)
200+
if err != nil {
201+
return nil, err
240202
}
241-
return routes
203+
return common.Map(routeRanges, func(it netip.Prefix) netlink.Route {
204+
return netlink.Route{
205+
Dst: prefixToIPNet(it),
206+
LinkIndex: tunLink.Attrs().Index,
207+
Table: t.options.TableIndex,
208+
}
209+
}), nil
242210
}
243211

244212
const (
@@ -615,7 +583,11 @@ func (t *NativeTun) rules() []*netlink.Rule {
615583
}
616584

617585
func (t *NativeTun) setRoute(tunLink netlink.Link) error {
618-
for i, route := range t.routes(tunLink) {
586+
routes, err := t.routes(tunLink)
587+
if err != nil {
588+
return err
589+
}
590+
for i, route := range routes {
619591
err := netlink.RouteAdd(&route)
620592
if err != nil {
621593
return E.Cause(err, "add route ", i)
@@ -646,8 +618,10 @@ func (t *NativeTun) unsetRoute() error {
646618
}
647619

648620
func (t *NativeTun) unsetRoute0(tunLink netlink.Link) error {
649-
for _, route := range t.routes(tunLink) {
650-
_ = netlink.RouteDel(&route)
621+
if routes, err := t.routes(tunLink); err == nil {
622+
for _, route := range routes {
623+
_ = netlink.RouteDel(&route)
624+
}
651625
}
652626
return nil
653627
}

tun_rules.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@ package tun
22

33
import (
44
"context"
5+
"net/netip"
56
"os"
7+
"runtime"
68
"sort"
79
"strconv"
810

911
"github.com/sagernet/sing/common"
1012
E "github.com/sagernet/sing/common/exceptions"
1113
"github.com/sagernet/sing/common/ranges"
14+
15+
"go4.org/netipx"
1216
)
1317

1418
const (
@@ -96,3 +100,74 @@ func buildExcludedRanges(includeRanges []ranges.Range[uint32], excludeRanges []r
96100
}
97101
return ranges.Merge(uidRanges)
98102
}
103+
104+
const autoRouteUseSubRanges = runtime.GOOS == "darwin"
105+
106+
func (o *Options) BuildAutoRouteRanges(underNetworkExtension bool) ([]netip.Prefix, error) {
107+
var routeRanges []netip.Prefix
108+
if o.AutoRoute && len(o.Inet4Address) > 0 {
109+
var inet4Ranges []netip.Prefix
110+
if len(o.Inet4RouteAddress) > 0 {
111+
inet4Ranges = o.Inet4RouteAddress
112+
} else if autoRouteUseSubRanges && !underNetworkExtension {
113+
inet4Ranges = []netip.Prefix{
114+
netip.PrefixFrom(netip.AddrFrom4([4]byte{1, 0, 0, 0}), 8),
115+
netip.PrefixFrom(netip.AddrFrom4([4]byte{2, 0, 0, 0}), 7),
116+
netip.PrefixFrom(netip.AddrFrom4([4]byte{4, 0, 0, 0}), 6),
117+
netip.PrefixFrom(netip.AddrFrom4([4]byte{8, 0, 0, 0}), 5),
118+
netip.PrefixFrom(netip.AddrFrom4([4]byte{16, 0, 0, 0}), 4),
119+
netip.PrefixFrom(netip.AddrFrom4([4]byte{32, 0, 0, 0}), 3),
120+
netip.PrefixFrom(netip.AddrFrom4([4]byte{64, 0, 0, 0}), 2),
121+
netip.PrefixFrom(netip.AddrFrom4([4]byte{128, 0, 0, 0}), 1),
122+
}
123+
} else {
124+
inet4Ranges = []netip.Prefix{netip.PrefixFrom(netip.IPv4Unspecified(), 0)}
125+
}
126+
if len(o.Inet4RouteExcludeAddress) == 0 {
127+
routeRanges = append(routeRanges, inet4Ranges...)
128+
} else {
129+
var builder netipx.IPSetBuilder
130+
for _, inet4Range := range inet4Ranges {
131+
builder.AddPrefix(inet4Range)
132+
}
133+
for _, prefix := range o.Inet4RouteExcludeAddress {
134+
builder.RemovePrefix(prefix)
135+
}
136+
resultSet, err := builder.IPSet()
137+
if err != nil {
138+
return nil, E.Cause(err, "build IPv4 route address")
139+
}
140+
routeRanges = append(routeRanges, resultSet.Prefixes()...)
141+
}
142+
}
143+
if len(o.Inet6Address) > 0 {
144+
var inet6Ranges []netip.Prefix
145+
if len(o.Inet6RouteAddress) > 0 {
146+
inet6Ranges = o.Inet6RouteAddress
147+
} else if autoRouteUseSubRanges && !underNetworkExtension {
148+
inet6Ranges = []netip.Prefix{
149+
netip.PrefixFrom(netip.IPv6Unspecified(), 1),
150+
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 128}), 1),
151+
}
152+
} else {
153+
inet6Ranges = []netip.Prefix{netip.PrefixFrom(netip.IPv6Unspecified(), 0)}
154+
}
155+
if len(o.Inet6RouteExcludeAddress) == 0 {
156+
routeRanges = append(routeRanges, inet6Ranges...)
157+
} else {
158+
var builder netipx.IPSetBuilder
159+
for _, inet6Range := range inet6Ranges {
160+
builder.AddPrefix(inet6Range)
161+
}
162+
for _, prefix := range o.Inet6RouteExcludeAddress {
163+
builder.RemovePrefix(prefix)
164+
}
165+
resultSet, err := builder.IPSet()
166+
if err != nil {
167+
return nil, E.Cause(err, "build IPv6 route address")
168+
}
169+
routeRanges = append(routeRanges, resultSet.Prefixes()...)
170+
}
171+
}
172+
return routeRanges, nil
173+
}

tun_windows.go

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -92,37 +92,18 @@ func (t *NativeTun) configure() error {
9292
_ = luid.DisableDNSRegistration()
9393
}
9494
if t.options.AutoRoute {
95-
if len(t.options.Inet4Address) > 0 {
96-
if len(t.options.Inet4RouteAddress) > 0 {
97-
for _, addr := range t.options.Inet4RouteAddress {
98-
err := luid.AddRoute(addr, netip.IPv4Unspecified(), 0)
99-
if err != nil {
100-
return E.Cause(err, "add ipv4 route: ", addr)
101-
}
102-
}
103-
} else {
104-
err := luid.AddRoute(netip.PrefixFrom(netip.IPv4Unspecified(), 0), netip.IPv4Unspecified(), 0)
105-
if err != nil {
106-
return E.Cause(err, "set ipv4 route")
107-
}
108-
}
95+
routeRanges, err := t.options.BuildAutoRouteRanges(false)
96+
if err != nil {
97+
return err
10998
}
110-
if len(t.options.Inet6Address) > 0 {
111-
if len(t.options.Inet6RouteAddress) > 0 {
112-
for _, addr := range t.options.Inet6RouteAddress {
113-
err := luid.AddRoute(addr, netip.IPv6Unspecified(), 0)
114-
if err != nil {
115-
return E.Cause(err, "add ipv6 route: ", addr)
116-
}
117-
}
99+
for _, routeRange := range routeRanges {
100+
if routeRange.Addr().Is4() {
101+
err = luid.AddRoute(routeRange, netip.IPv4Unspecified(), 0)
118102
} else {
119-
err := luid.AddRoute(netip.PrefixFrom(netip.IPv6Unspecified(), 0), netip.IPv6Unspecified(), 0)
120-
if err != nil {
121-
return E.Cause(err, "set ipv6 route")
122-
}
103+
err = luid.AddRoute(routeRange, netip.IPv6Unspecified(), 0)
123104
}
124105
}
125-
err := windnsapi.FlushResolverCache()
106+
err = windnsapi.FlushResolverCache()
126107
if err != nil {
127108
return err
128109
}

0 commit comments

Comments
 (0)