Fix default interface check for darwin

This commit is contained in:
世界 2023-04-19 09:10:45 +08:00
parent 53f50347e0
commit d744d03d93
No known key found for this signature in database
GPG key ID: CD109927C34A63C4

View file

@ -5,10 +5,9 @@ import (
"net" "net"
"net/netip" "net/netip"
"os" "os"
"runtime"
"strings"
"sync" "sync"
"syscall" "syscall"
"time"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
@ -85,32 +84,114 @@ func (m *defaultInterfaceMonitor) checkUpdate() error {
if err != nil { if err != nil {
return err return err
} }
var defaultInterface *net.Interface
for _, rawRouteMessage := range routeMessages { for _, rawRouteMessage := range routeMessages {
routeMessage := rawRouteMessage.(*route.RouteMessage) routeMessage := rawRouteMessage.(*route.RouteMessage)
if len(routeMessage.Addrs) <= unix.RTAX_NETMASK {
continue
}
destination, isIPv4Destination := routeMessage.Addrs[unix.RTAX_DST].(*route.Inet4Addr)
if !isIPv4Destination {
continue
}
if destination.IP != netip.IPv4Unspecified().As4() {
continue
}
mask, isIPv4Mask := routeMessage.Addrs[unix.RTAX_NETMASK].(*route.Inet4Addr)
if !isIPv4Mask {
continue
}
ones, _ := net.IPMask(mask.IP[:]).Size()
if ones != 0 {
continue
}
routeInterface, err := net.InterfaceByIndex(routeMessage.Index) routeInterface, err := net.InterfaceByIndex(routeMessage.Index)
if err != nil { if err != nil {
return err return err
} }
if runtime.GOOS == "ios" && strings.HasPrefix(routeInterface.Name, "utun") { if routeMessage.Flags&unix.RTF_UP == 0 {
continue continue
} }
if common.Any(common.FilterIsInstance(routeMessage.Addrs, func(it route.Addr) (*route.Inet4Addr, bool) { if routeMessage.Flags&unix.RTF_GATEWAY == 0 {
addr, loaded := it.(*route.Inet4Addr) continue
return addr, loaded }
}), func(addr *route.Inet4Addr) bool { if routeMessage.Flags&unix.RTF_IFSCOPE != 0 {
return addr.IP == netip.IPv4Unspecified().As4() continue
}) { }
defaultInterface = routeInterface
break
}
if defaultInterface == nil {
defaultInterface, err = getDefaultInterfaceBySocket()
if err != nil {
return err
}
}
oldInterface := m.defaultInterfaceName oldInterface := m.defaultInterfaceName
oldIndex := m.defaultInterfaceIndex oldIndex := m.defaultInterfaceIndex
m.defaultInterfaceIndex = defaultInterface.Index
m.defaultInterfaceIndex = routeMessage.Index m.defaultInterfaceName = defaultInterface.Name
m.defaultInterfaceName = routeInterface.Name
if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex { if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
return nil return nil
} }
m.emit(EventInterfaceUpdate) m.emit(EventInterfaceUpdate)
return nil return nil
} }
func getDefaultInterfaceBySocket() (*net.Interface, error) {
socketFd, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0)
if err != nil {
return nil, E.Cause(err, "create file descriptor")
} }
return ErrNoRoute defer unix.Close(socketFd)
go unix.Connect(socketFd, &unix.SockaddrInet4{
Addr: [4]byte{10, 255, 255, 255},
Port: 80,
})
result := make(chan netip.Addr, 1)
go func() {
for {
sockname, sockErr := unix.Getsockname(socketFd)
if sockErr != nil {
break
}
sockaddr, isInet4Sockaddr := sockname.(*unix.SockaddrInet4)
if !isInet4Sockaddr {
break
}
addr := netip.AddrFrom4(sockaddr.Addr)
if addr.IsUnspecified() {
time.Sleep(time.Millisecond)
continue
}
result <- addr
break
}
}()
var selectedAddr netip.Addr
select {
case selectedAddr = <-result:
case <-time.After(time.Second):
return nil, os.ErrDeadlineExceeded
}
interfaces, err := net.Interfaces()
if err != nil {
return nil, E.Cause(err, "net.Interfaces")
}
for _, netInterface := range interfaces {
interfaceAddrs, err := netInterface.Addrs()
if err != nil {
return nil, E.Cause(err, "net.Interfaces.Addrs")
}
for _, interfaceAddr := range interfaceAddrs {
ipNet, isIPNet := interfaceAddr.(*net.IPNet)
if !isIPNet {
continue
}
if ipNet.Contains(selectedAddr.AsSlice()) {
return &netInterface, nil
}
}
}
return nil, E.New("no interface found for address ", selectedAddr)
} }