Merge pull request #83 from tim-beatham/81-separate-synchronisation-into-independent-processes

81 separate synchronisation into independent processes
This commit is contained in:
Tim Beatham 2024-01-05 13:05:01 +00:00 committed by GitHub
commit 0136e44b36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 1478 additions and 851 deletions

BIN
api

Binary file not shown.

View File

@ -10,25 +10,26 @@ import (
ctrlserver "github.com/tim-beatham/smegmesh/pkg/ctrlserver"
"github.com/tim-beatham/smegmesh/pkg/ipc"
logging "github.com/tim-beatham/smegmesh/pkg/log"
"github.com/tim-beatham/smegmesh/pkg/mesh"
"github.com/tim-beatham/smegmesh/pkg/robin"
"github.com/tim-beatham/smegmesh/pkg/sync"
timer "github.com/tim-beatham/smegmesh/pkg/timers"
"golang.zx2c4.com/wireguard/wgctrl"
)
func main() {
if len(os.Args) != 2 {
logging.Log.WriteErrorf("Did not provide configuration")
return
}
conf, err := conf.ParseDaemonConfiguration(os.Args[1])
configuration, err := conf.ParseDaemonConfiguration(os.Args[1])
if err != nil {
logging.Log.WriteErrorf("Could not parse configuration: %s", err.Error())
return
}
logging.SetLogger(logging.NewLogrusLogger(configuration.LogLevel))
client, err := wgctrl.New()
if err != nil {
@ -36,7 +37,7 @@ func main() {
return
}
if conf.Profile {
if configuration.Profile {
go func() {
http.ListenAndServe("localhost:6060", nil)
}()
@ -45,17 +46,12 @@ func main() {
var robinRpc robin.WgRpc
var robinIpc robin.IpcHandler
var syncProvider sync.SyncServiceImpl
var syncRequester sync.SyncRequester
var syncer sync.Syncer
ctrlServerParams := ctrlserver.NewCtrlServerParams{
Conf: conf,
Conf: configuration,
CtrlProvider: &robinRpc,
SyncProvider: &syncProvider,
Client: client,
OnDelete: func(mp mesh.MeshProvider) {
syncer.SyncMeshes()
},
}
ctrlServer, err := ctrlserver.NewCtrlServer(&ctrlServerParams)
@ -64,11 +60,7 @@ func main() {
panic(err)
}
syncProvider.Server = ctrlServer
syncRequester = sync.NewSyncRequester(ctrlServer)
syncer = sync.NewSyncer(ctrlServer.MeshManager, conf, syncRequester)
syncScheduler := sync.NewSyncScheduler(ctrlServer, syncRequester, syncer)
keepAlive := timer.NewTimestampScheduler(ctrlServer)
syncProvider.MeshManager = ctrlServer.MeshManager
robinIpcParams := robin.RobinIpcParams{
CtrlServer: ctrlServer,
@ -82,16 +74,11 @@ func main() {
return
}
logging.Log.WriteInfof("Running IPC Handler")
logging.Log.WriteInfof("running ipc handler")
go ipc.RunIpcHandler(&robinIpc)
go syncScheduler.Run()
go keepAlive.Run()
closeResources := func() {
logging.Log.WriteInfof("Closing resources")
syncScheduler.Stop()
keepAlive.Stop()
logging.Log.WriteInfof("closing resources")
ctrlServer.Close()
client.Close()
}

View File

@ -11,17 +11,7 @@ import (
"github.com/tim-beatham/smegmesh/pkg/what8words"
)
type ApiServer interface {
GetMeshes(c *gin.Context)
Run(addr string) error
}
type SmegServer struct {
router *gin.Engine
client *ipc.SmegmeshIpc
words *what8words.What8Words
}
// routesToApiRoute: convert the returned type to a JSON object
func (s *SmegServer) routeToApiRoute(meshNode ctrlserver.MeshNode) []Route {
routes := make([]Route, len(meshNode.Routes))
@ -40,6 +30,7 @@ func (s *SmegServer) routeToApiRoute(meshNode ctrlserver.MeshNode) []Route {
return routes
}
// meshNodeToAPImeshNode: convert daemon node to a JSON node
func (s *SmegServer) meshNodeToAPIMeshNode(meshNode ctrlserver.MeshNode) *SmegNode {
if meshNode.Routes == nil {
meshNode.Routes = make([]ctrlserver.MeshRoute, 0)
@ -70,6 +61,7 @@ func (s *SmegServer) meshNodeToAPIMeshNode(meshNode ctrlserver.MeshNode) *SmegNo
}
}
// meshToAPIMesh: Convert daemon mesh network to a JSON mesh network
func (s *SmegServer) meshToAPIMesh(meshId string, nodes []ctrlserver.MeshNode) SmegMesh {
var smegMesh SmegMesh
smegMesh.MeshId = meshId
@ -175,6 +167,8 @@ func (s *SmegServer) GetMesh(c *gin.Context) {
c.JSON(http.StatusOK, mesh)
}
// GetMeshes: return all the mesh networks that the
// user is a part of
func (s *SmegServer) GetMeshes(c *gin.Context) {
listMeshesReply := new(ipc.ListMeshReply)
@ -205,11 +199,14 @@ func (s *SmegServer) GetMeshes(c *gin.Context) {
c.JSON(http.StatusOK, meshes)
}
// Run: run the API server
func (s *SmegServer) Run(addr string) error {
logging.Log.WriteInfof("Running API server")
return s.router.Run(addr)
}
// NewSmegServer: creates an instance of a new API server
// returns an error if something went wrong
func NewSmegServer(conf ApiServerConf) (ApiServer, error) {
client, err := ipc.NewClientIpc()

View File

@ -1,47 +1,105 @@
package api
import "time"
import (
"time"
"github.com/gin-gonic/gin"
"github.com/tim-beatham/smegmesh/pkg/ipc"
"github.com/tim-beatham/smegmesh/pkg/what8words"
)
// Route is an advertised route in the data store
type Route struct {
Prefix string `json:"prefix"`
Path []string `json:"path"`
// Prefix is the advertised route prefix
Prefix string `json:"prefix"`
// Path is the hops the destination
Path []string `json:"path"`
}
// SmegStats is the WireGuard stats that the underlying host
// has sent to the peer
type SmegStats struct {
TotalTransmit int64 `json:"totalTransmit"`
TotalReceived int64 `json:"totalReceived"`
// TotalTransmit number of bytes sent to the peer
TotalTransmit int64 `json:"totalTransmit"`
// TotalReceived number of bytes received from the peer
TotalReceived int64 `json:"totalReceived"`
// KeepAliveInterval WireGuard keepalive interval that is sent to the host
KeepAliveInterval time.Duration `json:"keepaliveInterval"`
AllowedIps []string `json:"allowedIps"`
// AllowsIps is the allowed path to the destination
AllowedIps []string `json:"allowedIps"`
}
// SmegNode is a node in the mesh network
type SmegNode struct {
Alias string `json:"alias"`
WgHost string `json:"wgHost"`
WgEndpoint string `json:"wgEndpoint"`
Endpoint string `json:"endpoint"`
Timestamp int `json:"timestamp"`
Description string `json:"description"`
PublicKey string `json:"publicKey"`
Routes []Route `json:"routes"`
Services map[string]string `json:"services"`
Stats SmegStats `json:"stats"`
// Alias is the human readable name that the node is assocaited with
Alias string `json:"alias"`
// WgHost is the WireGuard IP address of the node. This is an IPv6
// address
WgHost string `json:"wgHost"`
// WgEndpoint is the physical endpoint of the host that packets
// are forwarded to
WgEndpoint string `json:"wgEndpoint"`
// Endpoint is the control plane endpoint of the host which
// grpc connections are to be sent along
Endpoint string `json:"endpoint"`
// Timestamp is the last time the signified it was alive.
// if the node is the leader this is evert heartBeatInterval
// otherwise this is the time the node joined the network
Timestamp int `json:"timestamp"`
// Description is the human readable description of the node
Description string `json:"description"`
// PublicKey is the WireGuard public key of the node
PublicKey string `json:"publicKey"`
// Routes is the routes that the node is advertising
Routes []Route `json:"routes"`
// Services is information about services that the node offers
Services map[string]string `json:"services"`
// Stats is the WireGuard stats of the node (if any)
Stats SmegStats `json:"stats"`
}
// SmegMesh encapsulates a single mesh in the API
type SmegMesh struct {
MeshId string `json:"meshid"`
Nodes map[string]SmegNode `json:"nodes"`
// MeshId is the mesh id of the network
MeshId string `json:"meshid"`
// Nodes is the nodes in the network keyed by their public
// key
Nodes map[string]SmegNode `json:"nodes"`
}
// CreateMeshRequest encapsulates a request to create a mesh network
type CreateMeshRequest struct {
// WgPort is the WireGuard to create the mesh in
WgPort int `json:"port" binding:"omitempty,gte=1024,lt=65535"`
}
// JoinMeshRequests encapsulates a request to create a mesh network
type JoinMeshRequest struct {
WgPort int `json:"port" binding:"omitempty,gte=1024,lt=65535"`
// WgPort is the WireGuard port to run the service on
WgPort int `json:"port" binding:"omitempty,gte=1024,lt=65535"`
// Bootstrap is a bootstrap node to use to join the network
Bootstrap string `json:"bootstrap" binding:"required"`
MeshId string `json:"meshid" binding:"required"`
// MeshId is the ID of the mesh to join
MeshId string `json:"meshid" binding:"required"`
}
// ApiServerConf configuration to instantiate the API server
type ApiServerConf struct {
// WordsFile to use to map IP to words
WordsFile string
}
// SmegSever is the GIN api server that runs the service
type SmegServer struct {
// gin router to use
router *gin.Engine
// client to invoke operations
client *ipc.SmegmeshIpc
// what8words to use to convert IP to an alias
words *what8words.What8Words
}
// ApiSever absrtacts the API server
type ApiServer interface {
Run(addr string) error
}

View File

@ -1,3 +1,5 @@
// automerge: package is depracated and unused. Please refer to crdt
// for crdt operations in the mesh
package automerge
import (
@ -17,18 +19,28 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// CrdtMeshManager manages nodes in the crdt mesh
// CrdtMeshManager manage the CRDT datastore
type CrdtMeshManager struct {
MeshId string
IfName string
Client *wgctrl.Client
doc *automerge.Doc
LastHash automerge.ChangeHash
conf *conf.WgConfiguration
cache *MeshCrdt
// MeshID of the mesh the datastore represents
MeshId string
// IfName: corresponding ifName
IfName string
// Client: corresponding wireguard control client
Client *wgctrl.Client
// doc: autommerge document
doc *automerge.Doc
// LastHash: last hash that the changes were made to
LastHash automerge.ChangeHash
// conf: WireGuard configuration
conf *conf.WgConfiguration
// cache: stored cache of the list automerge document
// so that the store does not have to be repopulated each time
cache *MeshCrdt
// lastCachehash: hash of when the document was last changed
lastCacheHash automerge.ChangeHash
}
// AddNode as a node to the datastore
func (c *CrdtMeshManager) AddNode(node mesh.MeshNode) {
crdt, ok := node.(*MeshNodeCrdt)
@ -47,6 +59,7 @@ func (c *CrdtMeshManager) AddNode(node mesh.MeshNode) {
}
}
// isPeer: returns true if the given node has type peer
func (c *CrdtMeshManager) isPeer(nodeId string) bool {
node, err := c.doc.Path("nodes").Map().Get(nodeId)
@ -64,7 +77,8 @@ func (c *CrdtMeshManager) isPeer(nodeId string) bool {
}
// isAlive: checks that the node's configuration has been updated
// since the rquired keep alive time
// since the rquired keep alive time. Depracated no longer works
// due to changes in approach
func (c *CrdtMeshManager) isAlive(nodeId string) bool {
node, err := c.doc.Path("nodes").Map().Get(nodeId)
@ -78,10 +92,11 @@ func (c *CrdtMeshManager) isAlive(nodeId string) bool {
return false
}
return true
// return (time.Now().Unix() - keepAliveTime) < int64(c.conf.DeadTime)
return true
}
// GetPeers: get all the peers in the mesh
func (c *CrdtMeshManager) GetPeers() []string {
keys, _ := c.doc.Path("nodes").Map().Keys()
@ -92,7 +107,7 @@ func (c *CrdtMeshManager) GetPeers() []string {
return keys
}
// GetMesh(): Converts the document into a struct
// GetMesh: Converts the document into a mesh network
func (c *CrdtMeshManager) GetMesh() (mesh.MeshSnapshot, error) {
changes, err := c.doc.Changes(c.lastCacheHash)
@ -114,7 +129,7 @@ func (c *CrdtMeshManager) GetMesh() (mesh.MeshSnapshot, error) {
return c.cache, nil
}
// GetMeshId returns the meshid of the mesh
// GetMeshId: returns the meshid of the mesh
func (c *CrdtMeshManager) GetMeshId() string {
return c.MeshId
}
@ -135,6 +150,8 @@ func (c *CrdtMeshManager) Load(bytes []byte) error {
return nil
}
// NewCrdtNodeManagerParams: params to instantiate a new automerge
// datastore
type NewCrdtNodeMangerParams struct {
MeshId string
DevName string
@ -143,7 +160,7 @@ type NewCrdtNodeMangerParams struct {
Client *wgctrl.Client
}
// NewCrdtNodeManager: Create a new crdt node manager
// NewCrdtNodeManager: Create a new automerge crdt data store
func NewCrdtNodeManager(params *NewCrdtNodeMangerParams) (*CrdtMeshManager, error) {
var manager CrdtMeshManager
manager.MeshId = params.MeshId
@ -155,12 +172,13 @@ func NewCrdtNodeManager(params *NewCrdtNodeMangerParams) (*CrdtMeshManager, erro
return &manager, nil
}
// NodeExists: returns true if the node exists. Returns false
// NodeExists: returns true if the node exists other returns false
func (m *CrdtMeshManager) NodeExists(key string) bool {
node, err := m.doc.Path("nodes").Map().Get(key)
return node.Kind() == automerge.KindMap && err == nil
}
// GetNode: gets a node from the mesh network.
func (m *CrdtMeshManager) GetNode(endpoint string) (mesh.MeshNode, error) {
node, err := m.doc.Path("nodes").Map().Get(endpoint)
@ -181,10 +199,12 @@ func (m *CrdtMeshManager) GetNode(endpoint string) (mesh.MeshNode, error) {
return meshNode, nil
}
// Length: returns the number of nodes in the store
func (m *CrdtMeshManager) Length() int {
return m.doc.Path("nodes").Map().Len()
}
// GetDevice: get the underlying WireGuard device
func (m *CrdtMeshManager) GetDevice() (*wgtypes.Device, error) {
dev, err := m.Client.Device(m.IfName)
@ -195,7 +215,7 @@ func (m *CrdtMeshManager) GetDevice() (*wgtypes.Device, error) {
return dev, nil
}
// HasChanges returns true if we have changes since the last time we synced
// HasChanges: returns true if there are changes since last time synchronised
func (m *CrdtMeshManager) HasChanges() bool {
changes, err := m.doc.Changes(m.LastHash)
@ -209,6 +229,7 @@ func (m *CrdtMeshManager) HasChanges() bool {
return len(changes) > 0
}
// SaveChanges: save changes to the datastore
func (m *CrdtMeshManager) SaveChanges() {
hashes := m.doc.Heads()
hash := hashes[len(hashes)-1]
@ -217,6 +238,7 @@ func (m *CrdtMeshManager) SaveChanges() {
m.LastHash = hash
}
// UpdateTimeStamp: updates the timestamp of the document
func (m *CrdtMeshManager) UpdateTimeStamp(nodeId string) error {
node, err := m.doc.Path("nodes").Map().Get(nodeId)
@ -237,6 +259,7 @@ func (m *CrdtMeshManager) UpdateTimeStamp(nodeId string) error {
return err
}
// SetDescription: set the description of the given node
func (m *CrdtMeshManager) SetDescription(nodeId string, description string) error {
node, err := m.doc.Path("nodes").Map().Get(nodeId)
@ -257,6 +280,7 @@ func (m *CrdtMeshManager) SetDescription(nodeId string, description string) erro
return err
}
// SetAlias: set the alias of the given node
func (m *CrdtMeshManager) SetAlias(nodeId string, alias string) error {
node, err := m.doc.Path("nodes").Map().Get(nodeId)
@ -277,6 +301,7 @@ func (m *CrdtMeshManager) SetAlias(nodeId string, alias string) error {
return err
}
// AddService: add a service to the given node
func (m *CrdtMeshManager) AddService(nodeId, key, value string) error {
node, err := m.doc.Path("nodes").Map().Get(nodeId)
@ -298,6 +323,7 @@ func (m *CrdtMeshManager) AddService(nodeId, key, value string) error {
return err
}
// RemoveService: remove a service from a node
func (m *CrdtMeshManager) RemoveService(nodeId, key string) error {
node, err := m.doc.Path("nodes").Map().Get(nodeId)
@ -378,6 +404,7 @@ func (m *CrdtMeshManager) AddRoutes(nodeId string, routes ...mesh.Route) error {
return nil
}
// getRoutes: get the routes that the given node is directly advertising
func (m *CrdtMeshManager) getRoutes(nodeId string) ([]Route, error) {
nodeVal, err := m.doc.Path("nodes").Map().Get(nodeId)
@ -404,6 +431,8 @@ func (m *CrdtMeshManager) getRoutes(nodeId string) ([]Route, error) {
return lib.MapValues(routes), err
}
// GetRoutes: get all the routes that the node can see. The routes that the node
// can say may not be direct but cann also be indirect
func (m *CrdtMeshManager) GetRoutes(targetNode string) (map[string]mesh.Route, error) {
node, err := m.GetNode(targetNode)
@ -447,12 +476,13 @@ func (m *CrdtMeshManager) GetRoutes(targetNode string) (map[string]mesh.Route, e
return routes, nil
}
// RemoveNode: removes a node from the datastore
func (m *CrdtMeshManager) RemoveNode(nodeId string) error {
err := m.doc.Path("nodes").Map().Delete(nodeId)
return err
}
// DeleteRoutes deletes the specified routes
// RemoveRoutes: withdraw all the routes the nodeID is advertising
func (m *CrdtMeshManager) RemoveRoutes(nodeId string, routes ...mesh.Route) error {
nodeVal, err := m.doc.Path("nodes").Map().Get(nodeId)
@ -486,30 +516,37 @@ func (m *CrdtMeshManager) GetConfiguration() *conf.WgConfiguration {
func (m *CrdtMeshManager) Mark(nodeId string) {
}
// GetSyncer: get the bi-directionally syncer to synchronise the document
func (m *CrdtMeshManager) GetSyncer() mesh.MeshSyncer {
return NewAutomergeSync(m)
}
// Prune: prune all dead nodes
func (m *CrdtMeshManager) Prune() error {
return nil
}
// Compare: compare two mesh node for equality
func (m1 *MeshNodeCrdt) Compare(m2 *MeshNodeCrdt) int {
return strings.Compare(m1.PublicKey, m2.PublicKey)
}
// GetHostEndpoint: get the ctrl endpoint of the host
func (m *MeshNodeCrdt) GetHostEndpoint() string {
return m.HostEndpoint
}
// GetPublicKey: get the public key of the node
func (m *MeshNodeCrdt) GetPublicKey() (wgtypes.Key, error) {
return wgtypes.ParseKey(m.PublicKey)
}
// GetWgEndpoint: get the outer WireGuard endpoint
func (m *MeshNodeCrdt) GetWgEndpoint() string {
return m.WgEndpoint
}
// GetWgHost: get the WireGuard IP address of the host
func (m *MeshNodeCrdt) GetWgHost() *net.IPNet {
_, ipnet, err := net.ParseCIDR(m.WgHost)
@ -520,10 +557,12 @@ func (m *MeshNodeCrdt) GetWgHost() *net.IPNet {
return ipnet
}
// GetTimeStamp: get timestamp if when the node was last updated
func (m *MeshNodeCrdt) GetTimeStamp() int64 {
return m.Timestamp
}
// GetRoutes: get all the routes advertised by the node
func (m *MeshNodeCrdt) GetRoutes() []mesh.Route {
return lib.Map(lib.MapValues(m.Routes), func(r Route) mesh.Route {
return &Route{
@ -533,10 +572,12 @@ func (m *MeshNodeCrdt) GetRoutes() []mesh.Route {
})
}
// GetDescription: get the description of the node
func (m *MeshNodeCrdt) GetDescription() string {
return m.Description
}
// GetIdentifier: get the iderntifier section of the ipv6 address
func (m *MeshNodeCrdt) GetIdentifier() string {
ipv6 := m.WgHost[:len(m.WgHost)-4]
@ -545,10 +586,12 @@ func (m *MeshNodeCrdt) GetIdentifier() string {
return strings.Join(constituents, ":")
}
// GetAlias: get the alias of the node
func (m *MeshNodeCrdt) GetAlias() string {
return m.Alias
}
// GetServices: get all the services the node is advertising
func (m *MeshNodeCrdt) GetServices() map[string]string {
services := make(map[string]string)
@ -565,6 +608,7 @@ func (n *MeshNodeCrdt) GetType() conf.NodeType {
return conf.NodeType(n.Type)
}
// GetNodes: get all the nodes in the network
func (m *MeshCrdt) GetNodes() map[string]mesh.MeshNode {
nodes := make(map[string]mesh.MeshNode)
@ -586,15 +630,18 @@ func (m *MeshCrdt) GetNodes() map[string]mesh.MeshNode {
return nodes
}
// GetDestination: get destination of the route
func (r *Route) GetDestination() *net.IPNet {
_, ipnet, _ := net.ParseCIDR(r.Destination)
return ipnet
}
// GetHopCount: get the number of hops to the destination
func (r *Route) GetHopCount() int {
return len(r.Path)
}
// GetPath: get the total path which includes the number of hops
func (r *Route) GetPath() []string {
return r.Path
}

View File

@ -1,3 +1,5 @@
// automerge: automerge is a CRDT library. Defines a CRDT
// datastore and methods to resolve conflicts
package automerge
import (
@ -5,11 +7,18 @@ import (
logging "github.com/tim-beatham/smegmesh/pkg/log"
)
// AutomergeSync: defines a synchroniser to bi-directionally synchronise the
// two states
type AutomergeSync struct {
state *automerge.SyncState
// state: the automerge sync state to use
state *automerge.SyncState
// manager: the corresponding data store that we are merging
manager *CrdtMeshManager
}
// GenerateMessage: geenrate a new automerge message to synchronise
// returns a byte of the message and a boolean of whether or not there
// are more messages in the sequence
func (a *AutomergeSync) GenerateMessage() ([]byte, bool) {
msg, valid := a.state.GenerateMessage()
@ -20,6 +29,8 @@ func (a *AutomergeSync) GenerateMessage() ([]byte, bool) {
return msg.Bytes(), true
}
// RecvMessage: receive an automerge message to merge in the datastore
// returns an error if unsuccessful
func (a *AutomergeSync) RecvMessage(msg []byte) error {
_, err := a.state.ReceiveMessage(msg)
@ -30,11 +41,13 @@ func (a *AutomergeSync) RecvMessage(msg []byte) error {
return nil
}
// Complete: complete the synchronisation process
func (a *AutomergeSync) Complete() {
logging.Log.WriteInfof("Sync Completed")
logging.Log.WriteInfof("sync completed")
a.manager.SaveChanges()
}
// NewAutomergeSync: instantiates a new automerge syncer
func NewAutomergeSync(manager *CrdtMeshManager) *AutomergeSync {
return &AutomergeSync{
state: automerge.NewSyncState(manager.doc),

View File

@ -83,7 +83,6 @@ func TestAddNodeAddRoute(t *testing.T) {
testParams.manager.AddNode(testNode)
testParams.manager.AddRoutes(pubKey.String(), &mesh.RouteStub{
Destination: destination,
HopCount: 0,
Path: make([]string, 0),
})
updatedNode, err := testParams.manager.GetNode(pubKey.String())
@ -297,7 +296,6 @@ func TestAddRoutesNodeDoesNotExist(t *testing.T) {
err := testParams.manager.AddRoutes("AAAAA", &mesh.RouteStub{
Destination: destination,
HopCount: 0,
Path: make([]string, 0),
})

View File

@ -8,8 +8,11 @@ import (
"github.com/tim-beatham/smegmesh/pkg/mesh"
)
// CrdtProviderFactory: abstracts the instantiation of an automerge
// datastore
type CrdtProviderFactory struct{}
// CreateMesh: create a new mesh datastore
func (f *CrdtProviderFactory) CreateMesh(params *mesh.MeshProviderFactoryParams) (mesh.MeshProvider, error) {
return NewCrdtNodeManager(&NewCrdtNodeMangerParams{
MeshId: params.MeshId,
@ -19,11 +22,12 @@ func (f *CrdtProviderFactory) CreateMesh(params *mesh.MeshProviderFactoryParams)
})
}
// MeshNodeFactory: abstracts the instnatiation of a node
type MeshNodeFactory struct {
Config conf.DaemonConfiguration
}
// Build builds the mesh node that represents the host machine to add
// Build: builds the mesh node that represents the host machine to add
// to the mesh
func (f *MeshNodeFactory) Build(params *mesh.MeshNodeFactoryParams) mesh.MeshNode {
hostName := f.getAddress(params)
@ -48,7 +52,7 @@ func (f *MeshNodeFactory) Build(params *mesh.MeshNodeFactoryParams) mesh.MeshNod
}
}
// getAddress returns the routable address of the machine.
// getAddress: returns the routable address of the machine.
func (f *MeshNodeFactory) getAddress(params *mesh.MeshNodeFactoryParams) string {
var hostName string = ""
@ -59,7 +63,7 @@ func (f *MeshNodeFactory) getAddress(params *mesh.MeshNodeFactoryParams) string
} else {
ipFunc := lib.GetPublicIP
if *params.MeshConfig.IPDiscovery == conf.DNS_IP_DISCOVERY {
if *params.MeshConfig.IPDiscovery == conf.OUTGOING_IP_DISCOVERY {
ipFunc = lib.GetOutboundIP
}

View File

@ -6,10 +6,12 @@ import (
"strings"
)
// CmdRunner: run cmd commands when instantiating a network
type CmdRunner interface {
RunCommands(commands ...string) error
}
// UnixCmdRunner: Run UNIX commands
type UnixCmdRunner struct{}
// RunCommand: runs the unix command. It splits the command into fields
@ -20,6 +22,7 @@ func RunCommand(cmd string) error {
return c.Run()
}
// RunCommands: run a series of commands
func (l *UnixCmdRunner) RunCommands(commands ...string) error {
for _, cmd := range commands {
err := RunCommand(cmd)

View File

@ -8,14 +8,7 @@ import (
"gopkg.in/yaml.v3"
)
type WgMeshConfigurationError struct {
msg string
}
func (m *WgMeshConfigurationError) Error() string {
return m.msg
}
// NodeType types of the node either peer or client
type NodeType string
const (
@ -23,11 +16,23 @@ const (
CLIENT_ROLE NodeType = "client"
)
// IPDiscovery: what IPDiscovery service to use
type IPDiscovery string
const (
// Public IP use an IP service to discover your IP
PUBLIC_IP_DISCOVERY IPDiscovery = "public"
DNS_IP_DISCOVERY IPDiscovery = "dns"
// Outgonig: Use your labelled packet IP
OUTGOING_IP_DISCOVERY IPDiscovery = "outgoing"
)
// Loglevel: what log level to use either error info or warning
type LogLevel string
const (
ERROR LogLevel = "error"
WARNING LogLevel = "warning"
INFO LogLevel = "info"
)
// WgConfiguration contains per-mesh WireGuard configuration. Contains poitner types only so we can
@ -35,7 +40,7 @@ const (
type WgConfiguration struct {
// IPDIscovery: how to discover your IP if not specified. Use your outgoing IP or use a public
// service for IPDiscoverability
IPDiscovery *IPDiscovery `yaml:"ipDiscovery" validate:"required,eq=public|eq=dns"`
IPDiscovery *IPDiscovery `yaml:"ipDiscovery" validate:"required,eq=public|eq=outgoing"`
// AdvertiseRoutes: specifies whether the node can act as a router routing packets between meshes
AdvertiseRoutes *bool `yaml:"advertiseRoute" validate:"required"`
// AdvertiseDefaultRoute: specifies whether or not this route should advertise a default route
@ -76,24 +81,26 @@ type DaemonConfiguration struct {
Profile bool `yaml:"profile"`
// StubWg whether or not to stub the WireGuard types
StubWg bool `yaml:"stubWg"`
// SyncTime specifies how long the minimum time should be between synchronisation
SyncTime int `yaml:"syncTime" validate:"required,gte=1"`
// PullTime specifies the interval between checking for configuration changes
PullTime int `yaml:"pullTime" validate:"gte=0"`
// HeartBeat: number of seconds before the leader of the mesh sends an update to
// SyncInterval specifies how long the minimum time should be between synchronisation
SyncInterval int `yaml:"syncInterval" validate:"required,gte=1"`
// PullInterval specifies the interval between checking for configuration changes
PullInterval int `yaml:"pullInterval" validate:"gte=0"`
// Heartbeat: number of seconds before the leader of the mesh sends an update to
// send to every member in the mesh
HeartBeat int `yaml:"heartBeatTime" validate:"required,gte=1"`
Heartbeat int `yaml:"heartbeatInterval" validate:"required,gte=1"`
// ClusterSize specifies how many neighbours you should synchronise with per round
ClusterSize int `yaml:"clusterSize" validate:"gte=1"`
// InterClusterChance specifies the probabilityof inter-cluster communication in a sync round
InterClusterChance float64 `yaml:"interClusterChance" validate:"gt=0"`
// BranchRate specifies the number of nodes to synchronise with when a node has
// Branch specifies the number of nodes to synchronise with when a node has
// new changes to send to the mesh
BranchRate int `yaml:"branchRate" validate:"required,gte=1"`
Branch int `yaml:"branch" validate:"required,gte=1"`
// InfectionCount: number of time to sync before an update can no longer be 'caught'
InfectionCount int `yaml:"infectionCount" validate:"required,gte=1"`
// BaseConfiguration base WireGuard configuration to use, this is used when none is provided
BaseConfiguration WgConfiguration `yaml:"baseConfiguration" validate:"required"`
// LogLevel specifies the log level to output, defaults is warning
LogLevel LogLevel `yaml:"logLevel" validate:"eq=info|eq=warning|eq=error"`
}
// ValdiateMeshConfiguration: validates the mesh configuration
@ -121,9 +128,18 @@ func ValidateMeshConfiguration(conf *WgConfiguration) error {
}
// ValidateDaemonConfiguration: validates the dameon configuration that is used.
func ValidateDaemonConfiguration(c *DaemonConfiguration) error {
func ValidateDaemonConfiguration(conf *DaemonConfiguration) error {
if conf.BaseConfiguration.KeepAliveWg == nil {
var keepAlive int = 0
conf.BaseConfiguration.KeepAliveWg = &keepAlive
}
if conf.LogLevel == "" {
conf.LogLevel = WARNING
}
validate := validator.New(validator.WithRequiredStructEnabled())
err := validate.Struct(c)
err := validate.Struct(conf)
return err
}
@ -143,11 +159,6 @@ func ParseDaemonConfiguration(filePath string) (*DaemonConfiguration, error) {
return nil, err
}
if conf.BaseConfiguration.KeepAliveWg == nil {
var keepAlive int = 0
conf.BaseConfiguration.KeepAliveWg = &keepAlive
}
return &conf, ValidateDaemonConfiguration(&conf)
}

View File

@ -21,12 +21,12 @@ func getExampleConfiguration() *DaemonConfiguration {
Timeout: 5,
Profile: false,
StubWg: false,
SyncTime: 2,
HeartBeat: 2,
SyncInterval: 2,
Heartbeat: 2,
ClusterSize: 64,
InterClusterChance: 0.15,
BranchRate: 3,
PullTime: 0,
Branch: 3,
PullInterval: 0,
InfectionCount: 2,
BaseConfiguration: WgConfiguration{
IPDiscovery: &discovery,
@ -154,7 +154,7 @@ func TestRoleTypeNotSpecified(t *testing.T) {
func TestBranchRateZero(t *testing.T) {
conf := getExampleConfiguration()
conf.BranchRate = 0
conf.Branch = 0
err := ValidateDaemonConfiguration(conf)
@ -165,7 +165,7 @@ func TestBranchRateZero(t *testing.T) {
func TestsyncTimeZero(t *testing.T) {
conf := getExampleConfiguration()
conf.SyncTime = 0
conf.SyncInterval = 0
err := ValidateDaemonConfiguration(conf)
@ -176,7 +176,7 @@ func TestsyncTimeZero(t *testing.T) {
func TestKeepAliveTimeZero(t *testing.T) {
conf := getExampleConfiguration()
conf.HeartBeat = 0
conf.Heartbeat = 0
err := ValidateDaemonConfiguration(conf)
if err == nil {
@ -218,7 +218,7 @@ func TestInfectionCountOne(t *testing.T) {
func TestPullTimeNegative(t *testing.T) {
conf := getExampleConfiguration()
conf.PullTime = -1
conf.PullInterval = -1
err := ValidateDaemonConfiguration(conf)

View File

@ -7,17 +7,21 @@ import (
"slices"
)
// ConnCluster splits nodes into clusters where nodes in a cluster communicate
// ConnCluster: splits nodes into clusters where nodes in a cluster communicate
// frequently and nodes outside of a cluster communicate infrequently
type ConnCluster interface {
// Getneighbours: get neighbours of the cluster the node is in
GetNeighbours(global []string, selfId string) []string
// GetInterCluster: get the cluster to communicate with
GetInterCluster(global []string, selfId string) string
}
// ConnnClusterImpl: implementation of the connection cluster
type ConnClusterImpl struct {
clusterSize int
}
// perform binary search to attain a size of a group
func binarySearch(global []string, selfId string, groupSize int) (int, int) {
slices.Sort(global)
@ -39,7 +43,7 @@ func binarySearch(global []string, selfId string, groupSize int) (int, int) {
return lower, int(math.Min(float64(lower+groupSize), float64(len(global))))
}
// GetNeighbours return the neighbours 'nearest' to you. In this implementation the
// GetNeighbours: return the neighbours 'nearest' to you. In this implementation the
// neighbours aren't actually the ones nearest to you but just the ones nearest
// to you alphabetically. Perform binary search to get the total group
func (i *ConnClusterImpl) GetNeighbours(global []string, selfId string) []string {
@ -50,7 +54,7 @@ func (i *ConnClusterImpl) GetNeighbours(global []string, selfId string) []string
return global[lower:higher]
}
// GetInterCluster get nodes not in your cluster. Every round there is a given chance
// GetInterCluster: get nodes not in your cluster. Every round there is a given chance
// you will communicate with a random node that is not in your cluster.
func (i *ConnClusterImpl) GetInterCluster(global []string, selfId string) string {
// Doesn't matter if not in it. Get index of where the node 'should' be
@ -65,6 +69,7 @@ func (i *ConnClusterImpl) GetInterCluster(global []string, selfId string) string
return global[neighbourIndex]
}
// NewConnCluster: instantiate a new connection cluster of a given group size.
func NewConnCluster(clusterSize int) (ConnCluster, error) {
log2Cluster := math.Log2(float64(clusterSize))

View File

@ -18,6 +18,7 @@ type PeerConnection interface {
GetClient() (*grpc.ClientConn, error)
}
// PeerConenctionFactory: create a new connection to a peer
type PeerConnectionFactory = func(clientConfig *tls.Config, server string) (PeerConnection, error)
// WgCtrlConnection implements PeerConnection.

View File

@ -17,9 +17,12 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// Route: represents a route within the data store
type Route struct {
// Destination the route is advertising
Destination string
Path []string
// Path to the destination
Path []string
}
// GetDestination implements mesh.Route.
@ -248,7 +251,8 @@ func (m *TwoPhaseStoreMeshManager) SaveChanges() {
m.LastClock = clockValue
}
// UpdateTimeStamp: update the timestamp of the given node
// UpdateTimeStamp: update the timestamp of the given node, causes a configuration refresh if the node
// is the leader causing all nodes to update their vector clocks
func (m *TwoPhaseStoreMeshManager) UpdateTimeStamp(nodeId string) error {
if !m.store.Contains(nodeId) {
return fmt.Errorf("datastore: %s does not exist in the mesh", nodeId)
@ -264,7 +268,7 @@ func (m *TwoPhaseStoreMeshManager) UpdateTimeStamp(nodeId string) error {
peerToUpdate := peers[0]
if uint64(time.Now().Unix())-m.store.Clock.GetTimestamp(peerToUpdate) > 3*uint64(m.DaemonConf.HeartBeat) {
if uint64(time.Now().Unix())-m.store.Clock.GetTimestamp(peerToUpdate) > 3*uint64(m.DaemonConf.Heartbeat) {
m.store.Mark(peerToUpdate)
if len(peers) < 2 {
@ -312,6 +316,8 @@ func (m *TwoPhaseStoreMeshManager) AddRoutes(nodeId string, routes ...mesh.Route
}
}
// Only add nodes on changes. Otherwise the node will advertise new
// information whenever they get new routes
if changes {
m.store.Put(nodeId, node)
}
@ -319,7 +325,7 @@ func (m *TwoPhaseStoreMeshManager) AddRoutes(nodeId string, routes ...mesh.Route
return nil
}
// DeleteRoutes: deletes the routes from the node
// RemoveRoute: deletes the routes from the given node
func (m *TwoPhaseStoreMeshManager) RemoveRoutes(nodeId string, routes ...mesh.Route) error {
if !m.store.Contains(nodeId) {
return fmt.Errorf("datastore: %s does not exist in the mesh", nodeId)
@ -335,6 +341,7 @@ func (m *TwoPhaseStoreMeshManager) RemoveRoutes(nodeId string, routes ...mesh.Ro
for _, route := range routes {
changes = true
logging.Log.WriteInfof("deleting: %s", route.GetDestination().String())
delete(node.Routes, route.GetDestination().String())
}
@ -345,12 +352,12 @@ func (m *TwoPhaseStoreMeshManager) RemoveRoutes(nodeId string, routes ...mesh.Ro
return nil
}
// GetSyncer: returns the automerge syncer for sync
// GetSyncer: returns the bi-directionally synchroniser to merge documents
func (m *TwoPhaseStoreMeshManager) GetSyncer() mesh.MeshSyncer {
return NewTwoPhaseSyncer(m)
}
// GetNode get a particular not within the mesh
// GetNode: get a particular not within the mesh network
func (m *TwoPhaseStoreMeshManager) GetNode(nodeId string) (mesh.MeshNode, error) {
if !m.store.Contains(nodeId) {
return nil, fmt.Errorf("datastore: %s does not exist in the mesh", nodeId)
@ -378,7 +385,7 @@ func (m *TwoPhaseStoreMeshManager) SetDescription(nodeId string, description str
return nil
}
// SetAlias: set the alias of the nodeId
// SetAlias: set the alias of the given node
func (m *TwoPhaseStoreMeshManager) SetAlias(nodeId string, alias string) error {
if !m.store.Contains(nodeId) {
return fmt.Errorf("datastore: %s does not exist in the mesh", nodeId)
@ -391,7 +398,7 @@ func (m *TwoPhaseStoreMeshManager) SetAlias(nodeId string, alias string) error {
return nil
}
// AddService: adds the service to the given node
// AddService: adds a service to the given node
func (m *TwoPhaseStoreMeshManager) AddService(nodeId string, key string, value string) error {
if !m.store.Contains(nodeId) {
return fmt.Errorf("datastore: %s does not exist in the mesh", nodeId)
@ -403,7 +410,7 @@ func (m *TwoPhaseStoreMeshManager) AddService(nodeId string, key string, value s
return nil
}
// RemoveService: removes the service form the node. throws an error if the service does not exist
// RemoveService: removes the service form a node, throws an error if the service does not exist
func (m *TwoPhaseStoreMeshManager) RemoveService(nodeId string, key string) error {
if !m.store.Contains(nodeId) {
return fmt.Errorf("datastore: %s does not exist in the mesh", nodeId)
@ -420,7 +427,8 @@ func (m *TwoPhaseStoreMeshManager) RemoveService(nodeId string, key string) erro
return nil
}
// Prune: prunes all nodes that have not updated their timestamp in
// Prune: prunes all nodes that have not updated their vector clock in a given amount
// of time
func (m *TwoPhaseStoreMeshManager) Prune() error {
m.store.Prune()
return nil
@ -449,6 +457,7 @@ func (m *TwoPhaseStoreMeshManager) GetPeers() []string {
})
}
// getRoutes: get all routes the target node is advertising
func (m *TwoPhaseStoreMeshManager) getRoutes(targetNode string) (map[string]Route, error) {
if !m.store.Contains(targetNode) {
return nil, fmt.Errorf("getRoute: cannot get route %s does not exist", targetNode)
@ -458,7 +467,8 @@ func (m *TwoPhaseStoreMeshManager) getRoutes(targetNode string) (map[string]Rout
return node.Routes, nil
}
// GetRoutes(): Get all unique routes. Where the route with the least hop count is chosen
// GetRoutes: Get all unique routes the target node is advertising.
// on conflicts the route with the least hop count is chosen
func (m *TwoPhaseStoreMeshManager) GetRoutes(targetNode string) (map[string]mesh.Route, error) {
node, err := m.GetNode(targetNode)
@ -502,7 +512,7 @@ func (m *TwoPhaseStoreMeshManager) GetRoutes(targetNode string) (map[string]mesh
return routes, nil
}
// RemoveNode(): remove the node from the mesh
// RemoveNode: remove the node from the mesh
func (m *TwoPhaseStoreMeshManager) RemoveNode(nodeId string) error {
if !m.store.Contains(nodeId) {
return fmt.Errorf("datastore: %s does not exist in the mesh", nodeId)
@ -512,7 +522,8 @@ func (m *TwoPhaseStoreMeshManager) RemoveNode(nodeId string) error {
return nil
}
// GetConfiguration implements mesh.MeshProvider.
// GetConfiguration gets the WireGuard configuration to use for this
// network
func (m *TwoPhaseStoreMeshManager) GetConfiguration() *conf.WgConfiguration {
return m.Conf
}

View File

@ -21,7 +21,7 @@ func setUpTests() *TestParams {
advertiseRoutes := false
advertiseDefaultRoute := false
role := conf.PEER_ROLE
discovery := conf.DNS_IP_DISCOVERY
discovery := conf.OUTGOING_IP_DISCOVERY
factory := &TwoPhaseMapFactory{
Config: &conf.DaemonConfiguration{
@ -32,11 +32,11 @@ func setUpTests() *TestParams {
GrpcPort: 0,
Timeout: 20,
Profile: false,
SyncTime: 2,
HeartBeat: 10,
SyncInterval: 2,
Heartbeat: 10,
ClusterSize: 32,
InterClusterChance: 0.15,
BranchRate: 3,
Branch: 3,
InfectionCount: 3,
BaseConfiguration: conf.WgConfiguration{
IPDiscovery: &discovery,
@ -215,7 +215,6 @@ func TestAddRoutesAddsARouteToTheGivenMesh(t *testing.T) {
testParams.manager.AddRoutes(testParams.publicKey.String(), &mesh.RouteStub{
Destination: destination,
HopCount: 0,
Path: make([]string, 0),
})
@ -238,7 +237,6 @@ func TestRemoveRoutesWithdrawsRoutesFromTheMesh(t *testing.T) {
_, destination, _ := net.ParseCIDR("0353:1da7:7f33:acc0:7a3f:6e55:912b:bc1f/64")
route := &mesh.RouteStub{
Destination: destination,
HopCount: 0,
Path: make([]string, 0),
}

View File

@ -9,10 +9,13 @@ import (
"github.com/tim-beatham/smegmesh/pkg/mesh"
)
// TwoPhaseMapFactory: instantiate a new twophasemap
// datastore
type TwoPhaseMapFactory struct {
Config *conf.DaemonConfiguration
}
// CreateMesh: create a new mesh network
func (f *TwoPhaseMapFactory) CreateMesh(params *mesh.MeshProviderFactoryParams) (mesh.MeshProvider, error) {
return &TwoPhaseStoreMeshManager{
MeshId: params.MeshId,
@ -24,14 +27,16 @@ func (f *TwoPhaseMapFactory) CreateMesh(params *mesh.MeshProviderFactoryParams)
h := fnv.New64a()
h.Write([]byte(s))
return h.Sum64()
}, uint64(3*f.Config.HeartBeat)),
}, uint64(3*f.Config.Heartbeat)),
}, nil
}
// MeshNodeFactory: create a new node in the mesh network
type MeshNodeFactory struct {
Config conf.DaemonConfiguration
}
// Build: build a new mesh network
func (f *MeshNodeFactory) Build(params *mesh.MeshNodeFactoryParams) mesh.MeshNode {
hostName := f.getAddress(params)
@ -66,7 +71,7 @@ func (f *MeshNodeFactory) getAddress(params *mesh.MeshNodeFactoryParams) string
} else {
ipFunc := lib.GetPublicIP
if *params.MeshConfig.IPDiscovery == conf.DNS_IP_DISCOVERY {
if *params.MeshConfig.IPDiscovery == conf.OUTGOING_IP_DISCOVERY {
ipFunc = lib.GetOutboundIP
}

View File

@ -6,6 +6,7 @@ import (
"sync"
)
// Bucket: bucket represents a value in the grow only map
type Bucket[D any] struct {
Vector uint64
Contents D
@ -19,6 +20,7 @@ type GMap[K cmp.Ordered, D any] struct {
clock *VectorClock[K]
}
// Put: put a new entry in the grow-only-map
func (g *GMap[K, D]) Put(key K, value D) {
g.lock.Lock()
@ -32,6 +34,8 @@ func (g *GMap[K, D]) Put(key K, value D) {
g.lock.Unlock()
}
// Contains: returns whether or not the key is contained
// in the g-map
func (g *GMap[K, D]) Contains(key K) bool {
return g.contains(g.clock.hashFunc(key))
}
@ -64,6 +68,7 @@ func (g *GMap[K, D]) get(key uint64) Bucket[D] {
return bucket
}
// Get: get the value associated with the given key
func (g *GMap[K, D]) Get(key K) D {
if !g.Contains(key) {
var def D
@ -73,6 +78,8 @@ func (g *GMap[K, D]) Get(key K) D {
return g.get(g.clock.hashFunc(key)).Contents
}
// Mark: marks the node, this means the status of the node
// is an undefined state
func (g *GMap[K, D]) Mark(key K) {
if !g.Contains(key) {
return
@ -85,7 +92,7 @@ func (g *GMap[K, D]) Mark(key K) {
g.lock.Unlock()
}
// IsMarked: returns true if the node is marked
// IsMarked: returns true if the node is marked (in an undefined state)
func (g *GMap[K, D]) IsMarked(key K) bool {
marked := false
@ -101,6 +108,7 @@ func (g *GMap[K, D]) IsMarked(key K) bool {
return marked
}
// Keys: return all the keys in the grow-only map
func (g *GMap[K, D]) Keys() []uint64 {
g.lock.RLock()
@ -116,6 +124,7 @@ func (g *GMap[K, D]) Keys() []uint64 {
return contents
}
// Save: saves the grow only map
func (g *GMap[K, D]) Save() map[uint64]Bucket[D] {
buckets := make(map[uint64]Bucket[D])
g.lock.RLock()
@ -128,6 +137,7 @@ func (g *GMap[K, D]) Save() map[uint64]Bucket[D] {
return buckets
}
// SaveWithKeys: get all the values corresponding with the provided keys
func (g *GMap[K, D]) SaveWithKeys(keys []uint64) map[uint64]Bucket[D] {
buckets := make(map[uint64]Bucket[D])
g.lock.RLock()
@ -140,6 +150,7 @@ func (g *GMap[K, D]) SaveWithKeys(keys []uint64) map[uint64]Bucket[D] {
return buckets
}
// GetClock: get all the vector clocks in the g_map
func (g *GMap[K, D]) GetClock() map[uint64]uint64 {
clock := make(map[uint64]uint64)
g.lock.RLock()
@ -152,6 +163,7 @@ func (g *GMap[K, D]) GetClock() map[uint64]uint64 {
return clock
}
// GetHash: get the hash of the g_map representing its state
func (g *GMap[K, D]) GetHash() uint64 {
hash := uint64(0)
@ -165,6 +177,7 @@ func (g *GMap[K, D]) GetHash() uint64 {
return hash
}
// Prune: prune all stale entries
func (g *GMap[K, D]) Prune() {
stale := g.clock.getStale()
g.lock.Lock()

View File

@ -6,6 +6,7 @@ import (
"github.com/tim-beatham/smegmesh/pkg/lib"
)
// TwoPhaseMap: comprises of two grow-only maps
type TwoPhaseMap[K cmp.Ordered, D any] struct {
addMap *GMap[K, D]
removeMap *GMap[K, bool]
@ -23,7 +24,7 @@ func (m *TwoPhaseMap[K, D]) Contains(key K) bool {
return m.contains(m.Clock.hashFunc(key))
}
// Contains checks whether the value exists in the map
// contains: checks whether the key exists in the map
func (m *TwoPhaseMap[K, D]) contains(key uint64) bool {
if !m.addMap.contains(key) {
return false
@ -40,6 +41,7 @@ func (m *TwoPhaseMap[K, D]) contains(key uint64) bool {
return addValue.Vector >= removeValue.Vector
}
// Get: get the value corresponding with the given key
func (m *TwoPhaseMap[K, D]) Get(key K) D {
var result D
@ -60,18 +62,19 @@ func (m *TwoPhaseMap[K, D]) get(key uint64) D {
return m.addMap.get(key).Contents
}
// Put places the key K in the map
// Put: places the key K in the map with the associated data D
func (m *TwoPhaseMap[K, D]) Put(key K, data D) {
msgSequence := m.Clock.IncrementClock()
m.Clock.Put(key, msgSequence)
m.addMap.Put(key, data)
}
// Mark: marks the status of the node as undetermiend
func (m *TwoPhaseMap[K, D]) Mark(key K) {
m.addMap.Mark(key)
}
// Remove removes the value from the map
// Remove: removes the value from the map
func (m *TwoPhaseMap[K, D]) Remove(key K) {
m.removeMap.Put(key, true)
}
@ -92,6 +95,7 @@ func (m *TwoPhaseMap[K, D]) keys() []uint64 {
return keys
}
// AsList: convert the map to a list
func (m *TwoPhaseMap[K, D]) AsList() []D {
theList := make([]D, 0)
@ -104,6 +108,8 @@ func (m *TwoPhaseMap[K, D]) AsList() []D {
return theList
}
// Snapshot: convert the map into an immutable snapshot.
// contains the contents of the add and remove map
func (m *TwoPhaseMap[K, D]) Snapshot() *TwoPhaseMapSnapshot[K, D] {
return &TwoPhaseMapSnapshot[K, D]{
Add: m.addMap.Save(),
@ -111,6 +117,8 @@ func (m *TwoPhaseMap[K, D]) Snapshot() *TwoPhaseMapSnapshot[K, D] {
}
}
// SnapshotFromState: create a snapshot of the intersection of values provided
// in the given state
func (m *TwoPhaseMap[K, D]) SnapShotFromState(state *TwoPhaseMapState[K]) *TwoPhaseMapSnapshot[K, D] {
addKeys := lib.MapKeys(state.AddContents)
removeKeys := lib.MapKeys(state.RemoveContents)
@ -121,12 +129,18 @@ func (m *TwoPhaseMap[K, D]) SnapShotFromState(state *TwoPhaseMapState[K]) *TwoPh
}
}
// TwoPhaseMapState: encapsulates the state of the map
// without specifying the data that is stored
type TwoPhaseMapState[K cmp.Ordered] struct {
// Vectors: the vector ID of each process
Vectors map[uint64]uint64
// AddContents: the contents of the add map
AddContents map[uint64]uint64
// RemoveContents: the contents of the remove map
RemoveContents map[uint64]uint64
}
// IsMarked: returns true if the given value is marked in an undetermined state
func (m *TwoPhaseMap[K, D]) IsMarked(key K) bool {
return m.addMap.IsMarked(key)
}
@ -151,6 +165,8 @@ func (m *TwoPhaseMap[K, D]) GenerateMessage() *TwoPhaseMapState[K] {
}
}
// Difference: compute the set difference between the two states.
// highestStale represents the highest vector clock that has been marked as stale
func (m *TwoPhaseMapState[K]) Difference(highestStale uint64, state *TwoPhaseMapState[K]) *TwoPhaseMapState[K] {
mapState := &TwoPhaseMapState[K]{
AddContents: make(map[uint64]uint64),
@ -176,6 +192,7 @@ func (m *TwoPhaseMapState[K]) Difference(highestStale uint64, state *TwoPhaseMap
return mapState
}
// Merge: merge a snapshot into the map
func (m *TwoPhaseMap[K, D]) Merge(snapshot TwoPhaseMapSnapshot[K, D]) {
for key, value := range snapshot.Add {
// Gravestone is local only to that node.
@ -190,6 +207,7 @@ func (m *TwoPhaseMap[K, D]) Merge(snapshot TwoPhaseMapSnapshot[K, D]) {
}
}
// Prune: garbage collect all stale entries in the map
func (m *TwoPhaseMap[K, D]) Prune() {
m.addMap.Prune()
m.removeMap.Prune()

View File

@ -8,6 +8,9 @@ import (
"github.com/tim-beatham/smegmesh/pkg/lib"
)
// VectorBucket: represents a vector clock in the bucket
// recording both the time changes were last seen
// and when the lastUpdate epoch was recorded
type VectorBucket struct {
// clock current value of the node's clock
clock uint64
@ -15,8 +18,9 @@ type VectorBucket struct {
lastUpdate uint64
}
// Vector clock defines an abstract data type
// for a vector clock implementation
// VectorClock: defines an abstract data type
// for a vector clock implementation. Including a mechanism to
// garbage collect stale entries
type VectorClock[K cmp.Ordered] struct {
vectors map[uint64]*VectorBucket
lock sync.RWMutex
@ -62,6 +66,7 @@ func (m *VectorClock[K]) GetHash() uint64 {
return hash
}
// Merge: merge two clocks together
func (m *VectorClock[K]) Merge(vectors map[uint64]uint64) {
for key, value := range vectors {
m.put(key, value)
@ -97,6 +102,7 @@ func (m *VectorClock[K]) GetStaleCount() uint64 {
return staleCount
}
// Prune: prunes all stale entries in the vector clock
func (m *VectorClock[K]) Prune() {
stale := m.getStale()
@ -109,6 +115,8 @@ func (m *VectorClock[K]) Prune() {
m.lock.Unlock()
}
// GetTimeStamp: get the last time the node was updated in UNIX
// epoch time
func (m *VectorClock[K]) GetTimestamp(processId K) uint64 {
m.lock.RLock()
@ -118,6 +126,8 @@ func (m *VectorClock[K]) GetTimestamp(processId K) uint64 {
return lastUpdate
}
// Put: places the key with vector clock in the clock of the given
// process
func (m *VectorClock[K]) Put(key K, value uint64) {
m.put(m.hashFunc(key), value)
}
@ -133,7 +143,8 @@ func (m *VectorClock[K]) put(key uint64, value uint64) {
}
// Make sure that entries that were garbage collected don't get
// addded back
// highestStale represents the highest vector clock that has been
// invalidated
if value > clockValue && value > m.highestStale {
newBucket := VectorBucket{
clock: value,
@ -145,6 +156,7 @@ func (m *VectorClock[K]) put(key uint64, value uint64) {
m.lock.Unlock()
}
// GetClock: serialize the vector clock into an immutable map
func (m *VectorClock[K]) GetClock() map[uint64]uint64 {
clock := make(map[uint64]uint64)

View File

@ -10,6 +10,7 @@ import (
"github.com/tim-beatham/smegmesh/pkg/mesh"
"github.com/tim-beatham/smegmesh/pkg/query"
"github.com/tim-beatham/smegmesh/pkg/rpc"
"github.com/tim-beatham/smegmesh/pkg/sync"
"github.com/tim-beatham/smegmesh/pkg/wg"
"golang.zx2c4.com/wireguard/wgctrl"
)
@ -21,7 +22,6 @@ type NewCtrlServerParams struct {
CtrlProvider rpc.MeshCtrlServerServer
SyncProvider rpc.SyncServiceServer
Querier query.Querier
OnDelete func(mesh.MeshProvider)
}
// Create a new instance of the MeshCtrlServer or error if the
@ -38,8 +38,12 @@ func NewCtrlServer(params *NewCtrlServerParams) (*MeshCtrlServer, error) {
ipAllocator := &ip.ULABuilder{}
interfaceManipulator := wg.NewWgInterfaceManipulator(params.Client)
ctrlServer.timers = make([]*lib.Timer, 0)
configApplyer := mesh.NewWgMeshConfigApplyer()
var syncer sync.Syncer
meshManagerParams := &mesh.NewMeshManagerParams{
Conf: *params.Conf,
Client: params.Client,
@ -49,7 +53,13 @@ func NewCtrlServer(params *NewCtrlServerParams) (*MeshCtrlServer, error) {
IPAllocator: ipAllocator,
InterfaceManipulator: interfaceManipulator,
ConfigApplyer: configApplyer,
OnDelete: params.OnDelete,
OnDelete: func(mesh mesh.MeshProvider) {
_, err := syncer.Sync(mesh)
if err != nil {
logging.Log.WriteErrorf(err.Error())
}
},
}
ctrlServer.MeshManager = mesh.NewMeshManager(meshManagerParams)
@ -83,9 +93,37 @@ func NewCtrlServer(params *NewCtrlServerParams) (*MeshCtrlServer, error) {
return nil, err
}
syncer = sync.NewSyncer(&sync.NewSyncerParams{
MeshManager: ctrlServer.MeshManager,
ConnectionManager: ctrlServer.ConnectionManager,
Configuration: params.Conf,
})
// Check any syncs every 1 second
syncTimer := lib.NewTimer(func() error {
err = syncer.SyncMeshes()
if err != nil {
logging.Log.WriteErrorf(err.Error())
}
return nil
}, 1)
heartbeatTimer := lib.NewTimer(func() error {
logging.Log.WriteInfof("checking heartbeat")
return ctrlServer.MeshManager.UpdateTimeStamp()
}, params.Conf.Heartbeat)
ctrlServer.timers = append(ctrlServer.timers, syncTimer, heartbeatTimer)
ctrlServer.Querier = query.NewJmesQuerier(ctrlServer.MeshManager)
ctrlServer.ConnectionServer = connServer
for _, timer := range ctrlServer.timers {
go timer.Run()
}
return ctrlServer, nil
}
@ -123,5 +161,13 @@ func (s *MeshCtrlServer) Close() error {
logging.Log.WriteErrorf(err.Error())
}
for _, timer := range s.timers {
err := timer.Stop()
if err != nil {
logging.Log.WriteErrorf(err.Error())
}
}
return nil
}

View File

@ -13,12 +13,14 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// MeshRoute: represents a route in the mesh that is
// available to client applications
type MeshRoute struct {
Destination string
Path []string
}
// Represents the WireGuard configuration attached to the node
// WireGuardStats: Represents the WireGuard configuration attached to the node
type WireGuardStats struct {
AllowedIPs []string
TransmitBytes int64
@ -26,7 +28,8 @@ type WireGuardStats struct {
PersistentKeepAliveInterval time.Duration
}
// Represents a WireGuard MeshNode
// MeshNode: represents a node in the WireGuard mesh that can be
// sent to ip chandlers
type MeshNode struct {
HostEndpoint string
WgEndpoint string
@ -40,12 +43,13 @@ type MeshNode struct {
Stats WireGuardStats
}
// Represents a WireGuard Mesh
// Mesh: Represents a WireGuard Mesh network that can be sent
// along ipc to client frameworks
type Mesh struct {
SharedKey *wgtypes.Key
Nodes map[string]MeshNode
}
// CtrlServer: Encapsulates th ctrlserver
type CtrlServer interface {
GetConfiguration() *conf.DaemonConfiguration
GetClient() *wgctrl.Client
@ -55,7 +59,7 @@ type CtrlServer interface {
GetConnectionManager() conn.ConnectionManager
}
// Represents a ctrlserver to be used in WireGuard
// MeshCtrlServer: Represents a ctrlserver to be used in WireGuard
type MeshCtrlServer struct {
Client *wgctrl.Client
MeshManager mesh.MeshManager
@ -63,6 +67,7 @@ type MeshCtrlServer struct {
ConnectionServer *conn.ConnectionServer
Conf *conf.DaemonConfiguration
Querier query.Querier
timers []*lib.Timer
}
// NewCtrlNode create an instance of a ctrl node to send over an

View File

@ -1,3 +1,4 @@
// smegdns: example of how to implement dns in the mesh
package smegdns
import (
@ -45,6 +46,7 @@ func (d *DNSHandler) queryMesh(meshId, alias string) net.IP {
return ip
}
// handleQuery: handles a DNS query
func (d *DNSHandler) handleQuery(m *dns.Msg) {
for _, q := range m.Question {
switch q.Qtype {
@ -72,6 +74,7 @@ func (d *DNSHandler) handleQuery(m *dns.Msg) {
}
}
// handleDNS query: handle a DNS request
func (h *DNSHandler) handleDnsRequest(w dns.ResponseWriter, r *dns.Msg) {
msg := new(dns.Msg)
msg.SetReply(r)

View File

@ -29,6 +29,7 @@ type Graph interface {
GetType() GraphType
}
// Cluster: represents a subgraph in the graphs
type Cluster struct {
Type GraphType
Name string
@ -37,6 +38,7 @@ type Cluster struct {
edges map[string]Edge
}
// RootGraph: Represents the top level graph
type RootGraph struct {
Type GraphType
Label string
@ -45,6 +47,7 @@ type RootGraph struct {
edges map[string]Edge
}
// Node: represents a graphviz not
type Node struct {
Name string
Label string
@ -52,10 +55,12 @@ type Node struct {
Size int
}
// Edge: represents an edge between adjacent nodes
type Edge interface {
Dottable
}
// DirectEdge: contains a directed edge between any two nodes
type DirectedEdge struct {
Name string
Label string
@ -63,6 +68,8 @@ type DirectedEdge struct {
To string
}
// UndirectedEdge: contains an undirected edge between any two
// nodes
type UndirectedEdge struct {
Name string
Label string
@ -75,11 +82,7 @@ type Dottable interface {
GetDOT() (string, error)
}
func NewGraph(label string, graphType GraphType) *RootGraph {
return &RootGraph{Type: graphType, Label: label, clusters: map[string]*Cluster{}, nodes: make(map[string]*Node), edges: make(map[string]Edge)}
}
// PutNode: puts a node in the graph
// PutNode: puts a node in the root graph
func (g *RootGraph) PutNode(name, label string, size int, shape Shape) error {
_, exists := g.nodes[name]
@ -92,6 +95,7 @@ func (g *RootGraph) PutNode(name, label string, size int, shape Shape) error {
return nil
}
// PutCluster: puts a cluster in the root graph
func (g *RootGraph) PutCluster(graph *Cluster) {
g.clusters[graph.Label] = graph
}
@ -113,6 +117,7 @@ func writeContituents[D Dottable](result *strings.Builder, elements ...D) error
return nil
}
// GetDOT: convert the root graph into dot format
func (g *RootGraph) GetDOT() (string, error) {
var result strings.Builder
@ -138,7 +143,7 @@ func (g *RootGraph) GetDOT() (string, error) {
return result.String(), nil
}
// GetType implements Graph.
// GetType: get the graph type. DIRECTED|UNDIRECTED
func (r *RootGraph) GetType() GraphType {
return r.Type
}
@ -152,7 +157,7 @@ func constructEdge(graph Graph, name, label, from, to string) Edge {
}
}
// AddEdge: adds an edge between two nodes in the graph
// AddEdge: adds an edge between two nodes in the root graph
func (g *RootGraph) AddEdge(name string, label string, from string, to string) error {
g.edges[name] = constructEdge(g, name, label, from, to)
return nil
@ -166,15 +171,18 @@ func (n *Node) hash() int {
return (int(h.Sum32()) % numColours) + 1
}
// GetDOT: convert the node into DOT format
func (n *Node) GetDOT() (string, error) {
return fmt.Sprintf("node[label=\"%s\",shape=%s, style=\"filled\", fillcolor=%d, width=%d, height=%d, fixedsize=true] \"%s\";\n",
n.Label, n.Shape, n.hash(), n.Size, n.Size, n.Name), nil
}
// GetDOT: Convert a directed edge into dot format
func (e *DirectedEdge) GetDOT() (string, error) {
return fmt.Sprintf("\"%s\" -> \"%s\" [label=\"%s\"];\n", e.From, e.To, e.Label), nil
}
// GetDOT: convert an undirected edge into dot format
func (e *UndirectedEdge) GetDOT() (string, error) {
return fmt.Sprintf("\"%s\" -- \"%s\" [label=\"%s\"];\n", e.From, e.To, e.Label), nil
}
@ -198,6 +206,7 @@ func (g *Cluster) PutNode(name, label string, size int, shape Shape) error {
return nil
}
// GetDOT: convert the cluster into dot format
func (g *Cluster) GetDOT() (string, error) {
var builder strings.Builder
@ -212,10 +221,12 @@ func (g *Cluster) GetDOT() (string, error) {
return builder.String(), nil
}
// GetType: get the type of the subgraph (directed|undirected)
func (g *Cluster) GetType() GraphType {
return g.Type
}
// NewSubGraph: instantiate a new subgraph
func NewSubGraph(name string, label string, graphType GraphType) *Cluster {
return &Cluster{
Label: name,
@ -225,3 +236,14 @@ func NewSubGraph(name string, label string, graphType GraphType) *Cluster {
edges: make(map[string]Edge),
}
}
// NewGraph: create a new root graph
func NewGraph(label string, graphType GraphType) *RootGraph {
return &RootGraph{
Type: graphType,
Label: label,
clusters: map[string]*Cluster{},
nodes: make(map[string]*Node),
edges: make(map[string]Edge),
}
}

212
pkg/grpc/ctrlserver.pb.go Normal file
View File

@ -0,0 +1,212 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.21.12
// source: pkg/grpc/ctrlserver.proto
package rpc
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type GetMeshRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
MeshId string `protobuf:"bytes,1,opt,name=meshId,proto3" json:"meshId,omitempty"`
}
func (x *GetMeshRequest) Reset() {
*x = GetMeshRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_grpc_ctrlserver_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetMeshRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetMeshRequest) ProtoMessage() {}
func (x *GetMeshRequest) ProtoReflect() protoreflect.Message {
mi := &file_pkg_grpc_ctrlserver_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetMeshRequest.ProtoReflect.Descriptor instead.
func (*GetMeshRequest) Descriptor() ([]byte, []int) {
return file_pkg_grpc_ctrlserver_proto_rawDescGZIP(), []int{0}
}
func (x *GetMeshRequest) GetMeshId() string {
if x != nil {
return x.MeshId
}
return ""
}
type GetMeshReply struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Mesh []byte `protobuf:"bytes,1,opt,name=mesh,proto3" json:"mesh,omitempty"`
}
func (x *GetMeshReply) Reset() {
*x = GetMeshReply{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_grpc_ctrlserver_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetMeshReply) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetMeshReply) ProtoMessage() {}
func (x *GetMeshReply) ProtoReflect() protoreflect.Message {
mi := &file_pkg_grpc_ctrlserver_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetMeshReply.ProtoReflect.Descriptor instead.
func (*GetMeshReply) Descriptor() ([]byte, []int) {
return file_pkg_grpc_ctrlserver_proto_rawDescGZIP(), []int{1}
}
func (x *GetMeshReply) GetMesh() []byte {
if x != nil {
return x.Mesh
}
return nil
}
var File_pkg_grpc_ctrlserver_proto protoreflect.FileDescriptor
var file_pkg_grpc_ctrlserver_proto_rawDesc = []byte{
0x0a, 0x19, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x74, 0x72, 0x6c, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x72, 0x70, 0x63,
0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0x28, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x68,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x73, 0x68, 0x49,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x73, 0x68, 0x49, 0x64, 0x22,
0x22, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12,
0x12, 0x0a, 0x04, 0x6d, 0x65, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6d,
0x65, 0x73, 0x68, 0x32, 0x4f, 0x0a, 0x0e, 0x4d, 0x65, 0x73, 0x68, 0x43, 0x74, 0x72, 0x6c, 0x53,
0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x3d, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x68,
0x12, 0x18, 0x2e, 0x72, 0x70, 0x63, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x4d,
0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x70, 0x63,
0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x68, 0x52, 0x65, 0x70,
0x6c, 0x79, 0x22, 0x00, 0x42, 0x09, 0x5a, 0x07, 0x70, 0x6b, 0x67, 0x2f, 0x72, 0x70, 0x63, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_pkg_grpc_ctrlserver_proto_rawDescOnce sync.Once
file_pkg_grpc_ctrlserver_proto_rawDescData = file_pkg_grpc_ctrlserver_proto_rawDesc
)
func file_pkg_grpc_ctrlserver_proto_rawDescGZIP() []byte {
file_pkg_grpc_ctrlserver_proto_rawDescOnce.Do(func() {
file_pkg_grpc_ctrlserver_proto_rawDescData = protoimpl.X.CompressGZIP(file_pkg_grpc_ctrlserver_proto_rawDescData)
})
return file_pkg_grpc_ctrlserver_proto_rawDescData
}
var file_pkg_grpc_ctrlserver_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_pkg_grpc_ctrlserver_proto_goTypes = []interface{}{
(*GetMeshRequest)(nil), // 0: rpctypes.GetMeshRequest
(*GetMeshReply)(nil), // 1: rpctypes.GetMeshReply
}
var file_pkg_grpc_ctrlserver_proto_depIdxs = []int32{
0, // 0: rpctypes.MeshCtrlServer.GetMesh:input_type -> rpctypes.GetMeshRequest
1, // 1: rpctypes.MeshCtrlServer.GetMesh:output_type -> rpctypes.GetMeshReply
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_pkg_grpc_ctrlserver_proto_init() }
func file_pkg_grpc_ctrlserver_proto_init() {
if File_pkg_grpc_ctrlserver_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_pkg_grpc_ctrlserver_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetMeshRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pkg_grpc_ctrlserver_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetMeshReply); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pkg_grpc_ctrlserver_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_pkg_grpc_ctrlserver_proto_goTypes,
DependencyIndexes: file_pkg_grpc_ctrlserver_proto_depIdxs,
MessageInfos: file_pkg_grpc_ctrlserver_proto_msgTypes,
}.Build()
File_pkg_grpc_ctrlserver_proto = out.File
file_pkg_grpc_ctrlserver_proto_rawDesc = nil
file_pkg_grpc_ctrlserver_proto_goTypes = nil
file_pkg_grpc_ctrlserver_proto_depIdxs = nil
}

View File

@ -1,18 +0,0 @@
syntax = "proto3";
package rpctypes;
option go_package = "pkg/rpc";
service Authentication {
rpc JoinMesh(JoinAuthMeshRequest) returns (JoinAuthMeshReply) {}
}
message JoinAuthMeshRequest {
string meshId = 1;
string alias = 2;
}
message JoinAuthMeshReply {
bool success = 1;
optional string token = 2;
}

View File

@ -0,0 +1,105 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.21.12
// source: pkg/grpc/ctrlserver.proto
package rpc
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// MeshCtrlServerClient is the client API for MeshCtrlServer service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type MeshCtrlServerClient interface {
GetMesh(ctx context.Context, in *GetMeshRequest, opts ...grpc.CallOption) (*GetMeshReply, error)
}
type meshCtrlServerClient struct {
cc grpc.ClientConnInterface
}
func NewMeshCtrlServerClient(cc grpc.ClientConnInterface) MeshCtrlServerClient {
return &meshCtrlServerClient{cc}
}
func (c *meshCtrlServerClient) GetMesh(ctx context.Context, in *GetMeshRequest, opts ...grpc.CallOption) (*GetMeshReply, error) {
out := new(GetMeshReply)
err := c.cc.Invoke(ctx, "/rpctypes.MeshCtrlServer/GetMesh", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// MeshCtrlServerServer is the server API for MeshCtrlServer service.
// All implementations must embed UnimplementedMeshCtrlServerServer
// for forward compatibility
type MeshCtrlServerServer interface {
GetMesh(context.Context, *GetMeshRequest) (*GetMeshReply, error)
mustEmbedUnimplementedMeshCtrlServerServer()
}
// UnimplementedMeshCtrlServerServer must be embedded to have forward compatible implementations.
type UnimplementedMeshCtrlServerServer struct {
}
func (UnimplementedMeshCtrlServerServer) GetMesh(context.Context, *GetMeshRequest) (*GetMeshReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetMesh not implemented")
}
func (UnimplementedMeshCtrlServerServer) mustEmbedUnimplementedMeshCtrlServerServer() {}
// UnsafeMeshCtrlServerServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to MeshCtrlServerServer will
// result in compilation errors.
type UnsafeMeshCtrlServerServer interface {
mustEmbedUnimplementedMeshCtrlServerServer()
}
func RegisterMeshCtrlServerServer(s grpc.ServiceRegistrar, srv MeshCtrlServerServer) {
s.RegisterService(&MeshCtrlServer_ServiceDesc, srv)
}
func _MeshCtrlServer_GetMesh_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetMeshRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MeshCtrlServerServer).GetMesh(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/rpctypes.MeshCtrlServer/GetMesh",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MeshCtrlServerServer).GetMesh(ctx, req.(*GetMeshRequest))
}
return interceptor(ctx, in, info, handler)
}
// MeshCtrlServer_ServiceDesc is the grpc.ServiceDesc for MeshCtrlServer service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var MeshCtrlServer_ServiceDesc = grpc.ServiceDesc{
ServiceName: "rpctypes.MeshCtrlServer",
HandlerType: (*MeshCtrlServerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetMesh",
Handler: _MeshCtrlServer_GetMesh_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "pkg/grpc/ctrlserver.proto",
}

233
pkg/grpc/syncservice.pb.go Normal file
View File

@ -0,0 +1,233 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.21.12
// source: pkg/grpc/syncservice.proto
package rpc
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type SyncMeshRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
MeshId string `protobuf:"bytes,1,opt,name=meshId,proto3" json:"meshId,omitempty"`
Changes []byte `protobuf:"bytes,2,opt,name=changes,proto3" json:"changes,omitempty"`
}
func (x *SyncMeshRequest) Reset() {
*x = SyncMeshRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_grpc_syncservice_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SyncMeshRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SyncMeshRequest) ProtoMessage() {}
func (x *SyncMeshRequest) ProtoReflect() protoreflect.Message {
mi := &file_pkg_grpc_syncservice_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SyncMeshRequest.ProtoReflect.Descriptor instead.
func (*SyncMeshRequest) Descriptor() ([]byte, []int) {
return file_pkg_grpc_syncservice_proto_rawDescGZIP(), []int{0}
}
func (x *SyncMeshRequest) GetMeshId() string {
if x != nil {
return x.MeshId
}
return ""
}
func (x *SyncMeshRequest) GetChanges() []byte {
if x != nil {
return x.Changes
}
return nil
}
type SyncMeshReply struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
Changes []byte `protobuf:"bytes,2,opt,name=changes,proto3" json:"changes,omitempty"`
}
func (x *SyncMeshReply) Reset() {
*x = SyncMeshReply{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_grpc_syncservice_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SyncMeshReply) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SyncMeshReply) ProtoMessage() {}
func (x *SyncMeshReply) ProtoReflect() protoreflect.Message {
mi := &file_pkg_grpc_syncservice_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SyncMeshReply.ProtoReflect.Descriptor instead.
func (*SyncMeshReply) Descriptor() ([]byte, []int) {
return file_pkg_grpc_syncservice_proto_rawDescGZIP(), []int{1}
}
func (x *SyncMeshReply) GetSuccess() bool {
if x != nil {
return x.Success
}
return false
}
func (x *SyncMeshReply) GetChanges() []byte {
if x != nil {
return x.Changes
}
return nil
}
var File_pkg_grpc_syncservice_proto protoreflect.FileDescriptor
var file_pkg_grpc_syncservice_proto_rawDesc = []byte{
0x0a, 0x1a, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x73,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x73, 0x79,
0x6e, 0x63, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x43, 0x0a, 0x0f, 0x53, 0x79, 0x6e,
0x63, 0x4d, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06,
0x6d, 0x65, 0x73, 0x68, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65,
0x73, 0x68, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x22, 0x43,
0x0a, 0x0d, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x73, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12,
0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61,
0x6e, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e,
0x67, 0x65, 0x73, 0x32, 0x59, 0x0a, 0x0b, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x12, 0x4a, 0x0a, 0x08, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x73, 0x68, 0x12, 0x1c,
0x2e, 0x73, 0x79, 0x6e, 0x63, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x79, 0x6e,
0x63, 0x4d, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x73,
0x79, 0x6e, 0x63, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4d,
0x65, 0x73, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x09,
0x5a, 0x07, 0x70, 0x6b, 0x67, 0x2f, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
file_pkg_grpc_syncservice_proto_rawDescOnce sync.Once
file_pkg_grpc_syncservice_proto_rawDescData = file_pkg_grpc_syncservice_proto_rawDesc
)
func file_pkg_grpc_syncservice_proto_rawDescGZIP() []byte {
file_pkg_grpc_syncservice_proto_rawDescOnce.Do(func() {
file_pkg_grpc_syncservice_proto_rawDescData = protoimpl.X.CompressGZIP(file_pkg_grpc_syncservice_proto_rawDescData)
})
return file_pkg_grpc_syncservice_proto_rawDescData
}
var file_pkg_grpc_syncservice_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_pkg_grpc_syncservice_proto_goTypes = []interface{}{
(*SyncMeshRequest)(nil), // 0: syncservice.SyncMeshRequest
(*SyncMeshReply)(nil), // 1: syncservice.SyncMeshReply
}
var file_pkg_grpc_syncservice_proto_depIdxs = []int32{
0, // 0: syncservice.SyncService.SyncMesh:input_type -> syncservice.SyncMeshRequest
1, // 1: syncservice.SyncService.SyncMesh:output_type -> syncservice.SyncMeshReply
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_pkg_grpc_syncservice_proto_init() }
func file_pkg_grpc_syncservice_proto_init() {
if File_pkg_grpc_syncservice_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_pkg_grpc_syncservice_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SyncMeshRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pkg_grpc_syncservice_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SyncMeshReply); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pkg_grpc_syncservice_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_pkg_grpc_syncservice_proto_goTypes,
DependencyIndexes: file_pkg_grpc_syncservice_proto_depIdxs,
MessageInfos: file_pkg_grpc_syncservice_proto_msgTypes,
}.Build()
File_pkg_grpc_syncservice_proto = out.File
file_pkg_grpc_syncservice_proto_rawDesc = nil
file_pkg_grpc_syncservice_proto_goTypes = nil
file_pkg_grpc_syncservice_proto_depIdxs = nil
}

View File

@ -4,18 +4,9 @@ package syncservice;
option go_package = "pkg/rpc";
service SyncService {
rpc GetConf(GetConfRequest) returns (GetConfReply) {}
rpc SyncMesh(stream SyncMeshRequest) returns (stream SyncMeshReply) {}
}
message GetConfRequest {
string meshId = 1;
}
message GetConfReply {
bytes mesh = 1;
}
message SyncMeshRequest {
string meshId = 1;
bytes changes = 2;

View File

@ -0,0 +1,137 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.21.12
// source: pkg/grpc/syncservice.proto
package rpc
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// SyncServiceClient is the client API for SyncService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type SyncServiceClient interface {
SyncMesh(ctx context.Context, opts ...grpc.CallOption) (SyncService_SyncMeshClient, error)
}
type syncServiceClient struct {
cc grpc.ClientConnInterface
}
func NewSyncServiceClient(cc grpc.ClientConnInterface) SyncServiceClient {
return &syncServiceClient{cc}
}
func (c *syncServiceClient) SyncMesh(ctx context.Context, opts ...grpc.CallOption) (SyncService_SyncMeshClient, error) {
stream, err := c.cc.NewStream(ctx, &SyncService_ServiceDesc.Streams[0], "/syncservice.SyncService/SyncMesh", opts...)
if err != nil {
return nil, err
}
x := &syncServiceSyncMeshClient{stream}
return x, nil
}
type SyncService_SyncMeshClient interface {
Send(*SyncMeshRequest) error
Recv() (*SyncMeshReply, error)
grpc.ClientStream
}
type syncServiceSyncMeshClient struct {
grpc.ClientStream
}
func (x *syncServiceSyncMeshClient) Send(m *SyncMeshRequest) error {
return x.ClientStream.SendMsg(m)
}
func (x *syncServiceSyncMeshClient) Recv() (*SyncMeshReply, error) {
m := new(SyncMeshReply)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// SyncServiceServer is the server API for SyncService service.
// All implementations must embed UnimplementedSyncServiceServer
// for forward compatibility
type SyncServiceServer interface {
SyncMesh(SyncService_SyncMeshServer) error
mustEmbedUnimplementedSyncServiceServer()
}
// UnimplementedSyncServiceServer must be embedded to have forward compatible implementations.
type UnimplementedSyncServiceServer struct {
}
func (UnimplementedSyncServiceServer) SyncMesh(SyncService_SyncMeshServer) error {
return status.Errorf(codes.Unimplemented, "method SyncMesh not implemented")
}
func (UnimplementedSyncServiceServer) mustEmbedUnimplementedSyncServiceServer() {}
// UnsafeSyncServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to SyncServiceServer will
// result in compilation errors.
type UnsafeSyncServiceServer interface {
mustEmbedUnimplementedSyncServiceServer()
}
func RegisterSyncServiceServer(s grpc.ServiceRegistrar, srv SyncServiceServer) {
s.RegisterService(&SyncService_ServiceDesc, srv)
}
func _SyncService_SyncMesh_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(SyncServiceServer).SyncMesh(&syncServiceSyncMeshServer{stream})
}
type SyncService_SyncMeshServer interface {
Send(*SyncMeshReply) error
Recv() (*SyncMeshRequest, error)
grpc.ServerStream
}
type syncServiceSyncMeshServer struct {
grpc.ServerStream
}
func (x *syncServiceSyncMeshServer) Send(m *SyncMeshReply) error {
return x.ServerStream.SendMsg(m)
}
func (x *syncServiceSyncMeshServer) Recv() (*SyncMeshRequest, error) {
m := new(SyncMeshRequest)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// SyncService_ServiceDesc is the grpc.ServiceDesc for SyncService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var SyncService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "syncservice.SyncService",
HandlerType: (*SyncServiceServer)(nil),
Methods: []grpc.MethodDesc{},
Streams: []grpc.StreamDesc{
{
StreamName: "SyncMesh",
Handler: _SyncService_SyncMesh_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "pkg/grpc/syncservice.proto",
}

View File

@ -1,8 +1,7 @@
package ip
/*
* Use a WireGuard public key to generate a unique interface ID
*/
// Generates a CGA see RFC 3972
// https://datatracker.ietf.org/doc/html/rfc3972
import (
"crypto/rand"
@ -22,15 +21,19 @@ const (
InterfaceIdLen = 8
)
/*
* Cga parameters used to generate an IPV6 interface ID
*/
// CGAParameters: parameters used to create a new cryotpgraphically generated
// address
type CgaParameters struct {
Modifier [ModifierLength]byte
// SubnetPrefix: prefix of the subnetwork
SubnetPrefix [2 * InterfaceIdLen]byte
// CollisionCount: total number of times we have atempted to generate a porefix
CollisionCount uint8
// PublicKey: WireGuard public key of our interface
PublicKey wgtypes.Key
// interfaceId: the generated interfaceId
interfaceId [2 * InterfaceIdLen]byte
// flag: represents whether or not an IP address has been generated
flag byte
}
@ -49,22 +52,6 @@ func NewCga(key wgtypes.Key, collisionCount uint8, subnetPrefix [2 * InterfaceId
return &params, nil
}
func (c *CgaParameters) generateHash2() []byte {
var byteVal [hash2Length]byte
for i := 0; i < ModifierLength; i++ {
byteVal[i] = c.Modifier[i]
}
for i := 0; i < wgtypes.KeyLen; i++ {
byteVal[ModifierLength+ZeroLength+i] = c.PublicKey[i]
}
hash := sha1.Sum(byteVal[:])
return hash[:Hash2Prefix]
}
func (c *CgaParameters) generateHash1() []byte {
var byteVal [hash1Length]byte
@ -98,7 +85,6 @@ func (c *CgaParameters) generateInterface() []byte {
interfaceId[0] = clearBit(int(interfaceId[0]), 6)
interfaceId[0] = clearBit(int(interfaceId[1]), 7)
return interfaceId
}

View File

@ -6,6 +6,7 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// IPAllocator: abstracts the process of creating an IP address
type IPAllocator interface {
GetIP(key wgtypes.Key, meshId string, collisionCount uint8) (net.IP, error)
}

View File

@ -8,6 +8,7 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// ULABuilder: Create a new ULA in WireGuard
type ULABuilder struct{}
func getMeshPrefix(meshId string) [16]byte {

View File

@ -5,13 +5,13 @@ import (
"net"
"net/http"
"net/rpc"
ipcRpc "net/rpc"
ipcRPC "net/rpc"
"os"
"github.com/tim-beatham/smegmesh/pkg/ctrlserver"
)
const SockAddr = "/tmp/wgmesh_sock"
const SockAddr = "/tmp/smeg.sock"
type MeshIpc interface {
CreateMesh(args *NewMeshArgs, reply *string) error
@ -60,59 +60,83 @@ type JoinMeshArgs struct {
WgArgs WireGuardArgs
}
// PutServiceArgs: args to place a service into the data store
type PutServiceArgs struct {
Service string
Value string
MeshId string
}
// DeleteServiceArgs: args to remove a service from the data store
type DeleteServiceArgs struct {
Service string
MeshId string
}
// PutAliasArgs: args to assign an alias to a node
type PutAliasArgs struct {
Alias string
// Alias: represents the alias of the node
Alias string
// MeshId: represents the meshID of the node
MeshId string
}
// PutDescriptionArgs: args to assign a description to a node
type PutDescriptionArgs struct {
// Description: descriptio to add to the network
Description string
MeshId string
// MeshID to add to the mesh network
MeshId string
}
// GetMeshReply: ipc reply to get the mesh network
type GetMeshReply struct {
Nodes []ctrlserver.MeshNode
}
// ListMeshReply: ipc reply of the networks the node is part of
type ListMeshReply struct {
Meshes []string
}
// Querymesh: ipc args to query a mesh network
type QueryMesh struct {
// MeshId: id of the mesh to query
MeshId string
Query string
// JMESPath: query string to query
Query string
}
// ClientIpc: Framework to invoke ipc calls to the daemon
type ClientIpc interface {
// CreateMesh: create a mesh network, return an error if the operation failed
CreateMesh(args *NewMeshArgs, reply *string) error
// ListMesh: list mesh network the node is a part of, return an error if the operation failed
ListMeshes(args *ListMeshReply, reply *string) error
// JoinMesh: join a mesh network return an error if the operation failed
JoinMesh(args JoinMeshArgs, reply *string) error
// LeaveMesh: leave a mesh network, return an error if the operation failed
LeaveMesh(meshId string, reply *string) error
// GetMesh: get the given mesh network, return an error if the operation failed
GetMesh(meshId string, reply *GetMeshReply) error
// Query: query the given mesh network
Query(query QueryMesh, reply *string) error
// PutDescription: assign a description to yourself
PutDescription(args PutDescriptionArgs, reply *string) error
// PutAlias: assign an alias to yourself
PutAlias(args PutAliasArgs, reply *string) error
// PutService: assign a service to yourself
PutService(args PutServiceArgs, reply *string) error
// DeleteService: retract a service
DeleteService(args DeleteServiceArgs, reply *string) error
}
type SmegmeshIpc struct {
client *ipcRpc.Client
client *ipcRPC.Client
}
func NewClientIpc() (*SmegmeshIpc, error) {
client, err := ipcRpc.DialHTTP("unix", SockAddr)
client, err := ipcRPC.DialHTTP("unix", SockAddr)
if err != nil {
return nil, err
@ -164,7 +188,7 @@ func (c *SmegmeshIpc) DeleteService(args DeleteServiceArgs, reply *string) error
}
func (c *SmegmeshIpc) Close() error {
return c.Close()
return c.client.Close()
}
func RunIpcHandler(server MeshIpc) error {

View File

@ -36,11 +36,13 @@ func ConsistentHash[V any, K any](values []V, client K, bucketFunc func(V) int,
ourKey := keyFunc(client)
for _, record := range vs {
if ourKey < record.value {
return record.record
}
idx := sort.Search(len(vs), func(i int) bool {
return vs[i].value >= ourKey
})
if idx == len(vs) {
return vs[0].record
}
return vs[0].record
return vs[idx].record
}

View File

@ -10,23 +10,17 @@ import (
"golang.org/x/sys/unix"
)
// Maximum MTU to assin to WireGuard
// This isn't configurable
const WIREGUARD_MTU = 1420
// RtNetlinkConfig: represents an rtnetlkink configuration instance
type RtNetlinkConfig struct {
// conn: connection to the rtnetlink API
conn *rtnetlink.Conn
}
func NewRtNetlinkConfig() (*RtNetlinkConfig, error) {
conn, err := rtnetlink.Dial(nil)
if err != nil {
return nil, err
}
return &RtNetlinkConfig{conn: conn}, nil
}
const WIREGUARD_MTU = 1420
// Create a netlink interface if it does not exist. ifName is the name of the netlink interface
// CreateLink: Create a netlink interface if it does not exist. ifName is the name of the netlink interface
func (c *RtNetlinkConfig) CreateLink(ifName string) error {
_, err := net.InterfaceByName(ifName)
@ -51,7 +45,7 @@ func (c *RtNetlinkConfig) CreateLink(ifName string) error {
return nil
}
// Delete link delete the specified interface
// DeleteLink: delete the specified interface
func (c *RtNetlinkConfig) DeleteLink(ifName string) error {
iface, err := net.InterfaceByName(ifName)
@ -68,7 +62,7 @@ func (c *RtNetlinkConfig) DeleteLink(ifName string) error {
return nil
}
// AddAddress adds an address to the given interface.
// AddAddress: adds an address to the given interface.
func (c *RtNetlinkConfig) AddAddress(ifName string, address string) error {
iface, err := net.InterfaceByName(ifName)
@ -177,7 +171,7 @@ func (c *RtNetlinkConfig) AddRoute(ifName string, route Route) error {
return nil
}
// DeleteRoute deletes routes with the gateway and destination
// DeleteRoute: deletes routes with the gateway and destination
func (c *RtNetlinkConfig) DeleteRoute(ifName string, route Route) error {
iface, err := net.InterfaceByName(ifName)
@ -219,6 +213,7 @@ func (c *RtNetlinkConfig) DeleteRoute(ifName string, route Route) error {
return nil
}
// route: represents a rout to add to the RIB
type Route struct {
Gateway net.IP
Destination net.IPNet
@ -232,7 +227,7 @@ func (r1 Route) equal(r2 Route) bool {
(mask1Ones == 0 && mask2Ones == 0 || r1.Destination.IP.Equal(r2.Destination.IP))
}
// DeleteRoutes deletes all routes not in exclude
// DeleteRoutes: deletes all routes not in exclude on the given interface
func (c *RtNetlinkConfig) DeleteRoutes(ifName string, family uint8, exclude ...Route) error {
routes, err := c.listRoutes(ifName, family)
@ -282,7 +277,7 @@ func (c *RtNetlinkConfig) DeleteRoutes(ifName string, family uint8, exclude ...R
return nil
}
// listRoutes lists all routes on the interface
// listRoutes: lists all routes on the interface
func (c *RtNetlinkConfig) listRoutes(ifName string, family uint8) ([]rtnetlink.RouteMessage, error) {
iface, err := net.InterfaceByName(ifName)
@ -304,6 +299,18 @@ func (c *RtNetlinkConfig) listRoutes(ifName string, family uint8) ([]rtnetlink.R
return routes, nil
}
// Close: close the Rtnetlink API
func (c *RtNetlinkConfig) Close() error {
return c.conn.Close()
}
// newRtNetlinkConfig: connect to the RtnetlinkAPI
func NewRtNetlinkConfig() (*RtNetlinkConfig, error) {
conn, err := rtnetlink.Dial(nil)
if err != nil {
return nil, err
}
return &RtNetlinkConfig{conn: conn}, nil
}

View File

@ -6,6 +6,7 @@ import (
"os"
"github.com/sirupsen/logrus"
"github.com/tim-beatham/smegmesh/pkg/conf"
)
var (
@ -39,17 +40,29 @@ func (l *LogrusLogger) Writer() io.Writer {
return l.logger.Writer()
}
func NewLogrusLogger() *LogrusLogger {
func NewLogrusLogger(confLevel conf.LogLevel) *LogrusLogger {
var level logrus.Level
switch confLevel {
case conf.ERROR:
level = logrus.ErrorLevel
case conf.WARNING:
level = logrus.WarnLevel
case conf.INFO:
level = logrus.InfoLevel
}
logger := logrus.New()
logger.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})
logger.SetOutput(os.Stdout)
logger.SetLevel(logrus.InfoLevel)
logger.SetLevel(level)
return &LogrusLogger{logger: logger}
}
func init() {
SetLogger(NewLogrusLogger())
SetLogger(NewLogrusLogger(conf.INFO))
}
func SetLogger(l Logger) {

View File

@ -16,12 +16,13 @@ import (
// MeshConfigApplyer abstracts applying the mesh configuration
type MeshConfigApplyer interface {
// ApplyConfig: apply the configurtation
ApplyConfig() error
RemovePeers(meshId string) error
// SetMeshManager: sets the associated manager
SetMeshManager(manager MeshManager)
}
// WgMeshConfigApplyer applies WireGuard configuration
// WgMeshConfigApplyer: applies WireGuard configuration
type WgMeshConfigApplyer struct {
meshManager MeshManager
routeInstaller route.RouteInstaller
@ -120,7 +121,12 @@ func (m *WgMeshConfigApplyer) convertMeshNode(params convertMeshNodeParams) (*wg
// getRoutes: finds the routes with the least hop distance. If more than one route exists
// consistently hash to evenly spread the distribution of traffic
func (m *WgMeshConfigApplyer) getRoutes(meshProvider MeshProvider) (map[string][]routeNode, error) {
mesh, _ := meshProvider.GetMesh()
mesh, err := meshProvider.GetMesh()
if err != nil {
return nil, err
}
routes := make(map[string][]routeNode)
peers := lib.Filter(lib.MapValues(mesh.GetNodes()), func(p MeshNode) bool {
@ -169,7 +175,6 @@ func (m *WgMeshConfigApplyer) getRoutes(meshProvider MeshProvider) (map[string][
rn.gateway = peerPub.String()
rn.route = &RouteStub{
Destination: rn.route.GetDestination(),
HopCount: rn.route.GetHopCount() + 1,
Path: append(rn.route.GetPath(), peer.GetWgHost().IP.String()),
}
}
@ -196,6 +201,7 @@ func (m *WgMeshConfigApplyer) getCorrespondingPeer(peers []MeshNode, client Mesh
return peer
}
// getPeerCfgsToRemove: remove peer configurations that are no longer in the mesh
func (m *WgMeshConfigApplyer) getPeerCfgsToRemove(dev *wgtypes.Device, newPeers []wgtypes.PeerConfig) []wgtypes.PeerConfig {
peers := dev.Peers
peers = lib.Filter(peers, func(p1 wgtypes.Peer) bool {
@ -220,17 +226,22 @@ type GetConfigParams struct {
routes map[string][]routeNode
}
// getClientConfig: if the node is a client get their configuration
func (m *WgMeshConfigApplyer) getClientConfig(params *GetConfigParams) (*wgtypes.Config, error) {
ula := &ip.ULABuilder{}
meshNet, _ := ula.GetIPNet(params.mesh.GetMeshId())
routesForMesh := lib.Map(lib.MapValues(params.routes), func(rns []routeNode) []routeNode {
return lib.Filter(rns, func(rn routeNode) bool {
ip, _, _ := net.ParseCIDR(rn.gateway)
return meshNet.Contains(ip)
node, err := params.mesh.GetNode(rn.gateway)
return node != nil && err == nil
})
})
routesForMesh = lib.Filter(routesForMesh, func(rns []routeNode) bool {
return len(rns) != 0
})
routes := lib.Map(routesForMesh, func(rs []routeNode) net.IPNet {
return *rs[0].route.GetDestination()
})
@ -242,6 +253,10 @@ func (m *WgMeshConfigApplyer) getClientConfig(params *GetConfigParams) (*wgtypes
return nil, err
}
if len(params.peers) == 0 {
return nil, fmt.Errorf("no peers in the mesh")
}
peer := m.getCorrespondingPeer(params.peers, self)
pubKey, _ := peer.GetPublicKey()
@ -267,10 +282,14 @@ func (m *WgMeshConfigApplyer) getClientConfig(params *GetConfigParams) (*wgtypes
installedRoutes := make([]lib.Route, 0)
for _, route := range peerCfgs[0].AllowedIPs {
installedRoutes = append(installedRoutes, lib.Route{
Gateway: peer.GetWgHost().IP,
Destination: route,
})
// Don't install routes that we are directly apart
// Dont install default route wgctrl handles this for us
if !meshNet.Contains(route.IP) {
installedRoutes = append(installedRoutes, lib.Route{
Gateway: peer.GetWgHost().IP,
Destination: route,
})
}
}
cfg := wgtypes.Config{
@ -281,6 +300,8 @@ func (m *WgMeshConfigApplyer) getClientConfig(params *GetConfigParams) (*wgtypes
return &cfg, err
}
// getRoutesToInstall: work out if the given node is advertising routes that should be installed into the
// RIB
func (m *WgMeshConfigApplyer) getRoutesToInstall(wgNode *wgtypes.PeerConfig, mesh MeshProvider, node MeshNode) []lib.Route {
routes := make([]lib.Route, 0)
@ -288,9 +309,8 @@ func (m *WgMeshConfigApplyer) getRoutesToInstall(wgNode *wgtypes.PeerConfig, mes
ula := &ip.ULABuilder{}
ipNet, _ := ula.GetIPNet(mesh.GetMeshId())
_, defaultRoute, _ := net.ParseCIDR("::/0")
if !ipNet.Contains(route.IP) && !ipNet.IP.Equal(defaultRoute.IP) {
// Check there is no overlap in network and its not the default route
if !ipNet.Contains(route.IP) {
routes = append(routes, lib.Route{
Gateway: node.GetWgHost().IP,
Destination: route,
@ -301,6 +321,7 @@ func (m *WgMeshConfigApplyer) getRoutesToInstall(wgNode *wgtypes.PeerConfig, mes
return routes
}
// getPeerConfig: creates the WireGuard configuration for a peer
func (m *WgMeshConfigApplyer) getPeerConfig(params *GetConfigParams) (*wgtypes.Config, error) {
peerToClients := make(map[string][]net.IPNet)
installedRoutes := make([]lib.Route, 0)
@ -374,6 +395,7 @@ func (m *WgMeshConfigApplyer) getPeerConfig(params *GetConfigParams) (*wgtypes.C
return &cfg, err
}
// updateWgConf: update the WireGuard configuration
func (m *WgMeshConfigApplyer) updateWgConf(mesh MeshProvider, routes map[string][]routeNode) error {
snap, err := mesh.GetMesh()
@ -435,6 +457,8 @@ func (m *WgMeshConfigApplyer) updateWgConf(mesh MeshProvider, routes map[string]
return nil
}
// getAllRoutes: works out all the routes to install out of all the routes in the
// set of networks the node is a part of
func (m *WgMeshConfigApplyer) getAllRoutes() (map[string][]routeNode, error) {
allRoutes := make(map[string][]routeNode)
@ -464,6 +488,7 @@ func (m *WgMeshConfigApplyer) getAllRoutes() (map[string][]routeNode, error) {
return allRoutes, nil
}
// ApplyConfig: apply the WireGuard configuration
func (m *WgMeshConfigApplyer) ApplyConfig() error {
allRoutes, err := m.getAllRoutes()
@ -482,27 +507,6 @@ func (m *WgMeshConfigApplyer) ApplyConfig() error {
return nil
}
func (m *WgMeshConfigApplyer) RemovePeers(meshId string) error {
mesh := m.meshManager.GetMesh(meshId)
if mesh == nil {
return fmt.Errorf("mesh %s does not exist", meshId)
}
dev, err := mesh.GetDevice()
if err != nil {
return err
}
m.meshManager.GetClient().ConfigureDevice(dev.Name, wgtypes.Config{
Peers: make([]wgtypes.PeerConfig, 0),
ReplacePeers: true,
})
return nil
}
func (m *WgMeshConfigApplyer) SetMeshManager(manager MeshManager) {
m.meshManager = manager
}

View File

@ -16,6 +16,8 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// MeshManager: abstracts maanging meshes, including installing the WireGuard configuration
// to the device, and adding and removing nodes
type MeshManager interface {
CreateMesh(params *CreateMeshParams) (string, error)
AddMesh(params *AddMeshParams) error
@ -39,12 +41,10 @@ type MeshManager interface {
}
type MeshManagerImpl struct {
lock sync.RWMutex
Meshes map[string]MeshProvider
RouteManager RouteManager
Client *wgctrl.Client
// HostParameters contains information that uniquely locates
// the node in the mesh network.
meshLock sync.RWMutex
meshes map[string]MeshProvider
RouteManager RouteManager
Client *wgctrl.Client
HostParameters *HostParameters
conf *conf.DaemonConfiguration
meshProviderFactory MeshProviderFactory
@ -57,12 +57,11 @@ type MeshManagerImpl struct {
OnDelete func(MeshProvider)
}
// GetRouteManager implements MeshManager.
func (m *MeshManagerImpl) GetRouteManager() RouteManager {
return m.RouteManager
}
// RemoveService implements MeshManager.
// RemoveService: remove a service from the given mesh.
func (m *MeshManagerImpl) RemoveService(meshId, service string) error {
mesh := m.GetMesh(meshId)
@ -77,7 +76,7 @@ func (m *MeshManagerImpl) RemoveService(meshId, service string) error {
return mesh.RemoveService(m.HostParameters.GetPublicKey(), service)
}
// SetService implements MeshManager.
// SetService: add a service to the given mesh
func (m *MeshManagerImpl) SetService(meshId, service, value string) error {
mesh := m.GetMesh(meshId)
@ -92,8 +91,9 @@ func (m *MeshManagerImpl) SetService(meshId, service, value string) error {
return mesh.AddService(m.HostParameters.GetPublicKey(), service, value)
}
// GetNode: gets the node with given id in the mesh network
func (m *MeshManagerImpl) GetNode(meshid, nodeId string) MeshNode {
mesh, ok := m.Meshes[meshid]
mesh, ok := m.meshes[meshid]
if !ok {
return nil
@ -176,9 +176,9 @@ func (m *MeshManagerImpl) CreateMesh(args *CreateMeshParams) (string, error) {
return "", fmt.Errorf("error creating mesh: %w", err)
}
m.lock.Lock()
m.Meshes[meshId] = nodeManager
m.lock.Unlock()
m.meshLock.Lock()
m.meshes[meshId] = nodeManager
m.meshLock.Unlock()
m.cmdRunner.RunCommands(m.conf.BaseConfiguration.PostUp...)
@ -192,7 +192,7 @@ type AddMeshParams struct {
Conf *conf.WgConfiguration
}
// AddMesh: Add the mesh to the list of meshes
// AddMesh: Add a new mesh network to the list of addresses
func (m *MeshManagerImpl) AddMesh(params *AddMeshParams) error {
var ifName string
var err error
@ -235,20 +235,20 @@ func (m *MeshManagerImpl) AddMesh(params *AddMeshParams) error {
return err
}
m.lock.Lock()
m.Meshes[params.MeshId] = meshProvider
m.lock.Unlock()
m.meshLock.Lock()
m.meshes[params.MeshId] = meshProvider
m.meshLock.Unlock()
return nil
}
// HasChanges returns true if the mesh has changes
// HasChanges: returns true if the mesh has changes
func (m *MeshManagerImpl) HasChanges(meshId string) bool {
return m.Meshes[meshId].HasChanges()
return m.meshes[meshId].HasChanges()
}
// GetMesh returns the mesh with the given meshid
// GetMesh: returns the mesh with the given meshid
func (m *MeshManagerImpl) GetMesh(meshId string) MeshProvider {
theMesh := m.Meshes[meshId]
theMesh := m.meshes[meshId]
return theMesh
}
@ -258,6 +258,8 @@ func (s *MeshManagerImpl) GetPublicKey() *wgtypes.Key {
return &key
}
// AddSelfParams: parameters required to add yourself to a mesh
// network
type AddSelfParams struct {
// MeshId is the ID of the mesh to add this instance to
MeshId string
@ -267,7 +269,7 @@ type AddSelfParams struct {
Endpoint string
}
// AddSelf adds this host to the mesh
// AddSelf: adds this host to the mesh
func (s *MeshManagerImpl) AddSelf(params *AddSelfParams) error {
mesh := s.GetMesh(params.MeshId)
@ -341,11 +343,11 @@ func (s *MeshManagerImpl) AddSelf(params *AddSelfParams) error {
}
}
s.Meshes[params.MeshId].AddNode(node)
s.meshes[params.MeshId].AddNode(node)
return nil
}
// LeaveMesh leaves the mesh network
// LeaveMesh: leaves the mesh network and force a synchronsiation
func (s *MeshManagerImpl) LeaveMesh(meshId string) error {
mesh := s.GetMesh(meshId)
@ -363,9 +365,9 @@ func (s *MeshManagerImpl) LeaveMesh(meshId string) error {
s.OnDelete(mesh)
}
s.lock.Lock()
delete(s.Meshes, meshId)
s.lock.Unlock()
s.meshLock.Lock()
delete(s.meshes, meshId)
s.meshLock.Unlock()
s.cmdRunner.RunCommands(s.conf.BaseConfiguration.PreDown...)
@ -388,7 +390,7 @@ func (s *MeshManagerImpl) LeaveMesh(meshId string) error {
}
func (s *MeshManagerImpl) GetSelf(meshId string) (MeshNode, error) {
meshInstance, ok := s.Meshes[meshId]
meshInstance, ok := s.meshes[meshId]
if !ok {
return nil, fmt.Errorf("mesh %s does not exist", meshId)
@ -403,11 +405,12 @@ func (s *MeshManagerImpl) GetSelf(meshId string) (MeshNode, error) {
return node, nil
}
// ApplyConfig: applies the WireGuard configuration
// adds routes to the RIB and so forth.
func (s *MeshManagerImpl) ApplyConfig() error {
if s.conf.StubWg {
return nil
}
return s.configApplyer.ApplyConfig()
}
@ -425,7 +428,7 @@ func (s *MeshManagerImpl) SetDescription(meshId, description string) error {
return mesh.SetDescription(s.HostParameters.GetPublicKey(), description)
}
// SetAlias implements MeshManager.
// SetAlias sets the alias of the node for the given meshid
func (s *MeshManagerImpl) SetAlias(meshId, alias string) error {
mesh := s.GetMesh(meshId)
@ -440,7 +443,8 @@ func (s *MeshManagerImpl) SetAlias(meshId, alias string) error {
return mesh.SetAlias(s.HostParameters.GetPublicKey(), alias)
}
// UpdateTimeStamp updates the timestamp of this node in all meshes
// UpdateTimeStamp: updates the timestamp of this node in all meshes
// essentially performs heartbeat if the node is the leader
func (s *MeshManagerImpl) UpdateTimeStamp() error {
meshes := s.GetMeshes()
for _, mesh := range meshes {
@ -460,26 +464,30 @@ func (s *MeshManagerImpl) GetClient() *wgctrl.Client {
return s.Client
}
// GetMeshes: get all meshes the node is part of
func (s *MeshManagerImpl) GetMeshes() map[string]MeshProvider {
meshes := make(map[string]MeshProvider)
s.lock.RLock()
// GetMesh: copies the map of meshes to a new map
// to prevent a whole range of concurrency issues
// due to iteration and modification
s.meshLock.RLock()
for id, mesh := range s.Meshes {
for id, mesh := range s.meshes {
meshes[id] = mesh
}
s.lock.RUnlock()
s.meshLock.RUnlock()
return meshes
}
// Close the mesh manager
// Close: close the mesh manager
func (s *MeshManagerImpl) Close() error {
if s.conf.StubWg {
return nil
}
for _, mesh := range s.Meshes {
for _, mesh := range s.meshes {
dev, err := mesh.GetDevice()
if err != nil {
@ -496,7 +504,7 @@ func (s *MeshManagerImpl) Close() error {
return nil
}
// NewMeshManagerParams params required to create an instance of a mesh manager
// NewMeshManagerParams: params required to create an instance of a mesh manager
type NewMeshManagerParams struct {
Conf conf.DaemonConfiguration
Client *wgctrl.Client
@ -511,7 +519,7 @@ type NewMeshManagerParams struct {
OnDelete func(MeshProvider)
}
// Creates a new instance of a mesh manager with the given parameters
// NewMeshManager: Creates a new instance of a mesh manager with the given parameters
func NewMeshManager(params *NewMeshManagerParams) MeshManager {
privateKey, _ := wgtypes.GeneratePrivateKey()
hostParams := HostParameters{
@ -519,7 +527,7 @@ func NewMeshManager(params *NewMeshManagerParams) MeshManager {
}
m := &MeshManagerImpl{
Meshes: make(map[string]MeshProvider),
meshes: make(map[string]MeshProvider),
HostParameters: &hostParams,
meshProviderFactory: params.MeshProvider,
nodeFactory: params.NodeFactory,

View File

@ -24,11 +24,11 @@ func getMeshConfiguration() *conf.DaemonConfiguration {
Timeout: 5,
Profile: false,
StubWg: true,
SyncTime: 2,
HeartBeat: 60,
SyncInterval: 2,
Heartbeat: 60,
ClusterSize: 64,
InterClusterChance: 0.15,
BranchRate: 3,
Branch: 3,
InfectionCount: 3,
BaseConfiguration: conf.WgConfiguration{
IPDiscovery: &ipDiscovery,

View File

@ -7,7 +7,9 @@ import (
"github.com/tim-beatham/smegmesh/pkg/lib"
)
// RouteManager: manager that leaks routes between meshes
type RouteManager interface {
// UpdateRoutes: leak all routes in each mesh
UpdateRoutes() error
}
@ -19,6 +21,11 @@ func (r *RouteManagerImpl) UpdateRoutes() error {
meshes := r.meshManager.GetMeshes()
routes := make(map[string][]Route)
for _, mesh := range meshes {
// Make empty routes so that routes are retracted
routes[mesh.GetMeshId()] = make([]Route, 0)
}
for _, mesh1 := range meshes {
if !*mesh1.GetConfiguration().AdvertiseRoutes {
continue
@ -39,7 +46,6 @@ func (r *RouteManagerImpl) UpdateRoutes() error {
defaultRoute := &RouteStub{
Destination: ipv6Default,
HopCount: 0,
Path: []string{mesh1.GetMeshId()},
}
@ -68,7 +74,6 @@ func (r *RouteManagerImpl) UpdateRoutes() error {
routeValues = append(routeValues, &RouteStub{
Destination: mesh1IpNet,
HopCount: 0,
Path: []string{mesh1.GetMeshId()},
})
@ -99,15 +104,12 @@ func (r *RouteManagerImpl) UpdateRoutes() error {
}
toRemove := make([]Route, 0)
prevRoutes, err := mesh.GetRoutes(NodeID(self))
if err != nil {
return err
}
prevRoutes := self.GetRoutes()
for _, route := range prevRoutes {
if !lib.Contains(meshRoutes, func(r Route) bool {
return RouteEquals(r, route)
return RouteEqual(r, route)
}) {
toRemove = append(toRemove, route)
}

View File

@ -21,12 +21,6 @@ type Route interface {
}
func RouteEqual(r1 Route, r2 Route) bool {
return r1.GetDestination().IP.Equal(r2.GetDestination().IP) &&
r1.GetHopCount() == r2.GetHopCount() &&
slices.Equal(r1.GetPath(), r2.GetPath())
}
func RouteEquals(r1, r2 Route) bool {
return r1.GetDestination().String() == r2.GetDestination().String() &&
r1.GetHopCount() == r2.GetHopCount() &&
slices.Equal(r1.GetPath(), r2.GetPath())
@ -34,7 +28,6 @@ func RouteEquals(r1, r2 Route) bool {
type RouteStub struct {
Destination *net.IPNet
HopCount int
Path []string
}
@ -43,7 +36,7 @@ func (r *RouteStub) GetDestination() *net.IPNet {
}
func (r *RouteStub) GetHopCount() int {
return r.HopCount
return len(r.Path)
}
func (r *RouteStub) GetPath() []string {

View File

@ -17,20 +17,24 @@ type Querier interface {
Query(meshId string, queryParams string) ([]byte, error)
}
// JmesQuerier: queries the datstore in JMESPath syntax
type JmesQuerier struct {
manager mesh.MeshManager
}
// QueryError: query error if something went wrong
type QueryError struct {
msg string
}
// QuerRoute: represents a route in the query
type QueryRoute struct {
Destination string `json:"destination"`
HopCount int `json:"hopCount"`
Path string `json:"path"`
}
// QueryNode: represents a single node in the query
type QueryNode struct {
HostEndpoint string `json:"hostEndpoint"`
PublicKey string `json:"publicKey"`
@ -48,7 +52,7 @@ func (m *QueryError) Error() string {
return m.msg
}
// Query: queries the data
// Query: queries the the datastore at the given meshid
func (j *JmesQuerier) Query(meshId, queryParams string) ([]byte, error) {
mesh, ok := j.manager.GetMeshes()[meshId]
@ -74,6 +78,7 @@ func (j *JmesQuerier) Query(meshId, queryParams string) ([]byte, error) {
return bytes, err
}
// MeshNodeToQuerynode: convert the mesh node into a query abstraction
func MeshNodeToQueryNode(node mesh.MeshNode) *QueryNode {
queryNode := new(QueryNode)
queryNode.HostEndpoint = node.GetHostEndpoint()

View File

@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"strconv"
"time"
"github.com/tim-beatham/smegmesh/pkg/conf"
@ -14,10 +13,12 @@ import (
"github.com/tim-beatham/smegmesh/pkg/rpc"
)
// IpcHandler: represents a handler for ipc calls
type IpcHandler struct {
Server ctrlserver.CtrlServer
}
// getOverrideConfiguration: override any specific WireGuard configuration
func getOverrideConfiguration(args *ipc.WireGuardArgs) conf.WgConfiguration {
overrideConf := conf.WgConfiguration{}
@ -40,6 +41,7 @@ func getOverrideConfiguration(args *ipc.WireGuardArgs) conf.WgConfiguration {
return overrideConf
}
// CreateMesh: create a new mesh network
func (n *IpcHandler) CreateMesh(args *ipc.NewMeshArgs, reply *string) error {
overrideConf := getOverrideConfiguration(&args.WgArgs)
@ -66,6 +68,7 @@ func (n *IpcHandler) CreateMesh(args *ipc.NewMeshArgs, reply *string) error {
return err
}
// ListMeshes: list mesh networks
func (n *IpcHandler) ListMeshes(_ string, reply *ipc.ListMeshReply) error {
meshNames := make([]string, len(n.Server.GetMeshManager().GetMeshes()))
@ -79,6 +82,7 @@ func (n *IpcHandler) ListMeshes(_ string, reply *ipc.ListMeshReply) error {
return nil
}
// JoinMesh: join a mesh network
func (n *IpcHandler) JoinMesh(args *ipc.JoinMeshArgs, reply *string) error {
overrideConf := getOverrideConfiguration(&args.WgArgs)
@ -136,11 +140,11 @@ func (n *IpcHandler) JoinMesh(args *ipc.JoinMeshArgs, reply *string) error {
return err
}
*reply = strconv.FormatBool(true)
*reply = fmt.Sprintf("Successfully Joined: %s", args.MeshId)
return nil
}
// LeaveMesh leaves a mesh network
// LeaveMesh: leaves a mesh network
func (n *IpcHandler) LeaveMesh(meshId string, reply *string) error {
err := n.Server.GetMeshManager().LeaveMesh(meshId)
@ -150,6 +154,7 @@ func (n *IpcHandler) LeaveMesh(meshId string, reply *string) error {
return err
}
// GetMesh: get a mesh network at the given meshid
func (n *IpcHandler) GetMesh(meshId string, reply *ipc.GetMeshReply) error {
theMesh := n.Server.GetMeshManager().GetMesh(meshId)
@ -181,6 +186,7 @@ func (n *IpcHandler) GetMesh(meshId string, reply *ipc.GetMeshReply) error {
return nil
}
// Query: perform a jmespath query
func (n *IpcHandler) Query(params ipc.QueryMesh, reply *string) error {
queryResponse, err := n.Server.GetQuerier().Query(params.MeshId, params.Query)
@ -192,6 +198,7 @@ func (n *IpcHandler) Query(params ipc.QueryMesh, reply *string) error {
return nil
}
// PutDescription: change your description in the mesh
func (n *IpcHandler) PutDescription(args ipc.PutDescriptionArgs, reply *string) error {
err := n.Server.GetMeshManager().SetDescription(args.MeshId, args.Description)
@ -203,6 +210,7 @@ func (n *IpcHandler) PutDescription(args ipc.PutDescriptionArgs, reply *string)
return nil
}
// PutAlias: put your aliasin the mesh
func (n *IpcHandler) PutAlias(args ipc.PutAliasArgs, reply *string) error {
if args.Alias == "" {
return fmt.Errorf("alias not provided")
@ -218,6 +226,7 @@ func (n *IpcHandler) PutAlias(args ipc.PutAliasArgs, reply *string) error {
return nil
}
// PutService: place a service in the mesh
func (n *IpcHandler) PutService(service ipc.PutServiceArgs, reply *string) error {
err := n.Server.GetMeshManager().SetService(service.MeshId, service.Service, service.Value)
@ -225,10 +234,11 @@ func (n *IpcHandler) PutService(service ipc.PutServiceArgs, reply *string) error
return err
}
*reply = "success"
*reply = fmt.Sprintf("Set service %s in %s to %s", service.Service, service.MeshId, service.Value)
return nil
}
// DeleteService: withtract a service in the mesh
func (n *IpcHandler) DeleteService(service ipc.DeleteServiceArgs, reply *string) error {
err := n.Server.GetMeshManager().RemoveService(service.MeshId, service.Service)
@ -236,10 +246,11 @@ func (n *IpcHandler) DeleteService(service ipc.DeleteServiceArgs, reply *string)
return err
}
*reply = "success"
*reply = fmt.Sprintf("Removed service %s from %s", service.Service, service.MeshId)
return nil
}
// RobinIpcParams: parameters required to construct a new mesh network
type RobinIpcParams struct {
CtrlServer ctrlserver.CtrlServer
}

View File

@ -8,11 +8,13 @@ import (
"github.com/tim-beatham/smegmesh/pkg/rpc"
)
// WgRpc: represents a WireGuard rpc call
type WgRpc struct {
rpc.UnimplementedMeshCtrlServerServer
Server *ctrlserver.MeshCtrlServer
}
// GetMesh: serialise the mesh network into bytes
func (m *WgRpc) GetMesh(ctx context.Context, request *rpc.GetMeshRequest) (*rpc.GetMeshReply, error) {
mesh := m.Server.MeshManager.GetMesh(request.MeshId)

View File

@ -5,6 +5,7 @@ import (
"golang.org/x/sys/unix"
)
// RouteInstaller: install the routes to the given interface
type RouteInstaller interface {
InstallRoutes(devName string, routes ...lib.Route) error
}
@ -19,6 +20,8 @@ func (r *RouteInstallerImpl) InstallRoutes(devName string, routes ...lib.Route)
return err
}
defer rtnl.Close()
err = rtnl.DeleteRoutes(devName, unix.AF_INET6, routes...)
if err != nil {

View File

@ -1,235 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.21.12
// source: pkg/grpc/ctrlserver/authentication.proto
package rpc
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type JoinAuthMeshRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
MeshId string `protobuf:"bytes,1,opt,name=meshId,proto3" json:"meshId,omitempty"`
Alias string `protobuf:"bytes,2,opt,name=alias,proto3" json:"alias,omitempty"`
}
func (x *JoinAuthMeshRequest) Reset() {
*x = JoinAuthMeshRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_grpc_ctrlserver_authentication_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *JoinAuthMeshRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*JoinAuthMeshRequest) ProtoMessage() {}
func (x *JoinAuthMeshRequest) ProtoReflect() protoreflect.Message {
mi := &file_pkg_grpc_ctrlserver_authentication_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use JoinAuthMeshRequest.ProtoReflect.Descriptor instead.
func (*JoinAuthMeshRequest) Descriptor() ([]byte, []int) {
return file_pkg_grpc_ctrlserver_authentication_proto_rawDescGZIP(), []int{0}
}
func (x *JoinAuthMeshRequest) GetMeshId() string {
if x != nil {
return x.MeshId
}
return ""
}
func (x *JoinAuthMeshRequest) GetAlias() string {
if x != nil {
return x.Alias
}
return ""
}
type JoinAuthMeshReply struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
Token *string `protobuf:"bytes,2,opt,name=token,proto3,oneof" json:"token,omitempty"`
}
func (x *JoinAuthMeshReply) Reset() {
*x = JoinAuthMeshReply{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_grpc_ctrlserver_authentication_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *JoinAuthMeshReply) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*JoinAuthMeshReply) ProtoMessage() {}
func (x *JoinAuthMeshReply) ProtoReflect() protoreflect.Message {
mi := &file_pkg_grpc_ctrlserver_authentication_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use JoinAuthMeshReply.ProtoReflect.Descriptor instead.
func (*JoinAuthMeshReply) Descriptor() ([]byte, []int) {
return file_pkg_grpc_ctrlserver_authentication_proto_rawDescGZIP(), []int{1}
}
func (x *JoinAuthMeshReply) GetSuccess() bool {
if x != nil {
return x.Success
}
return false
}
func (x *JoinAuthMeshReply) GetToken() string {
if x != nil && x.Token != nil {
return *x.Token
}
return ""
}
var File_pkg_grpc_ctrlserver_authentication_proto protoreflect.FileDescriptor
var file_pkg_grpc_ctrlserver_authentication_proto_rawDesc = []byte{
0x0a, 0x28, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x74, 0x72, 0x6c, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x72, 0x70, 0x63, 0x74,
0x79, 0x70, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x13, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x75, 0x74, 0x68,
0x4d, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d,
0x65, 0x73, 0x68, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x73,
0x68, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x52, 0x0a, 0x11, 0x4a, 0x6f, 0x69,
0x6e, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x73, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18,
0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x19, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65,
0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e,
0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0x5a, 0x0a,
0x0e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12,
0x48, 0x0a, 0x08, 0x4a, 0x6f, 0x69, 0x6e, 0x4d, 0x65, 0x73, 0x68, 0x12, 0x1d, 0x2e, 0x72, 0x70,
0x63, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x4d,
0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x72, 0x70, 0x63,
0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65,
0x73, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x09, 0x5a, 0x07, 0x70, 0x6b, 0x67,
0x2f, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_pkg_grpc_ctrlserver_authentication_proto_rawDescOnce sync.Once
file_pkg_grpc_ctrlserver_authentication_proto_rawDescData = file_pkg_grpc_ctrlserver_authentication_proto_rawDesc
)
func file_pkg_grpc_ctrlserver_authentication_proto_rawDescGZIP() []byte {
file_pkg_grpc_ctrlserver_authentication_proto_rawDescOnce.Do(func() {
file_pkg_grpc_ctrlserver_authentication_proto_rawDescData = protoimpl.X.CompressGZIP(file_pkg_grpc_ctrlserver_authentication_proto_rawDescData)
})
return file_pkg_grpc_ctrlserver_authentication_proto_rawDescData
}
var file_pkg_grpc_ctrlserver_authentication_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_pkg_grpc_ctrlserver_authentication_proto_goTypes = []interface{}{
(*JoinAuthMeshRequest)(nil), // 0: rpctypes.JoinAuthMeshRequest
(*JoinAuthMeshReply)(nil), // 1: rpctypes.JoinAuthMeshReply
}
var file_pkg_grpc_ctrlserver_authentication_proto_depIdxs = []int32{
0, // 0: rpctypes.Authentication.JoinMesh:input_type -> rpctypes.JoinAuthMeshRequest
1, // 1: rpctypes.Authentication.JoinMesh:output_type -> rpctypes.JoinAuthMeshReply
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_pkg_grpc_ctrlserver_authentication_proto_init() }
func file_pkg_grpc_ctrlserver_authentication_proto_init() {
if File_pkg_grpc_ctrlserver_authentication_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_pkg_grpc_ctrlserver_authentication_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*JoinAuthMeshRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pkg_grpc_ctrlserver_authentication_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*JoinAuthMeshReply); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_pkg_grpc_ctrlserver_authentication_proto_msgTypes[1].OneofWrappers = []interface{}{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pkg_grpc_ctrlserver_authentication_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_pkg_grpc_ctrlserver_authentication_proto_goTypes,
DependencyIndexes: file_pkg_grpc_ctrlserver_authentication_proto_depIdxs,
MessageInfos: file_pkg_grpc_ctrlserver_authentication_proto_msgTypes,
}.Build()
File_pkg_grpc_ctrlserver_authentication_proto = out.File
file_pkg_grpc_ctrlserver_authentication_proto_rawDesc = nil
file_pkg_grpc_ctrlserver_authentication_proto_goTypes = nil
file_pkg_grpc_ctrlserver_authentication_proto_depIdxs = nil
}

View File

@ -1,105 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.21.12
// source: pkg/grpc/ctrlserver/authentication.proto
package rpc
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// AuthenticationClient is the client API for Authentication service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type AuthenticationClient interface {
JoinMesh(ctx context.Context, in *JoinAuthMeshRequest, opts ...grpc.CallOption) (*JoinAuthMeshReply, error)
}
type authenticationClient struct {
cc grpc.ClientConnInterface
}
func NewAuthenticationClient(cc grpc.ClientConnInterface) AuthenticationClient {
return &authenticationClient{cc}
}
func (c *authenticationClient) JoinMesh(ctx context.Context, in *JoinAuthMeshRequest, opts ...grpc.CallOption) (*JoinAuthMeshReply, error) {
out := new(JoinAuthMeshReply)
err := c.cc.Invoke(ctx, "/rpctypes.Authentication/JoinMesh", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// AuthenticationServer is the server API for Authentication service.
// All implementations must embed UnimplementedAuthenticationServer
// for forward compatibility
type AuthenticationServer interface {
JoinMesh(context.Context, *JoinAuthMeshRequest) (*JoinAuthMeshReply, error)
mustEmbedUnimplementedAuthenticationServer()
}
// UnimplementedAuthenticationServer must be embedded to have forward compatible implementations.
type UnimplementedAuthenticationServer struct {
}
func (UnimplementedAuthenticationServer) JoinMesh(context.Context, *JoinAuthMeshRequest) (*JoinAuthMeshReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method JoinMesh not implemented")
}
func (UnimplementedAuthenticationServer) mustEmbedUnimplementedAuthenticationServer() {}
// UnsafeAuthenticationServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to AuthenticationServer will
// result in compilation errors.
type UnsafeAuthenticationServer interface {
mustEmbedUnimplementedAuthenticationServer()
}
func RegisterAuthenticationServer(s grpc.ServiceRegistrar, srv AuthenticationServer) {
s.RegisterService(&Authentication_ServiceDesc, srv)
}
func _Authentication_JoinMesh_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(JoinAuthMeshRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthenticationServer).JoinMesh(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/rpctypes.Authentication/JoinMesh",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthenticationServer).JoinMesh(ctx, req.(*JoinAuthMeshRequest))
}
return interceptor(ctx, in, info, handler)
}
// Authentication_ServiceDesc is the grpc.ServiceDesc for Authentication service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Authentication_ServiceDesc = grpc.ServiceDesc{
ServiceName: "rpctypes.Authentication",
HandlerType: (*AuthenticationServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "JoinMesh",
Handler: _Authentication_JoinMesh_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "pkg/grpc/ctrlserver/authentication.proto",
}

View File

@ -16,34 +16,33 @@ import (
// Syncer: picks random nodes from the meshs
type Syncer interface {
Sync(theMesh mesh.MeshProvider) error
Sync(theMesh mesh.MeshProvider) (bool, error)
SyncMeshes() error
}
// SyncerImpl: implementation of a syncer to sync meshes
type SyncerImpl struct {
manager mesh.MeshManager
meshManager mesh.MeshManager
requester SyncRequester
infectionCount int
syncCount int
cluster conn.ConnCluster
conf *conf.DaemonConfiguration
configuration *conf.DaemonConfiguration
lastSync map[string]int64
lock sync.RWMutex
lastPoll map[string]int64
lastSyncLock sync.RWMutex
lastPollLock sync.RWMutex
}
// Sync: Sync with random nodes
func (s *SyncerImpl) Sync(correspondingMesh mesh.MeshProvider) error {
// Sync: Sync with random nodes. Returns true if there was changes false otherwise
func (s *SyncerImpl) Sync(correspondingMesh mesh.MeshProvider) (bool, error) {
if correspondingMesh == nil {
return fmt.Errorf("mesh provided was nil cannot sync nil mesh")
return false, fmt.Errorf("mesh provided was nil cannot sync nil mesh")
}
// Self can be nil if the node is removed
selfID := s.manager.GetPublicKey()
self, err := correspondingMesh.GetNode(selfID.String())
if err != nil {
logging.Log.WriteErrorf(err.Error())
}
selfID := s.meshManager.GetPublicKey()
self, _ := correspondingMesh.GetNode(selfID.String())
correspondingMesh.Prune()
@ -56,21 +55,16 @@ func (s *SyncerImpl) Sync(correspondingMesh mesh.MeshProvider) error {
logging.Log.WriteInfof("no changes for %s", correspondingMesh.GetMeshId())
// If not synchronised in certain time pull from random neighbour
if s.conf.PullTime != 0 && time.Now().Unix()-s.lastSync[correspondingMesh.GetMeshId()] > int64(s.conf.PullTime) {
if s.configuration.PullInterval != 0 && time.Now().Unix()-s.lastSync[correspondingMesh.GetMeshId()] > int64(s.configuration.PullInterval) {
return s.Pull(self, correspondingMesh)
}
return nil
return false, nil
}
before := time.Now()
err = s.manager.GetRouteManager().UpdateRoutes()
if err != nil {
logging.Log.WriteErrorf(err.Error())
}
publicKey := s.manager.GetPublicKey()
publicKey := s.meshManager.GetPublicKey()
nodeNames := correspondingMesh.GetPeers()
nodeNames = lib.Filter(nodeNames, func(s string) bool {
@ -85,7 +79,7 @@ func (s *SyncerImpl) Sync(correspondingMesh mesh.MeshProvider) error {
neighbours := s.cluster.GetNeighbours(nodeNames, publicKey.String())
if len(neighbours) == 0 {
return nil
return false, nil
}
// Peer with 2 nodes so that there is redundnacy in
@ -94,56 +88,68 @@ func (s *SyncerImpl) Sync(correspondingMesh mesh.MeshProvider) error {
gossipNodes = neighbours[:redundancyLength]
} else {
neighbours := s.cluster.GetNeighbours(nodeNames, publicKey.String())
gossipNodes = lib.RandomSubsetOfLength(neighbours, s.conf.BranchRate)
gossipNodes = lib.RandomSubsetOfLength(neighbours, s.configuration.Branch)
if len(nodeNames) > s.conf.ClusterSize && rand.Float64() < s.conf.InterClusterChance {
if len(nodeNames) > s.configuration.ClusterSize && rand.Float64() < s.configuration.InterClusterChance {
gossipNodes[len(gossipNodes)-1] = s.cluster.GetInterCluster(nodeNames, publicKey.String())
}
}
var succeeded bool = false
// Do this synchronously to conserve bandwidth
for _, node := range gossipNodes {
correspondingPeer, err := correspondingMesh.GetNode(node)
var wait sync.WaitGroup
if correspondingPeer == nil || err != nil {
logging.Log.WriteErrorf("node %s does not exist", node)
continue
for index, node := range gossipNodes {
wait.Add(1)
syncNode := func(i int) {
correspondingPeer, err := correspondingMesh.GetNode(node)
defer wait.Done()
if correspondingPeer == nil || err != nil {
logging.Log.WriteErrorf("node %s does not exist", node)
return
}
err = s.requester.SyncMesh(correspondingMesh, correspondingPeer)
if err == nil || err == io.EOF {
succeeded = true
}
if err != nil {
logging.Log.WriteErrorf(err.Error())
}
}
err = s.requester.SyncMesh(correspondingMesh.GetMeshId(), correspondingPeer)
if err == nil || err == io.EOF {
succeeded = true
}
if err != nil {
logging.Log.WriteErrorf(err.Error())
}
go syncNode(index)
}
wait.Wait()
s.syncCount++
logging.Log.WriteInfof("sync time: %v", time.Since(before))
logging.Log.WriteInfof("number of syncs: %d", s.syncCount)
s.infectionCount = ((s.conf.InfectionCount + s.infectionCount - 1) % s.conf.InfectionCount)
s.infectionCount = ((s.configuration.InfectionCount + s.infectionCount - 1) % s.configuration.InfectionCount)
if !succeeded {
s.infectionCount++
}
changes := correspondingMesh.HasChanges()
correspondingMesh.SaveChanges()
s.lock.Lock()
s.lastSyncLock.Lock()
s.lastSync[correspondingMesh.GetMeshId()] = time.Now().Unix()
s.lock.Unlock()
return nil
s.lastSyncLock.Unlock()
return changes, nil
}
// 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(self mesh.MeshNode, mesh mesh.MeshProvider) error {
func (s *SyncerImpl) Pull(self mesh.MeshNode, mesh mesh.MeshProvider) (bool, error) {
peers := mesh.GetPeers()
pubKey, _ := self.GetPublicKey()
@ -152,7 +158,7 @@ func (s *SyncerImpl) Pull(self mesh.MeshNode, mesh mesh.MeshProvider) error {
if len(neighbour) == 0 {
logging.Log.WriteInfof("no neighbours")
return nil
return false, nil
}
logging.Log.WriteInfof("pulling from node %s", neighbour[0])
@ -160,59 +166,114 @@ func (s *SyncerImpl) Pull(self mesh.MeshNode, mesh mesh.MeshProvider) error {
pullNode, err := mesh.GetNode(neighbour[0])
if err != nil || pullNode == nil {
return fmt.Errorf("node %s does not exist in the mesh", neighbour[0])
return false, fmt.Errorf("node %s does not exist in the mesh", neighbour[0])
}
err = s.requester.SyncMesh(mesh.GetMeshId(), pullNode)
err = s.requester.SyncMesh(mesh, pullNode)
if err == nil || err == io.EOF {
s.lastSync[mesh.GetMeshId()] = time.Now().Unix()
} else {
return err
return false, err
}
s.syncCount++
return nil
changes := mesh.HasChanges()
return changes, nil
}
// SyncMeshes: Sync all meshes
func (s *SyncerImpl) SyncMeshes() error {
var wg sync.WaitGroup
for _, mesh := range s.manager.GetMeshes() {
meshes := s.meshManager.GetMeshes()
s.lastPollLock.Lock()
meshesToSync := lib.Filter(lib.MapValues(meshes), func(mesh mesh.MeshProvider) bool {
return time.Now().Unix()-s.lastPoll[mesh.GetMeshId()] >= int64(s.configuration.SyncInterval)
})
s.lastPollLock.Unlock()
changes := make(chan bool, len(meshesToSync))
for i := 0; i < len(meshesToSync); {
wg.Add(1)
sync := func() {
sync := func(index int) {
defer wg.Done()
err := s.Sync(mesh)
var hasChanges bool = false
mesh := meshesToSync[index]
hasChanges, err := s.Sync(mesh)
changes <- hasChanges
if err != nil {
logging.Log.WriteErrorf(err.Error())
}
s.lastPollLock.Lock()
s.lastPoll[mesh.GetMeshId()] = time.Now().Unix()
s.lastPollLock.Unlock()
}
go sync()
go sync(i)
i++
}
wg.Wait()
logging.Log.WriteInfof("updating the WireGuard configuration")
err := s.manager.ApplyConfig()
hasChanges := false
if err != nil {
logging.Log.WriteInfof("failed to update config %w", err)
for i := 0; i < len(changes); i++ {
if <-changes {
hasChanges = true
}
}
return nil
var err error
if hasChanges {
logging.Log.WriteInfof("updating the WireGuard configuration")
err = s.meshManager.ApplyConfig()
if err != nil {
logging.Log.WriteErrorf("failed to update config %s", err.Error())
}
err = s.meshManager.GetRouteManager().UpdateRoutes()
if err != nil {
logging.Log.WriteErrorf("update routes failed %s", err.Error())
}
}
return err
}
func NewSyncer(m mesh.MeshManager, conf *conf.DaemonConfiguration, r SyncRequester) Syncer {
cluster, _ := conn.NewConnCluster(conf.ClusterSize)
type NewSyncerParams struct {
MeshManager mesh.MeshManager
ConnectionManager conn.ConnectionManager
Configuration *conf.DaemonConfiguration
Requester SyncRequester
}
func NewSyncer(params *NewSyncerParams) Syncer {
cluster, _ := conn.NewConnCluster(params.Configuration.ClusterSize)
syncRequester := NewSyncRequester(NewSyncRequesterParams{
MeshManager: params.MeshManager,
ConnectionManager: params.ConnectionManager,
Configuration: params.Configuration,
})
return &SyncerImpl{
manager: m,
conf: conf,
requester: r,
meshManager: params.MeshManager,
configuration: params.Configuration,
requester: syncRequester,
infectionCount: 0,
syncCount: 0,
cluster: cluster,
lastSync: make(map[string]int64)}
lastSync: make(map[string]int64),
lastPoll: make(map[string]int64)}
}

View File

@ -10,7 +10,7 @@ import (
// SyncErrorHandler: Handles errors when attempting to sync
type SyncErrorHandler interface {
Handle(meshId string, endpoint string, err error) bool
Handle(mesh mesh.MeshProvider, endpoint string, err error) bool
}
// SyncErrorHandlerImpl Is an implementation of the SyncErrorHandler
@ -19,8 +19,7 @@ type SyncErrorHandlerImpl struct {
connManager conn.ConnectionManager
}
func (s *SyncErrorHandlerImpl) handleFailed(meshId string, nodeId string) bool {
mesh := s.meshManager.GetMesh(meshId)
func (s *SyncErrorHandlerImpl) handleFailed(mesh mesh.MeshProvider, nodeId string) bool {
mesh.Mark(nodeId)
node, err := mesh.GetNode(nodeId)
@ -30,13 +29,7 @@ func (s *SyncErrorHandlerImpl) handleFailed(meshId string, nodeId string) bool {
return true
}
func (s *SyncErrorHandlerImpl) handleDeadlineExceeded(meshId string, nodeId string) bool {
mesh := s.meshManager.GetMesh(meshId)
if mesh == nil {
return true
}
func (s *SyncErrorHandlerImpl) handleDeadlineExceeded(mesh mesh.MeshProvider, nodeId string) bool {
node, err := mesh.GetNode(nodeId)
if err != nil {
@ -47,16 +40,16 @@ func (s *SyncErrorHandlerImpl) handleDeadlineExceeded(meshId string, nodeId stri
return true
}
func (s *SyncErrorHandlerImpl) Handle(meshId string, nodeId string, err error) bool {
func (s *SyncErrorHandlerImpl) Handle(mesh mesh.MeshProvider, nodeId string, err error) bool {
errStatus, _ := status.FromError(err)
logging.Log.WriteInfof("Handled gRPC error: %s", errStatus.Message())
switch errStatus.Code() {
case codes.Unavailable, codes.Unknown, codes.Internal, codes.NotFound:
return s.handleFailed(meshId, nodeId)
return s.handleFailed(mesh, nodeId)
case codes.DeadlineExceeded:
return s.handleDeadlineExceeded(meshId, nodeId)
return s.handleDeadlineExceeded(mesh, nodeId)
}
return false

View File

@ -2,11 +2,11 @@ package sync
import (
"context"
"errors"
"io"
"time"
"github.com/tim-beatham/smegmesh/pkg/ctrlserver"
"github.com/tim-beatham/smegmesh/pkg/conf"
"github.com/tim-beatham/smegmesh/pkg/conn"
logging "github.com/tim-beatham/smegmesh/pkg/log"
"github.com/tim-beatham/smegmesh/pkg/mesh"
"github.com/tim-beatham/smegmesh/pkg/rpc"
@ -14,64 +14,32 @@ import (
// SyncRequester: coordinates the syncing of meshes
type SyncRequester interface {
GetMesh(meshId string, ifName string, port int, endPoint string) error
SyncMesh(meshid string, meshNode mesh.MeshNode) error
SyncMesh(mesh mesh.MeshProvider, meshNode mesh.MeshNode) error
}
type SyncRequesterImpl struct {
server *ctrlserver.MeshCtrlServer
errorHdlr SyncErrorHandler
manager mesh.MeshManager
connectionManager conn.ConnectionManager
configuration *conf.DaemonConfiguration
errorHdlr SyncErrorHandler
}
// GetMesh: Retrieves the local state of the mesh at the endpoint
func (s *SyncRequesterImpl) GetMesh(meshId string, ifName string, port int, endPoint string) error {
peerConnection, err := s.server.ConnectionManager.GetConnection(endPoint)
if err != nil {
return err
}
client, err := peerConnection.GetClient()
if err != nil {
return err
}
c := rpc.NewSyncServiceClient(client)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
reply, err := c.GetConf(ctx, &rpc.GetConfRequest{MeshId: meshId})
if err != nil {
return err
}
err = s.server.MeshManager.AddMesh(&mesh.AddMeshParams{
MeshId: meshId,
WgPort: port,
MeshBytes: reply.Mesh,
})
return err
}
func (s *SyncRequesterImpl) handleErr(meshId, pubKey string, err error) error {
ok := s.errorHdlr.Handle(meshId, pubKey, err)
// handleErr: handleGrpc errors
func (s *SyncRequesterImpl) handleErr(mesh mesh.MeshProvider, pubKey string, err error) error {
ok := s.errorHdlr.Handle(mesh, pubKey, err)
if ok {
return nil
}
return err
}
// SyncMesh: Proactively send a sync request to the other mesh
func (s *SyncRequesterImpl) SyncMesh(meshId string, meshNode mesh.MeshNode) error {
func (s *SyncRequesterImpl) SyncMesh(mesh mesh.MeshProvider, meshNode mesh.MeshNode) error {
endpoint := meshNode.GetHostEndpoint()
pubKey, _ := meshNode.GetPublicKey()
peerConnection, err := s.server.ConnectionManager.GetConnection(endpoint)
peerConnection, err := s.connectionManager.GetConnection(endpoint)
if err != nil {
return err
@ -83,15 +51,9 @@ func (s *SyncRequesterImpl) SyncMesh(meshId string, meshNode mesh.MeshNode) erro
return err
}
mesh := s.server.MeshManager.GetMesh(meshId)
if mesh == nil {
return errors.New("mesh does not exist")
}
c := rpc.NewSyncServiceClient(client)
syncTimeOut := float64(s.server.Conf.SyncTime) * float64(time.Second)
syncTimeOut := float64(s.configuration.SyncInterval) * float64(time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(syncTimeOut))
defer cancel()
@ -99,10 +61,10 @@ func (s *SyncRequesterImpl) SyncMesh(meshId string, meshNode mesh.MeshNode) erro
err = s.syncMesh(mesh, ctx, c)
if err != nil {
s.handleErr(meshId, pubKey.String(), err)
s.handleErr(mesh, pubKey.String(), err)
}
logging.Log.WriteInfof("Synced with node: %s meshId: %s\n", endpoint, meshId)
logging.Log.WriteInfof("synced with node: %s meshId: %s\n", endpoint, mesh.GetMeshId())
return err
}
@ -127,7 +89,7 @@ func (s *SyncRequesterImpl) syncMesh(mesh mesh.MeshProvider, ctx context.Context
in, err := stream.Recv()
if err != nil && err != io.EOF {
logging.Log.WriteInfof("Stream recv error: %s\n", err.Error())
logging.Log.WriteInfof("stream recv error: %s\n", err.Error())
return err
}
@ -136,7 +98,7 @@ func (s *SyncRequesterImpl) syncMesh(mesh mesh.MeshProvider, ctx context.Context
}
if err != nil {
logging.Log.WriteInfof("Syncer recv error: %s\n", err.Error())
logging.Log.WriteInfof("syncer recv error: %s\n", err.Error())
return err
}
@ -150,7 +112,17 @@ func (s *SyncRequesterImpl) syncMesh(mesh mesh.MeshProvider, ctx context.Context
return nil
}
func NewSyncRequester(s *ctrlserver.MeshCtrlServer) SyncRequester {
errorHdlr := NewSyncErrorHandler(s.MeshManager, s.ConnectionManager)
return &SyncRequesterImpl{server: s, errorHdlr: errorHdlr}
type NewSyncRequesterParams struct {
MeshManager mesh.MeshManager
ConnectionManager conn.ConnectionManager
Configuration *conf.DaemonConfiguration
}
func NewSyncRequester(params NewSyncRequesterParams) SyncRequester {
errorHdlr := NewSyncErrorHandler(params.MeshManager, params.ConnectionManager)
return &SyncRequesterImpl{manager: params.MeshManager,
connectionManager: params.ConnectionManager,
configuration: params.Configuration,
errorHdlr: errorHdlr,
}
}

View File

@ -1,18 +0,0 @@
package sync
import (
"github.com/tim-beatham/smegmesh/pkg/ctrlserver"
"github.com/tim-beatham/smegmesh/pkg/lib"
)
// Run implements SyncScheduler.
func syncFunction(syncer Syncer) lib.TimerFunc {
return func() error {
syncer.SyncMeshes()
return nil
}
}
func NewSyncScheduler(s *ctrlserver.MeshCtrlServer, syncRequester SyncRequester, syncer Syncer) *lib.Timer {
return lib.NewTimer(syncFunction(syncer), s.Conf.SyncTime)
}

View File

@ -6,19 +6,18 @@ import (
"errors"
"io"
"github.com/tim-beatham/smegmesh/pkg/ctrlserver"
"github.com/tim-beatham/smegmesh/pkg/mesh"
"github.com/tim-beatham/smegmesh/pkg/rpc"
)
type SyncServiceImpl struct {
rpc.UnimplementedSyncServiceServer
Server *ctrlserver.MeshCtrlServer
MeshManager mesh.MeshManager
}
// GetMesh: Gets a nodes local mesh configuration as a CRDT
func (s *SyncServiceImpl) GetConf(context context.Context, request *rpc.GetConfRequest) (*rpc.GetConfReply, error) {
mesh := s.Server.MeshManager.GetMesh(request.MeshId)
mesh := s.MeshManager.GetMesh(request.MeshId)
if mesh == nil {
return nil, errors.New("mesh does not exist")
@ -56,7 +55,7 @@ func (s *SyncServiceImpl) SyncMesh(stream rpc.SyncService_SyncMeshServer) error
if len(meshId) == 0 {
meshId = in.MeshId
mesh := s.Server.MeshManager.GetMesh(meshId)
mesh := s.MeshManager.GetMesh(meshId)
if mesh == nil {
return errors.New("mesh does not exist")
@ -92,7 +91,3 @@ func (s *SyncServiceImpl) SyncMesh(stream rpc.SyncService_SyncMeshServer) error
}
}
}
func NewSyncService(server *ctrlserver.MeshCtrlServer) *SyncServiceImpl {
return &SyncServiceImpl{Server: server}
}

View File

@ -1,15 +0,0 @@
package timer
import (
"github.com/tim-beatham/smegmesh/pkg/ctrlserver"
"github.com/tim-beatham/smegmesh/pkg/lib"
logging "github.com/tim-beatham/smegmesh/pkg/log"
)
func NewTimestampScheduler(ctrlServer *ctrlserver.MeshCtrlServer) lib.Timer {
timerFunc := func() error {
logging.Log.WriteInfof("Updated Timestamp")
return ctrlServer.MeshManager.UpdateTimeStamp()
}
return *lib.NewTimer(timerFunc, ctrlServer.Conf.HeartBeat)
}