mirror of
https://github.com/SagerNet/sing-tun.git
synced 2025-10-05 16:47:27 +08:00
Add route exclude support
This commit is contained in:
1
go.mod
1
go.mod
@@ -10,6 +10,7 @@ require (
|
|||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
|
||||||
github.com/sagernet/sing v0.2.17
|
github.com/sagernet/sing v0.2.17
|
||||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9
|
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9
|
||||||
|
go4.org/netipx v0.0.0-20230824141953-6213f710f925
|
||||||
golang.org/x/net v0.18.0
|
golang.org/x/net v0.18.0
|
||||||
golang.org/x/sys v0.14.0
|
golang.org/x/sys v0.14.0
|
||||||
)
|
)
|
||||||
|
2
go.sum
2
go.sum
@@ -17,6 +17,8 @@ github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh
|
|||||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
|
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 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
|
go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ=
|
||||||
|
go4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||||
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
||||||
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
||||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
2
tun.go
2
tun.go
@@ -41,6 +41,8 @@ type Options struct {
|
|||||||
StrictRoute bool
|
StrictRoute bool
|
||||||
Inet4RouteAddress []netip.Prefix
|
Inet4RouteAddress []netip.Prefix
|
||||||
Inet6RouteAddress []netip.Prefix
|
Inet6RouteAddress []netip.Prefix
|
||||||
|
Inet4RouteExcludeAddress []netip.Prefix
|
||||||
|
Inet6RouteExcludeAddress []netip.Prefix
|
||||||
IncludeInterface []string
|
IncludeInterface []string
|
||||||
ExcludeInterface []string
|
ExcludeInterface []string
|
||||||
IncludeUID []ranges.Range[uint32]
|
IncludeUID []ranges.Range[uint32]
|
||||||
|
@@ -263,43 +263,16 @@ func configure(tunFd int, ifIndex int, name string, options Options) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if options.AutoRoute {
|
if options.AutoRoute {
|
||||||
if len(options.Inet4Address) > 0 {
|
var routeRanges []netip.Prefix
|
||||||
var routes []netip.Prefix
|
routeRanges, err = options.BuildAutoRouteRanges(false)
|
||||||
if len(options.Inet4RouteAddress) > 0 {
|
for _, routeRange := range routeRanges {
|
||||||
routes = append(options.Inet4RouteAddress, netip.PrefixFrom(options.Inet4Address[0].Addr().Next(), 32))
|
if routeRange.Addr().Is4() {
|
||||||
|
err = addRoute(routeRange, options.Inet4Address[0].Addr())
|
||||||
} else {
|
} else {
|
||||||
routes = []netip.Prefix{
|
err = addRoute(routeRange, options.Inet6Address[0].Addr())
|
||||||
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),
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
for _, subnet := range routes {
|
|
||||||
err = addRoute(subnet, options.Inet4Address[0].Addr())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "add ipv4 route ", subnet)
|
return E.Cause(err, "add route: ", routeRange)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flushDNSCache()
|
flushDNSCache()
|
||||||
|
74
tun_linux.go
74
tun_linux.go
@@ -188,57 +188,25 @@ func (t *NativeTun) Close() error {
|
|||||||
return E.Errors(t.unsetRoute(), t.unsetRules(), common.Close(common.PtrOrNil(t.tunFile)))
|
return E.Errors(t.unsetRoute(), t.unsetRules(), common.Close(common.PtrOrNil(t.tunFile)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *NativeTun) routes(tunLink netlink.Link) []netlink.Route {
|
func prefixToIPNet(prefix netip.Prefix) *net.IPNet {
|
||||||
var routes []netlink.Route
|
return &net.IPNet{
|
||||||
if len(t.options.Inet4Address) > 0 {
|
IP: prefix.Addr().AsSlice(),
|
||||||
if t.options.AutoRoute {
|
Mask: net.CIDRMask(prefix.Bits(), prefix.Addr().BitLen()),
|
||||||
if len(t.options.Inet4RouteAddress) > 0 {
|
}
|
||||||
for _, addr := range t.options.Inet4RouteAddress {
|
}
|
||||||
routes = append(routes, netlink.Route{
|
|
||||||
Dst: &net.IPNet{
|
func (t *NativeTun) routes(tunLink netlink.Link) ([]netlink.Route, error) {
|
||||||
IP: addr.Addr().AsSlice(),
|
routeRanges, err := t.options.BuildAutoRouteRanges(false)
|
||||||
Mask: net.CIDRMask(addr.Bits(), 32),
|
if err != nil {
|
||||||
},
|
return nil, err
|
||||||
|
}
|
||||||
|
return common.Map(routeRanges, func(it netip.Prefix) netlink.Route {
|
||||||
|
return netlink.Route{
|
||||||
|
Dst: prefixToIPNet(it),
|
||||||
LinkIndex: tunLink.Attrs().Index,
|
LinkIndex: tunLink.Attrs().Index,
|
||||||
Table: t.options.TableIndex,
|
Table: t.options.TableIndex,
|
||||||
})
|
|
||||||
}
|
}
|
||||||
} else {
|
}), nil
|
||||||
routes = append(routes, netlink.Route{
|
|
||||||
Dst: &net.IPNet{
|
|
||||||
IP: net.IPv4zero,
|
|
||||||
Mask: net.CIDRMask(0, 32),
|
|
||||||
},
|
|
||||||
LinkIndex: tunLink.Attrs().Index,
|
|
||||||
Table: t.options.TableIndex,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return routes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -626,7 +594,11 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *NativeTun) setRoute(tunLink netlink.Link) error {
|
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)
|
err := netlink.RouteAdd(&route)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "add route ", i)
|
return E.Cause(err, "add route ", i)
|
||||||
@@ -657,9 +629,11 @@ func (t *NativeTun) unsetRoute() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *NativeTun) unsetRoute0(tunLink netlink.Link) error {
|
func (t *NativeTun) unsetRoute0(tunLink netlink.Link) error {
|
||||||
for _, route := range t.routes(tunLink) {
|
if routes, err := t.routes(tunLink); err == nil {
|
||||||
|
for _, route := range routes {
|
||||||
_ = netlink.RouteDel(&route)
|
_ = netlink.RouteDel(&route)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
75
tun_rules.go
75
tun_rules.go
@@ -2,13 +2,17 @@ package tun
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/ranges"
|
"github.com/sagernet/sing/common/ranges"
|
||||||
|
|
||||||
|
"go4.org/netipx"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -96,3 +100,74 @@ func buildExcludedRanges(includeRanges []ranges.Range[uint32], excludeRanges []r
|
|||||||
}
|
}
|
||||||
return ranges.Merge(uidRanges)
|
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
|
||||||
|
}
|
||||||
|
@@ -92,37 +92,18 @@ func (t *NativeTun) configure() error {
|
|||||||
_ = luid.DisableDNSRegistration()
|
_ = luid.DisableDNSRegistration()
|
||||||
}
|
}
|
||||||
if t.options.AutoRoute {
|
if t.options.AutoRoute {
|
||||||
if len(t.options.Inet4Address) > 0 {
|
routeRanges, err := t.options.BuildAutoRouteRanges(false)
|
||||||
if len(t.options.Inet4RouteAddress) > 0 {
|
|
||||||
for _, addr := range t.options.Inet4RouteAddress {
|
|
||||||
err := luid.AddRoute(addr, netip.IPv4Unspecified(), 0)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "add ipv4 route: ", addr)
|
return err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
for _, routeRange := range routeRanges {
|
||||||
|
if routeRange.Addr().Is4() {
|
||||||
|
err = luid.AddRoute(routeRange, netip.IPv4Unspecified(), 0)
|
||||||
} else {
|
} else {
|
||||||
err := luid.AddRoute(netip.PrefixFrom(netip.IPv4Unspecified(), 0), netip.IPv4Unspecified(), 0)
|
err = luid.AddRoute(routeRange, netip.IPv6Unspecified(), 0)
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "set ipv4 route")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
err = windnsapi.FlushResolverCache()
|
||||||
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()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user