Skip to content

Commit

Permalink
Add route exclude support
Browse files Browse the repository at this point in the history
  • Loading branch information
nekohasekai committed Nov 4, 2023
1 parent 150b116 commit fb98efb
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 127 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
github.com/sagernet/sing v0.2.15
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9
go4.org/netipx v0.0.0-20230824141953-6213f710f925
golang.org/x/net v0.17.0
golang.org/x/sys v0.13.0
)
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
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.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down
40 changes: 21 additions & 19 deletions tun.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +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
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
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) {
Expand Down
43 changes: 8 additions & 35 deletions tun_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,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()
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),
}
}
for _, subnet := range routes {
err = addRoute(subnet, options.Inet4Address[0].Addr())
if err != nil {
return E.Cause(err, "add ipv4 route ", subnet)
}
err = addRoute(routeRange, options.Inet6Address[0].Addr())
}
}
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()
Expand Down
77 changes: 31 additions & 46 deletions tun_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,57 +188,36 @@ 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 {
func prefixToIPNet(prefix netip.Prefix) *net.IPNet {
return &net.IPNet{
IP: prefix.Addr().AsSlice(),
Mask: net.CIDRMask(prefix.Bits(), prefix.Addr().BitLen()),
}
}

func (t *NativeTun) routes(tunLink netlink.Link) ([]netlink.Route, error) {
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,
})
}
}
for _, address := range t.options.Inet6Address {
routes = append(routes, netlink.Route{
Dst: prefixToIPNet(address),
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 {
if t.options.AutoRoute {
routeRanges, err := t.options.BuildAutoRouteRanges()
if err != nil {
return nil, err
}
for _, routeRange := range routeRanges {
routes = append(routes, netlink.Route{
Dst: &net.IPNet{
IP: net.IPv6zero,
Mask: net.CIDRMask(0, 128),
},
Dst: prefixToIPNet(routeRange),
LinkIndex: tunLink.Attrs().Index,
Table: t.options.TableIndex,
})
}
}
return routes
return routes, nil
}

const (
Expand Down Expand Up @@ -615,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)
Expand Down Expand Up @@ -646,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
}
Expand Down
75 changes: 75 additions & 0 deletions tun_rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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() ([]netip.Prefix, error) {
var routeRanges []netip.Prefix
if len(o.Inet4Address) > 0 {
var inet4Ranges []netip.Prefix
if len(o.Inet4RouteAddress) > 0 {
inet4Ranges = o.Inet4RouteAddress
} else if autoRouteUseSubRanges {
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 {
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
}
35 changes: 8 additions & 27 deletions tun_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,37 +92,18 @@ func (t *NativeTun) configure() error {
_ = 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)
}
}
} else {
err := luid.AddRoute(netip.PrefixFrom(netip.IPv4Unspecified(), 0), netip.IPv4Unspecified(), 0)
if err != nil {
return E.Cause(err, "set ipv4 route")
}
}
routeRanges, err := t.options.BuildAutoRouteRanges()
if err != nil {
return err
}
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)
}
}
for _, routeRange := range routeRanges {
if routeRange.Addr().Is4() {
err = luid.AddRoute(routeRange, netip.IPv4Unspecified(), 0)
} else {
err := luid.AddRoute(netip.PrefixFrom(netip.IPv6Unspecified(), 0), netip.IPv6Unspecified(), 0)
if err != nil {
return E.Cause(err, "set ipv6 route")
}
err = luid.AddRoute(routeRange, netip.IPv6Unspecified(), 0)
}
}
err := windnsapi.FlushResolverCache()
err = windnsapi.FlushResolverCache()
if err != nil {
return err
}
Expand Down

0 comments on commit fb98efb

Please sign in to comment.