From 8d6beebac48674b4677a1acc6f389c0756d276a4 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 24 Nov 2018 20:04:14 -0600 Subject: [PATCH 1/4] clean up old requests during dht maintenance --- src/yggdrasil/dht.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/yggdrasil/dht.go b/src/yggdrasil/dht.go index 31acd8a..84d8e40 100644 --- a/src/yggdrasil/dht.go +++ b/src/yggdrasil/dht.go @@ -273,6 +273,19 @@ func (t *dht) ping(info *dhtInfo, target *NodeID) { // Periodic maintenance work to keep important DHT nodes alive. func (t *dht) doMaintenance() { now := time.Now() + for key, dests := range t.reqs { + for nodeID, start := range dests { + if now.Sub(start) > 6*time.Second { + if info, isIn := t.table[*getNodeID(&key)]; isIn { + info.pings++ + } + delete(dests, nodeID) + } + if len(dests) == 0 { + delete(t.reqs, key) + } + } + } for infoID, info := range t.table { if now.Sub(info.recv) > time.Minute || info.pings > 3 { delete(t.table, infoID) From 4e156bd4f72806142bc8712290099f715be8c8b3 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 25 Nov 2018 12:25:38 -0600 Subject: [PATCH 2/4] better cleanup of maps --- misc/sim/run-sim | 2 +- misc/sim/treesim.go | 16 ++++------------ src/yggdrasil/dht.go | 12 ++++++++---- src/yggdrasil/peer.go | 1 - src/yggdrasil/session.go | 35 +++++++++++++++++++++++++++++++++++ src/yggdrasil/signature.go | 5 +++++ 6 files changed, 53 insertions(+), 18 deletions(-) diff --git a/misc/sim/run-sim b/misc/sim/run-sim index 985fe2f..abe108c 100755 --- a/misc/sim/run-sim +++ b/misc/sim/run-sim @@ -1,4 +1,4 @@ #!/bin/bash export GOPATH=$PWD go get -d yggdrasil -go run -tags debug misc/sim/treesim.go +go run -tags debug misc/sim/treesim.go "$@" diff --git a/misc/sim/treesim.go b/misc/sim/treesim.go index 3a0959e..8a4bb2a 100644 --- a/misc/sim/treesim.go +++ b/misc/sim/treesim.go @@ -8,6 +8,7 @@ import "strconv" import "time" import "log" +import "runtime" import "runtime/pprof" import "flag" @@ -280,17 +281,7 @@ func pingNodes(store map[[32]byte]*Node) { } destAddr := dest.core.DEBUG_getAddr()[:] ticker := time.NewTicker(150 * time.Millisecond) - ch := make(chan bool, 1) - ch <- true - doTicker := func() { - for range ticker.C { - select { - case ch <- true: - default: - } - } - } - go doTicker() + sendTo(payload, destAddr) for loop := true; loop; { select { case packet := <-dest.recv: @@ -299,7 +290,7 @@ func pingNodes(store map[[32]byte]*Node) { loop = false } } - case <-ch: + case <-ticker.C: sendTo(payload, destAddr) //dumpDHTSize(store) // note that this uses racey functions to read things... } @@ -458,4 +449,5 @@ func main() { var block chan struct{} <-block } + runtime.GC() } diff --git a/src/yggdrasil/dht.go b/src/yggdrasil/dht.go index 84d8e40..9c2afc2 100644 --- a/src/yggdrasil/dht.go +++ b/src/yggdrasil/dht.go @@ -273,19 +273,23 @@ func (t *dht) ping(info *dhtInfo, target *NodeID) { // Periodic maintenance work to keep important DHT nodes alive. func (t *dht) doMaintenance() { now := time.Now() + newReqs := make(map[boxPubKey]map[NodeID]time.Time, len(t.reqs)) for key, dests := range t.reqs { + newDests := make(map[NodeID]time.Time, len(dests)) for nodeID, start := range dests { if now.Sub(start) > 6*time.Second { if info, isIn := t.table[*getNodeID(&key)]; isIn { info.pings++ } - delete(dests, nodeID) - } - if len(dests) == 0 { - delete(t.reqs, key) + continue } + newDests[nodeID] = start + } + if len(newDests) > 0 { + newReqs[key] = newDests } } + t.reqs = newReqs for infoID, info := range t.table { if now.Sub(info.recv) > time.Minute || info.pings > 3 { delete(t.table, infoID) diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index cf82792..e4d0998 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -175,7 +175,6 @@ func (p *peer) doSendSwitchMsgs() { // This must be launched in a separate goroutine by whatever sets up the peer struct. // It handles link protocol traffic. func (p *peer) linkLoop() { - go p.doSendSwitchMsgs() tick := time.NewTicker(time.Second) defer tick.Stop() for { diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index b0022d7..92ae262 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -311,6 +311,11 @@ func (ss *sessions) createSession(theirPermKey *boxPubKey) *sessionInfo { func (ss *sessions) cleanup() { // Time thresholds almost certainly could use some adjusting + for k := range ss.permShared { + // Delete a key, to make sure this eventually shrinks to 0 + delete(ss.permShared, k) + break + } if time.Since(ss.lastCleanup) < time.Minute { return } @@ -319,6 +324,36 @@ func (ss *sessions) cleanup() { s.close() } } + permShared := make(map[boxPubKey]*boxSharedKey, len(ss.permShared)) + for k, v := range ss.permShared { + permShared[k] = v + } + ss.permShared = permShared + sinfos := make(map[handle]*sessionInfo, len(ss.sinfos)) + for k, v := range ss.sinfos { + sinfos[k] = v + } + ss.sinfos = sinfos + byMySes := make(map[boxPubKey]*handle, len(ss.byMySes)) + for k, v := range ss.byMySes { + byMySes[k] = v + } + ss.byMySes = byMySes + byTheirPerm := make(map[boxPubKey]*handle, len(ss.byTheirPerm)) + for k, v := range ss.byTheirPerm { + byTheirPerm[k] = v + } + ss.byTheirPerm = byTheirPerm + addrToPerm := make(map[address]*boxPubKey, len(ss.addrToPerm)) + for k, v := range ss.addrToPerm { + addrToPerm[k] = v + } + ss.addrToPerm = addrToPerm + subnetToPerm := make(map[subnet]*boxPubKey, len(ss.subnetToPerm)) + for k, v := range ss.subnetToPerm { + subnetToPerm[k] = v + } + ss.subnetToPerm = subnetToPerm ss.lastCleanup = time.Now() } diff --git a/src/yggdrasil/signature.go b/src/yggdrasil/signature.go index 203c9ad..12a3d37 100644 --- a/src/yggdrasil/signature.go +++ b/src/yggdrasil/signature.go @@ -86,5 +86,10 @@ func (m *sigManager) cleanup() { delete(m.checked, s) } } + newChecked := make(map[sigBytes]knownSig, len(m.checked)) + for s, k := range m.checked { + newChecked[s] = k + } + m.checked = newChecked m.lastCleaned = time.Now() } From 9046dbde4fe8b71df2dbb0b5a0091d59d29adb95 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 25 Nov 2018 13:06:54 -0600 Subject: [PATCH 3/4] remove sigManager, it seems safer to just burn the CPU than to store a map of strings of potentially arbitrary length --- src/yggdrasil/core.go | 2 - src/yggdrasil/peer.go | 2 +- src/yggdrasil/router.go | 1 - src/yggdrasil/signature.go | 95 -------------------------------------- 4 files changed, 1 insertion(+), 99 deletions(-) delete mode 100644 src/yggdrasil/signature.go diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 2f60a1b..9b9bcc1 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -22,7 +22,6 @@ type Core struct { sigPriv sigPrivKey switchTable switchTable peers peers - sigs sigManager sessions sessions router router dht dht @@ -50,7 +49,6 @@ func (c *Core) init(bpub *boxPubKey, c.boxPub, c.boxPriv = *bpub, *bpriv c.sigPub, c.sigPriv = *spub, *spriv c.admin.core = c - c.sigs.init() c.searches.init(c) c.dht.init(c) c.sessions.init(c) diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index e4d0998..67aa805 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -316,7 +316,7 @@ func (p *peer) handleSwitchMsg(packet []byte) { sigMsg.Hops = msg.Hops[:idx] loc.coords = append(loc.coords, hop.Port) bs := getBytesForSig(&hop.Next, &sigMsg) - if !p.core.sigs.check(&prevKey, &hop.Sig, bs) { + if !verify(&prevKey, bs, &hop.Sig) { p.core.peers.removePeer(p.port) } prevKey = hop.Next diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 41e2863..b482476 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -121,7 +121,6 @@ func (r *router) mainLoop() { r.core.switchTable.doMaintenance() r.core.dht.doMaintenance() r.core.sessions.cleanup() - r.core.sigs.cleanup() util_getBytes() // To slowly drain things } case f := <-r.admin: diff --git a/src/yggdrasil/signature.go b/src/yggdrasil/signature.go deleted file mode 100644 index 12a3d37..0000000 --- a/src/yggdrasil/signature.go +++ /dev/null @@ -1,95 +0,0 @@ -package yggdrasil - -// This is where we record which signatures we've previously checked -// It's so we can avoid needlessly checking them again - -import ( - "sync" - "time" -) - -// This keeps track of what signatures have already been checked. -// It's used to skip expensive crypto operations, given that many signatures are likely to be the same for the average node's peers. -type sigManager struct { - mutex sync.RWMutex - checked map[sigBytes]knownSig - lastCleaned time.Time -} - -// Represents a known signature. -// Includes the key, the signature bytes, the bytes that were signed, and the time it was last used. -type knownSig struct { - key sigPubKey - sig sigBytes - bs []byte - time time.Time -} - -// Initializes the signature manager. -func (m *sigManager) init() { - m.checked = make(map[sigBytes]knownSig) -} - -// Checks if a key and signature match the supplied bytes. -// If the same key/sig/bytes have been checked before, it returns true from the cached results. -// If not, it checks the key, updates it in the cache if successful, and returns the checked results. -func (m *sigManager) check(key *sigPubKey, sig *sigBytes, bs []byte) bool { - if m.isChecked(key, sig, bs) { - return true - } - verified := verify(key, bs, sig) - if verified { - m.putChecked(key, sig, bs) - } - return verified -} - -// Checks the cache to see if this key/sig/bytes combination has already been verified. -// Returns true if it finds a match. -func (m *sigManager) isChecked(key *sigPubKey, sig *sigBytes, bs []byte) bool { - m.mutex.RLock() - defer m.mutex.RUnlock() - k, isIn := m.checked[*sig] - if !isIn { - return false - } - if k.key != *key || k.sig != *sig || len(bs) != len(k.bs) { - return false - } - for idx := 0; idx < len(bs); idx++ { - if bs[idx] != k.bs[idx] { - return false - } - } - k.time = time.Now() - return true -} - -// Puts a new result into the cache. -// This result is then used by isChecked to skip the expensive crypto verification if it's needed again. -// This is useful because, for nodes with multiple peers, there is often a lot of overlap between the signatures provided by each peer. -func (m *sigManager) putChecked(key *sigPubKey, newsig *sigBytes, bs []byte) { - m.mutex.Lock() - defer m.mutex.Unlock() - k := knownSig{key: *key, sig: *newsig, bs: bs, time: time.Now()} - m.checked[*newsig] = k -} - -func (m *sigManager) cleanup() { - m.mutex.Lock() - defer m.mutex.Unlock() - if time.Since(m.lastCleaned) < time.Minute { - return - } - for s, k := range m.checked { - if time.Since(k.time) > time.Minute { - delete(m.checked, s) - } - } - newChecked := make(map[sigBytes]knownSig, len(m.checked)) - for s, k := range m.checked { - newChecked[s] = k - } - m.checked = newChecked - m.lastCleaned = time.Now() -} From e17efb6e915296d6f0789cd4bf59cf2ee89badbf Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 25 Nov 2018 13:21:13 -0600 Subject: [PATCH 4/4] don't penalize dht timeouts a second time --- src/yggdrasil/dht.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/yggdrasil/dht.go b/src/yggdrasil/dht.go index 9c2afc2..e7815b7 100644 --- a/src/yggdrasil/dht.go +++ b/src/yggdrasil/dht.go @@ -278,9 +278,6 @@ func (t *dht) doMaintenance() { newDests := make(map[NodeID]time.Time, len(dests)) for nodeID, start := range dests { if now.Sub(start) > 6*time.Second { - if info, isIn := t.table[*getNodeID(&key)]; isIn { - info.pings++ - } continue } newDests[nodeID] = start