From 9c2ad7086c0daef9c914ef1e59d0142b50f789f3 Mon Sep 17 00:00:00 2001 From: Anton Tolchanov <anton@tailscale.com> Date: Fri, 14 Oct 2022 09:29:34 +0100 Subject: [PATCH] net/interfaces: deduplicate route table parsing on Darwin and FreeBSD Signed-off-by: Anton Tolchanov <anton@tailscale.com> --- net/interfaces/interfaces_bsd.go | 26 ++--- net/interfaces/interfaces_darwin.go | 116 +---------------------- net/interfaces/interfaces_darwin_test.go | 2 +- net/interfaces/interfaces_freebsd.go | 26 +++++ 4 files changed, 37 insertions(+), 133 deletions(-) create mode 100644 net/interfaces/interfaces_freebsd.go diff --git a/net/interfaces/interfaces_bsd.go b/net/interfaces/interfaces_bsd.go index 60bba1d95..8efda37f5 100644 --- a/net/interfaces/interfaces_bsd.go +++ b/net/interfaces/interfaces_bsd.go @@ -2,11 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This might work on other BSDs, but only tested on FreeBSD. -// Originally a fork of interfaces_darwin.go with slightly different flags. +// Common code for FreeBSD and Darwin. -//go:build freebsd -// +build freebsd +//go:build darwin || freebsd +// +build darwin freebsd package interfaces @@ -16,7 +15,6 @@ import ( "log" "net" "net/netip" - "syscall" "golang.org/x/net/route" "golang.org/x/sys/unix" @@ -37,11 +35,6 @@ func defaultRoute() (d DefaultRouteDetails, err error) { return d, nil } -// fetchRoutingTable calls route.FetchRIB, fetching NET_RT_DUMP. -func fetchRoutingTable() (rib []byte, err error) { - return route.FetchRIB(syscall.AF_UNSPEC, unix.NET_RT_DUMP, 0) -} - func DefaultRouteInterfaceIndex() (int, error) { // $ netstat -nr // Routing tables @@ -61,7 +54,7 @@ func DefaultRouteInterfaceIndex() (int, error) { if err != nil { return 0, fmt.Errorf("route.FetchRIB: %w", err) } - msgs, err := route.ParseRIB(unix.NET_RT_IFLIST, rib) + msgs, err := parseRoutingTable(rib) if err != nil { return 0, fmt.Errorf("route.ParseRIB: %w", err) } @@ -71,12 +64,10 @@ func DefaultRouteInterfaceIndex() (int, error) { if !ok { continue } - const RTF_GATEWAY = 0x2 - const RTF_IFSCOPE = 0x1000000 - if rm.Flags&RTF_GATEWAY == 0 { + if rm.Flags&unix.RTF_GATEWAY == 0 { continue } - if rm.Flags&RTF_IFSCOPE != 0 { + if rm.Flags&unix.RTF_IFSCOPE != 0 { continue } indexSeen[rm.Index]++ @@ -102,7 +93,7 @@ func likelyHomeRouterIPBSDFetchRIB() (ret netip.Addr, ok bool) { log.Printf("routerIP/FetchRIB: %v", err) return ret, false } - msgs, err := route.ParseRIB(unix.NET_RT_IFLIST, rib) + msgs, err := parseRoutingTable(rib) if err != nil { log.Printf("routerIP/ParseRIB: %v", err) return ret, false @@ -112,11 +103,10 @@ func likelyHomeRouterIPBSDFetchRIB() (ret netip.Addr, ok bool) { if !ok { continue } - const RTF_IFSCOPE = 0x1000000 if rm.Flags&unix.RTF_GATEWAY == 0 { continue } - if rm.Flags&RTF_IFSCOPE != 0 { + if rm.Flags&unix.RTF_IFSCOPE != 0 { continue } if len(rm.Addrs) > unix.RTAX_GATEWAY { diff --git a/net/interfaces/interfaces_darwin.go b/net/interfaces/interfaces_darwin.go index 60335bf80..d0a7e8ef7 100644 --- a/net/interfaces/interfaces_darwin.go +++ b/net/interfaces/interfaces_darwin.go @@ -5,128 +5,16 @@ package interfaces import ( - "errors" - "fmt" - "log" - "net" - "net/netip" "syscall" "golang.org/x/net/route" - "golang.org/x/sys/unix" - "tailscale.com/net/netaddr" ) -func defaultRoute() (d DefaultRouteDetails, err error) { - idx, err := DefaultRouteInterfaceIndex() - if err != nil { - return d, err - } - iface, err := net.InterfaceByIndex(idx) - if err != nil { - return d, err - } - d.InterfaceName = iface.Name - d.InterfaceIndex = idx - return d, nil -} - // fetchRoutingTable calls route.FetchRIB, fetching NET_RT_DUMP2. func fetchRoutingTable() (rib []byte, err error) { return route.FetchRIB(syscall.AF_UNSPEC, syscall.NET_RT_DUMP2, 0) } -func DefaultRouteInterfaceIndex() (int, error) { - // $ netstat -nr - // Routing tables - // Internet: - // Destination Gateway Flags Netif Expire - // default 10.0.0.1 UGSc en0 <-- want this one - // default 10.0.0.1 UGScI en1 - - // From man netstat: - // U RTF_UP Route usable - // G RTF_GATEWAY Destination requires forwarding by intermediary - // S RTF_STATIC Manually added - // c RTF_PRCLONING Protocol-specified generate new routes on use - // I RTF_IFSCOPE Route is associated with an interface scope - - rib, err := fetchRoutingTable() - if err != nil { - return 0, fmt.Errorf("route.FetchRIB: %w", err) - } - msgs, err := route.ParseRIB(syscall.NET_RT_IFLIST2, rib) - if err != nil { - return 0, fmt.Errorf("route.ParseRIB: %w", err) - } - indexSeen := map[int]int{} // index => count - for _, m := range msgs { - rm, ok := m.(*route.RouteMessage) - if !ok { - continue - } - const RTF_GATEWAY = 0x2 - const RTF_IFSCOPE = 0x1000000 - if rm.Flags&RTF_GATEWAY == 0 { - continue - } - if rm.Flags&RTF_IFSCOPE != 0 { - continue - } - indexSeen[rm.Index]++ - } - if len(indexSeen) == 0 { - return 0, errors.New("no gateway index found") - } - if len(indexSeen) == 1 { - for idx := range indexSeen { - return idx, nil - } - } - return 0, fmt.Errorf("ambiguous gateway interfaces found: %v", indexSeen) -} - -func init() { - likelyHomeRouterIP = likelyHomeRouterIPDarwinFetchRIB -} - -func likelyHomeRouterIPDarwinFetchRIB() (ret netip.Addr, ok bool) { - rib, err := fetchRoutingTable() - if err != nil { - log.Printf("routerIP/FetchRIB: %v", err) - return ret, false - } - msgs, err := route.ParseRIB(syscall.NET_RT_IFLIST2, rib) - if err != nil { - log.Printf("routerIP/ParseRIB: %v", err) - return ret, false - } - for _, m := range msgs { - rm, ok := m.(*route.RouteMessage) - if !ok { - continue - } - const RTF_GATEWAY = 0x2 - const RTF_IFSCOPE = 0x1000000 - if rm.Flags&RTF_GATEWAY == 0 { - continue - } - if rm.Flags&RTF_IFSCOPE != 0 { - continue - } - if len(rm.Addrs) > unix.RTAX_GATEWAY { - dst4, ok := rm.Addrs[unix.RTAX_DST].(*route.Inet4Addr) - if !ok || dst4.IP != ([4]byte{0, 0, 0, 0}) { - // Expect 0.0.0.0 as DST field. - continue - } - gw, ok := rm.Addrs[unix.RTAX_GATEWAY].(*route.Inet4Addr) - if !ok { - continue - } - return netaddr.IPv4(gw.IP[0], gw.IP[1], gw.IP[2], gw.IP[3]), true - } - } - - return ret, false +func parseRoutingTable(rib []byte) ([]route.Message, error) { + return route.ParseRIB(syscall.NET_RT_IFLIST2, rib) } diff --git a/net/interfaces/interfaces_darwin_test.go b/net/interfaces/interfaces_darwin_test.go index 245ea8211..3aff070ae 100644 --- a/net/interfaces/interfaces_darwin_test.go +++ b/net/interfaces/interfaces_darwin_test.go @@ -16,7 +16,7 @@ import ( ) func TestLikelyHomeRouterIPSyscallExec(t *testing.T) { - syscallIP, syscallOK := likelyHomeRouterIPDarwinFetchRIB() + syscallIP, syscallOK := likelyHomeRouterIPBSDFetchRIB() netstatIP, netstatOK := likelyHomeRouterIPDarwinExec() if syscallOK != netstatOK || syscallIP != netstatIP { t.Errorf("syscall() = %v, %v, netstat = %v, %v", diff --git a/net/interfaces/interfaces_freebsd.go b/net/interfaces/interfaces_freebsd.go new file mode 100644 index 000000000..8a7270071 --- /dev/null +++ b/net/interfaces/interfaces_freebsd.go @@ -0,0 +1,26 @@ +// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This might work on other BSDs, but only tested on FreeBSD. + +//go:build freebsd +// +build freebsd + +package interfaces + +import ( + "syscall" + + "golang.org/x/net/route" + "golang.org/x/sys/unix" +) + +// fetchRoutingTable calls route.FetchRIB, fetching NET_RT_DUMP. +func fetchRoutingTable() (rib []byte, err error) { + return route.FetchRIB(syscall.AF_UNSPEC, unix.NET_RT_DUMP, 0) +} + +func parseRoutingTable(rib []byte) ([]route.Message, error) { + return route.ParseRIB(syscall.NET_RT_IFLIST, rib) +}