2023-10-10 21:14:40 +02:00
|
|
|
package sync
|
|
|
|
|
2023-10-20 13:41:06 +02:00
|
|
|
import (
|
2023-12-18 21:45:56 +01:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2023-12-06 23:45:04 +01:00
|
|
|
"io"
|
2023-11-03 16:24:18 +01:00
|
|
|
"math/rand"
|
2023-10-24 17:00:46 +02:00
|
|
|
"time"
|
2023-10-20 13:41:06 +02:00
|
|
|
|
2023-11-03 16:24:18 +01:00
|
|
|
"github.com/tim-beatham/wgmesh/pkg/conf"
|
|
|
|
"github.com/tim-beatham/wgmesh/pkg/conn"
|
2023-10-20 13:41:06 +02:00
|
|
|
"github.com/tim-beatham/wgmesh/pkg/lib"
|
2023-10-24 17:00:46 +02:00
|
|
|
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
2023-10-22 14:34:49 +02:00
|
|
|
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
2023-10-20 13:41:06 +02:00
|
|
|
)
|
|
|
|
|
2023-12-07 19:18:13 +01:00
|
|
|
// Syncer: picks random nodes from the meshs
|
2023-10-10 21:14:40 +02:00
|
|
|
type Syncer interface {
|
|
|
|
Sync(meshId string) error
|
|
|
|
SyncMeshes() error
|
|
|
|
}
|
|
|
|
|
|
|
|
type SyncerImpl struct {
|
2023-11-05 19:03:58 +01:00
|
|
|
manager mesh.MeshManager
|
2023-11-03 16:24:18 +01:00
|
|
|
requester SyncRequester
|
|
|
|
infectionCount int
|
|
|
|
syncCount int
|
|
|
|
cluster conn.ConnCluster
|
2023-12-10 20:21:54 +01:00
|
|
|
conf *conf.DaemonConfiguration
|
2023-12-18 21:45:56 +01:00
|
|
|
lastSync map[string]uint64
|
2023-10-10 21:14:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sync: Sync random nodes
|
|
|
|
func (s *SyncerImpl) Sync(meshId string) error {
|
2023-12-10 16:10:36 +01:00
|
|
|
// Self can be nil if the node is removed
|
|
|
|
self, _ := s.manager.GetSelf(meshId)
|
2023-12-07 19:18:13 +01:00
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
correspondingMesh := s.manager.GetMesh(meshId)
|
|
|
|
|
|
|
|
correspondingMesh.Prune()
|
2023-12-07 19:18:13 +01:00
|
|
|
|
2023-12-18 21:45:56 +01:00
|
|
|
if self != nil && self.GetType() == conf.PEER_ROLE && !s.manager.HasChanges(meshId) && s.infectionCount == 0 {
|
|
|
|
logging.Log.WriteInfof("No changes for %s", meshId)
|
|
|
|
|
|
|
|
// If not synchronised in certain pull from random neighbour
|
|
|
|
if uint64(time.Now().Unix())-s.lastSync[meshId] > 20 {
|
|
|
|
return s.Pull(meshId)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-12-07 19:18:13 +01:00
|
|
|
before := time.Now()
|
2023-11-28 15:42:09 +01:00
|
|
|
s.manager.GetRouteManager().UpdateRoutes()
|
2023-11-05 19:03:58 +01:00
|
|
|
|
2023-11-28 15:42:09 +01:00
|
|
|
publicKey := s.manager.GetPublicKey()
|
2023-11-24 13:07:03 +01:00
|
|
|
|
2023-11-30 03:02:38 +01:00
|
|
|
logging.Log.WriteInfof(publicKey.String())
|
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
nodeNames := correspondingMesh.GetPeers()
|
2023-11-03 16:24:18 +01:00
|
|
|
|
2023-12-10 16:10:36 +01:00
|
|
|
if self != nil {
|
|
|
|
nodeNames = lib.Filter(nodeNames, func(s string) bool {
|
|
|
|
return s != mesh.NodeID(self)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-07 19:18:13 +01:00
|
|
|
var gossipNodes []string
|
2023-10-20 13:41:06 +02:00
|
|
|
|
2023-12-07 19:18:13 +01:00
|
|
|
// Clients always pings its peer for configuration
|
2023-12-10 16:10:36 +01:00
|
|
|
if self != nil && self.GetType() == conf.CLIENT_ROLE {
|
2023-12-07 19:18:13 +01:00
|
|
|
keyFunc := lib.HashString
|
|
|
|
bucketFunc := lib.HashString
|
|
|
|
|
|
|
|
neighbour := lib.ConsistentHash(nodeNames, publicKey.String(), keyFunc, bucketFunc)
|
|
|
|
gossipNodes = make([]string, 1)
|
|
|
|
gossipNodes[0] = neighbour
|
|
|
|
} else {
|
|
|
|
neighbours := s.cluster.GetNeighbours(nodeNames, publicKey.String())
|
|
|
|
gossipNodes = lib.RandomSubsetOfLength(neighbours, s.conf.BranchRate)
|
2023-10-24 17:00:46 +02:00
|
|
|
|
2023-12-07 19:18:13 +01:00
|
|
|
if len(nodeNames) > s.conf.ClusterSize && rand.Float64() < s.conf.InterClusterChance {
|
|
|
|
gossipNodes[len(gossipNodes)-1] = s.cluster.GetInterCluster(nodeNames, publicKey.String())
|
|
|
|
}
|
2023-11-03 16:24:18 +01:00
|
|
|
}
|
|
|
|
|
2023-12-06 23:45:04 +01:00
|
|
|
var succeeded bool = false
|
2023-10-23 19:13:08 +02:00
|
|
|
|
2023-12-06 23:45:04 +01:00
|
|
|
// Do this synchronously to conserve bandwidth
|
2023-12-07 19:18:13 +01:00
|
|
|
for _, node := range gossipNodes {
|
2023-12-06 23:45:04 +01:00
|
|
|
correspondingPeer := s.manager.GetNode(meshId, node)
|
2023-11-24 13:07:03 +01:00
|
|
|
|
2023-12-06 23:45:04 +01:00
|
|
|
if correspondingPeer == nil {
|
|
|
|
logging.Log.WriteErrorf("node %s does not exist", node)
|
|
|
|
}
|
2023-11-24 13:07:03 +01:00
|
|
|
|
2023-12-07 19:18:13 +01:00
|
|
|
err := s.requester.SyncMesh(meshId, correspondingPeer)
|
2023-11-24 13:07:03 +01:00
|
|
|
|
2023-12-06 23:45:04 +01:00
|
|
|
if err == nil || err == io.EOF {
|
|
|
|
succeeded = true
|
|
|
|
} else {
|
|
|
|
// If the synchronisation operation has failed them mark a gravestone
|
|
|
|
// preventing the peer from being re-contacted until it has updated
|
|
|
|
// itself
|
|
|
|
s.manager.GetMesh(meshId).Mark(node)
|
|
|
|
}
|
2023-10-20 13:41:06 +02:00
|
|
|
}
|
|
|
|
|
2023-11-03 16:24:18 +01:00
|
|
|
s.syncCount++
|
2023-11-17 23:13:51 +01:00
|
|
|
logging.Log.WriteInfof("SYNC TIME: %v", time.Since(before))
|
2023-11-03 16:24:18 +01:00
|
|
|
logging.Log.WriteInfof("SYNC COUNT: %d", s.syncCount)
|
2023-11-01 11:39:46 +01:00
|
|
|
|
2023-11-03 16:24:18 +01:00
|
|
|
s.infectionCount = ((s.conf.InfectionCount + s.infectionCount - 1) % s.conf.InfectionCount)
|
2023-12-06 23:45:04 +01:00
|
|
|
|
|
|
|
if !succeeded {
|
|
|
|
// If could not gossip with anyone then repeat.
|
|
|
|
s.infectionCount++
|
|
|
|
}
|
|
|
|
|
2023-11-30 16:58:26 +01:00
|
|
|
s.manager.GetMesh(meshId).SaveChanges()
|
2023-12-18 21:45:56 +01:00
|
|
|
s.lastSync[meshId] = uint64(time.Now().Unix())
|
2023-12-07 19:18:13 +01:00
|
|
|
|
|
|
|
logging.Log.WriteInfof("UPDATING WG CONF")
|
2023-12-10 16:10:36 +01:00
|
|
|
err := s.manager.ApplyConfig()
|
2023-12-07 19:18:13 +01:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
logging.Log.WriteInfof("Failed to update config %w", err)
|
|
|
|
}
|
|
|
|
|
2023-11-20 12:28:12 +01:00
|
|
|
return nil
|
2023-10-10 21:14:40 +02:00
|
|
|
}
|
|
|
|
|
2023-12-18 21:45:56 +01:00
|
|
|
// Pull one node in the cluster, if there has not been message dissemination
|
|
|
|
// in a certain period of time pull a random node within the cluster
|
|
|
|
func (s *SyncerImpl) Pull(meshId string) error {
|
|
|
|
mesh := s.manager.GetMesh(meshId)
|
|
|
|
self, err := s.manager.GetSelf(meshId)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
pubKey, _ := self.GetPublicKey()
|
|
|
|
|
|
|
|
if mesh == nil {
|
|
|
|
return errors.New("mesh is nil, invalid operation")
|
|
|
|
}
|
|
|
|
|
|
|
|
peers := mesh.GetPeers()
|
|
|
|
neighbours := s.cluster.GetNeighbours(peers, pubKey.String())
|
|
|
|
neighbour := lib.RandomSubsetOfLength(neighbours, 1)
|
|
|
|
|
|
|
|
if len(neighbour) == 0 {
|
|
|
|
logging.Log.WriteInfof("no neighbours")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
logging.Log.WriteInfof("PULLING from node %s", neighbour[0])
|
|
|
|
|
|
|
|
pullNode, err := mesh.GetNode(neighbour[0])
|
|
|
|
|
|
|
|
if err != nil || pullNode == nil {
|
|
|
|
return fmt.Errorf("node %s does not exist in the mesh", neighbour[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
err = s.requester.SyncMesh(meshId, pullNode)
|
|
|
|
|
|
|
|
if err == nil || err == io.EOF {
|
|
|
|
s.lastSync[meshId] = uint64(time.Now().Unix())
|
|
|
|
} else {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
s.syncCount++
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-10-20 13:41:06 +02:00
|
|
|
// SyncMeshes: Sync all meshes
|
2023-10-10 21:14:40 +02:00
|
|
|
func (s *SyncerImpl) SyncMeshes() error {
|
2023-11-17 23:13:51 +01:00
|
|
|
for meshId := range s.manager.GetMeshes() {
|
2023-10-26 17:53:12 +02:00
|
|
|
err := s.Sync(meshId)
|
2023-10-20 13:41:06 +02:00
|
|
|
|
|
|
|
if err != nil {
|
2023-12-08 21:02:57 +01:00
|
|
|
logging.Log.WriteErrorf(err.Error())
|
2023-10-20 13:41:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-24 17:00:46 +02:00
|
|
|
return nil
|
2023-10-10 21:14:40 +02:00
|
|
|
}
|
2023-10-20 13:41:06 +02:00
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
func NewSyncer(m mesh.MeshManager, conf *conf.DaemonConfiguration, r SyncRequester) Syncer {
|
2023-11-03 16:24:18 +01:00
|
|
|
cluster, _ := conn.NewConnCluster(conf.ClusterSize)
|
|
|
|
return &SyncerImpl{
|
|
|
|
manager: m,
|
|
|
|
conf: conf,
|
|
|
|
requester: r,
|
|
|
|
infectionCount: 0,
|
|
|
|
syncCount: 0,
|
2023-12-18 21:45:56 +01:00
|
|
|
cluster: cluster,
|
|
|
|
lastSync: make(map[string]uint64)}
|
2023-10-20 13:41:06 +02:00
|
|
|
}
|