From c0697e1febef115187ecd01e48f6427745343d44 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Thu, 9 Apr 2020 19:10:55 -0700 Subject: [PATCH] net/interfaces: add IsExpensive and up state to State Signed-off-by: Brad Fitzpatrick --- net/interfaces/interfaces.go | 16 +++++++++++++++- net/interfaces/interfaces_test.go | 18 ++++++++++++++++++ wgengine/userspace.go | 1 + 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/net/interfaces/interfaces.go b/net/interfaces/interfaces.go index 6e9e410b5..144f7880c 100644 --- a/net/interfaces/interfaces.go +++ b/net/interfaces/interfaces.go @@ -174,6 +174,12 @@ func ForeachInterfaceAddress(fn func(Interface, net.IP)) error { // For now it's pretty basic. type State struct { InterfaceIPs map[string][]net.IP + InterfaceUp map[string]bool + + // IsExpensive is whether the current network interface is + // considered "expensive", which currently means LTE/etc + // instead of Wifi. This field is not populated by GetState. + IsExpensive bool } func (s *State) Equal(s2 *State) bool { @@ -188,14 +194,22 @@ func (s *State) RemoveTailscaleInterfaces() { for name := range s.InterfaceIPs { if strings.HasPrefix(name, "tailscale") { // TODO: use --tun flag value, etc; see TODO in method doc delete(s.InterfaceIPs, name) + delete(s.InterfaceUp, name) } } } +// GetState returns the state of all the current machine's network interfaces. +// +// It does not set the returned State.IsExpensive. The caller can populate that. func GetState() (*State, error) { - s := &State{InterfaceIPs: make(map[string][]net.IP)} + s := &State{ + InterfaceIPs: make(map[string][]net.IP), + InterfaceUp: make(map[string]bool), + } if err := ForeachInterfaceAddress(func(ni Interface, ip net.IP) { s.InterfaceIPs[ni.Name] = append(s.InterfaceIPs[ni.Name], ip) + s.InterfaceUp[ni.Name] = ni.IsUp() }); err != nil { return nil, err } diff --git a/net/interfaces/interfaces_test.go b/net/interfaces/interfaces_test.go index ad36c437c..ea0c96987 100644 --- a/net/interfaces/interfaces_test.go +++ b/net/interfaces/interfaces_test.go @@ -27,5 +27,23 @@ func TestIsTailscaleIP(t *testing.T) { t.Errorf("F(%q) = %v; want %v", tt.ip, got, tt.want) } } +} + +func TestGetState(t *testing.T) { + st, err := GetState() + if err != nil { + t.Fatal(err) + } + t.Logf("Got: %#v", st) + st2, err := GetState() + if err != nil { + t.Fatal(err) + } + + if !st.Equal(st2) { + // let's assume nobody was changing the system network interfaces between + // the two GetState calls. + t.Fatal("two States back-to-back were not equal") + } } diff --git a/wgengine/userspace.go b/wgengine/userspace.go index 45a83778f..7425027cf 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -644,6 +644,7 @@ func (e *userspaceEngine) LinkChange(isExpensive bool) { e.logf("LinkChange: interfaces.GetState: %v", err) return } + cur.IsExpensive = isExpensive needRebind := e.setLinkState(cur) e.logf("LinkChange(isExpensive=%v); needsRebind=%v", isExpensive, needRebind)