refactor: new way to get interface change even for linux

This commit is contained in:
adlyq
2022-05-20 21:43:27 +08:00
parent cc1c1340a3
commit 0f43a19fdb
6 changed files with 169 additions and 62 deletions

View File

@ -0,0 +1,90 @@
package commons
import (
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/iface"
"github.com/Dreamacro/clash/log"
"github.com/vishvananda/netlink"
"go.uber.org/atomic"
"time"
)
var (
monitorStarted = atomic.NewBool(false)
monitorStop = make(chan struct{}, 2)
)
func StartDefaultInterfaceChangeMonitor() {
go func() {
if monitorStarted.Load() {
return
}
monitorStarted.Store(true)
done := make(chan struct{})
ch := make(chan netlink.RouteUpdate, 2)
err := netlink.RouteSubscribe(ch, done)
if err != nil {
log.Warnln("[TUN] auto detect interface fail: %s", err)
return
}
log.Debugln("[TUN] start auto detect interface monitor")
defer func() {
close(done)
monitorStarted.Store(false)
log.Debugln("[TUN] stop auto detect interface monitor")
}()
select {
case <-monitorStop:
default:
}
for {
select {
case <-monitorStop:
return
case <-ch:
}
interfaceName, err := GetAutoDetectInterface()
if err != nil {
t := time.NewTicker(2 * time.Second)
for {
select {
case ch <- <-ch:
break
case <-t.C:
interfaceName, err = GetAutoDetectInterface()
if err != nil {
continue
}
}
break
}
t.Stop()
}
if err != nil {
log.Debugln("[TUN] detect interface: %s", 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)
}
}()
}
func StopDefaultInterfaceChangeMonitor() {
if monitorStarted.Load() {
monitorStop <- struct{}{}
}
}

View File

@ -0,0 +1,67 @@
//go:build !linux
package commons
import (
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/iface"
"github.com/Dreamacro/clash/log"
"go.uber.org/atomic"
"time"
)
var (
monitorDuration = 3 * time.Second
monitorStarted = atomic.NewBool(false)
monitorStop = make(chan struct{}, 2)
)
func StartDefaultInterfaceChangeMonitor() {
go func() {
if monitorStarted.Load() {
return
}
monitorStarted.Store(true)
t := time.NewTicker(monitorDuration)
log.Debugln("[TUN] start auto detect interface monitor")
defer func() {
monitorStarted.Store(false)
t.Stop()
log.Debugln("[TUN] stop auto detect interface monitor")
}()
select {
case <-monitorStop:
default:
}
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
}
}
}()
}
func StopDefaultInterfaceChangeMonitor() {
if monitorStarted.Load() {
monitorStop <- struct{}{}
}
}

View File

@ -2,21 +2,13 @@ package commons
import (
"fmt"
"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"}
monitorDuration = 3 * time.Second
monitorStarted = false
monitorStop = make(chan struct{}, 2)
monitorMux sync.Mutex
)
func ipv4MaskString(bits int) string {
@ -28,59 +20,6 @@ func ipv4MaskString(bits int) string {
return fmt.Sprintf("%d.%d.%d.%d", m[0], m[1], m[2], m[3])
}
func StartDefaultInterfaceChangeMonitor() {
go func() {
monitorMux.Lock()
if monitorStarted {
monitorMux.Unlock()
return
}
monitorStarted = true
monitorMux.Unlock()
select {
case <-monitorStop:
default:
}
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
}
}
}()
}
func StopDefaultInterfaceChangeMonitor() {
monitorMux.Lock()
defer monitorMux.Unlock()
if monitorStarted {
monitorStop <- struct{}{}
monitorStarted = false
}
}
func WaitForTunClose(deviceName string) {
t := time.NewTicker(600 * time.Millisecond)
defer t.Stop()

View File

@ -10,7 +10,11 @@ import (
)
func GetAutoDetectInterface() (string, error) {
return cmd.ExecCmd("bash -c ip route show | grep 'default via' | awk -F ' ' 'NR==1{print $5}' | xargs echo -n")
execCmd, err := cmd.ExecCmd("bash -c ip route show | grep 'default via' | awk -F ' ' 'NR==1{print $5}' | xargs echo -n")
if execCmd == "" {
return "", fmt.Errorf("interface not found")
}
return execCmd, err
}
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute bool) error {