1
0
mirror of https://github.com/matrix-org/pinecone.git synced 2026-10-24 11:59:18 +03:00

Send teardowns instead of paths expiring automatically

This commit is contained in:
Neil Alexander
2021-05-14 11:42:36 +01:00
parent e4c793a3e3
commit 8be51d7606
9 changed files with 141 additions and 76 deletions

View File

@@ -91,6 +91,9 @@ func (r *Router) reader(conn net.Conn) {
}
r.recv <- frame
case types.TypeVirtualSnakeTeardown:
r.recv <- frame
case types.TypeSource:
// Check if the source path seems to be finished.
if len(frame.Destination) > 0 {

View File

@@ -101,9 +101,9 @@ func (e virtualSnakeTable) MarshalJSON() ([]byte, error) {
"source_port": value.SourcePort,
"last_seen": time.Since(value.LastSeen).String(),
}
if time.Since(value.LastSeen) > virtualSnakePathExpiryPeriod {
entry["expired"] = true
}
//if time.Since(value.LastSeen) > virtualSnakePathExpiryPeriod {
// entry["expired"] = true
//}
out = append(out, entry)
}
return json.Marshal(out)

View File

@@ -52,6 +52,9 @@ func (p *Peer) getNextHops(frame *types.Frame, from types.SwitchPortID) types.Sw
return nextHops
}
case types.TypeVirtualSnakeTeardown:
return p.r.snake.getVirtualSnakeTeardownNextHop(p, frame)
case types.TypeVirtualSnakeSetup:
nextHops := p.r.getGreedyRoutedNextHop(p, frame)
defer p.r.snake.handleSetup(p, frame, nextHops)

View File

@@ -25,8 +25,7 @@ import (
)
const virtualSnakeSetupInterval = time.Second * 16
const virtualSnakePathExpiryPeriod = virtualSnakeSetupInterval * 2
const virtualSnakeNeighExpiryPeriod = virtualSnakePathExpiryPeriod * 2
const virtualSnakeNeighExpiryPeriod = time.Minute * 10
type virtualSnake struct {
r *Router
@@ -43,8 +42,9 @@ type virtualSnake struct {
type virtualSnakeTable map[types.PublicKey]virtualSnakeEntry
type virtualSnakeEntry struct {
SourcePort types.SwitchPortID
LastSeen time.Time
SourcePort types.SwitchPortID
DestinationPort types.SwitchPortID
LastSeen time.Time
}
type virtualSnakeNeighbour struct {
@@ -128,18 +128,6 @@ func (v *virtualSnake) maintain() {
// portWasDisconnected is called by the router when a peer connects
// allowing us to start a new bootstrap.
func (t *virtualSnake) rootNodeChanged(root types.PublicKey) {
t.ascendingMutex.Lock()
if t.ascending != nil {
t.clearRoutingEntriesForPublicKey(t.ascending.PublicKey)
}
t.ascending = nil
t.ascendingMutex.Unlock()
t.descendingMutex.Lock()
if t.descending != nil {
t.clearRoutingEntriesForPublicKey(t.descending.PublicKey)
}
t.descending = nil
t.descendingMutex.Unlock()
t.maintainNow.Dispatch()
}
@@ -153,25 +141,7 @@ func (t *virtualSnake) portWasConnected(port types.SwitchPortID) {
// allowing us to clean up the virtual snake state.
func (t *virtualSnake) portWasDisconnected(port types.SwitchPortID) {
defer t.maintainNow.Dispatch()
// Check if our descending node was routable via the disconnected
// port. If they were, clear the entry so that the maintain function
// will send new bootstraps to find the next descending node.
t.descendingMutex.Lock()
if p := t.descending; p != nil && p.Port == port {
t.clearRoutingEntriesForPublicKey(t.descending.PublicKey)
t.descending = nil
}
t.descendingMutex.Unlock()
// Check if our ascending node was routable via the disconnected
// port. If they were then clear the entry too, it will get
// re-populated the next time we bootstrap.
t.ascendingMutex.Lock()
if s := t.ascending; s != nil && s.Port == port {
t.clearRoutingEntriesForPublicKey(t.ascending.PublicKey)
t.ascending = nil
}
t.ascendingMutex.Unlock()
// Finally, check if any of our routing table entries are
// Check if any of our routing table entries are
// via the port in question. If they were then let's nuke those
// too, otherwise we'll be trying to route traffic into black
// holes.
@@ -222,9 +192,6 @@ func (t *virtualSnake) getVirtualSnakeBootstrapNextHop(from *Peer, destKey types
// Check our DHT entries
t.tableMutex.RLock()
for dhtKey, entry := range t.table {
if time.Since(entry.LastSeen) > virtualSnakePathExpiryPeriod {
continue
}
switch {
case util.DHTOrdered(destKey, dhtKey, bestKey):
// If the DHT entry routes to a key that is closer to the
@@ -290,12 +257,6 @@ func (t *virtualSnake) getVirtualSnakeNextHop(from *Peer, destKey types.PublicKe
// Next, look at the routing table. This includes source-side paths
// from setups only.
for dhtKey, entry := range t.table {
if time.Since(entry.LastSeen) > virtualSnakePathExpiryPeriod {
// The routing table entry hasn't been refreshed recently so we'll
// ignore it. This will eventually get picked up and removed from
// the routing table.
continue
}
switch {
case destKey.EqualTo(dhtKey):
newCandidate(dhtKey, entry.SourcePort)
@@ -321,23 +282,54 @@ func (t *virtualSnake) getVirtualSnakeNextHop(from *Peer, destKey types.PublicKe
return candidates[canlength:]
}
func (t *virtualSnake) clearRoutingEntriesForPort(port types.SwitchPortID) {
func (t *virtualSnake) getVirtualSnakeTeardownNextHop(from *Peer, rx *types.Frame) types.SwitchPorts {
t.ascendingMutex.Lock()
if asc := t.ascending; asc != nil && asc.PublicKey == rx.DestinationKey {
t.ascending = nil
t.maintainNow.Dispatch()
}
t.ascendingMutex.Unlock()
t.descendingMutex.Lock()
if desc := t.descending; desc != nil && desc.PublicKey == rx.DestinationKey {
t.ascending = nil
t.maintainNow.Dispatch()
}
t.descendingMutex.Unlock()
t.tableMutex.Lock()
defer t.tableMutex.Unlock()
for k, v := range t.table {
if v.SourcePort == port {
if k == rx.DestinationKey {
delete(t.table, k)
if from.port == v.SourcePort {
return types.SwitchPorts{v.DestinationPort}
} else {
return types.SwitchPorts{v.SourcePort}
}
}
}
return types.SwitchPorts{}
}
func (t *virtualSnake) clearRoutingEntriesForPort(port types.SwitchPortID) {
t.tableMutex.RLock()
var key types.PublicKey
for k, v := range t.table {
if v.DestinationPort == port || v.SourcePort == port {
key = k
break
}
}
t.tableMutex.RUnlock()
t.r.send <- types.Frame{
Type: types.TypeVirtualSnakeTeardown,
DestinationKey: key,
}
}
func (t *virtualSnake) clearRoutingEntriesForPublicKey(key types.PublicKey) {
t.tableMutex.Lock()
defer t.tableMutex.Unlock()
for k := range t.table {
if k == key {
delete(t.table, k)
}
func (t *virtualSnake) clearRoutingEntriesForPublicKey(pk types.PublicKey) {
t.r.send <- types.Frame{
Type: types.TypeVirtualSnakeTeardown,
DestinationKey: pk,
}
}
@@ -423,6 +415,9 @@ func (t *virtualSnake) handleBootstrapACK(from *Peer, rx *types.Frame) {
// new node that we've received a bootstrap from is actually closer to
// us than the previous node. We'll update our record to use the new
// node instead and then send a new path setup message to it.
if t.ascending != nil && rx.SourceKey != t.ascending.PublicKey {
t.clearRoutingEntriesForPublicKey(t.ascending.PublicKey)
}
t.ascending = &virtualSnakeNeighbour{
PublicKey: rx.SourceKey,
Port: from.port,
@@ -452,20 +447,6 @@ func (t *virtualSnake) handleBootstrapACK(from *Peer, rx *types.Frame) {
// instead called for every setup packet being transited through this node.
// It will update the routing table with the new path.
func (t *virtualSnake) handleSetup(from *Peer, rx *types.Frame, nextHops types.SwitchPorts) {
if rx.SourceKey.EqualTo(t.r.public) {
return
}
t.tableMutex.Lock()
defer t.tableMutex.Unlock()
// Evict old entries.
for k, v := range t.table {
if time.Since(v.LastSeen) > virtualSnakePathExpiryPeriod {
delete(t.table, k)
}
}
// Check if the setup packet has a valid signed timestamp.
if !util.VerifySignedTimestamp(rx.SourceKey, rx.Payload) {
return
@@ -477,10 +458,16 @@ func (t *virtualSnake) handleSetup(from *Peer, rx *types.Frame, nextHops types.S
// bound the routing table safely, we may want to make sure that we have
// a reasonable spread of routes across keyspace so that we don't create
// any obvious routing holes.
t.table[rx.SourceKey] = virtualSnakeEntry{
entry := virtualSnakeEntry{
LastSeen: time.Now(),
SourcePort: from.port,
}
if len(nextHops) > 0 {
entry.DestinationPort = nextHops[0]
}
t.tableMutex.Lock()
t.table[rx.SourceKey] = entry
t.tableMutex.Unlock()
// If we're at the destination of the setup then update our predecessor
// with information from the bootstrap.
@@ -513,6 +500,9 @@ func (t *virtualSnake) handleSetup(from *Peer, rx *types.Frame, nextHops types.S
// new node that we've received a bootstrap from is actually closer to
// us than the previous node. We'll update our record to use the new
// node instead and then send back a bootstrap ACK.
if t.descending != nil && rx.SourceKey != t.descending.PublicKey {
t.clearRoutingEntriesForPublicKey(t.descending.PublicKey)
}
t.descending = &virtualSnakeNeighbour{
PublicKey: rx.SourceKey,
Port: from.port,

View File

@@ -224,6 +224,10 @@ func (p *Peer) reader() {
coordsLen := int(binary.BigEndian.Uint16(header[4:6]))
expecting = 10 + coordsLen + (ed25519.PublicKeySize * 2) + payloadLen
case types.TypeVirtualSnakeTeardown:
payloadLen := int(binary.BigEndian.Uint16(header[2:4]))
expecting = 8 + payloadLen + ed25519.PublicKeySize
case types.TypeVirtualSnake, types.TypeVirtualSnakePathfind:
payloadLen := int(binary.BigEndian.Uint16(header[2:4]))
expecting = 8 + payloadLen + (ed25519.PublicKeySize * 2)
@@ -300,7 +304,7 @@ func (p *Peer) reader() {
continue
}
case types.TypeDHTRequest, types.TypeDHTResponse, types.TypeVirtualSnakeBootstrap, types.TypeVirtualSnakeBootstrapACK, types.TypeVirtualSnakeSetup:
case types.TypeDHTRequest, types.TypeDHTResponse, types.TypeVirtualSnakeBootstrap, types.TypeVirtualSnakeBootstrapACK, types.TypeVirtualSnakeSetup, types.TypeVirtualSnakeTeardown:
select {
case dest.protoOut <- frame.Borrow():
dest.statistics.txProtoSuccessful.Inc()

View File

@@ -209,7 +209,7 @@ func (r *Router) Predecessor() *types.PublicKey {
r.snake.descendingMutex.RLock()
pr := r.snake.descending
r.snake.descendingMutex.RUnlock()
if pr == nil || time.Since(pr.LastSeen) >= virtualSnakeNeighExpiryPeriod {
if pr == nil { // || time.Since(pr.LastSeen) >= virtualSnakeNeighExpiryPeriod {
return nil
}
pk := pr.PublicKey
@@ -222,7 +222,7 @@ func (r *Router) Successor() *types.PublicKey {
r.snake.ascendingMutex.RLock()
su := r.snake.ascending
r.snake.ascendingMutex.RUnlock()
if su == nil || time.Since(su.LastSeen) >= virtualSnakeNeighExpiryPeriod {
if su == nil { //|| time.Since(su.LastSeen) >= virtualSnakeNeighExpiryPeriod {
return nil
}
pk := su.PublicKey

View File

@@ -371,6 +371,9 @@ func (t *spanningTree) Update(p *Peer, a *types.SwitchAnnouncement) error {
}
// If the root has changed then let's do something about it.
if newRoot.RootPublicKey != oldRoot.RootPublicKey {
go t.r.snake.rootNodeChanged(newRoot.RootPublicKey)
}
if newRoot != oldRoot {
t.rootMutex.Lock()
t.root = newRoot
@@ -379,8 +382,6 @@ func (t *spanningTree) Update(p *Peer, a *types.SwitchAnnouncement) error {
if p.port == t.updateCoordinates() {
t.advertise.Dispatch()
}
go t.r.snake.rootNodeChanged(newRoot.RootPublicKey)
}
return nil

View File

@@ -53,6 +53,7 @@ const (
TypeVirtualSnakeSetup // protocol frame, forwarded using greedy routing
TypeVirtualSnake // traffic frame, forwarded using SNEK successor ordering
TypeVirtualSnakePathfind // protocol frame, forwarded using SNEK successor ordering
TypeVirtualSnakeTeardown // protocol frame
)
const (
@@ -161,6 +162,15 @@ func (f *Frame) MarshalBinary(buffer []byte) (int, error) {
offset += copy(buffer[offset:], f.Payload[:payloadLen])
}
case TypeVirtualSnakeTeardown: // destination = key
payloadLen := len(f.Payload)
binary.BigEndian.PutUint16(buffer[offset+0:offset+2], uint16(payloadLen))
offset += 2
offset += copy(buffer[offset:], f.DestinationKey[:ed25519.PublicKeySize])
if f.Payload != nil {
offset += copy(buffer[:offset], f.Payload[:payloadLen])
}
case TypeVirtualSnake, TypeVirtualSnakePathfind: // destination = key, source = key
payloadLen := len(f.Payload)
binary.BigEndian.PutUint16(buffer[offset+0:offset+2], uint16(payloadLen))
@@ -250,6 +260,13 @@ func (f *Frame) UnmarshalBinary(data []byte) (int, error) {
f.Payload = append([]byte{}, data[offset:offset+payloadLen]...)
return offset, nil
case TypeVirtualSnakeTeardown: // destination = key
payloadLen := int(binary.BigEndian.Uint16(data[offset+0 : offset+2]))
offset += 2
offset += copy(f.DestinationKey[:], data[offset:])
f.Payload = append([]byte{}, data[offset:offset+payloadLen]...)
return offset, nil
case TypeVirtualSnake, TypeVirtualSnakePathfind: // destination = key, source = key
payloadLen := int(binary.BigEndian.Uint16(data[offset+0 : offset+2]))
offset += 2

View File

@@ -242,6 +242,53 @@ func TestMarshalUnmarshalSNEKSetupFrame(t *testing.T) {
}
}
func TestMarshalUnmarshalSNEKTeardownFrame(t *testing.T) {
pk1, _, _ := ed25519.GenerateKey(nil)
input := Frame{
Version: Version0,
Type: TypeVirtualSnakeTeardown,
}
copy(input.DestinationKey[:], pk1)
expected := []byte{
0x70, 0x69, 0x6e, 0x65, // magic bytes
0, // version 0
byte(TypeVirtualSnakeTeardown), // type greedy
0, 0, // payload length
}
expected = append(expected, pk1...)
buf := make([]byte, 65535)
n, err := input.MarshalBinary(buf)
if err != nil {
t.Fatal(err)
}
if n != len(expected) {
t.Fatalf("wrong marshalled length, \ngot %d, \nexpected %d", n, len(expected))
}
if !bytes.Equal(buf[:n], expected) {
t.Fatalf("wrong marshalled output, \ngot %v, \nexpected %v", buf[:n], expected)
}
t.Log("Got: ", buf[:n])
t.Log("Want:", expected)
var output Frame
if _, err := output.UnmarshalBinary(buf[:n]); err != nil {
t.Fatal(err)
}
if output.Version != input.Version {
t.Fatal("wrong version")
}
if output.Type != input.Type {
t.Fatal("wrong version")
}
if !output.Destination.EqualTo(input.Destination) {
t.Fatal("wrong path")
}
if !bytes.Equal(input.Payload, output.Payload) {
t.Fatal("wrong payload")
}
}
func TestMarshalUnmarshalSNEKFrame(t *testing.T) {
pk1, _, _ := ed25519.GenerateKey(nil)
pk2, _, _ := ed25519.GenerateKey(nil)