mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-04 15:35:38 +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"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"golang.org/x/net/route"
|
"golang.org/x/net/route"
|
||||||
@ -86,6 +87,76 @@ func DefaultRouteInterfaceIndex() (int, error) {
|
|||||||
return 0, fmt.Errorf("ambiguous gateway interfaces found: %v", indexSeen)
|
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() {
|
func init() {
|
||||||
likelyHomeRouterIP = likelyHomeRouterIPDarwinFetchRIB
|
likelyHomeRouterIP = likelyHomeRouterIPDarwinFetchRIB
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"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.
|
// Don't bind to an interface for localhost connections.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
idx, err := interfaces.DefaultRouteInterfaceIndex()
|
addr, err := netip.ParseAddr(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logf("[unexpected] netns: DefaultRouteInterfaceIndex: %v", err)
|
return fmt.Errorf("netip.ParseAddr(%s): %w", address, err)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
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
|
proto := unix.IPPROTO_IP
|
||||||
opt := unix.IP_BOUND_IF
|
opt := unix.IP_BOUND_IF
|
||||||
if v6 {
|
if v6 {
|
||||||
|
Loading…
Reference in New Issue
Block a user