net/interfaces: define DefaultRouteInterface and State.DefaultRouteInterface
It was pretty ill-defined before and mostly for logging. But I wanted to start depending on it, so define what it is and make Windows match the other operating systems, without losing the log output we had before. (and add tests for that) Change-Id: I0fbbba1cfc67a265d09dd6cb738b73f0f6005247 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
96cab21383
commit
04c2c5bd80
|
@ -172,6 +172,7 @@ func sortIPs(s []netaddr.IP) {
|
|||
type Interface struct {
|
||||
*net.Interface
|
||||
AltAddrs []net.Addr // if non-nil, returned by Addrs
|
||||
Desc string // extra description (used on Windows)
|
||||
}
|
||||
|
||||
func (i Interface) IsLoopback() bool { return isLoopback(i.Interface) }
|
||||
|
@ -278,13 +279,16 @@ type State struct {
|
|||
// instead of Wifi. This field is not populated by GetState.
|
||||
IsExpensive bool
|
||||
|
||||
// DefaultRouteInterface is the interface name for the machine's default route.
|
||||
// DefaultRouteInterface is the interface name for the
|
||||
// machine's default route.
|
||||
//
|
||||
// It is not yet populated on all OSes.
|
||||
// Its exact value should not be assumed to be a map key for
|
||||
// the Interface maps above; it's only used for debugging.
|
||||
//
|
||||
// When non-empty, its value is the map key into Interface and
|
||||
// InterfaceIPs.
|
||||
DefaultRouteInterface string
|
||||
|
||||
// HTTPProxy is the HTTP proxy to use.
|
||||
// HTTPProxy is the HTTP proxy to use, if any.
|
||||
HTTPProxy string
|
||||
|
||||
// PAC is the URL to the Proxy Autoconfig URL, if applicable.
|
||||
|
@ -293,7 +297,13 @@ type State struct {
|
|||
|
||||
func (s *State) String() string {
|
||||
var sb strings.Builder
|
||||
fmt.Fprintf(&sb, "interfaces.State{defaultRoute=%v ifs={", s.DefaultRouteInterface)
|
||||
fmt.Fprintf(&sb, "interfaces.State{defaultRoute=%v ", s.DefaultRouteInterface)
|
||||
if s.DefaultRouteInterface != "" {
|
||||
if iface, ok := s.Interface[s.DefaultRouteInterface]; ok && iface.Desc != "" {
|
||||
fmt.Fprintf(&sb, "(%s) ", iface.Desc)
|
||||
}
|
||||
}
|
||||
sb.WriteString("ifs={")
|
||||
ifs := make([]string, 0, len(s.Interface))
|
||||
for k := range s.Interface {
|
||||
if anyInterestingIP(s.InterfaceIPs[k]) {
|
||||
|
@ -507,7 +517,16 @@ func GetState() (*State, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
s.DefaultRouteInterface, _ = DefaultRouteInterface()
|
||||
dr, _ := DefaultRoute()
|
||||
s.DefaultRouteInterface = dr.InterfaceName
|
||||
|
||||
// Populate description (for Windows, primarily) if present.
|
||||
if desc := dr.InterfaceDesc; desc != "" {
|
||||
if iface, ok := s.Interface[dr.InterfaceName]; ok {
|
||||
iface.Desc = desc
|
||||
s.Interface[dr.InterfaceName] = iface
|
||||
}
|
||||
}
|
||||
|
||||
if s.AnyInterfaceUp() {
|
||||
req, err := http.NewRequest("GET", LoginEndpointForProxyDetermination, nil)
|
||||
|
@ -667,3 +686,36 @@ func netInterfaces() ([]Interface, error) {
|
|||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// DefaultRouteDetails are the
|
||||
type DefaultRouteDetails struct {
|
||||
// InterfaceName is the interface name. It must always be populated.
|
||||
// It's like "eth0" (Linux), "Ethernet 2" (Windows), "en0" (macOS).
|
||||
InterfaceName string
|
||||
|
||||
// InterfaceDesc is populated on Windows at least. It's a
|
||||
// longer description, like "Red Hat VirtIO Ethernet Adapter".
|
||||
InterfaceDesc string
|
||||
|
||||
// InterfaceIndex is like net.Interface.Index.
|
||||
// Zero means not populated.
|
||||
InterfaceIndex int
|
||||
|
||||
// TODO(bradfitz): break this out into v4-vs-v6 once that need arises.
|
||||
}
|
||||
|
||||
// DefaultRouteInterface is like DefaultRoute but only returns the
|
||||
// interface name.
|
||||
func DefaultRouteInterface() (string, error) {
|
||||
dr, err := DefaultRoute()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return dr.InterfaceName, nil
|
||||
}
|
||||
|
||||
// DefaultRoute returns details of the network interface that owns
|
||||
// the default route, not including any tailscale interfaces.
|
||||
func DefaultRoute() (DefaultRouteDetails, error) {
|
||||
return defaultRoute()
|
||||
}
|
||||
|
|
|
@ -16,16 +16,18 @@ import (
|
|||
"inet.af/netaddr"
|
||||
)
|
||||
|
||||
func DefaultRouteInterface() (string, error) {
|
||||
func defaultRoute() (d DefaultRouteDetails, err error) {
|
||||
idx, err := DefaultRouteInterfaceIndex()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return d, err
|
||||
}
|
||||
iface, err := net.InterfaceByIndex(idx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return d, err
|
||||
}
|
||||
return iface.Name, nil
|
||||
d.InterfaceName = iface.Name
|
||||
d.InterfaceIndex = idx
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// fetchRoutingTable calls route.FetchRIB, fetching NET_RT_DUMP2.
|
||||
|
|
|
@ -11,6 +11,6 @@ import "errors"
|
|||
|
||||
var errTODO = errors.New("TODO")
|
||||
|
||||
func DefaultRouteInterface() (string, error) {
|
||||
return "TODO", errTODO
|
||||
func defaultRoute() (DefaultRouteDetails, error) {
|
||||
return DefaultRouteDetails{}, errTODO
|
||||
}
|
||||
|
|
|
@ -122,17 +122,18 @@ func likelyHomeRouterIPAndroid() (ret netaddr.IP, ok bool) {
|
|||
return ret, !ret.IsZero()
|
||||
}
|
||||
|
||||
// DefaultRouteInterface returns the name of the network interface that owns
|
||||
// the default route, not including any tailscale interfaces.
|
||||
func DefaultRouteInterface() (string, error) {
|
||||
func defaultRoute() (d DefaultRouteDetails, err error) {
|
||||
v, err := defaultRouteInterfaceProcNet()
|
||||
if err == nil {
|
||||
return v, nil
|
||||
d.InterfaceName = v
|
||||
return d, nil
|
||||
}
|
||||
if runtime.GOOS == "android" {
|
||||
return defaultRouteInterfaceAndroidIPRoute()
|
||||
v, err = defaultRouteInterfaceAndroidIPRoute()
|
||||
d.InterfaceName = v
|
||||
return d, err
|
||||
}
|
||||
return v, err
|
||||
return d, err
|
||||
}
|
||||
|
||||
var zeroRouteBytes = []byte("00000000")
|
||||
|
|
|
@ -104,3 +104,55 @@ func TestStateEqualFilteredIPFilter(t *testing.T) {
|
|||
t.Errorf("%+v == %+v when restricting to interesting interfaces and IPs", s1, s2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateString(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
s *State
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "typical_linux",
|
||||
s: &State{
|
||||
DefaultRouteInterface: "eth0",
|
||||
Interface: map[string]Interface{
|
||||
"eth0": {
|
||||
Interface: &net.Interface{
|
||||
Flags: net.FlagUp,
|
||||
},
|
||||
},
|
||||
"wlan0": {
|
||||
Interface: &net.Interface{},
|
||||
},
|
||||
},
|
||||
InterfaceIPs: map[string][]netaddr.IPPrefix{
|
||||
"eth0": []netaddr.IPPrefix{
|
||||
netaddr.MustParseIPPrefix("10.0.0.2/8"),
|
||||
},
|
||||
},
|
||||
HaveV4: true,
|
||||
},
|
||||
want: `interfaces.State{defaultRoute=eth0 ifs={eth0:[10.0.0.2/8]} v4=true v6=false}`,
|
||||
},
|
||||
{
|
||||
name: "default_desc",
|
||||
s: &State{
|
||||
DefaultRouteInterface: "foo",
|
||||
Interface: map[string]Interface{
|
||||
"foo": {
|
||||
Desc: "a foo thing",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: `interfaces.State{defaultRoute=foo (a foo thing) ifs={} v4=false v6=false}`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := tt.s.String()
|
||||
if got != tt.want {
|
||||
t.Errorf("wrong\n got: %s\nwant: %s\n", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
package interfaces
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/url"
|
||||
|
@ -217,18 +216,20 @@ func GetWindowsDefault(family winipcfg.AddressFamily) (*winipcfg.IPAdapterAddres
|
|||
return bestIface, nil
|
||||
}
|
||||
|
||||
func DefaultRouteInterface() (string, error) {
|
||||
func defaultRoute() (d DefaultRouteDetails, err error) {
|
||||
// We always return the IPv4 default route.
|
||||
// TODO(bradfitz): adjust API if/when anything cares. They could in theory differ, though,
|
||||
// in which case we might send traffic to the wrong interface.
|
||||
iface, err := GetWindowsDefault(windows.AF_INET)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return d, err
|
||||
}
|
||||
if iface == nil {
|
||||
return "(none)", nil
|
||||
if iface != nil {
|
||||
d.InterfaceName = iface.FriendlyName()
|
||||
d.InterfaceDesc = iface.Description()
|
||||
d.InterfaceIndex = int(iface.IfIndex)
|
||||
}
|
||||
return fmt.Sprintf("%s (%s)", iface.FriendlyName(), iface.Description()), nil
|
||||
return d, nil
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
Loading…
Reference in New Issue