Feature: auto detect interface if switch network

This commit is contained in:
yaling888 2022-03-18 17:03:50 +08:00
parent 8e5f01597e
commit 7b7abf6973
4 changed files with 55 additions and 8 deletions

View file

@ -3,11 +3,19 @@ package commons
import ( import (
"fmt" "fmt"
"net" "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) m := net.CIDRMask(bits, 32)
if len(m) != 4 { if len(m) != 4 {
panic("ipv4Mask: len must be 4 bytes") 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]) 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)
}
}

View file

@ -9,7 +9,7 @@ import (
) )
func GetAutoDetectInterface() (string, error) { 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 { 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() interfaceName = dev.Name()
ip = addr.Masked().Addr().Next() ip = addr.Masked().Addr().Next()
gw = ip.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) 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 { func configInterfaceRouting(interfaceName string, addr netip.Prefix) error {
var ( var (
routes = append(Routes, addr.String()) routes = append(defaultRoutes, addr.String())
gateway = addr.Masked().Addr().Next() 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) return execRouterCmd("add", "-inet6", "2000::/3", interfaceName)
} }

View file

@ -36,11 +36,14 @@ func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int,
func configInterfaceRouting(interfaceName string, addr netip.Prefix) error { func configInterfaceRouting(interfaceName string, addr netip.Prefix) error {
linkIP := addr.Masked().Addr().Next() linkIP := addr.Masked().Addr().Next()
for _, route := range Routes { for _, route := range defaultRoutes {
if err := execRouterCmd("add", route, interfaceName, linkIP.String()); err != nil { if err := execRouterCmd("add", route, interfaceName, linkIP.String()); err != nil {
return err return err
} }
} }
go defaultInterfaceChangeMonitor()
return nil return nil
} }

View file

@ -15,6 +15,8 @@ import (
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
) )
var wintunInterfaceName string
func GetAutoDetectInterface() (string, error) { func GetAutoDetectInterface() (string, error) {
ifname, err := getAutoDetectInterfaceByFamily(winipcfg.AddressFamily(windows.AF_INET)) ifname, err := getAutoDetectInterfaceByFamily(winipcfg.AddressFamily(windows.AF_INET))
if err == nil { if err == nil {
@ -30,7 +32,7 @@ func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int,
var err error var err error
startOver: startOver:
if tryTimes > 0 { 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) time.Sleep(time.Second)
retryOnFailure = retryOnFailure && tryTimes < 15 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) return fmt.Errorf("unable to set DNS %s %s: %w", "198.18.0.2", "nil", err)
} }
wintunInterfaceName = dev.Name()
go defaultInterfaceChangeMonitor()
return nil return nil
} }
@ -221,7 +227,7 @@ func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, add
for address := iface.FirstUnicastAddress; address != nil; address = address.Next { for address := iface.FirstUnicastAddress; address != nil; address = address.Next {
if ip, _ := netip.AddrFromSlice(address.Address.IP()); addrHash[ip] { if ip, _ := netip.AddrFromSlice(address.Address.IP()); addrHash[ip] {
prefix := netip.PrefixFrom(ip, int(address.OnLinkPrefixLength)) 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) _ = iface.LUID.DeleteIPAddress(prefix)
} }
} }
@ -248,6 +254,10 @@ func getAutoDetectInterfaceByFamily(family winipcfg.AddressFamily) (string, erro
ifname := iface.FriendlyName() ifname := iface.FriendlyName()
if wintunInterfaceName == ifname {
continue
}
for gatewayAddress := iface.FirstGatewayAddress; gatewayAddress != nil; gatewayAddress = gatewayAddress.Next { for gatewayAddress := iface.FirstGatewayAddress; gatewayAddress != nil; gatewayAddress = gatewayAddress.Next {
nextHop, _ := netip.AddrFromSlice(gatewayAddress.Address.IP()) nextHop, _ := netip.AddrFromSlice(gatewayAddress.Address.IP())