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 (
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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())
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue