mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-12 11:14:40 +00:00
net/netns: use AF_ROUTE socket to determine what to bind to
Updates #5719 Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
This commit is contained in:
parent
0607832397
commit
eb6343cdaf
@ -10,6 +10,7 @@
|
||||
"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
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
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)
|
||||
}
|
||||
v6 := strings.Contains(address, "]:") || strings.HasSuffix(network, "6") // hacky test for v6
|
||||
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 := addr.Is6()
|
||||
proto := unix.IPPROTO_IP
|
||||
opt := unix.IP_BOUND_IF
|
||||
if v6 {
|
||||
|
Loading…
Reference in New Issue
Block a user