mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-06-18 17:12:47 +03:00
auto-redirect: Add route address set support for nftables
This commit is contained in:
232
inbound/tun.go
232
inbound/tun.go
@@ -3,7 +3,9 @@ package inbound
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -20,28 +22,91 @@ import (
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/ranges"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
|
||||
"go4.org/netipx"
|
||||
)
|
||||
|
||||
var _ adapter.Inbound = (*Tun)(nil)
|
||||
|
||||
type Tun struct {
|
||||
tag string
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
logger log.ContextLogger
|
||||
inboundOptions option.InboundOptions
|
||||
tunOptions tun.Options
|
||||
endpointIndependentNat bool
|
||||
udpTimeout int64
|
||||
stack string
|
||||
tunIf tun.Tun
|
||||
tunStack tun.Stack
|
||||
platformInterface platform.Interface
|
||||
platformOptions option.TunPlatformOptions
|
||||
autoRedirect tun.AutoRedirect
|
||||
tag string
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
logger log.ContextLogger
|
||||
inboundOptions option.InboundOptions
|
||||
tunOptions tun.Options
|
||||
endpointIndependentNat bool
|
||||
udpTimeout int64
|
||||
stack string
|
||||
tunIf tun.Tun
|
||||
tunStack tun.Stack
|
||||
platformInterface platform.Interface
|
||||
platformOptions option.TunPlatformOptions
|
||||
autoRedirect tun.AutoRedirect
|
||||
routeRuleSet []adapter.RuleSet
|
||||
routeRuleSetCallback []*list.Element[adapter.RuleSetUpdateCallback]
|
||||
routeExcludeRuleSet []adapter.RuleSet
|
||||
routeExcludeRuleSetCallback []*list.Element[adapter.RuleSetUpdateCallback]
|
||||
routeAddressSet []*netipx.IPSet
|
||||
routeExcludeAddressSet []*netipx.IPSet
|
||||
}
|
||||
|
||||
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions, platformInterface platform.Interface) (*Tun, error) {
|
||||
address := options.Address
|
||||
//nolint:staticcheck
|
||||
//goland:noinspection GoDeprecation
|
||||
if len(options.Inet4Address) > 0 {
|
||||
address = append(address, options.Inet4Address...)
|
||||
}
|
||||
//nolint:staticcheck
|
||||
//goland:noinspection GoDeprecation
|
||||
if len(options.Inet6Address) > 0 {
|
||||
address = append(address, options.Inet6Address...)
|
||||
}
|
||||
inet4Address := common.Filter(address, func(it netip.Prefix) bool {
|
||||
return it.Addr().Is4()
|
||||
})
|
||||
inet6Address := common.Filter(address, func(it netip.Prefix) bool {
|
||||
return it.Addr().Is6()
|
||||
})
|
||||
|
||||
routeAddress := options.RouteAddress
|
||||
//nolint:staticcheck
|
||||
//goland:noinspection GoDeprecation
|
||||
if len(options.Inet4RouteAddress) > 0 {
|
||||
routeAddress = append(routeAddress, options.Inet4RouteAddress...)
|
||||
}
|
||||
//nolint:staticcheck
|
||||
//goland:noinspection GoDeprecation
|
||||
if len(options.Inet6RouteAddress) > 0 {
|
||||
routeAddress = append(routeAddress, options.Inet6RouteAddress...)
|
||||
}
|
||||
inet4RouteAddress := common.Filter(routeAddress, func(it netip.Prefix) bool {
|
||||
return it.Addr().Is4()
|
||||
})
|
||||
inet6RouteAddress := common.Filter(routeAddress, func(it netip.Prefix) bool {
|
||||
return it.Addr().Is6()
|
||||
})
|
||||
|
||||
routeExcludeAddress := options.RouteExcludeAddress
|
||||
//nolint:staticcheck
|
||||
//goland:noinspection GoDeprecation
|
||||
if len(options.Inet4RouteExcludeAddress) > 0 {
|
||||
routeExcludeAddress = append(routeExcludeAddress, options.Inet4RouteExcludeAddress...)
|
||||
}
|
||||
//nolint:staticcheck
|
||||
//goland:noinspection GoDeprecation
|
||||
if len(options.Inet6RouteExcludeAddress) > 0 {
|
||||
routeExcludeAddress = append(routeExcludeAddress, options.Inet6RouteExcludeAddress...)
|
||||
}
|
||||
inet4RouteExcludeAddress := common.Filter(routeExcludeAddress, func(it netip.Prefix) bool {
|
||||
return it.Addr().Is4()
|
||||
})
|
||||
inet6RouteExcludeAddress := common.Filter(routeExcludeAddress, func(it netip.Prefix) bool {
|
||||
return it.Addr().Is6()
|
||||
})
|
||||
|
||||
tunMTU := options.MTU
|
||||
if tunMTU == 0 {
|
||||
tunMTU = 9000
|
||||
@@ -68,6 +133,23 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
||||
}
|
||||
}
|
||||
|
||||
tableIndex := options.IPRoute2TableIndex
|
||||
if tableIndex == 0 {
|
||||
tableIndex = tun.DefaultIPRoute2TableIndex
|
||||
}
|
||||
ruleIndex := options.IPRoute2RuleIndex
|
||||
if ruleIndex == 0 {
|
||||
ruleIndex = tun.DefaultIPRoute2RuleIndex
|
||||
}
|
||||
inputMark := options.AutoRedirectInputMark
|
||||
if inputMark == 0 {
|
||||
inputMark = tun.DefaultAutoRedirectInputMark
|
||||
}
|
||||
outputMark := options.AutoRedirectOutputMark
|
||||
if outputMark == 0 {
|
||||
outputMark = tun.DefaultAutoRedirectOutputMark
|
||||
}
|
||||
|
||||
inbound := &Tun{
|
||||
tag: tag,
|
||||
ctx: ctx,
|
||||
@@ -78,23 +160,26 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
||||
Name: options.InterfaceName,
|
||||
MTU: tunMTU,
|
||||
GSO: options.GSO,
|
||||
Inet4Address: options.Inet4Address,
|
||||
Inet6Address: options.Inet6Address,
|
||||
Inet4Address: inet4Address,
|
||||
Inet6Address: inet6Address,
|
||||
AutoRoute: options.AutoRoute,
|
||||
IPRoute2TableIndex: tableIndex,
|
||||
IPRoute2RuleIndex: ruleIndex,
|
||||
AutoRedirectInputMark: inputMark,
|
||||
AutoRedirectOutputMark: outputMark,
|
||||
StrictRoute: options.StrictRoute,
|
||||
IncludeInterface: options.IncludeInterface,
|
||||
ExcludeInterface: options.ExcludeInterface,
|
||||
Inet4RouteAddress: options.Inet4RouteAddress,
|
||||
Inet6RouteAddress: options.Inet6RouteAddress,
|
||||
Inet4RouteExcludeAddress: options.Inet4RouteExcludeAddress,
|
||||
Inet6RouteExcludeAddress: options.Inet6RouteExcludeAddress,
|
||||
Inet4RouteAddress: inet4RouteAddress,
|
||||
Inet6RouteAddress: inet6RouteAddress,
|
||||
Inet4RouteExcludeAddress: inet4RouteExcludeAddress,
|
||||
Inet6RouteExcludeAddress: inet6RouteExcludeAddress,
|
||||
IncludeUID: includeUID,
|
||||
ExcludeUID: excludeUID,
|
||||
IncludeAndroidUser: options.IncludeAndroidUser,
|
||||
IncludePackage: options.IncludePackage,
|
||||
ExcludePackage: options.ExcludePackage,
|
||||
InterfaceMonitor: router.InterfaceMonitor(),
|
||||
TableIndex: 2022,
|
||||
},
|
||||
endpointIndependentNat: options.EndpointIndependentNat,
|
||||
udpTimeout: int64(udpTimeout.Seconds()),
|
||||
@@ -108,15 +193,47 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
||||
}
|
||||
disableNFTables, dErr := strconv.ParseBool(os.Getenv("DISABLE_NFTABLES"))
|
||||
inbound.autoRedirect, err = tun.NewAutoRedirect(tun.AutoRedirectOptions{
|
||||
TunOptions: &inbound.tunOptions,
|
||||
Context: ctx,
|
||||
Handler: inbound,
|
||||
Logger: logger,
|
||||
TableName: "sing-box",
|
||||
DisableNFTables: dErr == nil && disableNFTables,
|
||||
TunOptions: &inbound.tunOptions,
|
||||
Context: ctx,
|
||||
Handler: inbound,
|
||||
Logger: logger,
|
||||
NetworkMonitor: router.NetworkMonitor(),
|
||||
InterfaceFinder: router.InterfaceFinder(),
|
||||
TableName: "sing-box",
|
||||
DisableNFTables: dErr == nil && disableNFTables,
|
||||
RouteAddressSet: &inbound.routeAddressSet,
|
||||
RouteExcludeAddressSet: &inbound.routeExcludeAddressSet,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize auto redirect")
|
||||
return nil, E.Cause(err, "initialize auto-redirect")
|
||||
}
|
||||
if runtime.GOOS != "android" {
|
||||
var markMode bool
|
||||
for _, routeAddressSet := range options.RouteAddressSet {
|
||||
ruleSet, loaded := router.RuleSet(routeAddressSet)
|
||||
if !loaded {
|
||||
return nil, E.New("parse route_address_set: rule-set not found: ", routeAddressSet)
|
||||
}
|
||||
ruleSet.IncRef()
|
||||
inbound.routeRuleSet = append(inbound.routeRuleSet, ruleSet)
|
||||
markMode = true
|
||||
}
|
||||
for _, routeExcludeAddressSet := range options.RouteExcludeAddressSet {
|
||||
ruleSet, loaded := router.RuleSet(routeExcludeAddressSet)
|
||||
if !loaded {
|
||||
return nil, E.New("parse route_exclude_address_set: rule-set not found: ", routeExcludeAddressSet)
|
||||
}
|
||||
ruleSet.IncRef()
|
||||
inbound.routeExcludeRuleSet = append(inbound.routeExcludeRuleSet, ruleSet)
|
||||
markMode = true
|
||||
}
|
||||
if markMode {
|
||||
inbound.tunOptions.AutoRedirectMarkMode = true
|
||||
err = router.RegisterAutoRedirectOutputMark(inbound.tunOptions.AutoRedirectOutputMark)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return inbound, nil
|
||||
@@ -141,11 +258,11 @@ func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges.
|
||||
}
|
||||
var start, end uint64
|
||||
var err error
|
||||
start, err = strconv.ParseUint(uidRange[:subIndex], 10, 32)
|
||||
start, err = strconv.ParseUint(uidRange[:subIndex], 0, 32)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse range start")
|
||||
}
|
||||
end, err = strconv.ParseUint(uidRange[subIndex+1:], 10, 32)
|
||||
end, err = strconv.ParseUint(uidRange[subIndex+1:], 0, 32)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse range end")
|
||||
}
|
||||
@@ -215,18 +332,57 @@ func (t *Tun) Start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t.autoRedirect != nil {
|
||||
monitor.Start("initiating auto redirect")
|
||||
err = t.autoRedirect.Start()
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return E.Cause(err, "auto redirect")
|
||||
}
|
||||
}
|
||||
t.logger.Info("started at ", t.tunOptions.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tun) PostStart() error {
|
||||
monitor := taskmonitor.New(t.logger, C.StartTimeout)
|
||||
if t.autoRedirect != nil {
|
||||
t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet)
|
||||
for _, routeRuleSet := range t.routeRuleSet {
|
||||
ipSets := routeRuleSet.ExtractIPSet()
|
||||
if len(ipSets) == 0 {
|
||||
t.logger.Warn("route_address_set: no destination IP CIDR rules found in rule-set: ", routeRuleSet.Name())
|
||||
}
|
||||
t.routeAddressSet = append(t.routeAddressSet, ipSets...)
|
||||
}
|
||||
t.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet)
|
||||
for _, routeExcludeRuleSet := range t.routeExcludeRuleSet {
|
||||
ipSets := routeExcludeRuleSet.ExtractIPSet()
|
||||
if len(ipSets) == 0 {
|
||||
t.logger.Warn("route_address_set: no destination IP CIDR rules found in rule-set: ", routeExcludeRuleSet.Name())
|
||||
}
|
||||
t.routeExcludeAddressSet = append(t.routeExcludeAddressSet, ipSets...)
|
||||
}
|
||||
monitor.Start("initiating auto-redirect")
|
||||
err := t.autoRedirect.Start()
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return E.Cause(err, "auto-redirect")
|
||||
}
|
||||
for _, routeRuleSet := range t.routeRuleSet {
|
||||
t.routeRuleSetCallback = append(t.routeRuleSetCallback, routeRuleSet.RegisterCallback(t.updateRouteAddressSet))
|
||||
routeRuleSet.DecRef()
|
||||
}
|
||||
for _, routeExcludeRuleSet := range t.routeExcludeRuleSet {
|
||||
t.routeExcludeRuleSetCallback = append(t.routeExcludeRuleSetCallback, routeExcludeRuleSet.RegisterCallback(t.updateRouteAddressSet))
|
||||
routeExcludeRuleSet.DecRef()
|
||||
}
|
||||
t.routeAddressSet = nil
|
||||
t.routeExcludeAddressSet = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tun) updateRouteAddressSet(it adapter.RuleSet) {
|
||||
t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet)
|
||||
t.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet)
|
||||
t.autoRedirect.UpdateRouteAddressSet()
|
||||
t.routeAddressSet = nil
|
||||
t.routeExcludeAddressSet = nil
|
||||
}
|
||||
|
||||
func (t *Tun) Close() error {
|
||||
return common.Close(
|
||||
t.tunStack,
|
||||
|
||||
Reference in New Issue
Block a user