diff --git a/config/config.go b/config/config.go index 08f48863..c89c63ca 100644 --- a/config/config.go +++ b/config/config.go @@ -113,6 +113,7 @@ type Tun struct { DNSHijack []netip.AddrPort `yaml:"dns-hijack" json:"dns-hijack"` AutoRoute bool `yaml:"auto-route" json:"auto-route"` AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"` + TunAddressPrefix *netip.Prefix `yaml:"-" json:"-"` } // IPTables config @@ -327,12 +328,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } 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) proxies, providers, err := parseProxies(rawCfg) @@ -361,6 +356,12 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } 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.Sniffer, err = parseSniffer(rawCfg.Sniffer) @@ -865,7 +866,7 @@ func parseAuthentication(rawRecords []string) []auth.AuthUser { 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 { autoDetectInterfaceName, err := commons.GetAutoDetectInterface() if err != nil { @@ -892,6 +893,11 @@ func parseTun(rawTun RawTun, general *General) (*Tun, error) { dnsHijack = append(dnsHijack, addrPort) } + var tunAddressPrefix *netip.Prefix + if dnsCfg.FakeIPRange != nil { + tunAddressPrefix = dnsCfg.FakeIPRange.IPNet() + } + return &Tun{ Enable: rawTun.Enable, Device: rawTun.Device, @@ -899,6 +905,7 @@ func parseTun(rawTun RawTun, general *General) (*Tun, error) { DNSHijack: dnsHijack, AutoRoute: rawTun.AutoRoute, AutoDetectInterface: rawTun.AutoDetectInterface, + TunAddressPrefix: tunAddressPrefix, }, nil } diff --git a/hub/executor/executor.go b/hub/executor/executor.go index ab3a7e18..17d3f8bc 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -85,7 +85,7 @@ func ApplyConfig(cfg *config.Config, force bool) { loadRuleProvider(cfg.RuleProviders) updateGeneral(cfg.General, force) updateIPTables(cfg) - updateTun(cfg.Tun, cfg.DNS) + updateTun(cfg.Tun) updateExperimental(cfg) log.SetLevel(cfg.General.LogLevel) @@ -244,12 +244,8 @@ func loadProxyProvider(proxyProviders map[string]provider.ProxyProvider) { wg.Wait() } -func updateTun(tun *config.Tun, dns *config.DNS) { - var tunAddressPrefix *netip.Prefix - if dns.FakeIPRange != nil { - tunAddressPrefix = dns.FakeIPRange.IPNet() - } - P.ReCreateTun(tun, tunAddressPrefix, tunnel.TCPIn(), tunnel.UDPIn()) +func updateTun(tun *config.Tun) { + P.ReCreateTun(tun, tunnel.TCPIn(), tunnel.UDPIn()) } func updateSniffer(sniffer *config.Sniffer) { @@ -428,7 +424,7 @@ func updateIPTables(cfg *config.Config) { } func Shutdown() { - P.Cleanup() + P.Cleanup(false) tproxy.CleanupTProxyIPTables() resolver.StoreFakePoolState() diff --git a/listener/listener.go b/listener/listener.go index 8644d500..cbac01a2 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/Dreamacro/clash/common/cmd" "github.com/Dreamacro/clash/listener/inner" + "github.com/Dreamacro/clash/listener/tun/ipstack/commons" "net" "net/netip" "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()) } -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() defer tunMux.Unlock() @@ -335,35 +336,23 @@ func ReCreateTun(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan defer func() { if err != nil { log.Errorln("Start TUN listening error: %s", err.Error()) + Cleanup(false) } }() - if tunAddressPrefix == nil { - tunAddressPrefix = lastTunAddressPrefix - } - - if !hasTunConfigChange(tunConf, tunAddressPrefix) { + if !hasTunConfigChange(tunConf) { return } - if tunStackListener != nil { - _ = tunStackListener.Close() - tunStackListener = nil - lastTunConf = nil - lastTunAddressPrefix = nil - } + Cleanup(true) if !tunConf.Enable { return } - tunStackListener, err = tun.New(tunConf, tunAddressPrefix, tcpIn, udpIn) - if err != nil { - log.Warnln("Failed to start TUN interface: %s", err) - } + tunStackListener, err = tun.New(tunConf, tcpIn, udpIn) lastTunConf = tunConf - lastTunAddressPrefix = tunAddressPrefix } // 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) } -func hasTunConfigChange(tunConf *config.Tun, tunAddressPrefix *netip.Prefix) bool { +func hasTunConfigChange(tunConf *config.Tun) bool { if lastTunConf == nil { return true } @@ -448,24 +437,27 @@ func hasTunConfigChange(tunConf *config.Tun, tunAddressPrefix *netip.Prefix) boo if lastTunConf.Enable != tunConf.Enable || lastTunConf.Device != tunConf.Device || lastTunConf.Stack != tunConf.Stack || - lastTunConf.AutoRoute != tunConf.AutoRoute { + lastTunConf.AutoRoute != tunConf.AutoRoute || + lastTunConf.AutoDetectInterface != tunConf.AutoDetectInterface { return true } - if (tunAddressPrefix != nil && lastTunAddressPrefix == nil) || (tunAddressPrefix == nil && lastTunAddressPrefix != nil) { - return true - } - - if tunAddressPrefix != nil && lastTunAddressPrefix != nil && *tunAddressPrefix != *lastTunAddressPrefix { + if tunConf.TunAddressPrefix.String() != lastTunConf.TunAddressPrefix.String() { return true } return false } -func Cleanup() { +func Cleanup(wait bool) { if tunStackListener != nil { _ = tunStackListener.Close() + commons.StopDefaultInterfaceChangeMonitor() + + if wait { + commons.WaitForTunClose(lastTunConf.Device) + } + if runtime.GOOS == "android" { prefs := []int{9000, 9001, 9002, 9003, 9004} for _, pref := range prefs { @@ -473,4 +465,7 @@ func Cleanup() { } } } + tunStackListener = nil + lastTunConf = nil + lastTunAddressPrefix = nil } diff --git a/listener/tun/ipstack/commons/router.go b/listener/tun/ipstack/commons/router.go index 5d83b70e..e4f66755 100644 --- a/listener/tun/ipstack/commons/router.go +++ b/listener/tun/ipstack/commons/router.go @@ -2,16 +2,21 @@ package commons import ( "fmt" - "net" - "time" - "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/iface" "github.com/Dreamacro/clash/log" + "net" + "sync" + "time" ) 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"} - defaultInterfaceMonitorDuration = 3 * time.Second + 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"} + + monitorDuration = 3 * time.Second + monitorStarted = false + monitorStop = make(chan struct{}, 2) + monitorMux sync.Mutex ) 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]) } -func DefaultInterfaceChangeMonitor() { - t := time.NewTicker(defaultInterfaceMonitorDuration) - defer t.Stop() +func StartDefaultInterfaceChangeMonitor() { + go func() { + monitorMux.Lock() + if monitorStarted { + monitorMux.Unlock() + return + } + monitorStarted = true + monitorMux.Unlock() - for { - <-t.C - - interfaceName, err := GetAutoDetectInterface() - if err != nil { - log.Warnln("[TUN] default interface monitor exited, cause: %v", err) - continue + select { + case <-monitorStop: + default: } - old := dialer.DefaultInterface.Load() - if interfaceName == old { - continue + t := time.NewTicker(monitorDuration) + defer t.Stop() + + 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 + } } } diff --git a/listener/tun/ipstack/commons/router_android.go b/listener/tun/ipstack/commons/router_android.go index c24ed5a9..4dc351fc 100644 --- a/listener/tun/ipstack/commons/router_android.go +++ b/listener/tun/ipstack/commons/router_android.go @@ -24,7 +24,7 @@ func GetAutoDetectInterface() (ifn string, err error) { 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 ( interfaceName = dev.Name() ip = addr.Masked().Addr().Next() @@ -45,12 +45,12 @@ func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, } if autoRoute { - err = configInterfaceRouting(interfaceName, addr, autoDetectInterface) + err = configInterfaceRouting(interfaceName, addr) } 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() 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("not from all iif lo lookup %d pref 9004", tableId)) - if autoDetectInterface { - go DefaultInterfaceChangeMonitor() - } - return nil } diff --git a/listener/tun/ipstack/commons/router_darwin.go b/listener/tun/ipstack/commons/router_darwin.go index abead15a..47e3db1a 100644 --- a/listener/tun/ipstack/commons/router_darwin.go +++ b/listener/tun/ipstack/commons/router_darwin.go @@ -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") } -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() { return fmt.Errorf("supported ipv4 only") } @@ -37,12 +37,12 @@ func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, } if autoRoute { - err = configInterfaceRouting(interfaceName, addr, autoDetectInterface) + err = configInterfaceRouting(interfaceName, addr) } return err } -func configInterfaceRouting(interfaceName string, addr netip.Prefix, autoDetectInterface bool) error { +func configInterfaceRouting(interfaceName string, addr netip.Prefix) error { var ( routes = append(defaultRoutes, addr.String()) 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) } diff --git a/listener/tun/ipstack/commons/router_linux.go b/listener/tun/ipstack/commons/router_linux.go index 4a11f99f..8c5b7165 100644 --- a/listener/tun/ipstack/commons/router_linux.go +++ b/listener/tun/ipstack/commons/router_linux.go @@ -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") } -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 ( interfaceName = dev.Name() ip = addr.Masked().Addr().Next() @@ -32,12 +32,12 @@ func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, } if autoRoute { - _ = configInterfaceRouting(interfaceName, addr, autoDetectInterface) + _ = configInterfaceRouting(interfaceName, addr) } 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() for _, route := range defaultRoutes { @@ -46,10 +46,6 @@ func configInterfaceRouting(interfaceName string, addr netip.Prefix, autoDetectI } } - if autoDetectInterface { - go DefaultInterfaceChangeMonitor() - } - return nil } diff --git a/listener/tun/ipstack/commons/router_others.go b/listener/tun/ipstack/commons/router_others.go index cec981cc..6c8ea341 100644 --- a/listener/tun/ipstack/commons/router_others.go +++ b/listener/tun/ipstack/commons/router_others.go @@ -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) } -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) } diff --git a/listener/tun/ipstack/commons/router_windows.go b/listener/tun/ipstack/commons/router_windows.go index b9d1a68c..b29b20ab 100644 --- a/listener/tun/ipstack/commons/router_windows.go +++ b/listener/tun/ipstack/commons/router_windows.go @@ -27,7 +27,7 @@ func GetAutoDetectInterface() (string, error) { 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() tryTimes := 0 var err error @@ -205,10 +205,6 @@ startOver: wintunInterfaceName = dev.Name() - if autoDetectInterface { - go DefaultInterfaceChangeMonitor() - } - return nil } diff --git a/listener/tun/tun_adapter.go b/listener/tun/tun_adapter.go index 96b2ca52..35a9d0a6 100644 --- a/listener/tun/tun_adapter.go +++ b/listener/tun/tun_adapter.go @@ -23,7 +23,7 @@ import ( ) // 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 ( tunAddress = netip.Prefix{} @@ -38,8 +38,8 @@ func New(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.Con err error ) - if tunAddressPrefix != nil { - tunAddress = *tunAddressPrefix + if tunConf.TunAddressPrefix != nil { + tunAddress = *tunConf.TunAddressPrefix } if devName == "" { @@ -90,12 +90,16 @@ func New(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan<- C.Con } // setting address and routing - err = commons.ConfigInterfaceAddress(tunDevice, tunAddress, mtu, autoRoute, tunConf.AutoDetectInterface) + err = commons.ConfigInterfaceAddress(tunDevice, tunAddress, mtu, autoRoute) if err != nil { _ = tunDevice.Close() return nil, fmt.Errorf("setting interface address and routing failed: %w", err) } + if tunConf.AutoDetectInterface { + commons.StartDefaultInterfaceChangeMonitor() + } + 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)