mirror of
https://github.com/tim-beatham/smegmesh.git
synced 2025-07-04 06:20:32 +02:00
Compare commits
5 Commits
bugfix-pul
...
61-improve
Author | SHA1 | Date | |
---|---|---|---|
0cc3141b58 | |||
ceb43a1db1 | |||
bed59f120f | |||
8aab4e99d8 | |||
cf4be1ccab |
@ -15,7 +15,6 @@ const SockAddr = "/tmp/wgmesh_ipc.sock"
|
|||||||
type CreateMeshParams struct {
|
type CreateMeshParams struct {
|
||||||
Client *ipcRpc.Client
|
Client *ipcRpc.Client
|
||||||
Endpoint string
|
Endpoint string
|
||||||
Role string
|
|
||||||
WgArgs ipc.WireGuardArgs
|
WgArgs ipc.WireGuardArgs
|
||||||
AdvertiseRoutes bool
|
AdvertiseRoutes bool
|
||||||
AdvertiseDefault bool
|
AdvertiseDefault bool
|
||||||
@ -56,7 +55,6 @@ type JoinMeshParams struct {
|
|||||||
MeshId string
|
MeshId string
|
||||||
IpAddress string
|
IpAddress string
|
||||||
Endpoint string
|
Endpoint string
|
||||||
Role string
|
|
||||||
WgArgs ipc.WireGuardArgs
|
WgArgs ipc.WireGuardArgs
|
||||||
AdvertiseRoutes bool
|
AdvertiseRoutes bool
|
||||||
AdvertiseDefault bool
|
AdvertiseDefault bool
|
||||||
@ -203,6 +201,7 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
var newMeshRole *string = newMeshCmd.Selector("r", "role", []string{"peer", "client"}, &argparse.Options{
|
var newMeshRole *string = newMeshCmd.Selector("r", "role", []string{"peer", "client"}, &argparse.Options{
|
||||||
|
Default: "peer",
|
||||||
Help: "Role in the mesh network. A value of peer means that the node is publicly routeable and thus considered" +
|
Help: "Role in the mesh network. A value of peer means that the node is publicly routeable and thus considered" +
|
||||||
" in the gossip protocol. Client means that the node is not publicly routeable and is not a candidate in the gossip" +
|
" in the gossip protocol. Client means that the node is not publicly routeable and is not a candidate in the gossip" +
|
||||||
" protocol",
|
" protocol",
|
||||||
@ -235,7 +234,7 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
var joinMeshRole *string = joinMeshCmd.Selector("r", "role", []string{"peer", "client"}, &argparse.Options{
|
var joinMeshRole *string = joinMeshCmd.Selector("r", "role", []string{"peer", "client"}, &argparse.Options{
|
||||||
Default: "Peer",
|
Default: "peer",
|
||||||
Help: "Role in the mesh network. A value of peer means that the node is publicly routeable and thus considered" +
|
Help: "Role in the mesh network. A value of peer means that the node is publicly routeable and thus considered" +
|
||||||
" in the gossip protocol. Client means that the node is not publicly routeable and is not a candidate in the gossip" +
|
" in the gossip protocol. Client means that the node is not publicly routeable and is not a candidate in the gossip" +
|
||||||
" protocol",
|
" protocol",
|
||||||
@ -319,7 +318,6 @@ func main() {
|
|||||||
fmt.Println(createMesh(&CreateMeshParams{
|
fmt.Println(createMesh(&CreateMeshParams{
|
||||||
Client: client,
|
Client: client,
|
||||||
Endpoint: *newMeshEndpoint,
|
Endpoint: *newMeshEndpoint,
|
||||||
Role: *newMeshRole,
|
|
||||||
WgArgs: ipc.WireGuardArgs{
|
WgArgs: ipc.WireGuardArgs{
|
||||||
Endpoint: *newMeshEndpoint,
|
Endpoint: *newMeshEndpoint,
|
||||||
Role: *newMeshRole,
|
Role: *newMeshRole,
|
||||||
@ -341,7 +339,6 @@ func main() {
|
|||||||
IpAddress: *joinMeshIpAddress,
|
IpAddress: *joinMeshIpAddress,
|
||||||
MeshId: *joinMeshId,
|
MeshId: *joinMeshId,
|
||||||
Endpoint: *joinMeshEndpoint,
|
Endpoint: *joinMeshEndpoint,
|
||||||
Role: *joinMeshRole,
|
|
||||||
WgArgs: ipc.WireGuardArgs{
|
WgArgs: ipc.WireGuardArgs{
|
||||||
Endpoint: *joinMeshEndpoint,
|
Endpoint: *joinMeshEndpoint,
|
||||||
Role: *joinMeshRole,
|
Role: *joinMeshRole,
|
||||||
|
@ -40,7 +40,11 @@ func (c *CrdtMeshManager) AddNode(node mesh.MeshNode) {
|
|||||||
crdt.Services = make(map[string]string)
|
crdt.Services = make(map[string]string)
|
||||||
crdt.Timestamp = time.Now().Unix()
|
crdt.Timestamp = time.Now().Unix()
|
||||||
|
|
||||||
c.doc.Path("nodes").Map().Set(crdt.PublicKey, crdt)
|
err := c.doc.Path("nodes").Map().Set(crdt.PublicKey, crdt)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logging.Log.WriteInfof("error")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CrdtMeshManager) isPeer(nodeId string) bool {
|
func (c *CrdtMeshManager) isPeer(nodeId string) bool {
|
||||||
@ -161,7 +165,7 @@ func (m *CrdtMeshManager) GetNode(endpoint string) (mesh.MeshNode, error) {
|
|||||||
node, err := m.doc.Path("nodes").Map().Get(endpoint)
|
node, err := m.doc.Path("nodes").Map().Get(endpoint)
|
||||||
|
|
||||||
if node.Kind() != automerge.KindMap {
|
if node.Kind() != automerge.KindMap {
|
||||||
return nil, fmt.Errorf("GetNode: something went wrong %s is not a map type")
|
return nil, fmt.Errorf("getnode: node is not a map")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package automerge
|
package automerge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"slices"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -22,7 +22,7 @@ func setUpTests() *TestParams {
|
|||||||
DevName: "wg0",
|
DevName: "wg0",
|
||||||
Port: 5000,
|
Port: 5000,
|
||||||
Client: nil,
|
Client: nil,
|
||||||
Conf: conf.DaemonConfiguration{},
|
Conf: &conf.WgConfiguration{},
|
||||||
})
|
})
|
||||||
|
|
||||||
return &TestParams{
|
return &TestParams{
|
||||||
@ -31,22 +31,26 @@ func setUpTests() *TestParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getTestNode() mesh.MeshNode {
|
func getTestNode() mesh.MeshNode {
|
||||||
|
pubKey, _ := wgtypes.GeneratePrivateKey()
|
||||||
|
|
||||||
return &MeshNodeCrdt{
|
return &MeshNodeCrdt{
|
||||||
HostEndpoint: "public-endpoint:8080",
|
HostEndpoint: "public-endpoint:8080",
|
||||||
WgEndpoint: "public-endpoint:21906",
|
WgEndpoint: "public-endpoint:21906",
|
||||||
WgHost: "3e9a:1fb3:5e50:8173:9690:f917:b1ab:d218/128",
|
WgHost: "3e9a:1fb3:5e50:8173:9690:f917:b1ab:d218/128",
|
||||||
PublicKey: "AAAAAAAAAAAA",
|
PublicKey: pubKey.String(),
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: time.Now().Unix(),
|
||||||
Description: "A node that we are adding",
|
Description: "A node that we are adding",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTestNode2() mesh.MeshNode {
|
func getTestNode2() mesh.MeshNode {
|
||||||
|
pubKey, _ := wgtypes.GeneratePrivateKey()
|
||||||
|
|
||||||
return &MeshNodeCrdt{
|
return &MeshNodeCrdt{
|
||||||
HostEndpoint: "public-endpoint:8081",
|
HostEndpoint: "public-endpoint:8081",
|
||||||
WgEndpoint: "public-endpoint:21907",
|
WgEndpoint: "public-endpoint:21907",
|
||||||
WgHost: "3e9a:1fb3:5e50:8173:9690:f917:b1ab:d219/128",
|
WgHost: "3e9a:1fb3:5e50:8173:9690:f917:b1ab:d219/128",
|
||||||
PublicKey: "BBBBBBBBB",
|
PublicKey: pubKey.String(),
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: time.Now().Unix(),
|
||||||
Description: "A node that we are adding",
|
Description: "A node that we are adding",
|
||||||
}
|
}
|
||||||
@ -54,9 +58,11 @@ func getTestNode2() mesh.MeshNode {
|
|||||||
|
|
||||||
func TestAddNodeNodeExists(t *testing.T) {
|
func TestAddNodeNodeExists(t *testing.T) {
|
||||||
testParams := setUpTests()
|
testParams := setUpTests()
|
||||||
testParams.manager.AddNode(getTestNode())
|
node := getTestNode()
|
||||||
|
testParams.manager.AddNode(node)
|
||||||
|
|
||||||
node, err := testParams.manager.GetNode("public-endpoint:8080")
|
pubKey, _ := node.GetPublicKey()
|
||||||
|
node, err := testParams.manager.GetNode(pubKey.String())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -70,25 +76,28 @@ func TestAddNodeNodeExists(t *testing.T) {
|
|||||||
func TestAddNodeAddRoute(t *testing.T) {
|
func TestAddNodeAddRoute(t *testing.T) {
|
||||||
testParams := setUpTests()
|
testParams := setUpTests()
|
||||||
testNode := getTestNode()
|
testNode := getTestNode()
|
||||||
testParams.manager.AddNode(testNode)
|
pubKey, _ := testNode.GetPublicKey()
|
||||||
testParams.manager.AddRoutes(testNode.GetHostEndpoint(), "fd:1c64:1d00::/48")
|
|
||||||
|
|
||||||
updatedNode, err := testParams.manager.GetNode(testNode.GetHostEndpoint())
|
_, destination, _ := net.ParseCIDR("fd:1c64:1d00::/48")
|
||||||
|
|
||||||
|
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())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if updatedNode == nil {
|
if updatedNode == nil {
|
||||||
t.Fatalf(`Node does not exist in the mesh`)
|
t.Fatalf(`node does not exist in the mesh`)
|
||||||
}
|
}
|
||||||
|
|
||||||
routes := updatedNode.GetRoutes()
|
routes := updatedNode.GetRoutes()
|
||||||
|
|
||||||
if !slices.Contains(routes, "fd:1c64:1d00::/48") {
|
|
||||||
t.Fatal("Route node not added")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(routes) != 1 {
|
if len(routes) != 1 {
|
||||||
t.Fatal(`Route length mismatch`)
|
t.Fatal(`Route length mismatch`)
|
||||||
}
|
}
|
||||||
@ -253,7 +262,9 @@ func TestUpdateTimeStampNodeExists(t *testing.T) {
|
|||||||
node := getTestNode()
|
node := getTestNode()
|
||||||
|
|
||||||
testParams.manager.AddNode(node)
|
testParams.manager.AddNode(node)
|
||||||
err := testParams.manager.UpdateTimeStamp(node.GetHostEndpoint())
|
pubKey, _ := node.GetPublicKey()
|
||||||
|
|
||||||
|
err := testParams.manager.UpdateTimeStamp(pubKey.String())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -282,7 +293,13 @@ func TestSetDescriptionNodeExists(t *testing.T) {
|
|||||||
func TestAddRoutesNodeDoesNotExist(t *testing.T) {
|
func TestAddRoutesNodeDoesNotExist(t *testing.T) {
|
||||||
testParams := setUpTests()
|
testParams := setUpTests()
|
||||||
|
|
||||||
err := testParams.manager.AddRoutes("AAAAA", "fd:1c64:1d00::/48")
|
_, destination, _ := net.ParseCIDR("fd:1c64:1d00::/48")
|
||||||
|
|
||||||
|
err := testParams.manager.AddRoutes("AAAAA", &mesh.RouteStub{
|
||||||
|
Destination: destination,
|
||||||
|
HopCount: 0,
|
||||||
|
Path: make([]string, 0),
|
||||||
|
})
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -293,16 +310,11 @@ func TestCompareComparesByPublicKey(t *testing.T) {
|
|||||||
node := getTestNode().(*MeshNodeCrdt)
|
node := getTestNode().(*MeshNodeCrdt)
|
||||||
node2 := getTestNode2().(*MeshNodeCrdt)
|
node2 := getTestNode2().(*MeshNodeCrdt)
|
||||||
|
|
||||||
if node.Compare(node2) != -1 {
|
pubKey1, _ := node.GetPublicKey()
|
||||||
t.Fatalf(`node is alphabetically before node2`)
|
pubKey2, _ := node2.GetPublicKey()
|
||||||
}
|
|
||||||
|
|
||||||
if node2.Compare(node) != 1 {
|
if node.Compare(node2) != strings.Compare(pubKey1.String(), pubKey2.String()) {
|
||||||
t.Fatalf(`node is alphabetical;y before node2`)
|
t.Fatalf(`compare failed`)
|
||||||
}
|
|
||||||
|
|
||||||
if node.Compare(node) != 0 {
|
|
||||||
t.Fatalf(`node is equal to node`)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ type MeshNodeFactory struct {
|
|||||||
func (f *MeshNodeFactory) Build(params *mesh.MeshNodeFactoryParams) mesh.MeshNode {
|
func (f *MeshNodeFactory) Build(params *mesh.MeshNodeFactoryParams) mesh.MeshNode {
|
||||||
hostName := f.getAddress(params)
|
hostName := f.getAddress(params)
|
||||||
|
|
||||||
grpcEndpoint := fmt.Sprintf("%s:%s", hostName, f.Config.GrpcPort)
|
grpcEndpoint := fmt.Sprintf("%s:%d", hostName, f.Config.GrpcPort)
|
||||||
|
|
||||||
if *params.MeshConfig.Role == conf.CLIENT_ROLE {
|
if *params.MeshConfig.Role == conf.CLIENT_ROLE {
|
||||||
grpcEndpoint = "-"
|
grpcEndpoint = "-"
|
||||||
|
@ -26,8 +26,8 @@ const (
|
|||||||
type IPDiscovery string
|
type IPDiscovery string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PUBLIC_IP_DISCOVERY = "public"
|
PUBLIC_IP_DISCOVERY IPDiscovery = "public"
|
||||||
DNS_IP_DISCOVERY = "dns"
|
DNS_IP_DISCOVERY IPDiscovery = "dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WgConfiguration contains per-mesh WireGuard configuration. Contains poitner types only so we can
|
// WgConfiguration contains per-mesh WireGuard configuration. Contains poitner types only so we can
|
||||||
@ -61,11 +61,11 @@ type WgConfiguration struct {
|
|||||||
|
|
||||||
type DaemonConfiguration struct {
|
type DaemonConfiguration struct {
|
||||||
// CertificatePath is the path to the certificate to use in mTLS
|
// CertificatePath is the path to the certificate to use in mTLS
|
||||||
CertificatePath string `yaml:"certificatePath" validate:"required,file"`
|
CertificatePath string `yaml:"certificatePath" validate:"required"`
|
||||||
// PrivateKeypath is the path to the clients private key in mTLS
|
// PrivateKeypath is the path to the clients private key in mTLS
|
||||||
PrivateKeyPath string `yaml:"privateKeyPath" validate:"required,file"`
|
PrivateKeyPath string `yaml:"privateKeyPath" validate:"required"`
|
||||||
// CaCeritifcatePath path to the certificate of the trust certificate authority
|
// CaCeritifcatePath path to the certificate of the trust certificate authority
|
||||||
CaCertificatePath string `yaml:"caCertificatePath" validate:"required,file"`
|
CaCertificatePath string `yaml:"caCertificatePath" validate:"required"`
|
||||||
// SkipCertVerification specify to skip certificate verification. Should only be used
|
// SkipCertVerification specify to skip certificate verification. Should only be used
|
||||||
// in test environments
|
// in test environments
|
||||||
SkipCertVerification bool `yaml:"skipCertVerification"`
|
SkipCertVerification bool `yaml:"skipCertVerification"`
|
||||||
@ -83,9 +83,9 @@ type DaemonConfiguration struct {
|
|||||||
// send to every member in the mesh
|
// send to every member in the mesh
|
||||||
KeepAliveTime int `yaml:"keepAliveTime" validate:"required,gte=1"`
|
KeepAliveTime int `yaml:"keepAliveTime" validate:"required,gte=1"`
|
||||||
// ClusterSize specifies how many neighbours you should synchronise with per round
|
// ClusterSize specifies how many neighbours you should synchronise with per round
|
||||||
ClusterSize int `yaml:"clusterSize" valdiate:"required,gt=0"`
|
ClusterSize int `yaml:"clusterSize" validate:"gte=1"`
|
||||||
// InterClusterChance specifies the probabilityof inter-cluster communication in a sync round
|
// InterClusterChance specifies the probabilityof inter-cluster communication in a sync round
|
||||||
InterClusterChance float64 `yaml:"interClusterChance" valdiate:"required,gt=0"`
|
InterClusterChance float64 `yaml:"interClusterChance" validate:"gt=0"`
|
||||||
// BranchRate specifies the number of nodes to synchronise with when a node has
|
// BranchRate specifies the number of nodes to synchronise with when a node has
|
||||||
// new changes to send to the mesh
|
// new changes to send to the mesh
|
||||||
BranchRate int `yaml:"branchRate" validate:"required,gte=1"`
|
BranchRate int `yaml:"branchRate" validate:"required,gte=1"`
|
||||||
|
@ -1,13 +1,40 @@
|
|||||||
package conf
|
package conf
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func getExampleConfiguration() *DaemonConfiguration {
|
func getExampleConfiguration() *DaemonConfiguration {
|
||||||
|
discovery := PUBLIC_IP_DISCOVERY
|
||||||
|
advertiseRoutes := false
|
||||||
|
advertiseDefaultRoute := false
|
||||||
|
endpoint := "abc.com:123"
|
||||||
|
nodeType := CLIENT_ROLE
|
||||||
|
keepAliveWg := 0
|
||||||
|
|
||||||
return &DaemonConfiguration{
|
return &DaemonConfiguration{
|
||||||
CertificatePath: "./cert/cert.pem",
|
CertificatePath: "../../../cert/cert.pem",
|
||||||
PrivateKeyPath: "./cert/key.pem",
|
PrivateKeyPath: "../../../cert/priv.pem",
|
||||||
CaCertificatePath: "./cert/ca.pems",
|
CaCertificatePath: "../../../cert/cacert.pem",
|
||||||
SkipCertVerification: true,
|
SkipCertVerification: true,
|
||||||
|
GrpcPort: 25,
|
||||||
|
Timeout: 5,
|
||||||
|
Profile: false,
|
||||||
|
StubWg: false,
|
||||||
|
SyncRate: 2,
|
||||||
|
KeepAliveTime: 2,
|
||||||
|
ClusterSize: 64,
|
||||||
|
InterClusterChance: 0.15,
|
||||||
|
BranchRate: 3,
|
||||||
|
InfectionCount: 2,
|
||||||
|
BaseConfiguration: WgConfiguration{
|
||||||
|
IPDiscovery: &discovery,
|
||||||
|
AdvertiseRoutes: &advertiseRoutes,
|
||||||
|
AdvertiseDefaultRoute: &advertiseDefaultRoute,
|
||||||
|
Endpoint: &endpoint,
|
||||||
|
Role: &nodeType,
|
||||||
|
KeepAliveWg: &keepAliveWg,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,9 +82,141 @@ func TestConfigurationGrpcPortEmpty(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIPDiscoveryNotSet(t *testing.T) {
|
||||||
|
conf := getExampleConfiguration()
|
||||||
|
ipDiscovery := IPDiscovery("djdsjdskd")
|
||||||
|
conf.BaseConfiguration.IPDiscovery = &ipDiscovery
|
||||||
|
|
||||||
|
err := ValidateDaemonConfiguration(conf)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAdvertiseRoutesNotSet(t *testing.T) {
|
||||||
|
conf := getExampleConfiguration()
|
||||||
|
conf.BaseConfiguration.AdvertiseRoutes = nil
|
||||||
|
|
||||||
|
err := ValidateDaemonConfiguration(conf)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAdvertiseDefaultRouteNotSet(t *testing.T) {
|
||||||
|
conf := getExampleConfiguration()
|
||||||
|
conf.BaseConfiguration.AdvertiseDefaultRoute = nil
|
||||||
|
|
||||||
|
err := ValidateDaemonConfiguration(conf)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeepAliveWgNegative(t *testing.T) {
|
||||||
|
conf := getExampleConfiguration()
|
||||||
|
keepAliveWg := -1
|
||||||
|
conf.BaseConfiguration.KeepAliveWg = &keepAliveWg
|
||||||
|
|
||||||
|
err := ValidateDaemonConfiguration(conf)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoleTypeNotValid(t *testing.T) {
|
||||||
|
conf := getExampleConfiguration()
|
||||||
|
role := NodeType("bruhhh")
|
||||||
|
conf.BaseConfiguration.Role = &role
|
||||||
|
|
||||||
|
err := ValidateDaemonConfiguration(conf)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoleTypeNotSpecified(t *testing.T) {
|
||||||
|
conf := getExampleConfiguration()
|
||||||
|
conf.BaseConfiguration.Role = nil
|
||||||
|
|
||||||
|
err := ValidateDaemonConfiguration(conf)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(`invalid role type`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBranchRateZero(t *testing.T) {
|
||||||
|
conf := getExampleConfiguration()
|
||||||
|
conf.BranchRate = 0
|
||||||
|
|
||||||
|
err := ValidateDaemonConfiguration(conf)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSyncRateZero(t *testing.T) {
|
||||||
|
conf := getExampleConfiguration()
|
||||||
|
conf.SyncRate = 0
|
||||||
|
|
||||||
|
err := ValidateDaemonConfiguration(conf)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeepAliveTimeZero(t *testing.T) {
|
||||||
|
conf := getExampleConfiguration()
|
||||||
|
conf.KeepAliveTime = 0
|
||||||
|
err := ValidateDaemonConfiguration(conf)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClusterSizeZero(t *testing.T) {
|
||||||
|
conf := getExampleConfiguration()
|
||||||
|
conf.ClusterSize = 0
|
||||||
|
err := ValidateDaemonConfiguration(conf)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInterClusterChanceZero(t *testing.T) {
|
||||||
|
conf := getExampleConfiguration()
|
||||||
|
conf.InterClusterChance = 0
|
||||||
|
|
||||||
|
err := ValidateDaemonConfiguration(conf)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInfectionCountOne(t *testing.T) {
|
||||||
|
conf := getExampleConfiguration()
|
||||||
|
conf.InfectionCount = 0
|
||||||
|
|
||||||
|
err := ValidateDaemonConfiguration(conf)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidConfiguration(t *testing.T) {
|
func TestValidConfiguration(t *testing.T) {
|
||||||
conf := getExampleConfiguration()
|
conf := getExampleConfiguration()
|
||||||
|
|
||||||
err := ValidateDaemonConfiguration(conf)
|
err := ValidateDaemonConfiguration(conf)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,84 +0,0 @@
|
|||||||
package conn
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"slices"
|
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ConnectionWindow maintains a sliding window of connections between users
|
|
||||||
type ConnectionWindow interface {
|
|
||||||
// GetWindow is a list of connections to choose from
|
|
||||||
GetWindow() []string
|
|
||||||
// SlideConnection removes a node from the window and adds a random node
|
|
||||||
// not already in the window. connList represents the list of possible
|
|
||||||
// connections to choose from
|
|
||||||
SlideConnection(connList []string) error
|
|
||||||
// PushConneciton is used when connection list less than window size.
|
|
||||||
PutConnection(conn []string) error
|
|
||||||
// IsFull returns true if the window is full. In which case we must slide the window
|
|
||||||
IsFull() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConnectionWindowImpl struct {
|
|
||||||
window []string
|
|
||||||
windowSize int
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetWindow gets the current list of active connections in
|
|
||||||
// the window
|
|
||||||
func (c *ConnectionWindowImpl) GetWindow() []string {
|
|
||||||
return c.window
|
|
||||||
}
|
|
||||||
|
|
||||||
// SlideConnection slides the connection window by one shuffling items
|
|
||||||
// in the windows
|
|
||||||
func (c *ConnectionWindowImpl) SlideConnection(connList []string) error {
|
|
||||||
// If the number of peer connections is less than the length of the window
|
|
||||||
// then exit early. Can't slide the window it should contain all nodes!
|
|
||||||
if len(c.window) < c.windowSize {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := func(node string) bool {
|
|
||||||
return !slices.Contains(c.window, node)
|
|
||||||
}
|
|
||||||
|
|
||||||
pool := lib.Filter(connList, filter)
|
|
||||||
newNode := lib.RandomSubsetOfLength(pool, 1)
|
|
||||||
|
|
||||||
if len(newNode) == 0 {
|
|
||||||
return errors.New("could not slide window")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := len(c.window) - 1; i >= 1; i-- {
|
|
||||||
c.window[i] = c.window[i-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
c.window[0] = newNode[0]
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PutConnection put random connections in the connection
|
|
||||||
func (c *ConnectionWindowImpl) PutConnection(connList []string) error {
|
|
||||||
if len(c.window) >= c.windowSize {
|
|
||||||
return errors.New("cannot place connection. Window full need to slide")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.window = lib.RandomSubsetOfLength(connList, c.windowSize)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ConnectionWindowImpl) IsFull() bool {
|
|
||||||
return len(c.window) >= c.windowSize
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConnectionWindow(windowLength int) ConnectionWindow {
|
|
||||||
window := &ConnectionWindowImpl{
|
|
||||||
window: make([]string, 0),
|
|
||||||
windowSize: windowLength,
|
|
||||||
}
|
|
||||||
|
|
||||||
return window
|
|
||||||
}
|
|
@ -1,132 +0,0 @@
|
|||||||
// hosts: utility for modifying the /etc/hosts file
|
|
||||||
package hosts
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HOSTS_FILE is the hosts file location
|
|
||||||
const HOSTS_FILE = "/etc/hosts"
|
|
||||||
|
|
||||||
const DOMAIN_HEADER = "#WG AUTO GENERATED HOSTS"
|
|
||||||
const DOMAIN_TRAILER = "#WG AUTO GENERATED HOSTS END"
|
|
||||||
|
|
||||||
type HostsEntry struct {
|
|
||||||
Alias string
|
|
||||||
Ip net.IP
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic interface to manipulate /etc/hosts file
|
|
||||||
type HostsManipulator interface {
|
|
||||||
// AddrAddr associates an aliasd with a given IP address
|
|
||||||
AddAddr(hosts ...HostsEntry)
|
|
||||||
// Remove deletes the entry from /etc/hosts
|
|
||||||
Remove(hosts ...HostsEntry)
|
|
||||||
// Writes the changes to /etc/hosts file
|
|
||||||
Write() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type HostsManipulatorImpl struct {
|
|
||||||
hosts map[string]HostsEntry
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddAddr implements HostsManipulator.
|
|
||||||
func (m *HostsManipulatorImpl) AddAddr(hosts ...HostsEntry) {
|
|
||||||
changed := false
|
|
||||||
|
|
||||||
for _, host := range hosts {
|
|
||||||
prev, ok := m.hosts[host.Ip.String()]
|
|
||||||
|
|
||||||
if !ok || prev.Alias != host.Alias {
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
m.hosts[host.Ip.String()] = host
|
|
||||||
}
|
|
||||||
|
|
||||||
if changed {
|
|
||||||
m.Write()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove implements HostsManipulator.
|
|
||||||
func (m *HostsManipulatorImpl) Remove(hosts ...HostsEntry) {
|
|
||||||
lenBefore := len(m.hosts)
|
|
||||||
|
|
||||||
for _, host := range hosts {
|
|
||||||
delete(m.hosts, host.Alias)
|
|
||||||
}
|
|
||||||
|
|
||||||
if lenBefore != len(m.hosts) {
|
|
||||||
m.Write()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *HostsManipulatorImpl) removeHosts() string {
|
|
||||||
hostsFile, err := os.ReadFile(HOSTS_FILE)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var contents strings.Builder
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(bytes.NewReader(hostsFile))
|
|
||||||
|
|
||||||
hostsSection := false
|
|
||||||
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
} else if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if !hostsSection && strings.Contains(line, DOMAIN_HEADER) {
|
|
||||||
hostsSection = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !hostsSection {
|
|
||||||
contents.WriteString(line + "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
if hostsSection && strings.Contains(line, DOMAIN_TRAILER) {
|
|
||||||
hostsSection = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if scanner.Err() != nil && scanner.Err() != io.EOF {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return contents.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write implements HostsManipulator
|
|
||||||
func (m *HostsManipulatorImpl) Write() error {
|
|
||||||
contents := m.removeHosts()
|
|
||||||
|
|
||||||
var nextHosts strings.Builder
|
|
||||||
nextHosts.WriteString(contents)
|
|
||||||
|
|
||||||
nextHosts.WriteString(DOMAIN_HEADER + "\n")
|
|
||||||
|
|
||||||
for _, host := range m.hosts {
|
|
||||||
nextHosts.WriteString(fmt.Sprintf("%s\t%s\n", host.Ip.String(), host.Alias))
|
|
||||||
}
|
|
||||||
|
|
||||||
nextHosts.WriteString(DOMAIN_TRAILER + "\n")
|
|
||||||
return os.WriteFile(HOSTS_FILE, []byte(nextHosts.String()), 0644)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHostsManipulator() HostsManipulator {
|
|
||||||
return &HostsManipulatorImpl{hosts: make(map[string]HostsEntry)}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
// lib contains helper functions for the implementation
|
|
||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"cmp"
|
|
||||||
"math"
|
|
||||||
|
|
||||||
"gonum.org/v1/gonum/stat"
|
|
||||||
"gonum.org/v1/gonum/stat/distuv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Modelling the distribution using a normal distribution get the count
|
|
||||||
// of the outliers
|
|
||||||
func GetOutliers[K cmp.Ordered](counts map[K]uint64, alpha float64) []K {
|
|
||||||
n := float64(len(counts))
|
|
||||||
|
|
||||||
keys := MapKeys(counts)
|
|
||||||
values := make([]float64, len(keys))
|
|
||||||
|
|
||||||
for index, key := range keys {
|
|
||||||
values[index] = float64(counts[key])
|
|
||||||
}
|
|
||||||
|
|
||||||
mean := stat.Mean(values, nil)
|
|
||||||
stdDev := stat.StdDev(values, nil)
|
|
||||||
|
|
||||||
moe := distuv.Normal{Mu: 0, Sigma: 1}.Quantile(1-alpha/2) * (stdDev / math.Sqrt(n))
|
|
||||||
|
|
||||||
lowerBound := mean - moe
|
|
||||||
|
|
||||||
var outliers []K
|
|
||||||
|
|
||||||
for i, count := range values {
|
|
||||||
if count < lowerBound {
|
|
||||||
outliers = append(outliers, keys[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return outliers
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
package mesh
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/hosts"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MeshAliasManager interface {
|
|
||||||
AddAliases(nodes []MeshNode)
|
|
||||||
RemoveAliases(node []MeshNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
type AliasManager struct {
|
|
||||||
hosts hosts.HostsManipulator
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddAliases: on node update or change add aliases to the hosts file
|
|
||||||
func (a *AliasManager) AddAliases(nodes []MeshNode) {
|
|
||||||
for _, node := range nodes {
|
|
||||||
if node.GetAlias() != "" {
|
|
||||||
a.hosts.AddAddr(hosts.HostsEntry{
|
|
||||||
Alias: fmt.Sprintf("%s.smeg", node.GetAlias()),
|
|
||||||
Ip: node.GetWgHost().IP,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveAliases: on node remove remove aliases from the hosts file
|
|
||||||
func (a *AliasManager) RemoveAliases(nodes []MeshNode) {
|
|
||||||
for _, node := range nodes {
|
|
||||||
if node.GetAlias() != "" {
|
|
||||||
a.hosts.Remove(hosts.HostsEntry{
|
|
||||||
Alias: fmt.Sprintf("%s.smeg", node.GetAlias()),
|
|
||||||
Ip: node.GetWgHost().IP,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAliasManager() MeshAliasManager {
|
|
||||||
return &AliasManager{
|
|
||||||
hosts: hosts.NewHostsManipulator(),
|
|
||||||
}
|
|
||||||
}
|
|
@ -32,7 +32,6 @@ type MeshManager interface {
|
|||||||
GetClient() *wgctrl.Client
|
GetClient() *wgctrl.Client
|
||||||
GetMeshes() map[string]MeshProvider
|
GetMeshes() map[string]MeshProvider
|
||||||
Close() error
|
Close() error
|
||||||
GetMonitor() MeshMonitor
|
|
||||||
GetNode(string, string) MeshNode
|
GetNode(string, string) MeshNode
|
||||||
GetRouteManager() RouteManager
|
GetRouteManager() RouteManager
|
||||||
}
|
}
|
||||||
@ -52,7 +51,6 @@ type MeshManagerImpl struct {
|
|||||||
idGenerator lib.IdGenerator
|
idGenerator lib.IdGenerator
|
||||||
ipAllocator ip.IPAllocator
|
ipAllocator ip.IPAllocator
|
||||||
interfaceManipulator wg.WgInterfaceManipulator
|
interfaceManipulator wg.WgInterfaceManipulator
|
||||||
Monitor MeshMonitor
|
|
||||||
cmdRunner cmd.CmdRunner
|
cmdRunner cmd.CmdRunner
|
||||||
OnDelete func(MeshProvider)
|
OnDelete func(MeshProvider)
|
||||||
}
|
}
|
||||||
@ -104,11 +102,6 @@ func (m *MeshManagerImpl) GetNode(meshid, nodeId string) MeshNode {
|
|||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMonitor implements MeshManager.
|
|
||||||
func (m *MeshManagerImpl) GetMonitor() MeshMonitor {
|
|
||||||
return m.Monitor
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateMeshParams contains the parameters required to create a mesh
|
// CreateMeshParams contains the parameters required to create a mesh
|
||||||
type CreateMeshParams struct {
|
type CreateMeshParams struct {
|
||||||
Port int
|
Port int
|
||||||
@ -521,11 +514,6 @@ func NewMeshManager(params *NewMeshManagerParams) MeshManager {
|
|||||||
m.ipAllocator = params.IPAllocator
|
m.ipAllocator = params.IPAllocator
|
||||||
m.interfaceManipulator = params.InterfaceManipulator
|
m.interfaceManipulator = params.InterfaceManipulator
|
||||||
|
|
||||||
m.Monitor = NewMeshMonitor(m)
|
|
||||||
|
|
||||||
aliasManager := NewAliasManager()
|
|
||||||
m.Monitor.AddUpdateCallback(aliasManager.AddAliases)
|
|
||||||
m.Monitor.AddRemoveCallback(aliasManager.RemoveAliases)
|
|
||||||
m.OnDelete = params.OnDelete
|
m.OnDelete = params.OnDelete
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,32 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func getMeshConfiguration() *conf.DaemonConfiguration {
|
func getMeshConfiguration() *conf.DaemonConfiguration {
|
||||||
|
advertiseRoutes := true
|
||||||
|
advertiseDefaultRoute := true
|
||||||
|
ipDiscovery := conf.PUBLIC_IP_DISCOVERY
|
||||||
|
role := conf.PEER_ROLE
|
||||||
|
|
||||||
return &conf.DaemonConfiguration{
|
return &conf.DaemonConfiguration{
|
||||||
GrpcPort: 8080,
|
GrpcPort: 8080,
|
||||||
|
CertificatePath: "./somecertificatepath",
|
||||||
|
PrivateKeyPath: "./someprivatekeypath",
|
||||||
|
CaCertificatePath: "./somecacertificatepath",
|
||||||
|
SkipCertVerification: true,
|
||||||
|
Timeout: 5,
|
||||||
|
Profile: false,
|
||||||
|
StubWg: true,
|
||||||
|
SyncRate: 2,
|
||||||
|
KeepAliveTime: 60,
|
||||||
|
ClusterSize: 64,
|
||||||
|
InterClusterChance: 0.15,
|
||||||
|
BranchRate: 3,
|
||||||
|
InfectionCount: 3,
|
||||||
|
BaseConfiguration: conf.WgConfiguration{
|
||||||
|
IPDiscovery: &ipDiscovery,
|
||||||
|
AdvertiseRoutes: &advertiseRoutes,
|
||||||
|
AdvertiseDefaultRoute: &advertiseDefaultRoute,
|
||||||
|
Role: &role,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +58,10 @@ func getMeshManager() MeshManager {
|
|||||||
func TestCreateMeshCreatesANewMeshProvider(t *testing.T) {
|
func TestCreateMeshCreatesANewMeshProvider(t *testing.T) {
|
||||||
manager := getMeshManager()
|
manager := getMeshManager()
|
||||||
|
|
||||||
meshId, err := manager.CreateMesh("wg0", 5000)
|
meshId, err := manager.CreateMesh(&CreateMeshParams{
|
||||||
|
Port: 0,
|
||||||
|
Conf: &conf.WgConfiguration{},
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -121,7 +148,7 @@ func TestAddSelfAddsSelfToTheMesh(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ok := mesh.GetNodes()["abc.com"]
|
_, ok := mesh.GetNodes()[manager.GetPublicKey().String()]
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf(`node has not been added`)
|
t.Fatalf(`node has not been added`)
|
||||||
@ -186,12 +213,51 @@ func TestLeaveMeshDeletesMesh(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetAlias(t *testing.T) {
|
||||||
|
manager := getMeshManager()
|
||||||
|
alias := "Firpo"
|
||||||
|
|
||||||
|
meshId, _ := manager.CreateMesh(&CreateMeshParams{
|
||||||
|
Port: 5000,
|
||||||
|
Conf: &conf.WgConfiguration{},
|
||||||
|
})
|
||||||
|
|
||||||
|
manager.AddSelf(&AddSelfParams{
|
||||||
|
MeshId: meshId,
|
||||||
|
WgPort: 5000,
|
||||||
|
Endpoint: "abc.com:8080",
|
||||||
|
})
|
||||||
|
|
||||||
|
err := manager.SetAlias(alias)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`failed to set the alias`)
|
||||||
|
}
|
||||||
|
|
||||||
|
self, err := manager.GetSelf(meshId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`failed to set the alias err: %s`, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if alias != self.GetAlias() {
|
||||||
|
t.Fatalf(`alias should be %s was %s`, alias, self.GetAlias())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSetDescription(t *testing.T) {
|
func TestSetDescription(t *testing.T) {
|
||||||
manager := getMeshManager()
|
manager := getMeshManager()
|
||||||
description := "wooooo"
|
description := "wooooo"
|
||||||
|
|
||||||
meshId1, _ := manager.CreateMesh(5000)
|
meshId1, _ := manager.CreateMesh(&CreateMeshParams{
|
||||||
meshId2, _ := manager.CreateMesh(5001)
|
Port: 5000,
|
||||||
|
Conf: &conf.WgConfiguration{},
|
||||||
|
})
|
||||||
|
|
||||||
|
meshId2, _ := manager.CreateMesh(&CreateMeshParams{
|
||||||
|
Port: 5001,
|
||||||
|
Conf: &conf.WgConfiguration{},
|
||||||
|
})
|
||||||
|
|
||||||
manager.AddSelf(&AddSelfParams{
|
manager.AddSelf(&AddSelfParams{
|
||||||
MeshId: meshId1,
|
MeshId: meshId1,
|
||||||
@ -209,13 +275,40 @@ func TestSetDescription(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(`failed to set the descriptions`)
|
t.Fatalf(`failed to set the descriptions`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self1, err := manager.GetSelf(meshId1)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`failed to set the description`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if description != self1.GetDescription() {
|
||||||
|
t.Fatalf(`description should be %s was %s`, description, self1.GetDescription())
|
||||||
|
}
|
||||||
|
|
||||||
|
self2, err := manager.GetSelf(meshId2)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`failed to set the description`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if description != self2.GetDescription() {
|
||||||
|
t.Fatalf(`description should be %s was %s`, description, self2.GetDescription())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateTimeStampUpdatesAllMeshes(t *testing.T) {
|
func TestUpdateTimeStampUpdatesAllMeshes(t *testing.T) {
|
||||||
manager := getMeshManager()
|
manager := getMeshManager()
|
||||||
|
|
||||||
meshId1, _ := manager.CreateMesh(5000)
|
meshId1, _ := manager.CreateMesh(&CreateMeshParams{
|
||||||
meshId2, _ := manager.CreateMesh(5001)
|
Port: 5000,
|
||||||
|
Conf: &conf.WgConfiguration{},
|
||||||
|
})
|
||||||
|
|
||||||
|
meshId2, _ := manager.CreateMesh(&CreateMeshParams{
|
||||||
|
Port: 5001,
|
||||||
|
Conf: &conf.WgConfiguration{},
|
||||||
|
})
|
||||||
|
|
||||||
manager.AddSelf(&AddSelfParams{
|
manager.AddSelf(&AddSelfParams{
|
||||||
MeshId: meshId1,
|
MeshId: meshId1,
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
package mesh
|
|
||||||
|
|
||||||
type OnChange = func([]MeshNode)
|
|
||||||
|
|
||||||
type MeshMonitor interface {
|
|
||||||
AddUpdateCallback(cb OnChange)
|
|
||||||
AddRemoveCallback(cb OnChange)
|
|
||||||
Trigger() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type MeshMonitorImpl struct {
|
|
||||||
updateCbs []OnChange
|
|
||||||
removeCbs []OnChange
|
|
||||||
nodes map[string]MeshNode
|
|
||||||
manager MeshManager
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger causes the mesh monitor to trigger all of
|
|
||||||
// the callbacks.
|
|
||||||
func (m *MeshMonitorImpl) Trigger() error {
|
|
||||||
changedNodes := make([]MeshNode, 0)
|
|
||||||
removedNodes := make([]MeshNode, 0)
|
|
||||||
|
|
||||||
nodes := make(map[string]MeshNode)
|
|
||||||
|
|
||||||
for _, mesh := range m.manager.GetMeshes() {
|
|
||||||
snapshot, err := mesh.GetMesh()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, node := range snapshot.GetNodes() {
|
|
||||||
previous, exists := m.nodes[node.GetWgHost().String()]
|
|
||||||
|
|
||||||
if !exists || !NodeEquals(previous, node) {
|
|
||||||
changedNodes = append(changedNodes, node)
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes[node.GetWgHost().String()] = node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, previous := range m.nodes {
|
|
||||||
_, ok := nodes[previous.GetWgHost().String()]
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
removedNodes = append(removedNodes, previous)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(removedNodes) > 0 {
|
|
||||||
for _, cb := range m.removeCbs {
|
|
||||||
cb(removedNodes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(changedNodes) > 0 {
|
|
||||||
for _, cb := range m.updateCbs {
|
|
||||||
cb(changedNodes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MeshMonitorImpl) AddUpdateCallback(cb OnChange) {
|
|
||||||
m.updateCbs = append(m.updateCbs, cb)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MeshMonitorImpl) AddRemoveCallback(cb OnChange) {
|
|
||||||
m.removeCbs = append(m.removeCbs, cb)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMeshMonitor(manager MeshManager) MeshMonitor {
|
|
||||||
return &MeshMonitorImpl{
|
|
||||||
updateCbs: make([]OnChange, 0),
|
|
||||||
nodes: make(map[string]MeshNode),
|
|
||||||
manager: manager,
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/wgmesh/pkg/conf"
|
||||||
|
"github.com/tim-beatham/wgmesh/pkg/lib"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl"
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
@ -19,6 +20,8 @@ type MeshNodeStub struct {
|
|||||||
routes []Route
|
routes []Route
|
||||||
identifier string
|
identifier string
|
||||||
description string
|
description string
|
||||||
|
alias string
|
||||||
|
services map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetType implements MeshNode.
|
// GetType implements MeshNode.
|
||||||
@ -32,8 +35,8 @@ func (*MeshNodeStub) GetServices() map[string]string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAlias implements MeshNode.
|
// GetAlias implements MeshNode.
|
||||||
func (*MeshNodeStub) GetAlias() string {
|
func (s *MeshNodeStub) GetAlias() string {
|
||||||
return ""
|
return s.alias
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MeshNodeStub) GetHostEndpoint() string {
|
func (m *MeshNodeStub) GetHostEndpoint() string {
|
||||||
@ -83,17 +86,26 @@ type MeshProviderStub struct {
|
|||||||
|
|
||||||
// GetConfiguration implements MeshProvider.
|
// GetConfiguration implements MeshProvider.
|
||||||
func (*MeshProviderStub) GetConfiguration() *conf.WgConfiguration {
|
func (*MeshProviderStub) GetConfiguration() *conf.WgConfiguration {
|
||||||
panic("unimplemented")
|
advertiseRoutes := true
|
||||||
|
advertiseDefaultRoute := true
|
||||||
|
ipDiscovery := conf.PUBLIC_IP_DISCOVERY
|
||||||
|
role := conf.PEER_ROLE
|
||||||
|
|
||||||
|
return &conf.WgConfiguration{
|
||||||
|
IPDiscovery: &ipDiscovery,
|
||||||
|
AdvertiseRoutes: &advertiseRoutes,
|
||||||
|
AdvertiseDefaultRoute: &advertiseDefaultRoute,
|
||||||
|
Role: &role,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark implements MeshProvider.
|
// Mark implements MeshProvider.
|
||||||
func (*MeshProviderStub) Mark(nodeId string) {
|
func (*MeshProviderStub) Mark(nodeId string) {
|
||||||
panic("unimplemented")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveNode implements MeshProvider.
|
// RemoveNode implements MeshProvider.
|
||||||
func (*MeshProviderStub) RemoveNode(nodeId string) error {
|
func (*MeshProviderStub) RemoveNode(nodeId string) error {
|
||||||
panic("unimplemented")
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*MeshProviderStub) GetRoutes(targetId string) (map[string]Route, error) {
|
func (*MeshProviderStub) GetRoutes(targetId string) (map[string]Route, error) {
|
||||||
@ -106,32 +118,53 @@ func (*MeshProviderStub) GetPeers() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetNode implements MeshProvider.
|
// GetNode implements MeshProvider.
|
||||||
func (*MeshProviderStub) GetNode(string) (MeshNode, error) {
|
func (m *MeshProviderStub) GetNode(nodeId string) (MeshNode, error) {
|
||||||
return nil, nil
|
return m.snapshot.nodes[nodeId], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeExists implements MeshProvider.
|
// NodeExists implements MeshProvider.
|
||||||
func (*MeshProviderStub) NodeExists(string) bool {
|
func (m *MeshProviderStub) NodeExists(nodeId string) bool {
|
||||||
return false
|
return m.snapshot.nodes[nodeId] != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddService implements MeshProvider.
|
// AddService implements MeshProvider.
|
||||||
func (*MeshProviderStub) AddService(nodeId string, key string, value string) error {
|
func (m *MeshProviderStub) AddService(nodeId string, key string, value string) error {
|
||||||
|
node := (m.snapshot.nodes[nodeId]).(*MeshNodeStub)
|
||||||
|
node.services[key] = value
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveService implements MeshProvider.
|
// RemoveService implements MeshProvider.
|
||||||
func (*MeshProviderStub) RemoveService(nodeId string, key string) error {
|
func (m *MeshProviderStub) RemoveService(nodeId string, key string) error {
|
||||||
|
node := (m.snapshot.nodes[nodeId]).(*MeshNodeStub)
|
||||||
|
delete(node.services, key)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAlias implements MeshProvider.
|
// SetAlias implements MeshProvider.
|
||||||
func (*MeshProviderStub) SetAlias(nodeId string, alias string) error {
|
func (m *MeshProviderStub) SetAlias(nodeId string, alias string) error {
|
||||||
|
node := (m.snapshot.nodes[nodeId]).(*MeshNodeStub)
|
||||||
|
node.alias = alias
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRoutes implements
|
||||||
|
func (m *MeshProviderStub) AddRoutes(nodeId string, route ...Route) error {
|
||||||
|
node := (m.snapshot.nodes[nodeId]).(*MeshNodeStub)
|
||||||
|
node.routes = append(node.routes, route...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveRoutes implements MeshProvider.
|
// RemoveRoutes implements MeshProvider.
|
||||||
func (*MeshProviderStub) RemoveRoutes(nodeId string, route ...Route) error {
|
func (m *MeshProviderStub) RemoveRoutes(nodeId string, route ...Route) error {
|
||||||
|
node := (m.snapshot.nodes[nodeId]).(*MeshNodeStub)
|
||||||
|
|
||||||
|
newRoutes := lib.Filter(node.routes, func(r1 Route) bool {
|
||||||
|
return !lib.Contains(route, func(r2 Route) bool {
|
||||||
|
return RouteEqual(r1, r2)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
node.routes = newRoutes
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,12 +174,15 @@ func (*MeshProviderStub) Prune() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateTimeStamp implements MeshProvider.
|
// UpdateTimeStamp implements MeshProvider.
|
||||||
func (*MeshProviderStub) UpdateTimeStamp(nodeId string) error {
|
func (m *MeshProviderStub) UpdateTimeStamp(nodeId string) error {
|
||||||
|
node := (m.snapshot.nodes[nodeId]).(*MeshNodeStub)
|
||||||
|
node.timeStamp = time.Now().Unix()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MeshProviderStub) AddNode(node MeshNode) {
|
func (s *MeshProviderStub) AddNode(node MeshNode) {
|
||||||
s.snapshot.nodes[node.GetHostEndpoint()] = node
|
pubKey, _ := node.GetPublicKey()
|
||||||
|
s.snapshot.nodes[pubKey.String()] = node
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MeshProviderStub) GetMesh() (MeshSnapshot, error) {
|
func (s *MeshProviderStub) GetMesh() (MeshSnapshot, error) {
|
||||||
@ -178,15 +214,13 @@ func (s *MeshProviderStub) HasChanges() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MeshProviderStub) AddRoutes(nodeId string, route ...Route) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *MeshProviderStub) GetSyncer() MeshSyncer {
|
func (s *MeshProviderStub) GetSyncer() MeshSyncer {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MeshProviderStub) SetDescription(nodeId string, description string) error {
|
func (s *MeshProviderStub) SetDescription(nodeId string, description string) error {
|
||||||
|
meshNode := (s.snapshot.nodes[nodeId]).(*MeshNodeStub)
|
||||||
|
meshNode.description = description
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,7 +243,7 @@ func (s *StubNodeFactory) Build(params *MeshNodeFactoryParams) MeshNode {
|
|||||||
return &MeshNodeStub{
|
return &MeshNodeStub{
|
||||||
hostEndpoint: params.Endpoint,
|
hostEndpoint: params.Endpoint,
|
||||||
publicKey: *params.PublicKey,
|
publicKey: *params.PublicKey,
|
||||||
wgEndpoint: fmt.Sprintf("%s:%s", params.Endpoint, s.Config.GrpcPort),
|
wgEndpoint: fmt.Sprintf("%s:%d", params.Endpoint, s.Config.GrpcPort),
|
||||||
wgHost: wgHost,
|
wgHost: wgHost,
|
||||||
timeStamp: time.Now().Unix(),
|
timeStamp: time.Now().Unix(),
|
||||||
routes: make([]Route, 0),
|
routes: make([]Route, 0),
|
||||||
@ -255,11 +289,6 @@ func (*MeshManagerStub) SetService(service string, value string) error {
|
|||||||
panic("unimplemented")
|
panic("unimplemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMonitor implements MeshManager.
|
|
||||||
func (*MeshManagerStub) GetMonitor() MeshMonitor {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAlias implements MeshManager.
|
// SetAlias implements MeshManager.
|
||||||
func (*MeshManagerStub) SetAlias(alias string) error {
|
func (*MeshManagerStub) SetAlias(alias string) error {
|
||||||
panic("unimplemented")
|
panic("unimplemented")
|
||||||
|
@ -20,6 +20,12 @@ type Route interface {
|
|||||||
GetPath() []string
|
GetPath() []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
func RouteEquals(r1, r2 Route) bool {
|
||||||
return r1.GetDestination().String() == r2.GetDestination().String() &&
|
return r1.GetDestination().String() == r2.GetDestination().String() &&
|
||||||
r1.GetHopCount() == r2.GetHopCount() &&
|
r1.GetHopCount() == r2.GetHopCount() &&
|
||||||
|
@ -3,6 +3,7 @@ package robin
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/tim-beatham/wgmesh/pkg/conf"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ctrlserver"
|
"github.com/tim-beatham/wgmesh/pkg/ctrlserver"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ipc"
|
"github.com/tim-beatham/wgmesh/pkg/ipc"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
||||||
@ -17,9 +18,11 @@ func TestCreateMeshRepliesMeshId(t *testing.T) {
|
|||||||
requester := getRequester()
|
requester := getRequester()
|
||||||
|
|
||||||
err := requester.CreateMesh(&ipc.NewMeshArgs{
|
err := requester.CreateMesh(&ipc.NewMeshArgs{
|
||||||
IfName: "wg0",
|
WgArgs: ipc.WireGuardArgs{
|
||||||
WgPort: 5000,
|
WgPort: 500,
|
||||||
Endpoint: "abc.com",
|
Endpoint: "abc.com:1234",
|
||||||
|
Role: "peer",
|
||||||
|
},
|
||||||
}, &reply)
|
}, &reply)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -52,9 +55,8 @@ func TestListMeshesMeshesNotEmpty(t *testing.T) {
|
|||||||
|
|
||||||
requester.Server.GetMeshManager().AddMesh(&mesh.AddMeshParams{
|
requester.Server.GetMeshManager().AddMesh(&mesh.AddMeshParams{
|
||||||
MeshId: "tim123",
|
MeshId: "tim123",
|
||||||
DevName: "wg0",
|
|
||||||
WgPort: 5000,
|
|
||||||
MeshBytes: make([]byte, 0),
|
MeshBytes: make([]byte, 0),
|
||||||
|
Conf: &conf.WgConfiguration{},
|
||||||
})
|
})
|
||||||
|
|
||||||
err := requester.ListMeshes("", &reply)
|
err := requester.ListMeshes("", &reply)
|
||||||
|
@ -1,15 +1,20 @@
|
|||||||
package wg
|
package wg
|
||||||
|
|
||||||
|
import "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
type WgInterfaceManipulatorStub struct{}
|
type WgInterfaceManipulatorStub struct{}
|
||||||
|
|
||||||
func (i *WgInterfaceManipulatorStub) CreateInterface(port int) (string, error) {
|
// CreateInterface creates a WireGuard interface
|
||||||
return "", nil
|
func (w *WgInterfaceManipulatorStub) CreateInterface(port int, privateKey *wgtypes.Key) (string, error) {
|
||||||
|
return "aninterface", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *WgInterfaceManipulatorStub) AddAddress(ifName string, addr string) error {
|
// AddAddress adds an address to the given interface name
|
||||||
|
func (w *WgInterfaceManipulatorStub) AddAddress(ifName string, addr string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *WgInterfaceManipulatorStub) RemoveInterface(ifName string) error {
|
// RemoveInterface removes the specified interface
|
||||||
|
func (w *WgInterfaceManipulatorStub) RemoveInterface(ifName string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,6 @@ package wg
|
|||||||
|
|
||||||
import "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
import "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
type WgError struct {
|
|
||||||
msg string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *WgError) Error() string {
|
|
||||||
return m.msg
|
|
||||||
}
|
|
||||||
|
|
||||||
type WgInterfaceManipulator interface {
|
type WgInterfaceManipulator interface {
|
||||||
// CreateInterface creates a WireGuard interface
|
// CreateInterface creates a WireGuard interface
|
||||||
CreateInterface(port int, privateKey *wgtypes.Key) (string, error)
|
CreateInterface(port int, privateKey *wgtypes.Key) (string, error)
|
||||||
@ -18,3 +10,11 @@ type WgInterfaceManipulator interface {
|
|||||||
// RemoveInterface removes the specified interface
|
// RemoveInterface removes the specified interface
|
||||||
RemoveInterface(ifName string) error
|
RemoveInterface(ifName string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WgError struct {
|
||||||
|
msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *WgError) Error() string {
|
||||||
|
return m.msg
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user