Skip to content

Commit d744d03

Browse files
committed
Fix default interface check for darwin
1 parent 53f5034 commit d744d03

File tree

1 file changed

+99
-18
lines changed

1 file changed

+99
-18
lines changed

monitor_darwin.go

Lines changed: 99 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ import (
55
"net"
66
"net/netip"
77
"os"
8-
"runtime"
9-
"strings"
108
"sync"
119
"syscall"
10+
"time"
1211

1312
"github.com/sagernet/sing/common"
1413
E "github.com/sagernet/sing/common/exceptions"
@@ -85,32 +84,114 @@ func (m *defaultInterfaceMonitor) checkUpdate() error {
8584
if err != nil {
8685
return err
8786
}
87+
var defaultInterface *net.Interface
8888
for _, rawRouteMessage := range routeMessages {
8989
routeMessage := rawRouteMessage.(*route.RouteMessage)
90+
if len(routeMessage.Addrs) <= unix.RTAX_NETMASK {
91+
continue
92+
}
93+
destination, isIPv4Destination := routeMessage.Addrs[unix.RTAX_DST].(*route.Inet4Addr)
94+
if !isIPv4Destination {
95+
continue
96+
}
97+
if destination.IP != netip.IPv4Unspecified().As4() {
98+
continue
99+
}
100+
mask, isIPv4Mask := routeMessage.Addrs[unix.RTAX_NETMASK].(*route.Inet4Addr)
101+
if !isIPv4Mask {
102+
continue
103+
}
104+
ones, _ := net.IPMask(mask.IP[:]).Size()
105+
if ones != 0 {
106+
continue
107+
}
90108
routeInterface, err := net.InterfaceByIndex(routeMessage.Index)
91109
if err != nil {
92110
return err
93111
}
94-
if runtime.GOOS == "ios" && strings.HasPrefix(routeInterface.Name, "utun") {
112+
if routeMessage.Flags&unix.RTF_UP == 0 {
113+
continue
114+
}
115+
if routeMessage.Flags&unix.RTF_GATEWAY == 0 {
116+
continue
117+
}
118+
if routeMessage.Flags&unix.RTF_IFSCOPE != 0 {
95119
continue
96120
}
97-
if common.Any(common.FilterIsInstance(routeMessage.Addrs, func(it route.Addr) (*route.Inet4Addr, bool) {
98-
addr, loaded := it.(*route.Inet4Addr)
99-
return addr, loaded
100-
}), func(addr *route.Inet4Addr) bool {
101-
return addr.IP == netip.IPv4Unspecified().As4()
102-
}) {
103-
oldInterface := m.defaultInterfaceName
104-
oldIndex := m.defaultInterfaceIndex
121+
defaultInterface = routeInterface
122+
break
123+
}
124+
if defaultInterface == nil {
125+
defaultInterface, err = getDefaultInterfaceBySocket()
126+
if err != nil {
127+
return err
128+
}
129+
}
130+
oldInterface := m.defaultInterfaceName
131+
oldIndex := m.defaultInterfaceIndex
132+
m.defaultInterfaceIndex = defaultInterface.Index
133+
m.defaultInterfaceName = defaultInterface.Name
134+
if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
135+
return nil
136+
}
137+
m.emit(EventInterfaceUpdate)
138+
return nil
139+
}
105140

106-
m.defaultInterfaceIndex = routeMessage.Index
107-
m.defaultInterfaceName = routeInterface.Name
108-
if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
109-
return nil
141+
func getDefaultInterfaceBySocket() (*net.Interface, error) {
142+
socketFd, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0)
143+
if err != nil {
144+
return nil, E.Cause(err, "create file descriptor")
145+
}
146+
defer unix.Close(socketFd)
147+
go unix.Connect(socketFd, &unix.SockaddrInet4{
148+
Addr: [4]byte{10, 255, 255, 255},
149+
Port: 80,
150+
})
151+
result := make(chan netip.Addr, 1)
152+
go func() {
153+
for {
154+
sockname, sockErr := unix.Getsockname(socketFd)
155+
if sockErr != nil {
156+
break
157+
}
158+
sockaddr, isInet4Sockaddr := sockname.(*unix.SockaddrInet4)
159+
if !isInet4Sockaddr {
160+
break
161+
}
162+
addr := netip.AddrFrom4(sockaddr.Addr)
163+
if addr.IsUnspecified() {
164+
time.Sleep(time.Millisecond)
165+
continue
166+
}
167+
result <- addr
168+
break
169+
}
170+
}()
171+
var selectedAddr netip.Addr
172+
select {
173+
case selectedAddr = <-result:
174+
case <-time.After(time.Second):
175+
return nil, os.ErrDeadlineExceeded
176+
}
177+
interfaces, err := net.Interfaces()
178+
if err != nil {
179+
return nil, E.Cause(err, "net.Interfaces")
180+
}
181+
for _, netInterface := range interfaces {
182+
interfaceAddrs, err := netInterface.Addrs()
183+
if err != nil {
184+
return nil, E.Cause(err, "net.Interfaces.Addrs")
185+
}
186+
for _, interfaceAddr := range interfaceAddrs {
187+
ipNet, isIPNet := interfaceAddr.(*net.IPNet)
188+
if !isIPNet {
189+
continue
190+
}
191+
if ipNet.Contains(selectedAddr.AsSlice()) {
192+
return &netInterface, nil
110193
}
111-
m.emit(EventInterfaceUpdate)
112-
return nil
113194
}
114195
}
115-
return ErrNoRoute
196+
return nil, E.New("no interface found for address ", selectedAddr)
116197
}

0 commit comments

Comments
 (0)