summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--net/interfaces/interfaces_darwin.go71
-rw-r--r--net/netns/netns_darwin_tailscaled.go17
2 files changed, 84 insertions, 4 deletions
diff --git a/net/interfaces/interfaces_darwin.go b/net/interfaces/interfaces_darwin.go
index 60335bf80..c95e7e058 100644
--- a/net/interfaces/interfaces_darwin.go
+++ b/net/interfaces/interfaces_darwin.go
@@ -10,6 +10,7 @@ import (
"log"
"net"
"net/netip"
+ "os"
"syscall"
"golang.org/x/net/route"
@@ -86,6 +87,76 @@ func DefaultRouteInterfaceIndex() (int, error) {
return 0, fmt.Errorf("ambiguous gateway interfaces found: %v", indexSeen)
}
+// InterfaceIndexFor returns the interface index that we should bind to in
+// order to send traffic to the provided address.
+func InterfaceIndexFor(addr netip.Addr) (int, error) {
+ fd, err := unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
+ if err != nil {
+ return 0, fmt.Errorf("creating AF_ROUTE socket: %w", err)
+ }
+ defer unix.Close(fd)
+
+ var routeAddr route.Addr
+ if addr.Is4() {
+ routeAddr = &route.Inet4Addr{IP: addr.As4()}
+ } else {
+ routeAddr = &route.Inet6Addr{IP: addr.As16()}
+ }
+
+ rm := route.RouteMessage{
+ Version: unix.RTM_VERSION,
+ Type: unix.RTM_GET,
+ Flags: unix.RTF_UP,
+ ID: uintptr(os.Getpid()),
+ Seq: 1,
+ Addrs: []route.Addr{
+ unix.RTAX_DST: routeAddr,
+ },
+ }
+ b, err := rm.Marshal()
+ if err != nil {
+ return 0, fmt.Errorf("marshaling RouteMessage: %w", err)
+ }
+ _, err = unix.Write(fd, b)
+ if err != nil {
+ return 0, fmt.Errorf("writing message: %w")
+ }
+ var buf [2048]byte
+ n, err := unix.Read(fd, buf[:])
+ if err != nil {
+ return 0, fmt.Errorf("reading message: %w", err)
+ }
+ msgs, err := route.ParseRIB(route.RIBTypeRoute, buf[:n])
+ if err != nil {
+ return 0, fmt.Errorf("route.ParseRIB: %w", err)
+ }
+ if len(msgs) == 0 {
+ return 0, fmt.Errorf("no messages")
+ }
+
+ for _, msg := range msgs {
+ rm, ok := msg.(*route.RouteMessage)
+ if !ok {
+ continue
+ }
+ if rm.Version < 3 || rm.Version > 5 || rm.Type != unix.RTM_GET {
+ continue
+ }
+ if len(rm.Addrs) < unix.RTAX_GATEWAY {
+ continue
+ }
+
+ laddr, ok := rm.Addrs[unix.RTAX_GATEWAY].(*route.LinkAddr)
+ if !ok {
+ continue
+ }
+
+ return laddr.Index, nil
+ }
+
+ return 0, fmt.Errorf("no valid address found")
+}
+
func init() {
likelyHomeRouterIP = likelyHomeRouterIPDarwinFetchRIB
}
diff --git a/net/netns/netns_darwin_tailscaled.go b/net/netns/netns_darwin_tailscaled.go
index ce35aa999..a1d0f69c8 100644
--- a/net/netns/netns_darwin_tailscaled.go
+++ b/net/netns/netns_darwin_tailscaled.go
@@ -9,6 +9,7 @@ package netns
import (
"fmt"
+ "net/netip"
"strings"
"syscall"
@@ -32,12 +33,20 @@ func controlLogf(logf logger.Logf, network, address string, c syscall.RawConn) e
// Don't bind to an interface for localhost connections.
return nil
}
- idx, err := interfaces.DefaultRouteInterfaceIndex()
+ addr, err := netip.ParseAddr(address)
if err != nil {
- logf("[unexpected] netns: DefaultRouteInterfaceIndex: %v", err)
- return nil
+ return fmt.Errorf("netip.ParseAddr(%s): %w", address, err)
+ }
+ idx, err := interfaces.InterfaceIndexFor(addr)
+ if err != nil {
+ logf("[unexpected] netns: InterfaceIndexFor failed; falling back to default: %v", err)
+ idx, err = interfaces.DefaultRouteInterfaceIndex()
+ if err != nil {
+ logf("[unexpected] netns: DefaultRouteInterfaceIndex: %v", err)
+ return nil
+ }
}
- v6 := strings.Contains(address, "]:") || strings.HasSuffix(network, "6") // hacky test for v6
+ v6 := addr.Is6()
proto := unix.IPPROTO_IP
opt := unix.IP_BOUND_IF
if v6 {