Fix default interface check for darwin
This commit is contained in:
parent
53f50347e0
commit
d744d03d93
1 changed files with 101 additions and 20 deletions
|
|
@ -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
|
||||||
}) {
|
}
|
||||||
oldInterface := m.defaultInterfaceName
|
defaultInterface = routeInterface
|
||||||
oldIndex := m.defaultInterfaceIndex
|
break
|
||||||
|
}
|
||||||
m.defaultInterfaceIndex = routeMessage.Index
|
if defaultInterface == nil {
|
||||||
m.defaultInterfaceName = routeInterface.Name
|
defaultInterface, err = getDefaultInterfaceBySocket()
|
||||||
if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
|
||||||
m.emit(EventInterfaceUpdate)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ErrNoRoute
|
oldInterface := m.defaultInterfaceName
|
||||||
|
oldIndex := m.defaultInterfaceIndex
|
||||||
|
m.defaultInterfaceIndex = defaultInterface.Index
|
||||||
|
m.defaultInterfaceName = defaultInterface.Name
|
||||||
|
if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
m.emit(EventInterfaceUpdate)
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue