From f9bc0b7aee8086bcac6cd35ba78b93cbf2ee5148 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Mon, 25 May 2020 11:49:25 -0500 Subject: [PATCH 1/6] use a more elaborate precomputed lookup table from the switch --- src/yggdrasil/switch.go | 116 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 6 deletions(-) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index a5c099ba..a3961c31 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -93,6 +93,20 @@ func (l *switchLocator) dist(dest []byte) int { return dist } +func (l *switchLocator) ldist(sl *switchLocator) int { + lca := -1 + for idx := 0; idx < len(l.coords); idx++ { + if idx >= len(sl.coords) { + break + } + if l.coords[idx] != sl.coords[idx] { + break + } + lca = idx + } + return len(l.coords) + len(sl.coords) - 2*(lca+1) +} + // Gets coords in wire encoded format, with *no* length prefix. func (l *switchLocator) getCoords() []byte { bs := make([]byte, 0, len(l.coords)) @@ -140,13 +154,15 @@ type tableElem struct { port switchPort locator switchLocator time time.Time + next map[switchPort]*tableElem } // This is the subset of the information about all peers needed to make routing decisions, and it stored separately in an atomically accessed table, which gets hammered in the "hot loop" of the routing logic (see: peer.handleTraffic in peers.go). type lookupTable struct { - self switchLocator - elems map[switchPort]tableElem - _msg switchMsg + self switchLocator + elems map[switchPort]tableElem // all switch peers, just for sanity checks + API/debugging + _start tableElem // used for lookups + _msg switchMsg } // This is switch information which is mutable and needs to be modified by other goroutines, but is not accessed atomically. @@ -517,10 +533,83 @@ func (t *switchTable) _handleMsg(msg *switchMsg, fromPort switchPort, reprocessi //////////////////////////////////////////////////////////////////////////////// -// The rest of these are related to the switch worker +// The rest of these are related to the switch lookup table + +func (t *switchTable) _updateTable() { + newTable := lookupTable{ + self: t.data.locator.clone(), + elems: make(map[switchPort]tableElem, len(t.data.peers)), + _msg: *t._getMsg(), + } + newTable._init() + for _, pinfo := range t.data.peers { + if pinfo.locator.root != newTable.self.root { + continue + } + loc := pinfo.locator.clone() + loc.coords = loc.coords[:len(loc.coords)-1] // Remove the them->self link + elem := tableElem{ + locator: loc, + port: pinfo.port, + time: pinfo.time, + } + newTable._insert(&elem) + newTable.elems[pinfo.port] = elem + } + t.core.peers.updateTables(t, &newTable) + t.core.router.updateTable(t, &newTable) +} + +func (t *lookupTable) _init() { + // WARNING: this relies on the convention that the self port is 0 + self := tableElem{locator: t.self} // create self elem + t._start = self // initialize _start to self + t._insert(&self) // insert self into table +} + +func (t *lookupTable) _insert(elem *tableElem) { + // This is a helper that should only be run during _updateTable + here := &t._start + for idx := 0; idx <= len(elem.locator.coords); idx++ { + refLoc := here.locator + refLoc.coords = refLoc.coords[:idx] // Note that this is length idx (starts at length 0) + oldDist := refLoc.ldist(&here.locator) + newDist := refLoc.ldist(&elem.locator) + var update bool + switch { + case newDist < oldDist: // new elem is closer to this point in the tree + update = true + case newDist > oldDist: // new elem is too far + case elem.locator.tstamp > refLoc.tstamp: // new elem has a closer timestamp + update = true + case elem.locator.tstamp < refLoc.tstamp: // new elem's timestamp is too old + case elem.time.Before(here.time): // same dist+timestamp, but new elem delivered it faster + update = true + } + if update { + here.port = elem.port + here.locator = elem.locator + here.time = elem.time + // Problem: here is a value, so this doesn't actually update anything... + } + if idx < len(elem.locator.coords) { + if here.next == nil { + here.next = make(map[switchPort]*tableElem) + } + var next *tableElem + var ok bool + if next, ok = here.next[elem.locator.coords[idx]]; !ok { + nextVal := *elem + next = &nextVal + here.next[next.locator.coords[idx]] = next + } + here = next + } + } +} // This is called via a sync.Once to update the atomically readable subset of switch information that gets used for routing decisions. -func (t *switchTable) _updateTable() { +func (t *switchTable) old_updateTable() { // WARNING this should only be called from within t.data.updater.Do() // It relies on the sync.Once for synchronization with messages and lookups // TODO use a pre-computed faster lookup table @@ -558,8 +647,23 @@ func (t *switchTable) start() error { return nil } -// Find the best port to forward to for a given set of coords func (t *lookupTable) lookup(coords []byte) switchPort { + var offset int + here := &t._start + for offset < len(coords) { + port, l := wire_decode_uint64(coords[offset:]) + offset += l + if next, ok := here.next[switchPort(port)]; ok { + here = next + } else { + break + } + } + return here.port +} + +// Find the best port to forward to for a given set of coords +func (t *lookupTable) old_lookup(coords []byte) switchPort { var bestPort switchPort myDist := t.self.dist(coords) bestDist := myDist From 905c28f7b213ac2f6cdf471e4af1587e307ea32f Mon Sep 17 00:00:00 2001 From: Arceliar Date: Wed, 27 May 2020 19:31:17 -0500 Subject: [PATCH 2/6] fix some issues with the rewritten switch lookup tables --- src/yggdrasil/switch.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index a3961c31..ae14d772 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -444,6 +444,9 @@ func (t *switchTable) _handleMsg(msg *switchMsg, fromPort switchPort, reprocessi } } } + if sender.blocked != oldSender.blocked { + doUpdate = true + } // Update sender t.data.peers[fromPort] = sender // Decide if we should also update our root info to make the sender our parent @@ -543,7 +546,9 @@ func (t *switchTable) _updateTable() { } newTable._init() for _, pinfo := range t.data.peers { - if pinfo.locator.root != newTable.self.root { + if pinfo.blocked || + pinfo.locator.root != newTable.self.root || + pinfo.key == t.key { continue } loc := pinfo.locator.clone() From 8775075c18192676d8a61e2d9e6218df06a9fb05 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Wed, 27 May 2020 19:35:19 -0500 Subject: [PATCH 3/6] debugging --- src/yggdrasil/switch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index ae14d772..ebdeea45 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -528,7 +528,7 @@ func (t *switchTable) _handleMsg(msg *switchMsg, fromPort switchPort, reprocessi t.parent = sender.port defer t.core.peers.sendSwitchMsgs(t) } - if doUpdate { + if true || doUpdate { defer t._updateTable() } return From 5e170e22e116423fb44310482709062614b5b07d Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 30 May 2020 10:47:54 -0500 Subject: [PATCH 4/6] more switch fixes --- src/yggdrasil/switch.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index ebdeea45..9cea91eb 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -392,6 +392,9 @@ func (t *switchTable) _handleMsg(msg *switchMsg, fromPort switchPort, reprocessi sender.key = prevKey prevKey = hop.Next } + if sender.key == t.key { + return // Don't peer with ourself via different interfaces + } sender.msg = *msg sender.port = fromPort sender.time = now @@ -516,8 +519,8 @@ func (t *switchTable) _handleMsg(msg *switchMsg, fromPort switchPort, reprocessi } // Note that we depend on the LIFO order of the stack of defers here... if updateRoot { + doUpdate = true if !equiv(&sender.locator, &t.data.locator) { - doUpdate = true t.data.seq++ defer t.core.router.reset(t) } @@ -528,8 +531,8 @@ func (t *switchTable) _handleMsg(msg *switchMsg, fromPort switchPort, reprocessi t.parent = sender.port defer t.core.peers.sendSwitchMsgs(t) } - if true || doUpdate { - defer t._updateTable() + if doUpdate { + t._updateTable() } return } @@ -546,9 +549,7 @@ func (t *switchTable) _updateTable() { } newTable._init() for _, pinfo := range t.data.peers { - if pinfo.blocked || - pinfo.locator.root != newTable.self.root || - pinfo.key == t.key { + if pinfo.blocked || pinfo.locator.root != newTable.self.root { continue } loc := pinfo.locator.clone() From 0f28862e99f43b2fee347cb51a4c250daf0d3f2e Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 30 May 2020 10:48:59 -0500 Subject: [PATCH 5/6] remove unused sequence number from switch --- src/yggdrasil/switch.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index 9cea91eb..be97466c 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -171,7 +171,6 @@ type switchData struct { // All data that's mutable and used by exported Table methods // To be read/written with atomic.Value Store/Load calls locator switchLocator - seq uint64 // Sequence number, reported to peers, so they know about changes peers map[switchPort]peerInfo msg *switchMsg } @@ -242,7 +241,6 @@ func (t *switchTable) _cleanRoot() { t.parent = switchPort(0) t.time = now if t.data.locator.root != t.key { - t.data.seq++ defer t.core.router.reset(nil) } t.data.locator = switchLocator{root: t.key, tstamp: now.Unix()} @@ -521,7 +519,6 @@ func (t *switchTable) _handleMsg(msg *switchMsg, fromPort switchPort, reprocessi if updateRoot { doUpdate = true if !equiv(&sender.locator, &t.data.locator) { - t.data.seq++ defer t.core.router.reset(t) } if t.data.locator.tstamp != sender.locator.tstamp { From c83b070c692de8c1c62fed9836c23a08ef0e39ad Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 30 May 2020 13:12:49 -0500 Subject: [PATCH 6/6] remove old switch lookup functions --- src/yggdrasil/switch.go | 69 ----------------------------------------- 1 file changed, 69 deletions(-) diff --git a/src/yggdrasil/switch.go b/src/yggdrasil/switch.go index be97466c..ed2edf2e 100644 --- a/src/yggdrasil/switch.go +++ b/src/yggdrasil/switch.go @@ -611,38 +611,6 @@ func (t *lookupTable) _insert(elem *tableElem) { } } -// This is called via a sync.Once to update the atomically readable subset of switch information that gets used for routing decisions. -func (t *switchTable) old_updateTable() { - // WARNING this should only be called from within t.data.updater.Do() - // It relies on the sync.Once for synchronization with messages and lookups - // TODO use a pre-computed faster lookup table - // Instead of checking distance for every destination every time - // Array of structs, indexed by first coord that differs from self - // Each struct has stores the best port to forward to, and a next coord map - // Move to struct, then iterate over coord maps until you dead end - // The last port before the dead end should be the closest - newTable := lookupTable{ - self: t.data.locator.clone(), - elems: make(map[switchPort]tableElem, len(t.data.peers)), - } - for _, pinfo := range t.data.peers { - //if !pinfo.forward { continue } - if pinfo.locator.root != newTable.self.root { - continue - } - loc := pinfo.locator.clone() - loc.coords = loc.coords[:len(loc.coords)-1] // Remove the them->self link - newTable.elems[pinfo.port] = tableElem{ - locator: loc, - port: pinfo.port, - time: pinfo.time, - } - } - newTable._msg = *t._getMsg() - t.core.peers.updateTables(t, &newTable) - t.core.router.updateTable(t, &newTable) -} - // Starts the switch worker func (t *switchTable) start() error { t.core.log.Infoln("Starting switch") @@ -664,40 +632,3 @@ func (t *lookupTable) lookup(coords []byte) switchPort { } return here.port } - -// Find the best port to forward to for a given set of coords -func (t *lookupTable) old_lookup(coords []byte) switchPort { - var bestPort switchPort - myDist := t.self.dist(coords) - bestDist := myDist - var bestElem tableElem - for _, info := range t.elems { - dist := info.locator.dist(coords) - if dist >= myDist { - continue - } - var update bool - switch { - case dist < bestDist: - // Closer to destination - update = true - case dist > bestDist: - // Further from destination - case info.locator.tstamp > bestElem.locator.tstamp: - // Newer root update - update = true - case info.locator.tstamp < bestElem.locator.tstamp: - // Older root update - case info.time.Before(bestElem.time): - // Received root update via this peer sooner - update = true - default: - } - if update { - bestPort = info.port - bestDist = dist - bestElem = info - } - } - return bestPort -}