refactor: new way to get interface change even for linux
This commit is contained in:
parent
cc1c1340a3
commit
0f43a19fdb
6 changed files with 169 additions and 62 deletions
2
go.mod
2
go.mod
|
@ -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
5
go.sum
|
@ -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=
|
||||||
|
|
90
listener/tun/ipstack/commons/auto_linux.go
Normal file
90
listener/tun/ipstack/commons/auto_linux.go
Normal 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{}{}
|
||||||
|
}
|
||||||
|
}
|
67
listener/tun/ipstack/commons/auto_others.go
Normal file
67
listener/tun/ipstack/commons/auto_others.go
Normal 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{}{}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue