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

2
go.mod
View file

@ -16,6 +16,7 @@ require (
github.com/oschwald/geoip2-golang v1.7.0 github.com/oschwald/geoip2-golang v1.7.0
github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.1 github.com/stretchr/testify v1.7.1
github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 github.com/xtls/go v0.0.0-20210920065950-d4af136d3672
go.etcd.io/bbolt v1.3.6 go.etcd.io/bbolt v1.3.6
go.uber.org/atomic v1.9.0 go.uber.org/atomic v1.9.0
@ -48,6 +49,7 @@ require (
github.com/oschwald/maxminddb-golang v1.9.0 // indirect github.com/oschwald/maxminddb-golang v1.9.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab // indirect golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab // indirect
golang.org/x/tools v0.1.10 // indirect golang.org/x/tools v0.1.10 // indirect

5
go.sum
View file

@ -202,6 +202,10 @@ github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 h1:hl6sK6aFgTLISijk6xIz
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86 h1:7SWt9pGCMaw+N1ZhRsaLKaYNviFhxambdoaoYlDqz1w=
github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 h1:4mkzGhKqt3JO1BWYjtD3iRFyAx4ow67hmSqOcGjuxqQ= github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 h1:4mkzGhKqt3JO1BWYjtD3iRFyAx4ow67hmSqOcGjuxqQ=
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672/go.mod h1:YGGVbz9cOxyKFUmhW7LGaLZaMA0cPlHJinvAmVxEMSU= github.com/xtls/go v0.0.0-20210920065950-d4af136d3672/go.mod h1:YGGVbz9cOxyKFUmhW7LGaLZaMA0cPlHJinvAmVxEMSU=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -289,6 +293,7 @@ golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

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 ( import (
"fmt" "fmt"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/iface"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
"net" "net"
"sync"
"time" "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"}
monitorDuration = 3 * time.Second
monitorStarted = false
monitorStop = make(chan struct{}, 2)
monitorMux sync.Mutex
) )
func ipv4MaskString(bits int) string { 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]) 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) { func WaitForTunClose(deviceName string) {
t := time.NewTicker(600 * time.Millisecond) t := time.NewTicker(600 * time.Millisecond)
defer t.Stop() defer t.Stop()

View file

@ -10,7 +10,11 @@ import (
) )
func GetAutoDetectInterface() (string, error) { 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 { func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute bool) error {