fix: 热重载Tun配置

This commit is contained in:
adlyq 2022-05-19 19:19:19 +08:00
parent 7aff9aac82
commit c787bbe0e5
10 changed files with 136 additions and 91 deletions

View file

@ -113,6 +113,7 @@ type Tun struct {
DNSHijack []netip.AddrPort `yaml:"dns-hijack" json:"dns-hijack"` DNSHijack []netip.AddrPort `yaml:"dns-hijack" json:"dns-hijack"`
AutoRoute bool `yaml:"auto-route" json:"auto-route"` AutoRoute bool `yaml:"auto-route" json:"auto-route"`
AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"` AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"`
TunAddressPrefix *netip.Prefix `yaml:"-" json:"-"`
} }
// IPTables config // IPTables config
@ -327,12 +328,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
} }
config.General = general config.General = general
tunCfg, err := parseTun(rawCfg.Tun, config.General)
if err != nil {
return nil, err
}
config.Tun = tunCfg
dialer.DefaultInterface.Store(config.General.Interface) dialer.DefaultInterface.Store(config.General.Interface)
proxies, providers, err := parseProxies(rawCfg) proxies, providers, err := parseProxies(rawCfg)
@ -361,6 +356,12 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
} }
config.DNS = dnsCfg config.DNS = dnsCfg
tunCfg, err := parseTun(rawCfg.Tun, config.General, dnsCfg)
if err != nil {
return nil, err
}
config.Tun = tunCfg
config.Users = parseAuthentication(rawCfg.Authentication) config.Users = parseAuthentication(rawCfg.Authentication)
config.Sniffer, err = parseSniffer(rawCfg.Sniffer) config.Sniffer, err = parseSniffer(rawCfg.Sniffer)
@ -865,7 +866,7 @@ func parseAuthentication(rawRecords []string) []auth.AuthUser {
return users return users
} }
func parseTun(rawTun RawTun, general *General) (*Tun, error) { func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) (*Tun, error) {
if rawTun.Enable && rawTun.AutoDetectInterface { if rawTun.Enable && rawTun.AutoDetectInterface {
autoDetectInterfaceName, err := commons.GetAutoDetectInterface() autoDetectInterfaceName, err := commons.GetAutoDetectInterface()
if err != nil { if err != nil {
@ -892,6 +893,11 @@ func parseTun(rawTun RawTun, general *General) (*Tun, error) {
dnsHijack = append(dnsHijack, addrPort) dnsHijack = append(dnsHijack, addrPort)
} }
var tunAddressPrefix *netip.Prefix
if dnsCfg.FakeIPRange != nil {
tunAddressPrefix = dnsCfg.FakeIPRange.IPNet()
}
return &Tun{ return &Tun{
Enable: rawTun.Enable, Enable: rawTun.Enable,
Device: rawTun.Device, Device: rawTun.Device,
@ -899,6 +905,7 @@ func parseTun(rawTun RawTun, general *General) (*Tun, error) {
DNSHijack: dnsHijack, DNSHijack: dnsHijack,
AutoRoute: rawTun.AutoRoute, AutoRoute: rawTun.AutoRoute,
AutoDetectInterface: rawTun.AutoDetectInterface, AutoDetectInterface: rawTun.AutoDetectInterface,
TunAddressPrefix: tunAddressPrefix,
}, nil }, nil
} }

View file

@ -85,7 +85,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
loadRuleProvider(cfg.RuleProviders) loadRuleProvider(cfg.RuleProviders)
updateGeneral(cfg.General, force) updateGeneral(cfg.General, force)
updateIPTables(cfg) updateIPTables(cfg)
updateTun(cfg.Tun, cfg.DNS) updateTun(cfg.Tun)
updateExperimental(cfg) updateExperimental(cfg)
log.SetLevel(cfg.General.LogLevel) log.SetLevel(cfg.General.LogLevel)
@ -244,12 +244,8 @@ func loadProxyProvider(proxyProviders map[string]provider.ProxyProvider) {
wg.Wait() wg.Wait()
} }
func updateTun(tun *config.Tun, dns *config.DNS) { func updateTun(tun *config.Tun) {
var tunAddressPrefix *netip.Prefix P.ReCreateTun(tun, tunnel.TCPIn(), tunnel.UDPIn())
if dns.FakeIPRange != nil {
tunAddressPrefix = dns.FakeIPRange.IPNet()
}
P.ReCreateTun(tun, tunAddressPrefix, tunnel.TCPIn(), tunnel.UDPIn())
} }
func updateSniffer(sniffer *config.Sniffer) { func updateSniffer(sniffer *config.Sniffer) {
@ -428,7 +424,7 @@ func updateIPTables(cfg *config.Config) {
} }
func Shutdown() { func Shutdown() {
P.Cleanup() P.Cleanup(false)
tproxy.CleanupTProxyIPTables() tproxy.CleanupTProxyIPTables()
resolver.StoreFakePoolState() resolver.StoreFakePoolState()

View file

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/Dreamacro/clash/common/cmd" "github.com/Dreamacro/clash/common/cmd"
"github.com/Dreamacro/clash/listener/inner" "github.com/Dreamacro/clash/listener/inner"
"github.com/Dreamacro/clash/listener/tun/ipstack/commons"
"net" "net"
"net/netip" "net/netip"
"runtime" "runtime"
@ -327,7 +328,7 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P
log.Infoln("Mixed(http+socks) proxy listening at: %s", mixedListener.Address()) log.Infoln("Mixed(http+socks) proxy listening at: %s", mixedListener.Address())
} }
func ReCreateTun(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { func ReCreateTun(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
tunMux.Lock() tunMux.Lock()
defer tunMux.Unlock() defer tunMux.Unlock()
@ -335,35 +336,23 @@ func ReCreateTun(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan
defer func() { defer func() {
if err != nil { if err != nil {
log.Errorln("Start TUN listening error: %s", err.Error()) log.Errorln("Start TUN listening error: %s", err.Error())
Cleanup(false)
} }
}() }()
if tunAddressPrefix == nil { if !hasTunConfigChange(tunConf) {
tunAddressPrefix = lastTunAddressPrefix
}
if !hasTunConfigChange(tunConf, tunAddressPrefix) {
return return
} }
if tunStackListener != nil { Cleanup(true)
_ = tunStackListener.Close()
tunStackListener = nil
lastTunConf = nil
lastTunAddressPrefix = nil
}
if !tunConf.Enable { if !tunConf.Enable {
return return
} }
tunStackListener, err = tun.New(tunConf, tunAddressPrefix, tcpIn, udpIn) tunStackListener, err = tun.New(tunConf, tcpIn, udpIn)
if err != nil {
log.Warnln("Failed to start TUN interface: %s", err)
}
lastTunConf = tunConf lastTunConf = tunConf
lastTunAddressPrefix = tunAddressPrefix
} }
// GetPorts return the ports of proxy servers // GetPorts return the ports of proxy servers
@ -422,7 +411,7 @@ func genAddr(host string, port int, allowLan bool) string {
return fmt.Sprintf("127.0.0.1:%d", port) return fmt.Sprintf("127.0.0.1:%d", port)
} }
func hasTunConfigChange(tunConf *config.Tun, tunAddressPrefix *netip.Prefix) bool { func hasTunConfigChange(tunConf *config.Tun) bool {
if lastTunConf == nil { if lastTunConf == nil {
return true return true
} }
@ -448,24 +437,27 @@ func hasTunConfigChange(tunConf *config.Tun, tunAddressPrefix *netip.Prefix) boo
if lastTunConf.Enable != tunConf.Enable || if lastTunConf.Enable != tunConf.Enable ||
lastTunConf.Device != tunConf.Device || lastTunConf.Device != tunConf.Device ||
lastTunConf.Stack != tunConf.Stack || lastTunConf.Stack != tunConf.Stack ||
lastTunConf.AutoRoute != tunConf.AutoRoute { lastTunConf.AutoRoute != tunConf.AutoRoute ||
lastTunConf.AutoDetectInterface != tunConf.AutoDetectInterface {
return true return true
} }
if (tunAddressPrefix != nil && lastTunAddressPrefix == nil) || (tunAddressPrefix == nil && lastTunAddressPrefix != nil) { if tunConf.TunAddressPrefix.String() != lastTunConf.TunAddressPrefix.String() {
return true
}
if tunAddressPrefix != nil && lastTunAddressPrefix != nil && *tunAddressPrefix != *lastTunAddressPrefix {
return true return true
} }
return false return false
} }
func Cleanup() { func Cleanup(wait bool) {
if tunStackListener != nil { if tunStackListener != nil {
_ = tunStackListener.Close() _ = tunStackListener.Close()
commons.StopDefaultInterfaceChangeMonitor()
if wait {
commons.WaitForTunClose(lastTunConf.Device)
}
if runtime.GOOS == "android" { if runtime.GOOS == "android" {
prefs := []int{9000, 9001, 9002, 9003, 9004} prefs := []int{9000, 9001, 9002, 9003, 9004}
for _, pref := range prefs { for _, pref := range prefs {
@ -473,4 +465,7 @@ func Cleanup() {
} }
} }
} }
tunStackListener = nil
lastTunConf = nil
lastTunAddressPrefix = nil
} }

View file

@ -2,16 +2,21 @@ package commons
import ( import (
"fmt" "fmt"
"net"
"time"
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/iface"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
"net"
"sync"
"time"
) )
var ( var (
defaultRoutes = []string{"1.0.0.0/8", "2.0.0.0/7", "4.0.0.0/6", "8.0.0.0/5", "16.0.0.0/4", "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/1"} defaultRoutes = []string{"1.0.0.0/8", "2.0.0.0/7", "4.0.0.0/6", "8.0.0.0/5", "16.0.0.0/4", "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/1"}
defaultInterfaceMonitorDuration = 3 * time.Second
monitorDuration = 3 * time.Second
monitorStarted = false
monitorStop = make(chan struct{}, 2)
monitorMux sync.Mutex
) )
func ipv4MaskString(bits int) string { func ipv4MaskString(bits int) string {
@ -23,26 +28,80 @@ func ipv4MaskString(bits int) string {
return fmt.Sprintf("%d.%d.%d.%d", m[0], m[1], m[2], m[3]) return fmt.Sprintf("%d.%d.%d.%d", m[0], m[1], m[2], m[3])
} }
func DefaultInterfaceChangeMonitor() { func StartDefaultInterfaceChangeMonitor() {
t := time.NewTicker(defaultInterfaceMonitorDuration) go func() {
defer t.Stop() monitorMux.Lock()
if monitorStarted {
monitorMux.Unlock()
return
}
monitorStarted = true
monitorMux.Unlock()
for { select {
<-t.C case <-monitorStop:
default:
interfaceName, err := GetAutoDetectInterface()
if err != nil {
log.Warnln("[TUN] default interface monitor exited, cause: %v", err)
continue
} }
old := dialer.DefaultInterface.Load() t := time.NewTicker(monitorDuration)
if interfaceName == old { defer t.Stop()
continue
for {
select {
case <-t.C:
interfaceName, err := GetAutoDetectInterface()
if err != nil {
log.Warnln("[TUN] default interface monitor err: %v", err)
continue
}
old := dialer.DefaultInterface.Load()
if interfaceName == old {
continue
}
dialer.DefaultInterface.Store(interfaceName)
iface.FlushCache()
log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, interfaceName)
case <-monitorStop:
break
}
} }
}()
}
dialer.DefaultInterface.Store(interfaceName) func StopDefaultInterfaceChangeMonitor() {
monitorMux.Lock()
defer monitorMux.Unlock()
log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, interfaceName) if monitorStarted {
monitorStop <- struct{}{}
monitorStarted = false
}
}
func WaitForTunClose(deviceName string) {
t := time.NewTicker(600 * time.Millisecond)
defer t.Stop()
log.Debugln("[TUN] waiting for device close")
for {
<-t.C
interfaces, err := net.Interfaces()
if err != nil {
break
}
found := false
for i := len(interfaces) - 1; i > -1; i-- {
if interfaces[i].Name == deviceName {
found = true
break
}
}
if !found {
break
}
} }
} }

View file

@ -24,7 +24,7 @@ func GetAutoDetectInterface() (ifn string, err error) {
return return
} }
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute, autoDetectInterface bool) error { func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute bool) error {
var ( var (
interfaceName = dev.Name() interfaceName = dev.Name()
ip = addr.Masked().Addr().Next() ip = addr.Masked().Addr().Next()
@ -45,12 +45,12 @@ func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int,
} }
if autoRoute { if autoRoute {
err = configInterfaceRouting(interfaceName, addr, autoDetectInterface) err = configInterfaceRouting(interfaceName, addr)
} }
return err return err
} }
func configInterfaceRouting(interfaceName string, addr netip.Prefix, autoDetectInterface bool) error { func configInterfaceRouting(interfaceName string, addr netip.Prefix) error {
linkIP := addr.Masked().Addr().Next() linkIP := addr.Masked().Addr().Next()
const tableId = 1981801 const tableId = 1981801
@ -65,10 +65,6 @@ func configInterfaceRouting(interfaceName string, addr netip.Prefix, autoDetectI
execAddRuleCmd(fmt.Sprintf("from all iif %s lookup main suppress_prefixlength 0 pref 9003", interfaceName)) execAddRuleCmd(fmt.Sprintf("from all iif %s lookup main suppress_prefixlength 0 pref 9003", interfaceName))
execAddRuleCmd(fmt.Sprintf("not from all iif lo lookup %d pref 9004", tableId)) execAddRuleCmd(fmt.Sprintf("not from all iif lo lookup %d pref 9004", tableId))
if autoDetectInterface {
go DefaultInterfaceChangeMonitor()
}
return nil return nil
} }

View file

@ -12,7 +12,7 @@ func GetAutoDetectInterface() (string, error) {
return cmd.ExecCmd("bash -c route -n get default | grep 'interface:' | awk -F ' ' 'NR==1{print $2}' | xargs echo -n") return cmd.ExecCmd("bash -c route -n get default | grep 'interface:' | awk -F ' ' 'NR==1{print $2}' | xargs echo -n")
} }
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute, autoDetectInterface bool) error { func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute bool) error {
if !addr.Addr().Is4() { if !addr.Addr().Is4() {
return fmt.Errorf("supported ipv4 only") return fmt.Errorf("supported ipv4 only")
} }
@ -37,12 +37,12 @@ func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int,
} }
if autoRoute { if autoRoute {
err = configInterfaceRouting(interfaceName, addr, autoDetectInterface) err = configInterfaceRouting(interfaceName, addr)
} }
return err return err
} }
func configInterfaceRouting(interfaceName string, addr netip.Prefix, autoDetectInterface bool) error { func configInterfaceRouting(interfaceName string, addr netip.Prefix) error {
var ( var (
routes = append(defaultRoutes, addr.String()) routes = append(defaultRoutes, addr.String())
gateway = addr.Masked().Addr().Next() gateway = addr.Masked().Addr().Next()
@ -54,10 +54,6 @@ func configInterfaceRouting(interfaceName string, addr netip.Prefix, autoDetectI
} }
} }
if autoDetectInterface {
go DefaultInterfaceChangeMonitor()
}
return execRouterCmd("add", "-inet6", "2000::/3", interfaceName) return execRouterCmd("add", "-inet6", "2000::/3", interfaceName)
} }

View file

@ -13,7 +13,7 @@ func GetAutoDetectInterface() (string, error) {
return cmd.ExecCmd("bash -c ip route show | grep 'default via' | awk -F ' ' 'NR==1{print $5}' | xargs echo -n") return cmd.ExecCmd("bash -c ip route show | grep 'default via' | awk -F ' ' 'NR==1{print $5}' | xargs echo -n")
} }
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute, autoDetectInterface bool) error { func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute bool) error {
var ( var (
interfaceName = dev.Name() interfaceName = dev.Name()
ip = addr.Masked().Addr().Next() ip = addr.Masked().Addr().Next()
@ -32,12 +32,12 @@ func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int,
} }
if autoRoute { if autoRoute {
_ = configInterfaceRouting(interfaceName, addr, autoDetectInterface) _ = configInterfaceRouting(interfaceName, addr)
} }
return nil return nil
} }
func configInterfaceRouting(interfaceName string, addr netip.Prefix, autoDetectInterface bool) error { func configInterfaceRouting(interfaceName string, addr netip.Prefix) error {
linkIP := addr.Masked().Addr().Next() linkIP := addr.Masked().Addr().Next()
for _, route := range defaultRoutes { for _, route := range defaultRoutes {
@ -46,10 +46,6 @@ func configInterfaceRouting(interfaceName string, addr netip.Prefix, autoDetectI
} }
} }
if autoDetectInterface {
go DefaultInterfaceChangeMonitor()
}
return nil return nil
} }

View file

@ -14,6 +14,6 @@ func GetAutoDetectInterface() (string, error) {
return "", fmt.Errorf("can not auto detect interface-name on this OS: %s, you must be detecting interface-name by manual", runtime.GOOS) return "", fmt.Errorf("can not auto detect interface-name on this OS: %s, you must be detecting interface-name by manual", runtime.GOOS)
} }
func ConfigInterfaceAddress(device.Device, netip.Prefix, int, bool, bool) error { func ConfigInterfaceAddress(device.Device, netip.Prefix, int, bool) error {
return fmt.Errorf("unsupported on this OS: %s", runtime.GOOS) return fmt.Errorf("unsupported on this OS: %s", runtime.GOOS)
} }

View file

@ -27,7 +27,7 @@ func GetAutoDetectInterface() (string, error) {
return getAutoDetectInterfaceByFamily(winipcfg.AddressFamily(windows.AF_INET6)) return getAutoDetectInterfaceByFamily(winipcfg.AddressFamily(windows.AF_INET6))
} }
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute, autoDetectInterface bool) error { func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute bool) error {
retryOnFailure := services.StartedAtBoot() retryOnFailure := services.StartedAtBoot()
tryTimes := 0 tryTimes := 0
var err error var err error
@ -205,10 +205,6 @@ startOver:
wintunInterfaceName = dev.Name() wintunInterfaceName = dev.Name()
if autoDetectInterface {
go DefaultInterfaceChangeMonitor()
}
return nil return nil
} }

View file

@ -23,7 +23,7 @@ import (
) )
// New TunAdapter // New TunAdapter
func New(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) { func New(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) {
var ( var (
tunAddress = netip.Prefix{} tunAddress = netip.Prefix{}
@ -38,8 +38,8 @@ func New(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.Con
err error err error
) )
if tunAddressPrefix != nil { if tunConf.TunAddressPrefix != nil {
tunAddress = *tunAddressPrefix tunAddress = *tunConf.TunAddressPrefix
} }
if devName == "" { if devName == "" {
@ -90,12 +90,16 @@ func New(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.Con
} }
// setting address and routing // setting address and routing
err = commons.ConfigInterfaceAddress(tunDevice, tunAddress, mtu, autoRoute, tunConf.AutoDetectInterface) err = commons.ConfigInterfaceAddress(tunDevice, tunAddress, mtu, autoRoute)
if err != nil { if err != nil {
_ = tunDevice.Close() _ = tunDevice.Close()
return nil, fmt.Errorf("setting interface address and routing failed: %w", err) return nil, fmt.Errorf("setting interface address and routing failed: %w", err)
} }
if tunConf.AutoDetectInterface {
commons.StartDefaultInterfaceChangeMonitor()
}
setAtLatest(stackType, devName) setAtLatest(stackType, devName)
log.Infoln("TUN stack listening at: %s(%s), mtu: %d, auto route: %v, ip stack: %s", tunDevice.Name(), tunAddress.Masked().Addr().Next().String(), mtu, autoRoute, stackType) log.Infoln("TUN stack listening at: %s(%s), mtu: %d, auto route: %v, ip stack: %s", tunDevice.Name(), tunAddress.Masked().Addr().Next().String(), mtu, autoRoute, stackType)