ipn: drop unchanged network map updates
This should never happen. But it is, so defend against it while I fix the bug elsewhere. Signed-off-by: David Crawshaw <crawshaw@tailscale.com>pull/158/head
parent
bb93d7aaba
commit
c9d2fb6d11
229
ipn/local.go
229
ipn/local.go
|
@ -5,6 +5,8 @@
|
||||||
package ipn
|
package ipn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
@ -199,103 +201,8 @@ func (b *LocalBackend) Start(opts Options) error {
|
||||||
cli.UpdateEndpoints(0, endpoints)
|
cli.UpdateEndpoints(0, endpoints)
|
||||||
}
|
}
|
||||||
|
|
||||||
cli.SetStatusFunc(func(newSt controlclient.Status) {
|
cli.SetStatusFunc(b.controlStatusUpdate)
|
||||||
if newSt.LoginFinished != nil {
|
b.e.SetStatusCallback(b.engineStatusUpdate)
|
||||||
// Auth completed, unblock the engine
|
|
||||||
b.blockEngineUpdates(false)
|
|
||||||
b.authReconfig()
|
|
||||||
b.send(Notify{LoginFinished: &empty.Message{}})
|
|
||||||
}
|
|
||||||
if newSt.Persist != nil {
|
|
||||||
persist := *newSt.Persist // copy
|
|
||||||
|
|
||||||
b.mu.Lock()
|
|
||||||
b.prefs.Persist = &persist
|
|
||||||
prefs := b.prefs.Clone()
|
|
||||||
stateKey := b.stateKey
|
|
||||||
b.mu.Unlock()
|
|
||||||
|
|
||||||
if stateKey != "" {
|
|
||||||
if err := b.store.WriteState(stateKey, prefs.ToBytes()); err != nil {
|
|
||||||
b.logf("Failed to save new controlclient state: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.send(Notify{Prefs: prefs})
|
|
||||||
}
|
|
||||||
if newSt.NetMap != nil {
|
|
||||||
b.mu.Lock()
|
|
||||||
if b.netMapCache != nil && b.cmpDiff != nil {
|
|
||||||
s1 := strings.Split(b.netMapCache.Concise(), "\n")
|
|
||||||
s2 := strings.Split(newSt.NetMap.Concise(), "\n")
|
|
||||||
diff := b.cmpDiff(s1, s2)
|
|
||||||
if strings.TrimSpace(diff) != "" {
|
|
||||||
b.logf("netmap diff:\n%v\n", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.netMapCache = newSt.NetMap
|
|
||||||
b.mu.Unlock()
|
|
||||||
|
|
||||||
b.send(Notify{NetMap: newSt.NetMap})
|
|
||||||
b.updateFilter(newSt.NetMap)
|
|
||||||
}
|
|
||||||
if newSt.URL != "" {
|
|
||||||
b.logf("Received auth URL: %.20v...\n", newSt.URL)
|
|
||||||
|
|
||||||
b.mu.Lock()
|
|
||||||
interact := b.interact
|
|
||||||
b.authURL = newSt.URL
|
|
||||||
b.mu.Unlock()
|
|
||||||
|
|
||||||
if interact > 0 {
|
|
||||||
b.popBrowserAuthNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if newSt.Err != "" {
|
|
||||||
// TODO(crawshaw): display in the UI.
|
|
||||||
log.Print(newSt.Err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if newSt.NetMap != nil {
|
|
||||||
b.mu.Lock()
|
|
||||||
if b.state == NeedsLogin {
|
|
||||||
b.prefs.WantRunning = true
|
|
||||||
}
|
|
||||||
prefs := b.prefs
|
|
||||||
b.mu.Unlock()
|
|
||||||
|
|
||||||
b.SetPrefs(prefs)
|
|
||||||
}
|
|
||||||
b.stateMachine()
|
|
||||||
})
|
|
||||||
|
|
||||||
b.e.SetStatusCallback(func(s *wgengine.Status, err error) {
|
|
||||||
if err != nil {
|
|
||||||
b.logf("wgengine status error: %#v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if s == nil {
|
|
||||||
log.Fatalf("weird: non-error wgengine update with status=nil\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
es := b.parseWgStatus(s)
|
|
||||||
|
|
||||||
b.mu.Lock()
|
|
||||||
c := b.c
|
|
||||||
b.engineStatus = es
|
|
||||||
b.endpoints = append([]string{}, s.LocalAddrs...)
|
|
||||||
b.mu.Unlock()
|
|
||||||
|
|
||||||
if c != nil {
|
|
||||||
c.UpdateEndpoints(0, s.LocalAddrs)
|
|
||||||
}
|
|
||||||
b.stateMachine()
|
|
||||||
|
|
||||||
b.statusLock.Lock()
|
|
||||||
b.statusChanged.Broadcast()
|
|
||||||
b.statusLock.Unlock()
|
|
||||||
|
|
||||||
b.send(Notify{Engine: &es})
|
|
||||||
})
|
|
||||||
|
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
prefs := b.prefs.Clone()
|
prefs := b.prefs.Clone()
|
||||||
|
@ -310,6 +217,134 @@ func (b *LocalBackend) Start(opts Options) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// controlStatusUpdate is passed as a callback to controlclient SetStatusFunc.
|
||||||
|
func (b *LocalBackend) controlStatusUpdate(newSt controlclient.Status) {
|
||||||
|
if newSt.LoginFinished != nil {
|
||||||
|
// Auth completed, unblock the engine
|
||||||
|
b.blockEngineUpdates(false)
|
||||||
|
b.authReconfig()
|
||||||
|
b.send(Notify{LoginFinished: &empty.Message{}})
|
||||||
|
}
|
||||||
|
if newSt.Persist != nil {
|
||||||
|
persist := *newSt.Persist // copy
|
||||||
|
|
||||||
|
b.mu.Lock()
|
||||||
|
b.prefs.Persist = &persist
|
||||||
|
prefs := b.prefs.Clone()
|
||||||
|
stateKey := b.stateKey
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
|
if stateKey != "" {
|
||||||
|
if err := b.store.WriteState(stateKey, prefs.ToBytes()); err != nil {
|
||||||
|
b.logf("Failed to save new controlclient state: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.send(Notify{Prefs: prefs})
|
||||||
|
}
|
||||||
|
if newSt.NetMap != nil {
|
||||||
|
changed := true
|
||||||
|
|
||||||
|
b.mu.Lock()
|
||||||
|
if b.netMapCache != nil {
|
||||||
|
hash1, err1 := jsonHash(newSt.NetMap)
|
||||||
|
hash2, err2 := jsonHash(b.netMapCache)
|
||||||
|
if err1 == nil && err2 == nil {
|
||||||
|
changed = hash1 != hash2
|
||||||
|
} else {
|
||||||
|
b.logf("[unexpected] netmap hash encode failed: %v, %v", err1, err2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if changed && b.cmpDiff != nil {
|
||||||
|
s1 := strings.Split(b.netMapCache.Concise(), "\n")
|
||||||
|
s2 := strings.Split(newSt.NetMap.Concise(), "\n")
|
||||||
|
diff := b.cmpDiff(s1, s2)
|
||||||
|
if strings.TrimSpace(diff) != "" {
|
||||||
|
b.logf("netmap diff:\n%v\n", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.netMapCache = newSt.NetMap
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
|
if changed {
|
||||||
|
b.send(Notify{NetMap: newSt.NetMap})
|
||||||
|
b.updateFilter(newSt.NetMap)
|
||||||
|
} else {
|
||||||
|
// This should never happen. A properly
|
||||||
|
// functioning CONTROL should skip sending
|
||||||
|
// equal netmaps. We detect and log here both
|
||||||
|
// as defense-in-depth and to give us warning
|
||||||
|
// (in the logs) that something is wrong.
|
||||||
|
b.logf("[unexpected] unchanged netmap, skipping")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if newSt.URL != "" {
|
||||||
|
b.logf("Received auth URL: %.20v...\n", newSt.URL)
|
||||||
|
|
||||||
|
b.mu.Lock()
|
||||||
|
interact := b.interact
|
||||||
|
b.authURL = newSt.URL
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
|
if interact > 0 {
|
||||||
|
b.popBrowserAuthNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if newSt.Err != "" {
|
||||||
|
// TODO(crawshaw): display in the UI.
|
||||||
|
log.Print(newSt.Err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if newSt.NetMap != nil {
|
||||||
|
b.mu.Lock()
|
||||||
|
if b.state == NeedsLogin {
|
||||||
|
b.prefs.WantRunning = true
|
||||||
|
}
|
||||||
|
prefs := b.prefs
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
|
b.SetPrefs(prefs)
|
||||||
|
}
|
||||||
|
b.stateMachine()
|
||||||
|
}
|
||||||
|
|
||||||
|
// engineStatusUpdate is passed as a callback to wgengine SetStatusCallback.
|
||||||
|
func (b *LocalBackend) engineStatusUpdate(s *wgengine.Status, err error) {
|
||||||
|
if err != nil {
|
||||||
|
b.logf("wgengine status error: %#v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s == nil {
|
||||||
|
log.Fatalf("weird: non-error wgengine update with status=nil\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
es := b.parseWgStatus(s)
|
||||||
|
|
||||||
|
b.mu.Lock()
|
||||||
|
c := b.c
|
||||||
|
b.engineStatus = es
|
||||||
|
b.endpoints = append([]string{}, s.LocalAddrs...)
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
|
if c != nil {
|
||||||
|
c.UpdateEndpoints(0, s.LocalAddrs)
|
||||||
|
}
|
||||||
|
b.stateMachine()
|
||||||
|
|
||||||
|
b.statusLock.Lock()
|
||||||
|
b.statusChanged.Broadcast()
|
||||||
|
b.statusLock.Unlock()
|
||||||
|
|
||||||
|
b.send(Notify{Engine: &es})
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsonHash(v interface{}) (hash [sha256.Size]byte, err error) {
|
||||||
|
h := sha256.New()
|
||||||
|
err = json.NewEncoder(h).Encode(v)
|
||||||
|
h.Sum(hash[:0])
|
||||||
|
return hash, err
|
||||||
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) updateFilter(netMap *controlclient.NetworkMap) {
|
func (b *LocalBackend) updateFilter(netMap *controlclient.NetworkMap) {
|
||||||
if !b.Prefs().UsePacketFilter {
|
if !b.Prefs().UsePacketFilter {
|
||||||
b.e.SetFilter(filter.NewAllowAll())
|
b.e.SetFilter(filter.NewAllowAll())
|
||||||
|
|
Loading…
Reference in New Issue