Feature: auto detect interface if switch network
This commit is contained in:
parent
8e5f01597e
commit
7b7abf6973
4 changed files with 55 additions and 8 deletions
|
@ -3,11 +3,19 @@ package commons
|
|||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/component/dialer"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
)
|
||||
|
||||
var Routes = []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"}
|
||||
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"}
|
||||
|
||||
func IPv4MaskString(bits int) string {
|
||||
defaultInterfaceMonitorDuration = 20 * time.Second
|
||||
)
|
||||
|
||||
func ipv4MaskString(bits int) string {
|
||||
m := net.CIDRMask(bits, 32)
|
||||
if len(m) != 4 {
|
||||
panic("ipv4Mask: len must be 4 bytes")
|
||||
|
@ -15,3 +23,27 @@ 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()
|
||||
|
||||
for {
|
||||
<-t.C
|
||||
|
||||
interfaceName, err := GetAutoDetectInterface()
|
||||
if err != nil {
|
||||
log.Warnln("[TUN] default interface monitor exited, cause: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
old := dialer.DefaultInterface.Load()
|
||||
if interfaceName == old {
|
||||
continue
|
||||
}
|
||||
|
||||
dialer.DefaultInterface.Store(interfaceName)
|
||||
|
||||
log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, interfaceName)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
func GetAutoDetectInterface() (string, error) {
|
||||
return cmd.ExecCmd("bash -c netstat -rnf inet | grep 'default' | awk -F ' ' 'NR==1{print $6}' | 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 bool) error {
|
||||
|
@ -21,7 +21,7 @@ func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int,
|
|||
interfaceName = dev.Name()
|
||||
ip = addr.Masked().Addr().Next()
|
||||
gw = ip.Next()
|
||||
netmask = IPv4MaskString(addr.Bits())
|
||||
netmask = ipv4MaskString(addr.Bits())
|
||||
)
|
||||
|
||||
cmdStr := fmt.Sprintf("ifconfig %s inet %s netmask %s %s", interfaceName, ip, netmask, gw)
|
||||
|
@ -44,7 +44,7 @@ func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int,
|
|||
|
||||
func configInterfaceRouting(interfaceName string, addr netip.Prefix) error {
|
||||
var (
|
||||
routes = append(Routes, addr.String())
|
||||
routes = append(defaultRoutes, addr.String())
|
||||
gateway = addr.Masked().Addr().Next()
|
||||
)
|
||||
|
||||
|
@ -54,6 +54,8 @@ func configInterfaceRouting(interfaceName string, addr netip.Prefix) error {
|
|||
}
|
||||
}
|
||||
|
||||
go defaultInterfaceChangeMonitor()
|
||||
|
||||
return execRouterCmd("add", "-inet6", "2000::/3", interfaceName)
|
||||
}
|
||||
|
||||
|
|
|
@ -36,11 +36,14 @@ func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int,
|
|||
|
||||
func configInterfaceRouting(interfaceName string, addr netip.Prefix) error {
|
||||
linkIP := addr.Masked().Addr().Next()
|
||||
for _, route := range Routes {
|
||||
for _, route := range defaultRoutes {
|
||||
if err := execRouterCmd("add", route, interfaceName, linkIP.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
go defaultInterfaceChangeMonitor()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ import (
|
|||
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
||||
)
|
||||
|
||||
var wintunInterfaceName string
|
||||
|
||||
func GetAutoDetectInterface() (string, error) {
|
||||
ifname, err := getAutoDetectInterfaceByFamily(winipcfg.AddressFamily(windows.AF_INET))
|
||||
if err == nil {
|
||||
|
@ -30,7 +32,7 @@ func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int,
|
|||
var err error
|
||||
startOver:
|
||||
if tryTimes > 0 {
|
||||
log.Infoln("Retrying interface configuration after failure because system just booted (T+%v): %v", windows.DurationSinceBoot(), err)
|
||||
log.Infoln("[TUN] retrying interface configuration after failure because system just booted (T+%v): %v", windows.DurationSinceBoot(), err)
|
||||
time.Sleep(time.Second)
|
||||
retryOnFailure = retryOnFailure && tryTimes < 15
|
||||
}
|
||||
|
@ -199,6 +201,10 @@ startOver:
|
|||
return fmt.Errorf("unable to set DNS %s %s: %w", "198.18.0.2", "nil", err)
|
||||
}
|
||||
|
||||
wintunInterfaceName = dev.Name()
|
||||
|
||||
go defaultInterfaceChangeMonitor()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -221,7 +227,7 @@ func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, add
|
|||
for address := iface.FirstUnicastAddress; address != nil; address = address.Next {
|
||||
if ip, _ := netip.AddrFromSlice(address.Address.IP()); addrHash[ip] {
|
||||
prefix := netip.PrefixFrom(ip, int(address.OnLinkPrefixLength))
|
||||
log.Infoln("Cleaning up stale address %s from interface ‘%s’", prefix.String(), iface.FriendlyName())
|
||||
log.Infoln("[TUN] cleaning up stale address %s from interface ‘%s’", prefix.String(), iface.FriendlyName())
|
||||
_ = iface.LUID.DeleteIPAddress(prefix)
|
||||
}
|
||||
}
|
||||
|
@ -248,6 +254,10 @@ func getAutoDetectInterfaceByFamily(family winipcfg.AddressFamily) (string, erro
|
|||
|
||||
ifname := iface.FriendlyName()
|
||||
|
||||
if wintunInterfaceName == ifname {
|
||||
continue
|
||||
}
|
||||
|
||||
for gatewayAddress := iface.FirstGatewayAddress; gatewayAddress != nil; gatewayAddress = gatewayAddress.Next {
|
||||
nextHop, _ := netip.AddrFromSlice(gatewayAddress.Address.IP())
|
||||
|
||||
|
|
Loading…
Reference in a new issue