feat: tun support auto-redirect, route-address-set and route-exclude-address-set

This commit is contained in:
wwqgtxx
2024-06-17 22:04:51 +08:00
parent 0738e18100
commit 09be5cbc99
28 changed files with 745 additions and 247 deletions

View File

@@ -27,27 +27,36 @@ type Tun struct {
AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"`
RedirectToTun []string `yaml:"-" json:"-"`
MTU uint32 `yaml:"mtu" json:"mtu,omitempty"`
GSO bool `yaml:"gso" json:"gso,omitempty"`
GSOMaxSize uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"`
Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"`
Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"`
StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"`
MTU uint32 `yaml:"mtu" json:"mtu,omitempty"`
GSO bool `yaml:"gso" json:"gso,omitempty"`
GSOMaxSize uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"`
Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"`
Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"`
IPRoute2TableIndex int `yaml:"iproute2-table-index" json:"iproute2_table_index,omitempty"`
IPRoute2RuleIndex int `yaml:"iproute2-rule-index" json:"iproute2_rule_index,omitempty"`
AutoRedirect bool `yaml:"auto-redirect" json:"auto_redirect,omitempty"`
AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto_redirect_input_mark,omitempty"`
AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto_redirect_output_mark,omitempty"`
StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"`
RouteAddress []netip.Prefix `yaml:"route-address" json:"route_address,omitempty"`
RouteAddressSet []string `yaml:"route-address-set" json:"route_address_set,omitempty"`
RouteExcludeAddress []netip.Prefix `yaml:"route-exclude-address" json:"route_exclude_address,omitempty"`
RouteExcludeAddressSet []string `yaml:"route-exclude-address-set" json:"route_exclude_address_set,omitempty"`
IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"`
ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"`
IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"`
IncludeUIDRange []string `yaml:"include-uid-range" json:"include-uid-range,omitempty"`
ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"`
ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"`
IncludeAndroidUser []int `yaml:"include-android-user" json:"include-android-user,omitempty"`
IncludePackage []string `yaml:"include-package" json:"include-package,omitempty"`
ExcludePackage []string `yaml:"exclude-package" json:"exclude-package,omitempty"`
EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"`
UDPTimeout int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"`
FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"`
Inet4RouteAddress []netip.Prefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"`
Inet6RouteAddress []netip.Prefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"`
Inet4RouteExcludeAddress []netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4-route-exclude-address,omitempty"`
Inet6RouteExcludeAddress []netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6-route-exclude-address,omitempty"`
IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"`
ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"`
IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"`
IncludeUIDRange []string `yaml:"include-uid-range" json:"include-uid-range,omitempty"`
ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"`
ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"`
IncludeAndroidUser []int `yaml:"include-android-user" json:"include-android-user,omitempty"`
IncludePackage []string `yaml:"include-package" json:"include-package,omitempty"`
ExcludePackage []string `yaml:"exclude-package" json:"exclude-package,omitempty"`
EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"`
UDPTimeout int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"`
FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"`
TableIndex int `yaml:"table-index" json:"table-index"`
}

View File

@@ -18,29 +18,38 @@ type TunOption struct {
AutoRoute bool `inbound:"auto-route,omitempty"`
AutoDetectInterface bool `inbound:"auto-detect-interface,omitempty"`
MTU uint32 `inbound:"mtu,omitempty"`
GSO bool `inbound:"gso,omitempty"`
GSOMaxSize uint32 `inbound:"gso-max-size,omitempty"`
Inet4Address []string `inbound:"inet4_address,omitempty"`
Inet6Address []string `inbound:"inet6_address,omitempty"`
StrictRoute bool `inbound:"strict_route,omitempty"`
MTU uint32 `inbound:"mtu,omitempty"`
GSO bool `inbound:"gso,omitempty"`
GSOMaxSize uint32 `inbound:"gso-max-size,omitempty"`
Inet4Address []string `inbound:"inet4_address,omitempty"`
Inet6Address []string `inbound:"inet6_address,omitempty"`
IPRoute2TableIndex int `inbound:"iproute2-table-index"`
IPRoute2RuleIndex int `inbound:"iproute2-rule-index"`
AutoRedirect bool `inbound:"auto-redirect"`
AutoRedirectInputMark uint32 `inbound:"auto-redirect-input-mark"`
AutoRedirectOutputMark uint32 `inbound:"auto-redirect-output-mark"`
StrictRoute bool `inbound:"strict_route,omitempty"`
RouteAddress []string `inbound:"route-address"`
RouteAddressSet []string `inbound:"route-address-set"`
RouteExcludeAddress []string `inbound:"route-exclude-address"`
RouteExcludeAddressSet []string `inbound:"route-exclude-address-set"`
IncludeInterface []string `inbound:"include-interface,omitempty"`
ExcludeInterface []string `inbound:"exclude-interface"`
IncludeUID []uint32 `inbound:"include_uid,omitempty"`
IncludeUIDRange []string `inbound:"include_uid_range,omitempty"`
ExcludeUID []uint32 `inbound:"exclude_uid,omitempty"`
ExcludeUIDRange []string `inbound:"exclude_uid_range,omitempty"`
IncludeAndroidUser []int `inbound:"include_android_user,omitempty"`
IncludePackage []string `inbound:"include_package,omitempty"`
ExcludePackage []string `inbound:"exclude_package,omitempty"`
EndpointIndependentNat bool `inbound:"endpoint_independent_nat,omitempty"`
UDPTimeout int64 `inbound:"udp_timeout,omitempty"`
FileDescriptor int `inbound:"file-descriptor,omitempty"`
Inet4RouteAddress []string `inbound:"inet4_route_address,omitempty"`
Inet6RouteAddress []string `inbound:"inet6_route_address,omitempty"`
Inet4RouteExcludeAddress []string `inbound:"inet4_route_exclude_address,omitempty"`
Inet6RouteExcludeAddress []string `inbound:"inet6_route_exclude_address,omitempty"`
IncludeInterface []string `inbound:"include-interface,omitempty"`
ExcludeInterface []string `inbound:"exclude-interface" json:"exclude-interface,omitempty"`
IncludeUID []uint32 `inbound:"include_uid,omitempty"`
IncludeUIDRange []string `inbound:"include_uid_range,omitempty"`
ExcludeUID []uint32 `inbound:"exclude_uid,omitempty"`
ExcludeUIDRange []string `inbound:"exclude_uid_range,omitempty"`
IncludeAndroidUser []int `inbound:"include_android_user,omitempty"`
IncludePackage []string `inbound:"include_package,omitempty"`
ExcludePackage []string `inbound:"exclude_package,omitempty"`
EndpointIndependentNat bool `inbound:"endpoint_independent_nat,omitempty"`
UDPTimeout int64 `inbound:"udp_timeout,omitempty"`
FileDescriptor int `inbound:"file-descriptor,omitempty"`
TableIndex int `inbound:"table-index,omitempty"`
}
func (o TunOption) Equal(config C.InboundConfig) bool {
@@ -63,6 +72,16 @@ func NewTun(options *TunOption) (*Tun, error) {
if !exist {
return nil, errors.New("invalid tun stack")
}
routeAddress, err := LC.StringSliceToNetipPrefixSlice(options.RouteAddress)
if err != nil {
return nil, err
}
routeExcludeAddress, err := LC.StringSliceToNetipPrefixSlice(options.RouteExcludeAddress)
if err != nil {
return nil, err
}
inet4Address, err := LC.StringSliceToNetipPrefixSlice(options.Inet4Address)
if err != nil {
return nil, err
@@ -91,35 +110,44 @@ func NewTun(options *TunOption) (*Tun, error) {
Base: base,
config: options,
tun: LC.Tun{
Enable: true,
Device: options.Device,
Stack: stack,
DNSHijack: options.DNSHijack,
AutoRoute: options.AutoRoute,
AutoDetectInterface: options.AutoDetectInterface,
MTU: options.MTU,
GSO: options.GSO,
GSOMaxSize: options.GSOMaxSize,
Inet4Address: inet4Address,
Inet6Address: inet6Address,
StrictRoute: options.StrictRoute,
Enable: true,
Device: options.Device,
Stack: stack,
DNSHijack: options.DNSHijack,
AutoRoute: options.AutoRoute,
AutoDetectInterface: options.AutoDetectInterface,
MTU: options.MTU,
GSO: options.GSO,
GSOMaxSize: options.GSOMaxSize,
Inet4Address: inet4Address,
Inet6Address: inet6Address,
IPRoute2TableIndex: options.IPRoute2TableIndex,
IPRoute2RuleIndex: options.IPRoute2RuleIndex,
AutoRedirect: options.AutoRedirect,
AutoRedirectInputMark: options.AutoRedirectInputMark,
AutoRedirectOutputMark: options.AutoRedirectOutputMark,
StrictRoute: options.StrictRoute,
RouteAddress: routeAddress,
RouteAddressSet: options.RouteAddressSet,
RouteExcludeAddress: routeExcludeAddress,
RouteExcludeAddressSet: options.RouteExcludeAddressSet,
IncludeInterface: options.IncludeInterface,
ExcludeInterface: options.ExcludeInterface,
IncludeUID: options.IncludeUID,
IncludeUIDRange: options.IncludeUIDRange,
ExcludeUID: options.ExcludeUID,
ExcludeUIDRange: options.ExcludeUIDRange,
IncludeAndroidUser: options.IncludeAndroidUser,
IncludePackage: options.IncludePackage,
ExcludePackage: options.ExcludePackage,
EndpointIndependentNat: options.EndpointIndependentNat,
UDPTimeout: options.UDPTimeout,
FileDescriptor: options.FileDescriptor,
Inet4RouteAddress: inet4RouteAddress,
Inet6RouteAddress: inet6RouteAddress,
Inet4RouteExcludeAddress: inet4RouteExcludeAddress,
Inet6RouteExcludeAddress: inet6RouteExcludeAddress,
IncludeInterface: options.IncludeInterface,
ExcludeInterface: options.ExcludeInterface,
IncludeUID: options.IncludeUID,
IncludeUIDRange: options.IncludeUIDRange,
ExcludeUID: options.ExcludeUID,
ExcludeUIDRange: options.ExcludeUIDRange,
IncludeAndroidUser: options.IncludeAndroidUser,
IncludePackage: options.IncludePackage,
ExcludePackage: options.ExcludePackage,
EndpointIndependentNat: options.EndpointIndependentNat,
UDPTimeout: options.UDPTimeout,
FileDescriptor: options.FileDescriptor,
TableIndex: options.TableIndex,
},
}, nil
}

View File

@@ -820,11 +820,15 @@ func hasTunConfigChange(tunConf *LC.Tun) bool {
LastTunConf.MTU != tunConf.MTU ||
LastTunConf.GSO != tunConf.GSO ||
LastTunConf.GSOMaxSize != tunConf.GSOMaxSize ||
LastTunConf.IPRoute2TableIndex != tunConf.IPRoute2TableIndex ||
LastTunConf.IPRoute2RuleIndex != tunConf.IPRoute2RuleIndex ||
LastTunConf.AutoRedirect != tunConf.AutoRedirect ||
LastTunConf.AutoRedirectInputMark != tunConf.AutoRedirectInputMark ||
LastTunConf.AutoRedirectOutputMark != tunConf.AutoRedirectOutputMark ||
LastTunConf.StrictRoute != tunConf.StrictRoute ||
LastTunConf.EndpointIndependentNat != tunConf.EndpointIndependentNat ||
LastTunConf.UDPTimeout != tunConf.UDPTimeout ||
LastTunConf.FileDescriptor != tunConf.FileDescriptor ||
LastTunConf.TableIndex != tunConf.TableIndex {
LastTunConf.FileDescriptor != tunConf.FileDescriptor {
return true
}
@@ -836,6 +840,22 @@ func hasTunConfigChange(tunConf *LC.Tun) bool {
return tunConf.DNSHijack[i] < tunConf.DNSHijack[j]
})
sort.Slice(tunConf.RouteAddress, func(i, j int) bool {
return tunConf.RouteAddress[i].String() < tunConf.RouteAddress[j].String()
})
sort.Slice(tunConf.RouteAddressSet, func(i, j int) bool {
return tunConf.RouteAddressSet[i] < tunConf.RouteAddressSet[j]
})
sort.Slice(tunConf.RouteExcludeAddress, func(i, j int) bool {
return tunConf.RouteExcludeAddress[i].String() < tunConf.RouteExcludeAddress[j].String()
})
sort.Slice(tunConf.RouteExcludeAddressSet, func(i, j int) bool {
return tunConf.RouteExcludeAddressSet[i] < tunConf.RouteExcludeAddressSet[j]
})
sort.Slice(tunConf.Inet4Address, func(i, j int) bool {
return tunConf.Inet4Address[i].String() < tunConf.Inet4Address[j].String()
})
@@ -897,6 +917,10 @@ func hasTunConfigChange(tunConf *LC.Tun) bool {
})
if !slices.Equal(tunConf.DNSHijack, LastTunConf.DNSHijack) ||
!slices.Equal(tunConf.RouteAddress, LastTunConf.RouteAddress) ||
!slices.Equal(tunConf.RouteAddressSet, LastTunConf.RouteAddressSet) ||
!slices.Equal(tunConf.RouteExcludeAddress, LastTunConf.RouteExcludeAddress) ||
!slices.Equal(tunConf.RouteExcludeAddressSet, LastTunConf.RouteExcludeAddressSet) ||
!slices.Equal(tunConf.Inet4Address, LastTunConf.Inet4Address) ||
!slices.Equal(tunConf.Inet6Address, LastTunConf.Inet6Address) ||
!slices.Equal(tunConf.Inet4RouteAddress, LastTunConf.Inet4RouteAddress) ||

View File

@@ -198,6 +198,12 @@ func (h *ListenerHandler) NewError(ctx context.Context, err error) {
log.Warnln("%s listener get error: %+v", h.Type.String(), err)
}
func (h *ListenerHandler) TypeMutation(typ C.Type) *ListenerHandler {
handler := *h
handler.Type = typ
return &handler
}
func ShouldIgnorePacketError(err error) bool {
// ignore simple error
if E.IsTimeout(err) || E.IsClosed(err) || E.IsCanceled(err) {

View File

@@ -8,6 +8,7 @@ import (
"time"
"github.com/metacubex/mihomo/component/resolver"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/listener/sing"
"github.com/metacubex/mihomo/log"
@@ -124,3 +125,9 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
}
return h.ListenerHandler.NewPacketConnection(ctx, conn, metadata)
}
func (h *ListenerHandler) TypeMutation(typ C.Type) *ListenerHandler {
handle := *h
handle.ListenerHandler = h.ListenerHandler.TypeMutation(typ)
return &handle
}

View File

@@ -0,0 +1,70 @@
package sing_tun
import (
"errors"
"net/netip"
"github.com/metacubex/mihomo/component/iface"
"github.com/sagernet/sing/common/control"
)
type defaultInterfaceFinder struct{}
var DefaultInterfaceFinder control.InterfaceFinder = (*defaultInterfaceFinder)(nil)
func (f *defaultInterfaceFinder) Interfaces() []control.Interface {
ifaces, err := iface.Interfaces()
if err != nil {
return nil
}
interfaces := make([]control.Interface, 0, len(ifaces))
for _, _interface := range ifaces {
interfaces = append(interfaces, control.Interface(*_interface))
}
return interfaces
}
var errNoSuchInterface = errors.New("no such network interface")
func (f *defaultInterfaceFinder) InterfaceIndexByName(name string) (int, error) {
ifaces, err := iface.Interfaces()
if err != nil {
return 0, err
}
for _, netInterface := range ifaces {
if netInterface.Name == name {
return netInterface.Index, nil
}
}
return 0, errNoSuchInterface
}
func (f *defaultInterfaceFinder) InterfaceNameByIndex(index int) (string, error) {
ifaces, err := iface.Interfaces()
if err != nil {
return "", err
}
for _, netInterface := range ifaces {
if netInterface.Index == index {
return netInterface.Name, nil
}
}
return "", errNoSuchInterface
}
func (f *defaultInterfaceFinder) InterfaceByAddr(addr netip.Addr) (*control.Interface, error) {
ifaces, err := iface.Interfaces()
if err != nil {
return nil, err
}
for _, netInterface := range ifaces {
for _, prefix := range netInterface.Addresses {
if prefix.Contains(addr) {
return (*control.Interface)(netInterface), nil
}
}
}
return nil, errNoSuchInterface
}

View File

@@ -3,27 +3,33 @@ package sing_tun
import (
"context"
"fmt"
"io"
"net"
"net/netip"
"os"
"runtime"
"strconv"
"strings"
"sync"
"github.com/metacubex/mihomo/adapter/inbound"
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/iface"
"github.com/metacubex/mihomo/component/resolver"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/constant/provider"
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/listener/sing"
"github.com/metacubex/mihomo/log"
tun "github.com/metacubex/sing-tun"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/ranges"
"go4.org/netipx"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
@@ -43,10 +49,21 @@ type Listener struct {
networkUpdateMonitor tun.NetworkUpdateMonitor
defaultInterfaceMonitor tun.DefaultInterfaceMonitor
packageManager tun.PackageManager
autoRedirect tun.AutoRedirect
autoRedirectOutputMark int32
ruleUpdateCallbackCloser io.Closer
ruleUpdateMutex sync.Mutex
routeAddressMap map[string]*netipx.IPSet
routeExcludeAddressMap map[string]*netipx.IPSet
routeAddressSet []*netipx.IPSet
routeExcludeAddressSet []*netipx.IPSet
dnsServerIp []string
}
var emptyAddressSet = []*netipx.IPSet{{}}
func CalculateInterfaceName(name string) (tunName string) {
if runtime.GOOS == "darwin" {
tunName = "utun"
@@ -110,14 +127,45 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
inbound.WithSpecialRules(""),
}
}
ctx := context.TODO()
rpTunnel := tunnel.(provider.Tunnel)
if options.GSOMaxSize == 0 {
options.GSOMaxSize = 65536
}
if runtime.GOOS != "linux" {
options.AutoRedirect = false
}
tunName := options.Device
if tunName == "" || !checkTunName(tunName) {
tunName = CalculateInterfaceName(InterfaceName)
options.Device = tunName
}
routeAddress := options.RouteAddress
if len(options.Inet4RouteAddress) > 0 {
routeAddress = append(routeAddress, options.Inet4RouteAddress...)
}
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
if len(options.Inet4RouteExcludeAddress) > 0 {
routeExcludeAddress = append(routeExcludeAddress, options.Inet4RouteExcludeAddress...)
}
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
@@ -128,9 +176,21 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
} else {
udpTimeout = int64(sing.UDPTimeout.Seconds())
}
tableIndex := options.TableIndex
tableIndex := options.IPRoute2TableIndex
if tableIndex == 0 {
tableIndex = 2022
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
}
includeUID := uidToRange(options.IncludeUID)
if len(options.IncludeUIDRange) > 0 {
@@ -202,6 +262,8 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
}
}()
interfaceFinder := DefaultInterfaceFinder
networkUpdateMonitor, err := tun.NewNetworkUpdateMonitor(log.SingLogger)
if err != nil {
err = E.Cause(err, "create NetworkUpdateMonitor")
@@ -236,11 +298,15 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
Inet4Address: options.Inet4Address,
Inet6Address: options.Inet6Address,
AutoRoute: options.AutoRoute,
IPRoute2TableIndex: tableIndex,
IPRoute2RuleIndex: ruleIndex,
AutoRedirectInputMark: inputMark,
AutoRedirectOutputMark: outputMark,
StrictRoute: options.StrictRoute,
Inet4RouteAddress: options.Inet4RouteAddress,
Inet6RouteAddress: options.Inet6RouteAddress,
Inet4RouteExcludeAddress: options.Inet4RouteExcludeAddress,
Inet6RouteExcludeAddress: options.Inet6RouteExcludeAddress,
Inet4RouteAddress: inet4RouteAddress,
Inet6RouteAddress: inet6RouteAddress,
Inet4RouteExcludeAddress: inet4RouteExcludeAddress,
Inet6RouteExcludeAddress: inet6RouteExcludeAddress,
IncludeInterface: options.IncludeInterface,
ExcludeInterface: options.ExcludeInterface,
IncludeUID: includeUID,
@@ -250,7 +316,56 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
ExcludePackage: options.ExcludePackage,
FileDescriptor: options.FileDescriptor,
InterfaceMonitor: defaultInterfaceMonitor,
TableIndex: tableIndex,
}
if options.AutoRedirect {
l.routeAddressMap = make(map[string]*netipx.IPSet)
l.routeExcludeAddressMap = make(map[string]*netipx.IPSet)
if !options.AutoRoute {
return nil, E.New("`auto-route` is required by `auto-redirect`")
}
disableNFTables, dErr := strconv.ParseBool(os.Getenv("DISABLE_NFTABLES"))
l.autoRedirect, err = tun.NewAutoRedirect(tun.AutoRedirectOptions{
TunOptions: &tunOptions,
Context: ctx,
Handler: handler.TypeMutation(C.REDIR),
Logger: log.SingLogger,
NetworkMonitor: networkUpdateMonitor,
InterfaceFinder: interfaceFinder,
TableName: "mihomo",
DisableNFTables: dErr == nil && disableNFTables,
RouteAddressSet: &l.routeAddressSet,
RouteExcludeAddressSet: &l.routeExcludeAddressSet,
})
if err != nil {
err = E.Cause(err, "initialize auto redirect")
return
}
var markMode bool
for _, routeAddressSet := range options.RouteAddressSet {
rp, loaded := rpTunnel.RuleProviders()[routeAddressSet]
if !loaded {
err = E.New("parse route-address-set: rule-set not found: ", routeAddressSet)
return
}
l.updateRule(rp, false, false)
markMode = true
}
for _, routeExcludeAddressSet := range options.RouteExcludeAddressSet {
rp, loaded := rpTunnel.RuleProviders()[routeExcludeAddressSet]
if !loaded {
err = E.New("parse route-exclude_address-set: rule-set not found: ", routeExcludeAddressSet)
return
}
l.updateRule(rp, true, false)
markMode = true
}
if markMode {
tunOptions.AutoRedirectMarkMode = true
}
}
err = l.buildAndroidRules(&tunOptions)
@@ -269,14 +384,14 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
resolver.AddSystemDnsBlacklist(dnsServerIp...)
stackOptions := tun.StackOptions{
Context: context.TODO(),
Context: ctx,
Tun: tunIf,
TunOptions: tunOptions,
EndpointIndependentNat: options.EndpointIndependentNat,
UDPTimeout: udpTimeout,
Handler: handler,
Logger: log.SingLogger,
InterfaceFinder: control.DefaultInterfaceFinder(),
InterfaceFinder: interfaceFinder,
EnforceBindInterface: EnforceBindInterface,
}
@@ -299,13 +414,76 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
}
l.tunStack = tunStack
if l.autoRedirect != nil {
if len(l.options.RouteAddressSet) > 0 && len(l.routeAddressSet) == 0 {
l.routeAddressSet = emptyAddressSet // without this we can't call UpdateRouteAddressSet after Start
}
if len(l.options.RouteExcludeAddressSet) > 0 && len(l.routeExcludeAddressSet) == 0 {
l.routeExcludeAddressSet = emptyAddressSet // without this we can't call UpdateRouteAddressSet after Start
}
err = l.autoRedirect.Start()
if err != nil {
err = E.Cause(err, "auto redirect")
return
}
if tunOptions.AutoRedirectMarkMode {
l.autoRedirectOutputMark = int32(outputMark)
dialer.DefaultRoutingMark.Store(l.autoRedirectOutputMark)
l.autoRedirect.UpdateRouteAddressSet()
l.ruleUpdateCallbackCloser = rpTunnel.RuleUpdateCallback().Register(l.ruleUpdateCallback)
}
}
//l.openAndroidHotspot(tunOptions)
l.addrStr = fmt.Sprintf("%s(%s,%s), mtu: %d, auto route: %v, ip stack: %s",
tunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.Stack)
l.addrStr = fmt.Sprintf("%s(%s,%s), mtu: %d, auto route: %v, auto redir: %v, ip stack: %s",
tunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.AutoRedirect, options.Stack)
return
}
func (l *Listener) ruleUpdateCallback(ruleProvider provider.RuleProvider) {
name := ruleProvider.Name()
if slices.Contains(l.options.RouteAddressSet, name) {
l.updateRule(ruleProvider, false, true)
return
}
if slices.Contains(l.options.RouteExcludeAddressSet, name) {
l.updateRule(ruleProvider, true, true)
return
}
}
func (l *Listener) updateRule(ruleProvider provider.RuleProvider, exclude bool, update bool) {
l.ruleUpdateMutex.Lock()
defer l.ruleUpdateMutex.Unlock()
name := ruleProvider.Name()
switch rp := ruleProvider.Strategy().(type) {
case interface{ ToIpCidr() *netipx.IPSet }:
if !exclude {
ipCidr := rp.ToIpCidr()
if ipCidr != nil {
l.routeAddressMap[name] = ipCidr
} else {
delete(l.routeAddressMap, name)
}
l.routeAddressSet = maps.Values(l.routeAddressMap)
} else {
ipCidr := rp.ToIpCidr()
if ipCidr != nil {
l.routeExcludeAddressMap[name] = ipCidr
} else {
delete(l.routeExcludeAddressMap, name)
}
l.routeExcludeAddressSet = maps.Values(l.routeExcludeAddressMap)
}
default:
return
}
if update && l.autoRedirect != nil {
l.autoRedirect.UpdateRouteAddressSet()
}
}
func (l *Listener) FlushDefaultInterface() {
if l.options.AutoDetectInterface {
for _, destination := range []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified(), netip.MustParseAddr("1.1.1.1")} {
@@ -347,11 +525,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")
}
@@ -363,9 +541,14 @@ func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges.
func (l *Listener) Close() error {
l.closed = true
resolver.RemoveSystemDnsBlacklist(l.dnsServerIp...)
if l.autoRedirectOutputMark != 0 {
dialer.DefaultRoutingMark.CompareAndSwap(l.autoRedirectOutputMark, 0)
}
return common.Close(
l.ruleUpdateCallbackCloser,
l.tunStack,
l.tunIf,
l.autoRedirect,
l.defaultInterfaceMonitor,
l.networkUpdateMonitor,
l.packageManager,

View File

@@ -119,9 +119,7 @@ func CleanupTProxyIPTables() {
log.Warnln("Cleanup tproxy linux iptables")
if int(dialer.DefaultRoutingMark.Load()) == 2158 {
dialer.DefaultRoutingMark.Store(0)
}
dialer.DefaultRoutingMark.CompareAndSwap(2158, 0)
if _, err := cmd.ExecCmd("iptables -t mangle -L mihomo_divert"); err != nil {
return