mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2026-03-04 21:07:30 +00:00
feat: tun support auto-redirect, route-address-set and route-exclude-address-set
This commit is contained in:
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) ||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
70
listener/sing_tun/iface.go
Normal file
70
listener/sing_tun/iface.go
Normal 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
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user