mirror of
https://github.com/tim-beatham/smegmesh.git
synced 2025-07-03 05:50:22 +02:00
Compare commits
55 Commits
53-run-com
...
bugfix-nod
Author | SHA1 | Date | |
---|---|---|---|
1f0914e2df | |||
27e00196cd | |||
dea6f1a22d | |||
913de57568 | |||
ce829114b1 | |||
cd844ff46e | |||
d0b1913796 | |||
90cfe820d2 | |||
8a49809855 | |||
dbc18bddc6 | |||
36e82dba47 | |||
3cc87bc252 | |||
a9ed7c0a20 | |||
fd29af73e3 | |||
9e1058e0f2 | |||
c29eb197f3 | |||
1a9d9d61ad | |||
6954608c32 | |||
2e6aed6f93 | |||
b0893a0b8e | |||
e7d6055fa3 | |||
e0f3f116b9 | |||
352648b7cb | |||
2d5df25b1d | |||
cabe173831 | |||
d2c8a52ec6 | |||
bf53108384 | |||
77aac5534b | |||
58439fcd56 | |||
311a15363a | |||
255d3c8b39 | |||
41899c5831 | |||
fe4ca66ff6 | |||
0b91ba744a | |||
67483c2a90 | |||
af26e81bd3 | |||
0cc3141b58 | |||
186acbe915 | |||
ceb43a1db1 | |||
bed59f120f | |||
8aab4e99d8 | |||
cf4be1ccab | |||
6ed32f3a79 | |||
b6199892f0 | |||
ad22f04b0d | |||
092d9a4af5 | |||
19abf712a6 | |||
b296e1f45a | |||
2dc89d171b | |||
13bea10638 | |||
3222d7e388 | |||
1789d203f6 | |||
a5074a536e | |||
acb90a5679 | |||
27ec23f133 |
@ -3,7 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/api"
|
"github.com/tim-beatham/smegmesh/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -3,7 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
smegdns "github.com/tim-beatham/wgmesh/pkg/dns"
|
smegdns "github.com/tim-beatham/smegmesh/pkg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
409
cmd/smegctl/main.go
Normal file
409
cmd/smegctl/main.go
Normal file
@ -0,0 +1,409 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
ipcRpc "net/rpc"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/akamensky/argparse"
|
||||||
|
"github.com/tim-beatham/smegmesh/pkg/ctrlserver"
|
||||||
|
graph "github.com/tim-beatham/smegmesh/pkg/dot"
|
||||||
|
"github.com/tim-beatham/smegmesh/pkg/ipc"
|
||||||
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const SockAddr = "/tmp/wgmesh_ipc.sock"
|
||||||
|
|
||||||
|
type CreateMeshParams struct {
|
||||||
|
Client *ipcRpc.Client
|
||||||
|
Endpoint string
|
||||||
|
WgArgs ipc.WireGuardArgs
|
||||||
|
AdvertiseRoutes bool
|
||||||
|
AdvertiseDefault bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func createMesh(client *ipc.SmegmeshIpc, args *ipc.NewMeshArgs) {
|
||||||
|
var reply string
|
||||||
|
err := client.CreateMesh(args, &reply)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listMeshes(client *ipc.SmegmeshIpc) {
|
||||||
|
reply := new(ipc.ListMeshReply)
|
||||||
|
|
||||||
|
err := client.ListMeshes(reply)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logging.Log.WriteErrorf(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, meshId := range reply.Meshes {
|
||||||
|
fmt.Println(meshId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func joinMesh(client *ipc.SmegmeshIpc, args ipc.JoinMeshArgs) {
|
||||||
|
var reply string
|
||||||
|
|
||||||
|
err := client.JoinMesh(args, &reply)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func leaveMesh(client *ipc.SmegmeshIpc, meshId string) {
|
||||||
|
var reply string
|
||||||
|
|
||||||
|
err := client.LeaveMesh(meshId, &reply)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGraph(client *ipc.SmegmeshIpc) {
|
||||||
|
listMeshesReply := new(ipc.ListMeshReply)
|
||||||
|
|
||||||
|
err := client.ListMeshes(listMeshesReply)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
meshes := make(map[string][]ctrlserver.MeshNode)
|
||||||
|
|
||||||
|
for _, meshId := range listMeshesReply.Meshes {
|
||||||
|
var meshReply ipc.GetMeshReply
|
||||||
|
|
||||||
|
err := client.GetMesh(meshId, &meshReply)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
meshes[meshId] = meshReply.Nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
dotGenerator := graph.NewMeshGraphConverter(meshes)
|
||||||
|
dot, err := dotGenerator.Generate()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(dot)
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryMesh(client *ipc.SmegmeshIpc, meshId, query string) {
|
||||||
|
var reply string
|
||||||
|
|
||||||
|
args := ipc.QueryMesh{
|
||||||
|
MeshId: meshId,
|
||||||
|
Query: query,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.Query(args, &reply)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func putDescription(client *ipc.SmegmeshIpc, meshId, description string) {
|
||||||
|
var reply string
|
||||||
|
|
||||||
|
err := client.PutDescription(ipc.PutDescriptionArgs{
|
||||||
|
MeshId: meshId,
|
||||||
|
Description: description,
|
||||||
|
}, &reply)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
// putAlias: puts an alias for the node
|
||||||
|
func putAlias(client *ipc.SmegmeshIpc, meshid, alias string) {
|
||||||
|
var reply string
|
||||||
|
|
||||||
|
err := client.PutAlias(ipc.PutAliasArgs{
|
||||||
|
MeshId: meshid,
|
||||||
|
Alias: alias,
|
||||||
|
}, &reply)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setService(client *ipc.SmegmeshIpc, meshId, service, value string) {
|
||||||
|
var reply string
|
||||||
|
|
||||||
|
err := client.PutService(ipc.PutServiceArgs{
|
||||||
|
MeshId: meshId,
|
||||||
|
Service: service,
|
||||||
|
Value: value,
|
||||||
|
}, &reply)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteService(client *ipc.SmegmeshIpc, meshId, service string) {
|
||||||
|
var reply string
|
||||||
|
|
||||||
|
err := client.DeleteService(ipc.DeleteServiceArgs{
|
||||||
|
MeshId: meshId,
|
||||||
|
Service: service,
|
||||||
|
}, &reply)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
parser := argparse.NewParser("smgctl",
|
||||||
|
"smegctl Manipulate WireGuard mesh networks")
|
||||||
|
|
||||||
|
newMeshCmd := parser.NewCommand("new-mesh", "Create a new mesh")
|
||||||
|
listMeshCmd := parser.NewCommand("list-meshes", "List meshes the node is connected to")
|
||||||
|
joinMeshCmd := parser.NewCommand("join-mesh", "Join a mesh network")
|
||||||
|
getGraphCmd := parser.NewCommand("get-graph", "Convert a mesh into DOT format")
|
||||||
|
leaveMeshCmd := parser.NewCommand("leave-mesh", "Leave a mesh network")
|
||||||
|
queryMeshCmd := parser.NewCommand("query-mesh", "Query a mesh network using JMESPath")
|
||||||
|
putDescriptionCmd := parser.NewCommand("put-description", "Place a description for the node")
|
||||||
|
putAliasCmd := parser.NewCommand("put-alias", "Place an alias for the node")
|
||||||
|
setServiceCmd := parser.NewCommand("set-service", "Place a service into your advertisements")
|
||||||
|
deleteServiceCmd := parser.NewCommand("delete-service", "Remove a service from your advertisements")
|
||||||
|
|
||||||
|
var newMeshPort *int = newMeshCmd.Int("p", "wgport", &argparse.Options{
|
||||||
|
Default: 0,
|
||||||
|
Help: "WireGuard port to use to the interface. A default of 0 uses an unused ephmeral port.",
|
||||||
|
})
|
||||||
|
|
||||||
|
var newMeshEndpoint *string = newMeshCmd.String("e", "endpoint", &argparse.Options{
|
||||||
|
Help: "Publicly routeable endpoint to advertise within the mesh",
|
||||||
|
})
|
||||||
|
|
||||||
|
var newMeshRole *string = newMeshCmd.Selector("r", "role", []string{"peer", "client"}, &argparse.Options{
|
||||||
|
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" +
|
||||||
|
" protocol",
|
||||||
|
})
|
||||||
|
var newMeshKeepAliveWg *int = newMeshCmd.Int("k", "KeepAliveWg", &argparse.Options{
|
||||||
|
Default: 0,
|
||||||
|
Help: "WireGuard KeepAlive value for NAT traversal and firewall holepunching",
|
||||||
|
})
|
||||||
|
|
||||||
|
var newMeshAdvertiseRoutes *bool = newMeshCmd.Flag("a", "advertise", &argparse.Options{
|
||||||
|
Help: "Advertise routes to other mesh network into the mesh",
|
||||||
|
})
|
||||||
|
|
||||||
|
var newMeshAdvertiseDefaults *bool = newMeshCmd.Flag("d", "defaults", &argparse.Options{
|
||||||
|
Help: "Advertise ::/0 into the mesh network",
|
||||||
|
})
|
||||||
|
|
||||||
|
var joinMeshId *string = joinMeshCmd.String("m", "meshid", &argparse.Options{
|
||||||
|
Required: true,
|
||||||
|
Help: "MeshID of the mesh network to join",
|
||||||
|
})
|
||||||
|
|
||||||
|
var joinMeshIpAddress *string = joinMeshCmd.String("i", "ip", &argparse.Options{
|
||||||
|
Required: true,
|
||||||
|
Help: "IP address of the bootstrapping node to join through",
|
||||||
|
})
|
||||||
|
|
||||||
|
var joinMeshEndpoint *string = joinMeshCmd.String("e", "endpoint", &argparse.Options{
|
||||||
|
Help: "Publicly routeable endpoint to advertise within the mesh",
|
||||||
|
})
|
||||||
|
|
||||||
|
var joinMeshRole *string = joinMeshCmd.Selector("r", "role", []string{"peer", "client"}, &argparse.Options{
|
||||||
|
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" +
|
||||||
|
" protocol",
|
||||||
|
})
|
||||||
|
|
||||||
|
var joinMeshPort *int = joinMeshCmd.Int("p", "wgport", &argparse.Options{
|
||||||
|
Default: 0,
|
||||||
|
Help: "WireGuard port to use to the interface. A default of 0 uses an unused ephmeral port.",
|
||||||
|
})
|
||||||
|
|
||||||
|
var joinMeshKeepAliveWg *int = joinMeshCmd.Int("k", "KeepAliveWg", &argparse.Options{
|
||||||
|
Default: 0,
|
||||||
|
Help: "WireGuard KeepAlive value for NAT traversal and firewall ho;lepunching",
|
||||||
|
})
|
||||||
|
|
||||||
|
var joinMeshAdvertiseRoutes *bool = joinMeshCmd.Flag("a", "advertise", &argparse.Options{
|
||||||
|
Help: "Advertise routes to other mesh network into the mesh",
|
||||||
|
})
|
||||||
|
|
||||||
|
var joinMeshAdvertiseDefaults *bool = joinMeshCmd.Flag("d", "defaults", &argparse.Options{
|
||||||
|
Help: "Advertise ::/0 into the mesh network",
|
||||||
|
})
|
||||||
|
|
||||||
|
var leaveMeshMeshId *string = leaveMeshCmd.String("m", "mesh", &argparse.Options{
|
||||||
|
Required: true,
|
||||||
|
Help: "MeshID of the mesh to leave",
|
||||||
|
})
|
||||||
|
|
||||||
|
var queryMeshMeshId *string = queryMeshCmd.String("m", "mesh", &argparse.Options{
|
||||||
|
Required: true,
|
||||||
|
Help: "MeshID of the mesh to query",
|
||||||
|
})
|
||||||
|
var queryMeshQuery *string = queryMeshCmd.String("q", "query", &argparse.Options{
|
||||||
|
Required: true,
|
||||||
|
Help: "JMESPath Query Of The Mesh Network To Query",
|
||||||
|
})
|
||||||
|
|
||||||
|
var description *string = putDescriptionCmd.String("d", "description", &argparse.Options{
|
||||||
|
Required: true,
|
||||||
|
Help: "Description of the node in the mesh",
|
||||||
|
})
|
||||||
|
|
||||||
|
var descriptionMeshId *string = putDescriptionCmd.String("m", "meshid", &argparse.Options{
|
||||||
|
Required: true,
|
||||||
|
Help: "MeshID of the mesh network to join",
|
||||||
|
})
|
||||||
|
|
||||||
|
var aliasMeshId *string = putAliasCmd.String("m", "meshid", &argparse.Options{
|
||||||
|
Required: true,
|
||||||
|
Help: "MeshID of the mesh network to join",
|
||||||
|
})
|
||||||
|
|
||||||
|
var alias *string = putAliasCmd.String("a", "alias", &argparse.Options{
|
||||||
|
Required: true,
|
||||||
|
Help: "Alias of the node to set can be used in DNS to lookup an IP address",
|
||||||
|
})
|
||||||
|
|
||||||
|
var serviceKey *string = setServiceCmd.String("s", "service", &argparse.Options{
|
||||||
|
Required: true,
|
||||||
|
Help: "Key of the service to advertise in the mesh network",
|
||||||
|
})
|
||||||
|
var serviceValue *string = setServiceCmd.String("v", "value", &argparse.Options{
|
||||||
|
Required: true,
|
||||||
|
Help: "Value of the service to advertise in the mesh network",
|
||||||
|
})
|
||||||
|
|
||||||
|
var serviceMeshId *string = setServiceCmd.String("m", "meshid", &argparse.Options{
|
||||||
|
Required: true,
|
||||||
|
Help: "MeshID of the mesh network to join",
|
||||||
|
})
|
||||||
|
|
||||||
|
var deleteServiceKey *string = deleteServiceCmd.String("s", "service", &argparse.Options{
|
||||||
|
Required: true,
|
||||||
|
Help: "Key of the service to remove",
|
||||||
|
})
|
||||||
|
|
||||||
|
var deleteServiceMeshid *string = deleteServiceCmd.String("m", "meshid", &argparse.Options{
|
||||||
|
Required: true,
|
||||||
|
Help: "MeshID of the mesh network to join",
|
||||||
|
})
|
||||||
|
|
||||||
|
err := parser.Parse(os.Args)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(parser.Usage(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := ipc.NewClientIpc()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if newMeshCmd.Happened() {
|
||||||
|
args := &ipc.NewMeshArgs{
|
||||||
|
WgArgs: ipc.WireGuardArgs{
|
||||||
|
Endpoint: *newMeshEndpoint,
|
||||||
|
Role: *newMeshRole,
|
||||||
|
WgPort: *newMeshPort,
|
||||||
|
KeepAliveWg: *newMeshKeepAliveWg,
|
||||||
|
AdvertiseDefaultRoute: *newMeshAdvertiseDefaults,
|
||||||
|
AdvertiseRoutes: *newMeshAdvertiseRoutes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
createMesh(client, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
if listMeshCmd.Happened() {
|
||||||
|
listMeshes(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
if joinMeshCmd.Happened() {
|
||||||
|
args := ipc.JoinMeshArgs{
|
||||||
|
IpAddress: *joinMeshIpAddress,
|
||||||
|
MeshId: *joinMeshId,
|
||||||
|
WgArgs: ipc.WireGuardArgs{
|
||||||
|
Endpoint: *joinMeshEndpoint,
|
||||||
|
Role: *joinMeshRole,
|
||||||
|
WgPort: *joinMeshPort,
|
||||||
|
KeepAliveWg: *joinMeshKeepAliveWg,
|
||||||
|
AdvertiseDefaultRoute: *joinMeshAdvertiseDefaults,
|
||||||
|
AdvertiseRoutes: *joinMeshAdvertiseRoutes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
joinMesh(client, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
if getGraphCmd.Happened() {
|
||||||
|
getGraph(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
if leaveMeshCmd.Happened() {
|
||||||
|
leaveMesh(client, *leaveMeshMeshId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if queryMeshCmd.Happened() {
|
||||||
|
queryMesh(client, *queryMeshMeshId, *queryMeshQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
if putDescriptionCmd.Happened() {
|
||||||
|
putDescription(client, *descriptionMeshId, *description)
|
||||||
|
}
|
||||||
|
|
||||||
|
if putAliasCmd.Happened() {
|
||||||
|
putAlias(client, *aliasMeshId, *alias)
|
||||||
|
}
|
||||||
|
|
||||||
|
if setServiceCmd.Happened() {
|
||||||
|
setService(client, *serviceMeshId, *serviceKey, *serviceValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
if deleteServiceCmd.Happened() {
|
||||||
|
deleteService(client, *deleteServiceMeshid, *deleteServiceKey)
|
||||||
|
}
|
||||||
|
}
|
@ -6,14 +6,14 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
ctrlserver "github.com/tim-beatham/wgmesh/pkg/ctrlserver"
|
ctrlserver "github.com/tim-beatham/smegmesh/pkg/ctrlserver"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ipc"
|
"github.com/tim-beatham/smegmesh/pkg/ipc"
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/robin"
|
"github.com/tim-beatham/smegmesh/pkg/robin"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/sync"
|
"github.com/tim-beatham/smegmesh/pkg/sync"
|
||||||
timer "github.com/tim-beatham/wgmesh/pkg/timers"
|
timer "github.com/tim-beatham/smegmesh/pkg/timers"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl"
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -59,6 +59,11 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctrlServer, err := ctrlserver.NewCtrlServer(&ctrlServerParams)
|
ctrlServer, err := ctrlserver.NewCtrlServer(&ctrlServerParams)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
syncProvider.Server = ctrlServer
|
syncProvider.Server = ctrlServer
|
||||||
syncRequester = sync.NewSyncRequester(ctrlServer)
|
syncRequester = sync.NewSyncRequester(ctrlServer)
|
||||||
syncer = sync.NewSyncer(ctrlServer.MeshManager, conf, syncRequester)
|
syncer = sync.NewSyncer(ctrlServer.MeshManager, conf, syncRequester)
|
@ -1,331 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
ipcRpc "net/rpc"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/akamensky/argparse"
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ipc"
|
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
const SockAddr = "/tmp/wgmesh_ipc.sock"
|
|
||||||
|
|
||||||
type CreateMeshParams struct {
|
|
||||||
Client *ipcRpc.Client
|
|
||||||
WgPort int
|
|
||||||
Endpoint string
|
|
||||||
Role string
|
|
||||||
}
|
|
||||||
|
|
||||||
func createMesh(args *CreateMeshParams) string {
|
|
||||||
var reply string
|
|
||||||
newMeshParams := ipc.NewMeshArgs{
|
|
||||||
WgPort: args.WgPort,
|
|
||||||
Endpoint: args.Endpoint,
|
|
||||||
Role: args.Role,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := args.Client.Call("IpcHandler.CreateMesh", &newMeshParams, &reply)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
return reply
|
|
||||||
}
|
|
||||||
|
|
||||||
func listMeshes(client *ipcRpc.Client) {
|
|
||||||
reply := new(ipc.ListMeshReply)
|
|
||||||
|
|
||||||
err := client.Call("IpcHandler.ListMeshes", "", &reply)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
logging.Log.WriteErrorf(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, meshId := range reply.Meshes {
|
|
||||||
fmt.Println(meshId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type JoinMeshParams struct {
|
|
||||||
Client *ipcRpc.Client
|
|
||||||
MeshId string
|
|
||||||
IpAddress string
|
|
||||||
IfName string
|
|
||||||
WgPort int
|
|
||||||
Endpoint string
|
|
||||||
Role string
|
|
||||||
}
|
|
||||||
|
|
||||||
func joinMesh(params *JoinMeshParams) string {
|
|
||||||
var reply string
|
|
||||||
|
|
||||||
args := ipc.JoinMeshArgs{
|
|
||||||
MeshId: params.MeshId,
|
|
||||||
IpAdress: params.IpAddress,
|
|
||||||
Port: params.WgPort,
|
|
||||||
Role: params.Role,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := params.Client.Call("IpcHandler.JoinMesh", &args, &reply)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
return reply
|
|
||||||
}
|
|
||||||
|
|
||||||
func leaveMesh(client *ipcRpc.Client, meshId string) {
|
|
||||||
var reply string
|
|
||||||
|
|
||||||
err := client.Call("IpcHandler.LeaveMesh", &meshId, &reply)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
func enableInterface(client *ipcRpc.Client, meshId string) {
|
|
||||||
var reply string
|
|
||||||
|
|
||||||
err := client.Call("IpcHandler.EnableInterface", &meshId, &reply)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getGraph(client *ipcRpc.Client, meshId string) {
|
|
||||||
var reply string
|
|
||||||
|
|
||||||
err := client.Call("IpcHandler.GetDOT", &meshId, &reply)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
func queryMesh(client *ipcRpc.Client, meshId, query string) {
|
|
||||||
var reply string
|
|
||||||
|
|
||||||
err := client.Call("IpcHandler.Query", &ipc.QueryMesh{MeshId: meshId, Query: query}, &reply)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
// putDescription: puts updates the description about the node to the meshes
|
|
||||||
func putDescription(client *ipcRpc.Client, description string) {
|
|
||||||
var reply string
|
|
||||||
|
|
||||||
err := client.Call("IpcHandler.PutDescription", &description, &reply)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
// putAlias: puts an alias for the node
|
|
||||||
func putAlias(client *ipcRpc.Client, alias string) {
|
|
||||||
var reply string
|
|
||||||
|
|
||||||
err := client.Call("IpcHandler.PutAlias", &alias, &reply)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setService(client *ipcRpc.Client, service, value string) {
|
|
||||||
var reply string
|
|
||||||
|
|
||||||
serviceArgs := &ipc.PutServiceArgs{
|
|
||||||
Service: service,
|
|
||||||
Value: value,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := client.Call("IpcHandler.PutService", serviceArgs, &reply)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteService(client *ipcRpc.Client, service string) {
|
|
||||||
var reply string
|
|
||||||
|
|
||||||
err := client.Call("IpcHandler.PutService", &service, &reply)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNode(client *ipcRpc.Client, nodeId, meshId string) {
|
|
||||||
var reply string
|
|
||||||
args := &ipc.GetNodeArgs{
|
|
||||||
NodeId: nodeId,
|
|
||||||
MeshId: meshId,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := client.Call("IpcHandler.GetNode", &args, &reply)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
parser := argparse.NewParser("wg-mesh",
|
|
||||||
"wg-mesh Manipulate WireGuard meshes")
|
|
||||||
|
|
||||||
newMeshCmd := parser.NewCommand("new-mesh", "Create a new mesh")
|
|
||||||
listMeshCmd := parser.NewCommand("list-meshes", "List meshes the node is connected to")
|
|
||||||
joinMeshCmd := parser.NewCommand("join-mesh", "Join a mesh network")
|
|
||||||
enableInterfaceCmd := parser.NewCommand("enable-interface", "Enable A Specific Mesh Interface")
|
|
||||||
getGraphCmd := parser.NewCommand("get-graph", "Convert a mesh into DOT format")
|
|
||||||
leaveMeshCmd := parser.NewCommand("leave-mesh", "Leave a mesh network")
|
|
||||||
queryMeshCmd := parser.NewCommand("query-mesh", "Query a mesh network using JMESPath")
|
|
||||||
putDescriptionCmd := parser.NewCommand("put-description", "Place a description for the node")
|
|
||||||
putAliasCmd := parser.NewCommand("put-alias", "Place an alias for the node")
|
|
||||||
setServiceCmd := parser.NewCommand("set-service", "Place a service into your advertisements")
|
|
||||||
deleteServiceCmd := parser.NewCommand("delete-service", "Remove a service from your advertisements")
|
|
||||||
getNodeCmd := parser.NewCommand("get-node", "Get a specific node from the mesh")
|
|
||||||
|
|
||||||
var newMeshPort *int = newMeshCmd.Int("p", "wgport", &argparse.Options{})
|
|
||||||
var newMeshEndpoint *string = newMeshCmd.String("e", "endpoint", &argparse.Options{})
|
|
||||||
var newMeshRole *string = newMeshCmd.Selector("r", "role", []string{"peer", "client"}, &argparse.Options{})
|
|
||||||
|
|
||||||
var joinMeshId *string = joinMeshCmd.String("m", "mesh", &argparse.Options{Required: true})
|
|
||||||
var joinMeshIpAddress *string = joinMeshCmd.String("i", "ip", &argparse.Options{Required: true})
|
|
||||||
var joinMeshPort *int = joinMeshCmd.Int("p", "wgport", &argparse.Options{})
|
|
||||||
var joinMeshEndpoint *string = joinMeshCmd.String("e", "endpoint", &argparse.Options{})
|
|
||||||
var joinMeshRole *string = joinMeshCmd.Selector("r", "role", []string{"peer", "client"}, &argparse.Options{})
|
|
||||||
|
|
||||||
var enableInterfaceMeshId *string = enableInterfaceCmd.String("m", "mesh", &argparse.Options{Required: true})
|
|
||||||
|
|
||||||
var getGraphMeshId *string = getGraphCmd.String("m", "mesh", &argparse.Options{Required: true})
|
|
||||||
|
|
||||||
var leaveMeshMeshId *string = leaveMeshCmd.String("m", "mesh", &argparse.Options{Required: true})
|
|
||||||
|
|
||||||
var queryMeshMeshId *string = queryMeshCmd.String("m", "mesh", &argparse.Options{Required: true})
|
|
||||||
var queryMeshQuery *string = queryMeshCmd.String("q", "query", &argparse.Options{Required: true})
|
|
||||||
|
|
||||||
var description *string = putDescriptionCmd.String("d", "description", &argparse.Options{Required: true})
|
|
||||||
|
|
||||||
var alias *string = putAliasCmd.String("a", "alias", &argparse.Options{Required: true})
|
|
||||||
|
|
||||||
var serviceKey *string = setServiceCmd.String("s", "service", &argparse.Options{Required: true})
|
|
||||||
var serviceValue *string = setServiceCmd.String("v", "value", &argparse.Options{Required: true})
|
|
||||||
|
|
||||||
var deleteServiceKey *string = deleteServiceCmd.String("s", "service", &argparse.Options{Required: true})
|
|
||||||
|
|
||||||
var getNodeNodeId *string = getNodeCmd.String("n", "nodeid", &argparse.Options{Required: true})
|
|
||||||
var getNodeMeshId *string = getNodeCmd.String("m", "meshid", &argparse.Options{Required: true})
|
|
||||||
|
|
||||||
err := parser.Parse(os.Args)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Print(parser.Usage(err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := ipcRpc.DialHTTP("unix", SockAddr)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if newMeshCmd.Happened() {
|
|
||||||
fmt.Println(createMesh(&CreateMeshParams{
|
|
||||||
Client: client,
|
|
||||||
WgPort: *newMeshPort,
|
|
||||||
Endpoint: *newMeshEndpoint,
|
|
||||||
Role: *newMeshRole,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
if listMeshCmd.Happened() {
|
|
||||||
listMeshes(client)
|
|
||||||
}
|
|
||||||
|
|
||||||
if joinMeshCmd.Happened() {
|
|
||||||
fmt.Println(joinMesh(&JoinMeshParams{
|
|
||||||
Client: client,
|
|
||||||
WgPort: *joinMeshPort,
|
|
||||||
IpAddress: *joinMeshIpAddress,
|
|
||||||
MeshId: *joinMeshId,
|
|
||||||
Endpoint: *joinMeshEndpoint,
|
|
||||||
Role: *joinMeshRole,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
if getGraphCmd.Happened() {
|
|
||||||
getGraph(client, *getGraphMeshId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if enableInterfaceCmd.Happened() {
|
|
||||||
enableInterface(client, *enableInterfaceMeshId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if leaveMeshCmd.Happened() {
|
|
||||||
leaveMesh(client, *leaveMeshMeshId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if queryMeshCmd.Happened() {
|
|
||||||
queryMesh(client, *queryMeshMeshId, *queryMeshQuery)
|
|
||||||
}
|
|
||||||
|
|
||||||
if putDescriptionCmd.Happened() {
|
|
||||||
putDescription(client, *description)
|
|
||||||
}
|
|
||||||
|
|
||||||
if putAliasCmd.Happened() {
|
|
||||||
putAlias(client, *alias)
|
|
||||||
}
|
|
||||||
|
|
||||||
if setServiceCmd.Happened() {
|
|
||||||
setService(client, *serviceKey, *serviceValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
if deleteServiceCmd.Happened() {
|
|
||||||
deleteService(client, *deleteServiceKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
if getNodeCmd.Happened() {
|
|
||||||
getNode(client, *getNodeNodeId, *getNodeMeshId)
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,5 +10,5 @@ syncRate: 1
|
|||||||
interClusterChance: 0.15
|
interClusterChance: 0.15
|
||||||
branchRate: 3
|
branchRate: 3
|
||||||
infectionCount: 3
|
infectionCount: 3
|
||||||
keepAliveTime: 10
|
heartBeatTime: 10
|
||||||
pruneTime: 20
|
pruneTime: 20
|
||||||
|
@ -10,5 +10,5 @@ syncRate: 1
|
|||||||
interClusterChance: 0.15
|
interClusterChance: 0.15
|
||||||
branchRate: 3
|
branchRate: 3
|
||||||
infectionCount: 3
|
infectionCount: 3
|
||||||
keepAliveTime: 10
|
heartBeatTime: 10
|
||||||
pruneTime: 20
|
pruneTime: 20
|
||||||
|
@ -10,5 +10,5 @@ syncRate: 1
|
|||||||
interClusterChance: 0.15
|
interClusterChance: 0.15
|
||||||
branchRate: 3
|
branchRate: 3
|
||||||
infectionCount: 3
|
infectionCount: 3
|
||||||
keepAliveTime: 10
|
heartBeatTime: 10
|
||||||
pruneTime: 20
|
pruneTime: 20
|
||||||
|
16
go.mod
16
go.mod
@ -1,14 +1,18 @@
|
|||||||
module github.com/tim-beatham/wgmesh
|
module github.com/tim-beatham/smegmesh
|
||||||
|
|
||||||
go 1.21.3
|
go 1.21.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/akamensky/argparse v1.4.0
|
github.com/akamensky/argparse v1.4.0
|
||||||
|
github.com/anandvarma/namegen v0.0.0-20230727084436-5197c6ea3255
|
||||||
github.com/automerge/automerge-go v0.0.0-20230903201930-b80ce8aadbb9
|
github.com/automerge/automerge-go v0.0.0-20230903201930-b80ce8aadbb9
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
|
github.com/go-playground/validator/v10 v10.16.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/jmespath/go-jmespath v0.4.0
|
github.com/jmespath/go-jmespath v0.4.0
|
||||||
github.com/jsimonetti/rtnetlink v1.3.5
|
github.com/jsimonetti/rtnetlink v1.3.5
|
||||||
|
github.com/lithammer/shortuuid v3.0.0+incompatible
|
||||||
|
github.com/miekg/dns v1.1.57
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
golang.org/x/sys v0.14.0
|
golang.org/x/sys v0.14.0
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||||
@ -24,7 +28,6 @@ require (
|
|||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/google/go-cmp v0.5.9 // indirect
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
@ -42,10 +45,13 @@ require (
|
|||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||||
golang.org/x/arch v0.3.0 // indirect
|
golang.org/x/arch v0.3.0 // indirect
|
||||||
golang.org/x/crypto v0.13.0 // indirect
|
golang.org/x/crypto v0.14.0 // indirect
|
||||||
golang.org/x/net v0.15.0 // indirect
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
||||||
golang.org/x/sync v0.3.0 // indirect
|
golang.org/x/mod v0.12.0 // indirect
|
||||||
|
golang.org/x/net v0.17.0 // indirect
|
||||||
|
golang.org/x/sync v0.4.0 // indirect
|
||||||
golang.org/x/text v0.13.0 // indirect
|
golang.org/x/text v0.13.0 // indirect
|
||||||
|
golang.org/x/tools v0.13.0 // indirect
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20230704135630-469159ecf7d1 // indirect
|
golang.zx2c4.com/wireguard v0.0.0-20230704135630-469159ecf7d1 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||||
)
|
)
|
||||||
|
141
go.sum
Normal file
141
go.sum
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
github.com/akamensky/argparse v1.4.0 h1:YGzvsTqCvbEZhL8zZu2AiA5nq805NZh75JNj4ajn1xc=
|
||||||
|
github.com/akamensky/argparse v1.4.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
|
||||||
|
github.com/anandvarma/namegen v0.0.0-20230727084436-5197c6ea3255 h1:aIAyyj4XPrke9Tc/umbBCzP5SKX/CHf3dKrL/PhH2lo=
|
||||||
|
github.com/anandvarma/namegen v0.0.0-20230727084436-5197c6ea3255/go.mod h1:MFyILur9tG8PxaCXGZVr/2BOnHtRIgxYejYFZdWLxr0=
|
||||||
|
github.com/automerge/automerge-go v0.0.0-20230903201930-b80ce8aadbb9 h1:+6JSfuxZgmURoIlGdnYnY/FLRGWGagLyiBjt/VLtwi4=
|
||||||
|
github.com/automerge/automerge-go v0.0.0-20230903201930-b80ce8aadbb9/go.mod h1:6UxoDE+thWsISXK93pxaOuOfkcAfCvDbg0eAnFmxL5E=
|
||||||
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
|
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||||
|
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||||
|
github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
|
||||||
|
github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
|
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||||
|
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
|
||||||
|
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
|
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||||
|
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
|
github.com/jsimonetti/rtnetlink v1.3.5 h1:hVlNQNRlLDGZz31gBPicsG7Q53rnlsz1l1Ix/9XlpVA=
|
||||||
|
github.com/jsimonetti/rtnetlink v1.3.5/go.mod h1:0LFedyiTkebnd43tE4YAkWGIq9jQphow4CcwxaT2Y00=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
|
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||||
|
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||||
|
github.com/lithammer/shortuuid v3.0.0+incompatible h1:NcD0xWW/MZYXEHa6ITy6kaXN5nwm/V115vj2YXfhS0w=
|
||||||
|
github.com/lithammer/shortuuid v3.0.0+incompatible/go.mod h1:FR74pbAuElzOUuenUHTK2Tciko1/vKuIKS9dSkDrA4w=
|
||||||
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
|
||||||
|
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
|
||||||
|
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||||
|
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
||||||
|
github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
|
||||||
|
github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
|
||||||
|
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
|
||||||
|
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
|
||||||
|
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
|
||||||
|
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
|
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||||
|
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
|
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||||
|
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
|
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||||
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||||
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
|
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||||
|
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||||
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
|
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||||
|
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
|
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||||
|
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||||
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||||
|
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20230704135630-469159ecf7d1 h1:EY138uSo1JYlDq+97u1FtcOUwPpIU6WL1Lkt7WpYjPA=
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20230704135630-469159ecf7d1/go.mod h1:tqur9LnfstdR9ep2LaJT4lFUl0EjlHtge+gAjmsHUG4=
|
||||||
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
||||||
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
|
||||||
|
google.golang.org/grpc v1.58.1 h1:OL+Vz23DTtrrldqHK49FUOPHyY75rvFqJfXC84NYW58=
|
||||||
|
google.golang.org/grpc v1.58.1/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||||
|
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
@ -4,17 +4,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
ipcRpc "net/rpc"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ctrlserver"
|
"github.com/tim-beatham/smegmesh/pkg/ctrlserver"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ipc"
|
"github.com/tim-beatham/smegmesh/pkg/ipc"
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/what8words"
|
"github.com/tim-beatham/smegmesh/pkg/what8words"
|
||||||
)
|
)
|
||||||
|
|
||||||
const SockAddr = "/tmp/wgmesh_ipc.sock"
|
|
||||||
|
|
||||||
type ApiServer interface {
|
type ApiServer interface {
|
||||||
GetMeshes(c *gin.Context)
|
GetMeshes(c *gin.Context)
|
||||||
Run(addr string) error
|
Run(addr string) error
|
||||||
@ -22,7 +18,7 @@ type ApiServer interface {
|
|||||||
|
|
||||||
type SmegServer struct {
|
type SmegServer struct {
|
||||||
router *gin.Engine
|
router *gin.Engine
|
||||||
client *ipcRpc.Client
|
client *ipc.SmegmeshIpc
|
||||||
words *what8words.What8Words
|
words *what8words.What8Words
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +61,12 @@ func (s *SmegServer) meshNodeToAPIMeshNode(meshNode ctrlserver.MeshNode) *SmegNo
|
|||||||
PublicKey: meshNode.PublicKey,
|
PublicKey: meshNode.PublicKey,
|
||||||
Alias: alias,
|
Alias: alias,
|
||||||
Services: meshNode.Services,
|
Services: meshNode.Services,
|
||||||
|
Stats: SmegStats{
|
||||||
|
TotalTransmit: meshNode.Stats.TransmitBytes,
|
||||||
|
TotalReceived: meshNode.Stats.ReceivedBytes,
|
||||||
|
KeepAliveInterval: meshNode.Stats.PersistentKeepAliveInterval,
|
||||||
|
AllowedIps: meshNode.Stats.AllowedIPs,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,12 +95,14 @@ func (s *SmegServer) CreateMesh(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ipcRequest := ipc.NewMeshArgs{
|
ipcRequest := ipc.NewMeshArgs{
|
||||||
WgPort: createMesh.WgPort,
|
WgArgs: ipc.WireGuardArgs{
|
||||||
|
WgPort: createMesh.WgPort,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var reply string
|
var reply string
|
||||||
|
|
||||||
err := s.client.Call("IpcHandler.CreateMesh", &ipcRequest, &reply)
|
err := s.client.CreateMesh(&ipcRequest, &reply)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusBadRequest, &gin.H{
|
c.JSON(http.StatusBadRequest, &gin.H{
|
||||||
@ -124,14 +128,16 @@ func (s *SmegServer) JoinMesh(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ipcRequest := ipc.JoinMeshArgs{
|
ipcRequest := ipc.JoinMeshArgs{
|
||||||
MeshId: joinMesh.MeshId,
|
MeshId: joinMesh.MeshId,
|
||||||
IpAdress: joinMesh.Bootstrap,
|
IpAddress: joinMesh.Bootstrap,
|
||||||
Port: joinMesh.WgPort,
|
WgArgs: ipc.WireGuardArgs{
|
||||||
|
WgPort: joinMesh.WgPort,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var reply string
|
var reply string
|
||||||
|
|
||||||
err := s.client.Call("IpcHandler.JoinMesh", &ipcRequest, &reply)
|
err := s.client.JoinMesh(ipcRequest, &reply)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusBadRequest, &gin.H{
|
c.JSON(http.StatusBadRequest, &gin.H{
|
||||||
@ -154,7 +160,7 @@ func (s *SmegServer) GetMesh(c *gin.Context) {
|
|||||||
|
|
||||||
getMeshReply := new(ipc.GetMeshReply)
|
getMeshReply := new(ipc.GetMeshReply)
|
||||||
|
|
||||||
err := s.client.Call("IpcHandler.GetMesh", &meshid, &getMeshReply)
|
err := s.client.GetMesh(meshid, getMeshReply)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusNotFound,
|
c.JSON(http.StatusNotFound,
|
||||||
@ -172,7 +178,7 @@ func (s *SmegServer) GetMesh(c *gin.Context) {
|
|||||||
func (s *SmegServer) GetMeshes(c *gin.Context) {
|
func (s *SmegServer) GetMeshes(c *gin.Context) {
|
||||||
listMeshesReply := new(ipc.ListMeshReply)
|
listMeshesReply := new(ipc.ListMeshReply)
|
||||||
|
|
||||||
err := s.client.Call("IpcHandler.ListMeshes", "", &listMeshesReply)
|
err := s.client.ListMeshes(listMeshesReply)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Log.WriteErrorf(err.Error())
|
logging.Log.WriteErrorf(err.Error())
|
||||||
@ -185,7 +191,7 @@ func (s *SmegServer) GetMeshes(c *gin.Context) {
|
|||||||
for _, mesh := range listMeshesReply.Meshes {
|
for _, mesh := range listMeshesReply.Meshes {
|
||||||
getMeshReply := new(ipc.GetMeshReply)
|
getMeshReply := new(ipc.GetMeshReply)
|
||||||
|
|
||||||
err := s.client.Call("IpcHandler.GetMesh", &mesh, &getMeshReply)
|
err := s.client.GetMesh(mesh, getMeshReply)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Log.WriteErrorf(err.Error())
|
logging.Log.WriteErrorf(err.Error())
|
||||||
@ -205,7 +211,7 @@ func (s *SmegServer) Run(addr string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewSmegServer(conf ApiServerConf) (ApiServer, error) {
|
func NewSmegServer(conf ApiServerConf) (ApiServer, error) {
|
||||||
client, err := ipcRpc.DialHTTP("unix", SockAddr)
|
client, err := ipc.NewClientIpc()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -229,9 +235,19 @@ func NewSmegServer(conf ApiServerConf) (ApiServer, error) {
|
|||||||
words: words,
|
words: words,
|
||||||
}
|
}
|
||||||
|
|
||||||
router.GET("/meshes", smegServer.GetMeshes)
|
v1 := router.Group("/api/v1")
|
||||||
router.GET("/mesh/:meshid", smegServer.GetMesh)
|
{
|
||||||
router.POST("/mesh/create", smegServer.CreateMesh)
|
meshes := v1.Group("/meshes")
|
||||||
router.POST("/mesh/join", smegServer.JoinMesh)
|
{
|
||||||
|
meshes.GET("/", smegServer.GetMeshes)
|
||||||
|
}
|
||||||
|
mesh := v1.Group("/mesh")
|
||||||
|
{
|
||||||
|
mesh.GET("/:meshid", smegServer.GetMesh)
|
||||||
|
mesh.POST("/create", smegServer.CreateMesh)
|
||||||
|
mesh.POST("/join", smegServer.JoinMesh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return smegServer, nil
|
return smegServer, nil
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
type Route struct {
|
type Route struct {
|
||||||
Prefix string `json:"prefix"`
|
Prefix string `json:"prefix"`
|
||||||
Path []string `json:"path"`
|
Path []string `json:"path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SmegStats struct {
|
||||||
|
TotalTransmit int64 `json:"totalTransmit"`
|
||||||
|
TotalReceived int64 `json:"totalReceived"`
|
||||||
|
KeepAliveInterval time.Duration `json:"keepaliveInterval"`
|
||||||
|
AllowedIps []string `json:"allowedIps"`
|
||||||
|
}
|
||||||
|
|
||||||
type SmegNode struct {
|
type SmegNode struct {
|
||||||
Alias string `json:"alias"`
|
Alias string `json:"alias"`
|
||||||
WgHost string `json:"wgHost"`
|
WgHost string `json:"wgHost"`
|
||||||
@ -15,6 +24,7 @@ type SmegNode struct {
|
|||||||
PublicKey string `json:"publicKey"`
|
PublicKey string `json:"publicKey"`
|
||||||
Routes []Route `json:"routes"`
|
Routes []Route `json:"routes"`
|
||||||
Services map[string]string `json:"services"`
|
Services map[string]string `json:"services"`
|
||||||
|
Stats SmegStats `json:"stats"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SmegMesh struct {
|
type SmegMesh struct {
|
||||||
|
@ -9,10 +9,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/automerge/automerge-go"
|
"github.com/automerge/automerge-go"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl"
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
@ -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 {
|
||||||
|
@ -2,7 +2,7 @@ package automerge
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/automerge/automerge-go"
|
"github.com/automerge/automerge-go"
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AutomergeSync struct {
|
type AutomergeSync struct {
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package automerge
|
package automerge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"slices"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -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`)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,9 +3,9 @@ package automerge
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CrdtProviderFactory struct{}
|
type CrdtProviderFactory struct{}
|
||||||
@ -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
|
||||||
@ -37,17 +37,16 @@ type WgConfiguration struct {
|
|||||||
// service for IPDiscoverability
|
// service for IPDiscoverability
|
||||||
IPDiscovery *IPDiscovery `yaml:"ipDiscovery" validate:"required,eq=public|eq=dns"`
|
IPDiscovery *IPDiscovery `yaml:"ipDiscovery" validate:"required,eq=public|eq=dns"`
|
||||||
// AdvertiseRoutes: specifies whether the node can act as a router routing packets between meshes
|
// AdvertiseRoutes: specifies whether the node can act as a router routing packets between meshes
|
||||||
AdvertiseRoutes *bool `yaml:"advertiseRoutes"`
|
AdvertiseRoutes *bool `yaml:"advertiseRoute" validate:"required"`
|
||||||
// AdvertiseDefaultRoute: specifies whether or not this route should advertise a default route
|
// AdvertiseDefaultRoute: specifies whether or not this route should advertise a default route
|
||||||
// for all nodes to route their packets to
|
// for all nodes to route their packets to
|
||||||
AdvertiseDefaultRoute *bool `yaml:"advertiseDefaults"`
|
AdvertiseDefaultRoute *bool `yaml:"advertiseDefaults" validate:"required"`
|
||||||
// Endpoint contains what value should be set as the public endpoint of this node
|
// Endpoint contains what value should be set as the public endpoint of this node
|
||||||
Endpoint *string `yaml:"publicEndpoint"`
|
Endpoint *string `yaml:"publicEndpoint"`
|
||||||
// Role specifies whether or not the user is globally accessible.
|
// Role specifies whether or not the user is globally accessible.
|
||||||
// If the user is globaly accessible they specify themselves as a client.
|
// If the user is globaly accessible they specify themselves as a client.
|
||||||
Role *NodeType `yaml:"role" validate:"required,eq=client|eq=peer"`
|
Role *NodeType `yaml:"role" validate:"required,eq=client|eq=peer"`
|
||||||
// KeepAliveWg configures the implementation so that we send keep alive packets to peers.
|
// KeepAliveWg configures the implementation so that we send keep alive packets to peers.
|
||||||
// KeepAlive can only be set if role is type client
|
|
||||||
KeepAliveWg *int `yaml:"keepAliveWg" validate:"omitempty,gte=0"`
|
KeepAliveWg *int `yaml:"keepAliveWg" validate:"omitempty,gte=0"`
|
||||||
// PreUp are WireGuard commands to run before adding the WG interface
|
// PreUp are WireGuard commands to run before adding the WG interface
|
||||||
PreUp []string `yaml:"preUp"`
|
PreUp []string `yaml:"preUp"`
|
||||||
@ -61,11 +60,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"`
|
||||||
@ -77,15 +76,17 @@ type DaemonConfiguration struct {
|
|||||||
Profile bool `yaml:"profile"`
|
Profile bool `yaml:"profile"`
|
||||||
// StubWg whether or not to stub the WireGuard types
|
// StubWg whether or not to stub the WireGuard types
|
||||||
StubWg bool `yaml:"stubWg"`
|
StubWg bool `yaml:"stubWg"`
|
||||||
// SyncRate specifies how long the minimum time should be between synchronisation
|
// SyncTime specifies how long the minimum time should be between synchronisation
|
||||||
SyncRate int `yaml:"syncRate" validate:"required,gte=1"`
|
SyncTime int `yaml:"syncTime" validate:"required,gte=1"`
|
||||||
// KeepAliveTime: number of seconds before the leader of the mesh sends an update to
|
// 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
|
||||||
// send to every member in the mesh
|
// send to every member in the mesh
|
||||||
KeepAliveTime int `yaml:"keepAliveTime" validate:"required,gte=1"`
|
HeartBeat int `yaml:"heartBeatTime" 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"`
|
||||||
@ -126,26 +127,6 @@ func ValidateDaemonConfiguration(c *DaemonConfiguration) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseMeshConfiguration: parses the mesh network configuration. Parses parameters such as
|
|
||||||
// keepalive time, role and so forth.
|
|
||||||
func ParseMeshConfiguration(filePath string) (*WgConfiguration, error) {
|
|
||||||
var conf WgConfiguration
|
|
||||||
|
|
||||||
yamlBytes, err := os.ReadFile(filePath)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = yaml.Unmarshal(yamlBytes, &conf)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &conf, ValidateMeshConfiguration(&conf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseDaemonConfiguration parses the mesh configuration and validates the configuration
|
// ParseDaemonConfiguration parses the mesh configuration and validates the configuration
|
||||||
func ParseDaemonConfiguration(filePath string) (*DaemonConfiguration, error) {
|
func ParseDaemonConfiguration(filePath string) (*DaemonConfiguration, error) {
|
||||||
var conf DaemonConfiguration
|
var conf DaemonConfiguration
|
||||||
@ -162,6 +143,11 @@ func ParseDaemonConfiguration(filePath string) (*DaemonConfiguration, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if conf.BaseConfiguration.KeepAliveWg == nil {
|
||||||
|
var keepAlive int = 0
|
||||||
|
conf.BaseConfiguration.KeepAliveWg = &keepAlive
|
||||||
|
}
|
||||||
|
|
||||||
return &conf, ValidateDaemonConfiguration(&conf)
|
return &conf, ValidateDaemonConfiguration(&conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,41 @@
|
|||||||
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,
|
||||||
|
SyncTime: 2,
|
||||||
|
HeartBeat: 2,
|
||||||
|
ClusterSize: 64,
|
||||||
|
InterClusterChance: 0.15,
|
||||||
|
BranchRate: 3,
|
||||||
|
PullTime: 0,
|
||||||
|
InfectionCount: 2,
|
||||||
|
BaseConfiguration: WgConfiguration{
|
||||||
|
IPDiscovery: &discovery,
|
||||||
|
AdvertiseRoutes: &advertiseRoutes,
|
||||||
|
AdvertiseDefaultRoute: &advertiseDefaultRoute,
|
||||||
|
Endpoint: &endpoint,
|
||||||
|
Role: &nodeType,
|
||||||
|
KeepAliveWg: &keepAliveWg,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,9 +83,152 @@ 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 TestsyncTimeZero(t *testing.T) {
|
||||||
|
conf := getExampleConfiguration()
|
||||||
|
conf.SyncTime = 0
|
||||||
|
|
||||||
|
err := ValidateDaemonConfiguration(conf)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeepAliveTimeZero(t *testing.T) {
|
||||||
|
conf := getExampleConfiguration()
|
||||||
|
conf.HeartBeat = 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 TestPullTimeNegative(t *testing.T) {
|
||||||
|
conf := getExampleConfiguration()
|
||||||
|
conf.PullTime = -1
|
||||||
|
|
||||||
|
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 {
|
||||||
|
@ -23,9 +23,10 @@ func binarySearch(global []string, selfId string, groupSize int) (int, int) {
|
|||||||
|
|
||||||
lower := 0
|
lower := 0
|
||||||
higher := len(global) - 1
|
higher := len(global) - 1
|
||||||
mid := (lower + higher) / 2
|
|
||||||
|
|
||||||
for (higher+1)-lower > groupSize {
|
for (higher+1)-lower > groupSize {
|
||||||
|
mid := (lower + higher) / 2
|
||||||
|
|
||||||
if global[mid] < selfId {
|
if global[mid] < selfId {
|
||||||
lower = mid + 1
|
lower = mid + 1
|
||||||
} else if global[mid] > selfId {
|
} else if global[mid] > selfId {
|
||||||
@ -33,8 +34,6 @@ func binarySearch(global []string, selfId string, groupSize int) (int, int) {
|
|||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
mid = (lower + higher) / 2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return lower, int(math.Min(float64(lower+groupSize), float64(len(global))))
|
return lower, int(math.Min(float64(lower+groupSize), float64(len(global))))
|
||||||
@ -55,12 +54,14 @@ func (i *ConnClusterImpl) GetNeighbours(global []string, selfId string) []string
|
|||||||
// you will communicate with a random node that is not in your cluster.
|
// you will communicate with a random node that is not in your cluster.
|
||||||
func (i *ConnClusterImpl) GetInterCluster(global []string, selfId string) string {
|
func (i *ConnClusterImpl) GetInterCluster(global []string, selfId string) string {
|
||||||
// Doesn't matter if not in it. Get index of where the node 'should' be
|
// Doesn't matter if not in it. Get index of where the node 'should' be
|
||||||
|
slices.Sort(global)
|
||||||
|
|
||||||
index, _ := binarySearch(global, selfId, 1)
|
index, _ := binarySearch(global, selfId, 1)
|
||||||
numClusters := math.Ceil(float64(len(global)) / float64(i.clusterSize))
|
|
||||||
|
|
||||||
randomCluster := rand.Intn(int(numClusters)-1) + 1
|
randomCluster := rand.Intn(2) + 1
|
||||||
|
|
||||||
neighbourIndex := (index + randomCluster) % len(global)
|
// cluster is considered a heap
|
||||||
|
neighbourIndex := (2*index + (randomCluster * i.clusterSize)) % len(global)
|
||||||
return global[neighbourIndex]
|
return global[neighbourIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
)
|
)
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConnectionManager defines an interface for maintaining peer connections
|
// ConnectionManager defines an interface for maintaining peer connections
|
||||||
@ -19,9 +19,11 @@ type ConnectionManager interface {
|
|||||||
// If the endpoint does not exist then add the connection. Returns an error
|
// If the endpoint does not exist then add the connection. Returns an error
|
||||||
// if something went wrong
|
// if something went wrong
|
||||||
GetConnection(endPoint string) (PeerConnection, error)
|
GetConnection(endPoint string) (PeerConnection, error)
|
||||||
// HasConnections returns true if a client has already registered at the givne
|
// HasConnections returns true if a peer has already registered at the given
|
||||||
// endpoint or false otherwise.
|
// endpoint or false otherwise.
|
||||||
HasConnection(endPoint string) bool
|
HasConnection(endPoint string) bool
|
||||||
|
// Removes a connection if it exists
|
||||||
|
RemoveConnection(endPoint string) error
|
||||||
// Goes through all the connections and closes eachone
|
// Goes through all the connections and closes eachone
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
@ -32,7 +34,6 @@ type ConnectionManagerImpl struct {
|
|||||||
// clientConnections maps an endpoint to a connection
|
// clientConnections maps an endpoint to a connection
|
||||||
conLoc sync.RWMutex
|
conLoc sync.RWMutex
|
||||||
clientConnections map[string]PeerConnection
|
clientConnections map[string]PeerConnection
|
||||||
serverConfig *tls.Config
|
|
||||||
clientConfig *tls.Config
|
clientConfig *tls.Config
|
||||||
connFactory PeerConnectionFactory
|
connFactory PeerConnectionFactory
|
||||||
}
|
}
|
||||||
@ -61,37 +62,25 @@ func NewConnectionManager(params *NewConnectionManagerParams) (ConnectionManager
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
serverAuth := tls.RequireAndVerifyClientCert
|
|
||||||
|
|
||||||
if params.SkipCertVerification {
|
|
||||||
serverAuth = tls.RequireAnyClientCert
|
|
||||||
}
|
|
||||||
|
|
||||||
certPool := x509.NewCertPool()
|
certPool := x509.NewCertPool()
|
||||||
|
|
||||||
if !params.SkipCertVerification {
|
if params.CaCert == "" {
|
||||||
|
return nil, errors.New("CA Cert is not specified")
|
||||||
if params.CaCert == "" {
|
|
||||||
return nil, errors.New("CA Cert is not specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
caCert, err := os.ReadFile(params.CaCert)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
certPool.AppendCertsFromPEM(caCert)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
serverConfig := &tls.Config{
|
caCert, err := os.ReadFile(params.CaCert)
|
||||||
ClientAuth: serverAuth,
|
|
||||||
Certificates: []tls.Certificate{cert},
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := certPool.AppendCertsFromPEM(caCert); !ok {
|
||||||
|
return nil, errors.New("could not parse PEM")
|
||||||
}
|
}
|
||||||
|
|
||||||
clientConfig := &tls.Config{
|
clientConfig := &tls.Config{
|
||||||
Certificates: []tls.Certificate{cert},
|
|
||||||
InsecureSkipVerify: params.SkipCertVerification,
|
InsecureSkipVerify: params.SkipCertVerification,
|
||||||
|
Certificates: []tls.Certificate{cert},
|
||||||
RootCAs: certPool,
|
RootCAs: certPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,7 +88,6 @@ func NewConnectionManager(params *NewConnectionManagerParams) (ConnectionManager
|
|||||||
connMgr := ConnectionManagerImpl{
|
connMgr := ConnectionManagerImpl{
|
||||||
sync.RWMutex{},
|
sync.RWMutex{},
|
||||||
connections,
|
connections,
|
||||||
serverConfig,
|
|
||||||
clientConfig,
|
clientConfig,
|
||||||
params.ConnFactory,
|
params.ConnFactory,
|
||||||
}
|
}
|
||||||
@ -150,6 +138,15 @@ func (m *ConnectionManagerImpl) HasConnection(endPoint string) bool {
|
|||||||
return exists
|
return exists
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveConnection removes the given connection if it exists
|
||||||
|
func (m *ConnectionManagerImpl) RemoveConnection(endPoint string) error {
|
||||||
|
m.conLoc.Lock()
|
||||||
|
err := m.clientConnections[endPoint].Close()
|
||||||
|
|
||||||
|
delete(m.clientConnections, endPoint)
|
||||||
|
m.conLoc.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
func (m *ConnectionManagerImpl) Close() error {
|
func (m *ConnectionManagerImpl) Close() error {
|
||||||
for _, conn := range m.clientConnections {
|
for _, conn := range m.clientConnections {
|
||||||
if err := conn.Close(); err != nil {
|
if err := conn.Close(); err != nil {
|
||||||
|
@ -53,13 +53,13 @@ func TestNewConnectionManagerCACertDoesNotExistAndVerify(t *testing.T) {
|
|||||||
|
|
||||||
func TestNewConnectionManagerCACertDoesNotExistAndNotVerify(t *testing.T) {
|
func TestNewConnectionManagerCACertDoesNotExistAndNotVerify(t *testing.T) {
|
||||||
params := getConnectionManagerParams()
|
params := getConnectionManagerParams()
|
||||||
params.CaCert = ""
|
params.CaCert = "./cert/sdjsdjsdjk.pem"
|
||||||
params.SkipCertVerification = true
|
params.SkipCertVerification = true
|
||||||
|
|
||||||
_, err := NewConnectionManager(params)
|
_, err := NewConnectionManager(params)
|
||||||
|
|
||||||
if err != nil {
|
if err == nil {
|
||||||
t.Fatal(`an error should not be thrown`)
|
t.Fatalf(`an error should be thrown`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,22 +2,23 @@ package conn
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/rpc"
|
"github.com/tim-beatham/smegmesh/pkg/rpc"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConnectionServer manages gRPC server peer connections
|
// ConnectionServer manages gRPC server peer connections
|
||||||
type ConnectionServer struct {
|
type ConnectionServer struct {
|
||||||
// tlsConfiguration of the server
|
|
||||||
serverConfig *tls.Config
|
|
||||||
// server an instance of the grpc server
|
// server an instance of the grpc server
|
||||||
server *grpc.Server // the authentication service to authenticate nodes
|
server *grpc.Server
|
||||||
// the ctrl service to manage node
|
// the ctrl service to manage node
|
||||||
ctrlProvider rpc.MeshCtrlServerServer
|
ctrlProvider rpc.MeshCtrlServerServer
|
||||||
// the sync service to synchronise nodes
|
// the sync service to synchronise nodes
|
||||||
@ -48,9 +49,26 @@ func NewConnectionServer(params *NewConnectionServerParams) (*ConnectionServer,
|
|||||||
serverAuth = tls.RequireAnyClientCert
|
serverAuth = tls.RequireAnyClientCert
|
||||||
}
|
}
|
||||||
|
|
||||||
|
certPool := x509.NewCertPool()
|
||||||
|
|
||||||
|
if params.Conf.CaCertificatePath == "" {
|
||||||
|
return nil, errors.New("CA Cert is not specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
caCert, err := os.ReadFile(params.Conf.CaCertificatePath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := certPool.AppendCertsFromPEM(caCert); !ok {
|
||||||
|
return nil, errors.New("could not parse PEM")
|
||||||
|
}
|
||||||
|
|
||||||
serverConfig := &tls.Config{
|
serverConfig := &tls.Config{
|
||||||
ClientAuth: serverAuth,
|
ClientAuth: serverAuth,
|
||||||
Certificates: []tls.Certificate{cert},
|
Certificates: []tls.Certificate{cert},
|
||||||
|
ClientCAs: certPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
server := grpc.NewServer(
|
server := grpc.NewServer(
|
||||||
@ -61,7 +79,6 @@ func NewConnectionServer(params *NewConnectionServerParams) (*ConnectionServer,
|
|||||||
syncProvider := params.SyncProvider
|
syncProvider := params.SyncProvider
|
||||||
|
|
||||||
connServer := ConnectionServer{
|
connServer := ConnectionServer{
|
||||||
serverConfig: serverConfig,
|
|
||||||
server: server,
|
server: server,
|
||||||
ctrlProvider: ctrlProvider,
|
ctrlProvider: ctrlProvider,
|
||||||
syncProvider: syncProvider,
|
syncProvider: syncProvider,
|
||||||
@ -74,7 +91,6 @@ func NewConnectionServer(params *NewConnectionServerParams) (*ConnectionServer,
|
|||||||
// Listen for incoming requests. Returns an error if something went wrong.
|
// Listen for incoming requests. Returns an error if something went wrong.
|
||||||
func (s *ConnectionServer) Listen() error {
|
func (s *ConnectionServer) Listen() error {
|
||||||
rpc.RegisterMeshCtrlServerServer(s.server, s.ctrlProvider)
|
rpc.RegisterMeshCtrlServerServer(s.server, s.ctrlProvider)
|
||||||
|
|
||||||
rpc.RegisterSyncServiceServer(s.server, s.syncProvider)
|
rpc.RegisterSyncServiceServer(s.server, s.syncProvider)
|
||||||
|
|
||||||
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", s.Conf.GrpcPort))
|
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", s.Conf.GrpcPort))
|
||||||
|
@ -16,6 +16,11 @@ func (s *ConnectionManagerStub) AddConnection(endPoint string) (PeerConnection,
|
|||||||
return mock, nil
|
return mock, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ConnectionManagerStub) RemoveConnection(endPoint string) error {
|
||||||
|
delete(s.Endpoints, endPoint)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ConnectionManagerStub) GetConnection(endPoint string) (PeerConnection, error) {
|
func (s *ConnectionManagerStub) GetConnection(endPoint string) (PeerConnection, error) {
|
||||||
endpoint, ok := s.Endpoints[endPoint]
|
endpoint, ok := s.Endpoints[endPoint]
|
||||||
|
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -9,10 +9,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl"
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
@ -158,8 +158,8 @@ type TwoPhaseStoreMeshManager struct {
|
|||||||
IfName string
|
IfName string
|
||||||
Client *wgctrl.Client
|
Client *wgctrl.Client
|
||||||
LastClock uint64
|
LastClock uint64
|
||||||
conf *conf.WgConfiguration
|
Conf *conf.WgConfiguration
|
||||||
daemonConf *conf.DaemonConfiguration
|
DaemonConf *conf.DaemonConfiguration
|
||||||
store *TwoPhaseMap[string, MeshNode]
|
store *TwoPhaseMap[string, MeshNode]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +204,6 @@ func (m *TwoPhaseStoreMeshManager) Save() []byte {
|
|||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
enc := gob.NewEncoder(&buf)
|
enc := gob.NewEncoder(&buf)
|
||||||
|
|
||||||
err := enc.Encode(*snapshot)
|
err := enc.Encode(*snapshot)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -265,7 +264,7 @@ func (m *TwoPhaseStoreMeshManager) UpdateTimeStamp(nodeId string) error {
|
|||||||
|
|
||||||
peerToUpdate := peers[0]
|
peerToUpdate := peers[0]
|
||||||
|
|
||||||
if uint64(time.Now().Unix())-m.store.Clock.GetTimestamp(peerToUpdate) > 3*uint64(m.daemonConf.KeepAliveTime) {
|
if uint64(time.Now().Unix())-m.store.Clock.GetTimestamp(peerToUpdate) > 3*uint64(m.DaemonConf.HeartBeat) {
|
||||||
m.store.Mark(peerToUpdate)
|
m.store.Mark(peerToUpdate)
|
||||||
|
|
||||||
if len(peers) < 2 {
|
if len(peers) < 2 {
|
||||||
@ -386,7 +385,7 @@ func (m *TwoPhaseStoreMeshManager) SetAlias(nodeId string, alias string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node := m.store.Get(nodeId)
|
node := m.store.Get(nodeId)
|
||||||
node.Description = alias
|
node.Alias = alias
|
||||||
|
|
||||||
m.store.Put(nodeId, node)
|
m.store.Put(nodeId, node)
|
||||||
return nil
|
return nil
|
||||||
@ -411,6 +410,11 @@ func (m *TwoPhaseStoreMeshManager) RemoveService(nodeId string, key string) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
node := m.store.Get(nodeId)
|
node := m.store.Get(nodeId)
|
||||||
|
|
||||||
|
if _, ok := node.Services[key]; !ok {
|
||||||
|
return fmt.Errorf("datastore: node does not contain service %s", key)
|
||||||
|
}
|
||||||
|
|
||||||
delete(node.Services, key)
|
delete(node.Services, key)
|
||||||
m.store.Put(nodeId, node)
|
m.store.Put(nodeId, node)
|
||||||
return nil
|
return nil
|
||||||
@ -510,5 +514,5 @@ func (m *TwoPhaseStoreMeshManager) RemoveNode(nodeId string) error {
|
|||||||
|
|
||||||
// GetConfiguration implements mesh.MeshProvider.
|
// GetConfiguration implements mesh.MeshProvider.
|
||||||
func (m *TwoPhaseStoreMeshManager) GetConfiguration() *conf.WgConfiguration {
|
func (m *TwoPhaseStoreMeshManager) GetConfiguration() *conf.WgConfiguration {
|
||||||
return m.conf
|
return m.Conf
|
||||||
}
|
}
|
||||||
|
442
pkg/crdt/datastore_test.go
Normal file
442
pkg/crdt/datastore_test.go
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
package crdt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestParams struct {
|
||||||
|
manager mesh.MeshProvider
|
||||||
|
publicKey *wgtypes.Key
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUpTests() *TestParams {
|
||||||
|
advertiseRoutes := false
|
||||||
|
advertiseDefaultRoute := false
|
||||||
|
role := conf.PEER_ROLE
|
||||||
|
discovery := conf.DNS_IP_DISCOVERY
|
||||||
|
|
||||||
|
factory := &TwoPhaseMapFactory{
|
||||||
|
Config: &conf.DaemonConfiguration{
|
||||||
|
CertificatePath: "/somecertificatepath",
|
||||||
|
PrivateKeyPath: "/someprivatekeypath",
|
||||||
|
CaCertificatePath: "/somecacertificatepath",
|
||||||
|
SkipCertVerification: true,
|
||||||
|
GrpcPort: 0,
|
||||||
|
Timeout: 20,
|
||||||
|
Profile: false,
|
||||||
|
SyncTime: 2,
|
||||||
|
HeartBeat: 10,
|
||||||
|
ClusterSize: 32,
|
||||||
|
InterClusterChance: 0.15,
|
||||||
|
BranchRate: 3,
|
||||||
|
InfectionCount: 3,
|
||||||
|
BaseConfiguration: conf.WgConfiguration{
|
||||||
|
IPDiscovery: &discovery,
|
||||||
|
AdvertiseRoutes: &advertiseRoutes,
|
||||||
|
AdvertiseDefaultRoute: &advertiseDefaultRoute,
|
||||||
|
Role: &role,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
key, _ := wgtypes.GeneratePrivateKey()
|
||||||
|
|
||||||
|
mesh, _ := factory.CreateMesh(&mesh.MeshProviderFactoryParams{
|
||||||
|
DevName: "bob",
|
||||||
|
MeshId: "meshid123",
|
||||||
|
Client: nil,
|
||||||
|
Conf: &factory.Config.BaseConfiguration,
|
||||||
|
DaemonConf: factory.Config,
|
||||||
|
NodeID: "bob",
|
||||||
|
})
|
||||||
|
|
||||||
|
publicKey := key.PublicKey()
|
||||||
|
|
||||||
|
return &TestParams{
|
||||||
|
manager: mesh,
|
||||||
|
publicKey: &publicKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOurNode(testParams *TestParams) *MeshNode {
|
||||||
|
return &MeshNode{
|
||||||
|
HostEndpoint: "public-endpoint:8080",
|
||||||
|
WgEndpoint: "public-endpoint:21906",
|
||||||
|
WgHost: "3e9a:1fb3:5e50:8173:9690:f917:b1ab:d218/128",
|
||||||
|
PublicKey: testParams.publicKey.String(),
|
||||||
|
Timestamp: time.Now().Unix(),
|
||||||
|
Description: "A node that we are adding",
|
||||||
|
Type: "peer",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRandomNode() *MeshNode {
|
||||||
|
key, _ := wgtypes.GeneratePrivateKey()
|
||||||
|
publicKey := key.PublicKey()
|
||||||
|
|
||||||
|
return &MeshNode{
|
||||||
|
HostEndpoint: "public-endpoint:8081",
|
||||||
|
WgEndpoint: "public-endpoint:21907",
|
||||||
|
WgHost: "3e9a:1fb3:5e50:8173:9690:f917:b1ab:d234/128",
|
||||||
|
PublicKey: publicKey.String(),
|
||||||
|
Timestamp: time.Now().Unix(),
|
||||||
|
Description: "A node that we are adding",
|
||||||
|
Type: "peer",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddNodeAddsTheNodesToTheStore(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
testParams.manager.AddNode(getOurNode(testParams))
|
||||||
|
|
||||||
|
if !testParams.manager.NodeExists(testParams.publicKey.String()) {
|
||||||
|
t.Fatalf(`node %s should have been added to the mesh network`, testParams.publicKey.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddNodeNodeAlreadyExistsReplacesTheNode(t *testing.T) {
|
||||||
|
TestAddNodeAddsTheNodesToTheStore(t)
|
||||||
|
TestAddNodeAddsTheNodesToTheStore(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSaveThenLoad(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
testParams.manager.AddNode(getOurNode(testParams))
|
||||||
|
testParams.manager.AddNode(getRandomNode())
|
||||||
|
testParams.manager.AddNode(getRandomNode())
|
||||||
|
testParams.manager.AddNode(getRandomNode())
|
||||||
|
testParams.manager.AddNode(getRandomNode())
|
||||||
|
|
||||||
|
bytes := testParams.manager.Save()
|
||||||
|
|
||||||
|
if err := testParams.manager.Load(bytes); err != nil {
|
||||||
|
t.Fatalf(`error caused by loading datastore: %s`, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasChangesReturnsTrueWhenThereAreChangesInTheMesh(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
testParams.manager.AddNode(getOurNode(testParams))
|
||||||
|
testParams.manager.AddNode(getRandomNode())
|
||||||
|
testParams.manager.AddNode(getRandomNode())
|
||||||
|
testParams.manager.AddNode(getRandomNode())
|
||||||
|
|
||||||
|
if !testParams.manager.HasChanges() {
|
||||||
|
t.Fatalf(`mesh has change but HasChanges returned false`)
|
||||||
|
}
|
||||||
|
|
||||||
|
testParams.manager.SetDescription(testParams.publicKey.String(), "Bob marley")
|
||||||
|
|
||||||
|
if !testParams.manager.HasChanges() {
|
||||||
|
t.Fatalf(`mesh has change but HasChanges returned false`)
|
||||||
|
}
|
||||||
|
|
||||||
|
testParams.manager.SaveChanges()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasChangesWhenThereAreNoChangesInTheMeshReturnsFalse(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
testParams.manager.AddNode(getOurNode(testParams))
|
||||||
|
testParams.manager.AddNode(getRandomNode())
|
||||||
|
testParams.manager.AddNode(getRandomNode())
|
||||||
|
testParams.manager.AddNode(getRandomNode())
|
||||||
|
|
||||||
|
testParams.manager.SaveChanges()
|
||||||
|
|
||||||
|
if testParams.manager.HasChanges() {
|
||||||
|
t.Fatalf(`mesh has no changes but HasChanges was true`)
|
||||||
|
}
|
||||||
|
|
||||||
|
testParams.manager.SetDescription(testParams.publicKey.String(), "Bob marley")
|
||||||
|
|
||||||
|
testParams.manager.SaveChanges()
|
||||||
|
|
||||||
|
if testParams.manager.HasChanges() {
|
||||||
|
t.Fatalf(`mesh has no changes but HasChanges was true`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateTimeStampUpdatesTheTimeStampOfTheGivenNodeIfItIsTheLeader(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
testParams.manager.AddNode(getOurNode(testParams))
|
||||||
|
|
||||||
|
before, _ := testParams.manager.GetNode(testParams.publicKey.String())
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
testParams.manager.UpdateTimeStamp(testParams.publicKey.String())
|
||||||
|
|
||||||
|
after, _ := testParams.manager.GetNode(testParams.publicKey.String())
|
||||||
|
|
||||||
|
if before.GetTimeStamp() >= after.GetTimeStamp() {
|
||||||
|
t.Fatalf(`before should not be after after`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateTimeStampUpdatesTheTimeStampOfTheGivenNodeIfItIsNotLeader(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
testParams.manager.AddNode(getOurNode(testParams))
|
||||||
|
|
||||||
|
newNode := getRandomNode()
|
||||||
|
newNode.PublicKey = "aaaaaaaaaa"
|
||||||
|
|
||||||
|
testParams.manager.AddNode(newNode)
|
||||||
|
|
||||||
|
before, _ := testParams.manager.GetNode(testParams.publicKey.String())
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
after, _ := testParams.manager.GetNode(testParams.publicKey.String())
|
||||||
|
|
||||||
|
if before.GetTimeStamp() != after.GetTimeStamp() {
|
||||||
|
t.Fatalf(`before and after should be the same`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddRoutesAddsARouteToTheGivenMesh(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
testParams.manager.AddNode(getOurNode(testParams))
|
||||||
|
|
||||||
|
_, destination, _ := net.ParseCIDR("0353:1da7:7f33:acc0:7a3f:6e55:912b:bc1f/64")
|
||||||
|
|
||||||
|
testParams.manager.AddRoutes(testParams.publicKey.String(), &mesh.RouteStub{
|
||||||
|
Destination: destination,
|
||||||
|
HopCount: 0,
|
||||||
|
Path: make([]string, 0),
|
||||||
|
})
|
||||||
|
|
||||||
|
node, _ := testParams.manager.GetNode(testParams.publicKey.String())
|
||||||
|
|
||||||
|
containsDestination := lib.Contains(node.GetRoutes(), func(r mesh.Route) bool {
|
||||||
|
return r.GetDestination().Contains(destination.IP)
|
||||||
|
})
|
||||||
|
|
||||||
|
if !containsDestination {
|
||||||
|
t.Fatalf(`route has not been added to the node`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveRoutesWithdrawsRoutesFromTheMesh(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
testParams.manager.AddNode(getOurNode(testParams))
|
||||||
|
|
||||||
|
_, destination, _ := net.ParseCIDR("0353:1da7:7f33:acc0:7a3f:6e55:912b:bc1f/64")
|
||||||
|
route := &mesh.RouteStub{
|
||||||
|
Destination: destination,
|
||||||
|
HopCount: 0,
|
||||||
|
Path: make([]string, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
testParams.manager.AddRoutes(testParams.publicKey.String(), route)
|
||||||
|
testParams.manager.RemoveRoutes(testParams.publicKey.String(), route)
|
||||||
|
|
||||||
|
node, _ := testParams.manager.GetNode(testParams.publicKey.String())
|
||||||
|
|
||||||
|
containsDestination := lib.Contains(node.GetRoutes(), func(r mesh.Route) bool {
|
||||||
|
return r.GetDestination().Contains(destination.IP)
|
||||||
|
})
|
||||||
|
|
||||||
|
if containsDestination {
|
||||||
|
t.Fatalf(`route has not been removed from the node`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetNodeGetsTheNodeWhenItExists(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
testParams.manager.AddNode(getOurNode(testParams))
|
||||||
|
node, _ := testParams.manager.GetNode(testParams.publicKey.String())
|
||||||
|
|
||||||
|
if node == nil {
|
||||||
|
t.Fatalf(`node not found returned nil`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetNodeReturnsNilWhenItDoesNotExist(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
testParams.manager.AddNode(getOurNode(testParams))
|
||||||
|
testParams.manager.RemoveNode(testParams.publicKey.String())
|
||||||
|
|
||||||
|
node, _ := testParams.manager.GetNode(testParams.publicKey.String())
|
||||||
|
|
||||||
|
if node != nil {
|
||||||
|
t.Fatalf(`node found but should be nil`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNodeExistsReturnsFalseWhenNotExists(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
testParams.manager.AddNode(getOurNode(testParams))
|
||||||
|
testParams.manager.RemoveNode(testParams.publicKey.String())
|
||||||
|
|
||||||
|
if testParams.manager.NodeExists(testParams.publicKey.String()) {
|
||||||
|
t.Fatalf(`nodeexists should be false`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetDescriptionReturnsErrorWhenNodeDoesNotExist(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
err := testParams.manager.SetDescription("djdjdj", "djdsjkd")
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetDescriptionSetsTheDescription(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
descriptionToSet := "djdsjkd"
|
||||||
|
testParams.manager.AddNode(getOurNode(testParams))
|
||||||
|
err := testParams.manager.SetDescription(testParams.publicKey.String(), descriptionToSet)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`error %s thrown`, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
node, _ := testParams.manager.GetNode(testParams.publicKey.String())
|
||||||
|
|
||||||
|
description := node.GetDescription()
|
||||||
|
|
||||||
|
if description != descriptionToSet {
|
||||||
|
t.Fatalf(`description was %s should be %s`, description, descriptionToSet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAliasNodeDoesNotExist(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
err := testParams.manager.SetAlias("djdjdj", "djdsjkd")
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetAliasSetsAlias(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
aliasToSet := "djdsjkd"
|
||||||
|
testParams.manager.AddNode(getOurNode(testParams))
|
||||||
|
err := testParams.manager.SetAlias(testParams.publicKey.String(), aliasToSet)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`error %s thrown`, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
node, _ := testParams.manager.GetNode(testParams.publicKey.String())
|
||||||
|
|
||||||
|
alias := node.GetAlias()
|
||||||
|
|
||||||
|
if alias != aliasToSet {
|
||||||
|
t.Fatalf(`description was %s should be %s`, alias, aliasToSet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddServiceNodeDoesNotExist(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
err := testParams.manager.AddService("djdjdj", "djdsjkd", "sddsds")
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddServiceNodeExists(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
service := "djdsjkd"
|
||||||
|
serviceValue := "dsdsds"
|
||||||
|
testParams.manager.AddNode(getOurNode(testParams))
|
||||||
|
err := testParams.manager.AddService(testParams.publicKey.String(), service, serviceValue)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`error %s thrown`, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
node, _ := testParams.manager.GetNode(testParams.publicKey.String())
|
||||||
|
|
||||||
|
services := node.GetServices()
|
||||||
|
|
||||||
|
if value, ok := services[service]; !ok || value != serviceValue {
|
||||||
|
t.Fatalf(`service not added to the data store`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveServiceDoesNotExists(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
err := testParams.manager.RemoveService("djdjdj", "dsdssd")
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveServiceServiceDoesNotExist(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
testParams.manager.AddNode(getOurNode(testParams))
|
||||||
|
|
||||||
|
if err := testParams.manager.RemoveService(testParams.publicKey.String(), "dhsdh"); err == nil {
|
||||||
|
t.Fatalf(`error should be thrown`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetPeersReturnsAllPeersInTheMesh(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
peer1 := getRandomNode()
|
||||||
|
peer2 := getRandomNode()
|
||||||
|
client := getRandomNode()
|
||||||
|
client.Type = "client"
|
||||||
|
|
||||||
|
testParams.manager.AddNode(peer1)
|
||||||
|
testParams.manager.AddNode(peer2)
|
||||||
|
testParams.manager.AddNode(client)
|
||||||
|
|
||||||
|
peers := testParams.manager.GetPeers()
|
||||||
|
slices.Sort(peers)
|
||||||
|
|
||||||
|
if len(peers) != 2 {
|
||||||
|
t.Fatalf(`there should be two peers in the mesh`)
|
||||||
|
}
|
||||||
|
|
||||||
|
peer1Pub, _ := peer1.GetPublicKey()
|
||||||
|
|
||||||
|
if !slices.Contains(peers, peer1Pub.String()) {
|
||||||
|
t.Fatalf(`peer1 not in the list`)
|
||||||
|
}
|
||||||
|
|
||||||
|
peer2Pub, _ := peer2.GetPublicKey()
|
||||||
|
|
||||||
|
if !slices.Contains(peers, peer2Pub.String()) {
|
||||||
|
t.Fatalf(`peer2 not in the list`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveNodeReturnsErrorIfNodeDoesNotExist(t *testing.T) {
|
||||||
|
testParams := setUpTests()
|
||||||
|
|
||||||
|
err := testParams.manager.RemoveNode("dsjdssjk")
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf(`error should have returned`)
|
||||||
|
}
|
||||||
|
}
|
@ -4,9 +4,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TwoPhaseMapFactory struct {
|
type TwoPhaseMapFactory struct {
|
||||||
@ -18,13 +18,13 @@ func (f *TwoPhaseMapFactory) CreateMesh(params *mesh.MeshProviderFactoryParams)
|
|||||||
MeshId: params.MeshId,
|
MeshId: params.MeshId,
|
||||||
IfName: params.DevName,
|
IfName: params.DevName,
|
||||||
Client: params.Client,
|
Client: params.Client,
|
||||||
conf: params.Conf,
|
Conf: params.Conf,
|
||||||
daemonConf: params.DaemonConf,
|
DaemonConf: params.DaemonConf,
|
||||||
store: NewTwoPhaseMap[string, MeshNode](params.NodeID, func(s string) uint64 {
|
store: NewTwoPhaseMap[string, MeshNode](params.NodeID, func(s string) uint64 {
|
||||||
h := fnv.New64a()
|
h := fnv.New64a()
|
||||||
h.Write([]byte(s))
|
h.Write([]byte(s))
|
||||||
return h.Sum64()
|
return h.Sum64()
|
||||||
}, uint64(3*f.Config.KeepAliveTime)),
|
}, uint64(3*f.Config.HeartBeat)),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// crdt is a golang implementation of a crdt
|
// crdt provides go implementations for crdts
|
||||||
package crdt
|
package crdt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -65,10 +65,19 @@ func (g *GMap[K, D]) get(key uint64) Bucket[D] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *GMap[K, D]) Get(key K) D {
|
func (g *GMap[K, D]) Get(key K) D {
|
||||||
|
if !g.Contains(key) {
|
||||||
|
var def D
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
return g.get(g.clock.hashFunc(key)).Contents
|
return g.get(g.clock.hashFunc(key)).Contents
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GMap[K, D]) Mark(key K) {
|
func (g *GMap[K, D]) Mark(key K) {
|
||||||
|
if !g.Contains(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
g.lock.Lock()
|
g.lock.Lock()
|
||||||
bucket := g.contents[g.clock.hashFunc(key)]
|
bucket := g.contents[g.clock.hashFunc(key)]
|
||||||
bucket.Gravestone = true
|
bucket.Gravestone = true
|
||||||
@ -89,7 +98,6 @@ func (g *GMap[K, D]) IsMarked(key K) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
g.lock.RUnlock()
|
g.lock.RUnlock()
|
||||||
|
|
||||||
return marked
|
return marked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
224
pkg/crdt/g_map_test.go
Normal file
224
pkg/crdt/g_map_test.go
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
// crdt_test unit tests the crdt implementations
|
||||||
|
package crdt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"hash/fnv"
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewGmap() *GMap[string, bool] {
|
||||||
|
vectorClock := NewVectorClock("a", func(key string) uint64 {
|
||||||
|
hash := fnv.New64a()
|
||||||
|
hash.Write([]byte(key))
|
||||||
|
return hash.Sum64()
|
||||||
|
}, 1) // 1 second stale time
|
||||||
|
|
||||||
|
gMap := NewGMap[string, bool](vectorClock)
|
||||||
|
return gMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGMapPutInsertsItems(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
gMap.Put("bruh1234", true)
|
||||||
|
|
||||||
|
if !gMap.Contains("bruh1234") {
|
||||||
|
t.Fatalf(`value not added to map`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGMapPutReplacesItems(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
gMap.Put("bruh1234", true)
|
||||||
|
gMap.Put("bruh1234", false)
|
||||||
|
|
||||||
|
value := gMap.Get("bruh1234")
|
||||||
|
|
||||||
|
if value {
|
||||||
|
t.Fatalf(`value should ahve been replaced to false`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContainsValueNotPresent(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
|
||||||
|
if gMap.Contains("sdhjsdhsdj") {
|
||||||
|
t.Fatalf(`value should not be present in the map`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContainsValuePresent(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
key := "hehehehe"
|
||||||
|
gMap.Put(key, false)
|
||||||
|
|
||||||
|
if !gMap.Contains(key) {
|
||||||
|
t.Fatalf(`%s should not be present in the map`, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGMapGetNotPresentReturnsError(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
value := gMap.Get("bruh123")
|
||||||
|
|
||||||
|
if value != false {
|
||||||
|
t.Fatalf(`value should be default type false`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGMapGetReturnsValue(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
gMap.Put("bobdylan", true)
|
||||||
|
|
||||||
|
value := gMap.Get("bobdylan")
|
||||||
|
|
||||||
|
if !value {
|
||||||
|
t.Fatalf("value should be true but was false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarkMarksTheValue(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
gMap.Put("hello123", true)
|
||||||
|
|
||||||
|
gMap.Mark("hello123")
|
||||||
|
|
||||||
|
if !gMap.IsMarked("hello123") {
|
||||||
|
t.Fatal(`hello123 should be marked`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarkValueNotPresent(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
gMap.Mark("ok123456")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeysMapEmpty(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
|
||||||
|
keys := gMap.Keys()
|
||||||
|
|
||||||
|
if len(keys) != 0 {
|
||||||
|
t.Fatal(`list of keys was not empty but should be empty`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeysMapReturnsKeysInMap(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
|
||||||
|
gMap.Put("a", false)
|
||||||
|
gMap.Put("b", false)
|
||||||
|
gMap.Put("c", false)
|
||||||
|
|
||||||
|
keys := gMap.Keys()
|
||||||
|
|
||||||
|
if len(keys) != 3 {
|
||||||
|
t.Fatal(`key length should be 3`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSaveMapEmptyReturnsEmptyMap(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
|
||||||
|
saveMap := gMap.Save()
|
||||||
|
|
||||||
|
if len(saveMap) != 0 {
|
||||||
|
t.Fatal(`saves should be empty`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSaveMapReturnsMapOfBuckets(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
gMap.Put("a", false)
|
||||||
|
gMap.Put("b", false)
|
||||||
|
gMap.Put("c", false)
|
||||||
|
|
||||||
|
saveMap := gMap.Save()
|
||||||
|
|
||||||
|
if len(saveMap) != 3 {
|
||||||
|
t.Fatalf(`save length should be 3`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSaveWithKeysNoKeysReturnsEmptyBucket(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
gMap.Put("a", false)
|
||||||
|
gMap.Put("b", false)
|
||||||
|
gMap.Put("c", false)
|
||||||
|
|
||||||
|
saveMap := gMap.SaveWithKeys([]uint64{})
|
||||||
|
|
||||||
|
if len(saveMap) != 0 {
|
||||||
|
t.Fatalf(`save map should be empty`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSaveWithKeysReturnsIntersection(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
gMap.Put("a", false)
|
||||||
|
gMap.Put("b", false)
|
||||||
|
gMap.Put("c", false)
|
||||||
|
|
||||||
|
clock := lib.MapKeys(gMap.GetClock())
|
||||||
|
clock = clock[:len(clock)-1]
|
||||||
|
|
||||||
|
values := gMap.SaveWithKeys(clock)
|
||||||
|
if len(values) != len(clock) {
|
||||||
|
t.Fatalf(`intersection not returned`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetClockMapEmptyReturnsEmptyClock(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
|
||||||
|
clocks := gMap.GetClock()
|
||||||
|
|
||||||
|
if len(clocks) != 0 {
|
||||||
|
t.Fatalf(`vector clock is not empty`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetClockReturnsAllCLocks(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
gMap.Put("a", false)
|
||||||
|
gMap.Put("b", false)
|
||||||
|
gMap.Put("c", false)
|
||||||
|
|
||||||
|
clocks := lib.MapValues(gMap.GetClock())
|
||||||
|
slices.Sort(clocks)
|
||||||
|
|
||||||
|
if !slices.Equal([]uint64{0, 1, 2}, clocks) {
|
||||||
|
t.Fatalf(`clocks are invalid`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetHashChangesHashOnValueAdded(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
gMap.Put("a", false)
|
||||||
|
prevHash := gMap.GetHash()
|
||||||
|
|
||||||
|
gMap.Put("b", true)
|
||||||
|
|
||||||
|
if prevHash == gMap.GetHash() {
|
||||||
|
t.Fatalf(`hash should be different`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPruneGarbageCollectsValuesThatHaveNotBeenUpdated(t *testing.T) {
|
||||||
|
gMap := NewGmap()
|
||||||
|
gMap.clock.Put("c", 12)
|
||||||
|
gMap.Put("c", false)
|
||||||
|
gMap.Put("a", false)
|
||||||
|
|
||||||
|
time.Sleep(4 * time.Second)
|
||||||
|
gMap.Put("a", true)
|
||||||
|
|
||||||
|
gMap.Prune()
|
||||||
|
|
||||||
|
if gMap.Contains("c") {
|
||||||
|
t.Fatalf(`a should have been pruned`)
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@ package crdt
|
|||||||
import (
|
import (
|
||||||
"cmp"
|
"cmp"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TwoPhaseMap[K cmp.Ordered, D any] struct {
|
type TwoPhaseMap[K cmp.Ordered, D any] struct {
|
||||||
@ -151,7 +151,7 @@ func (m *TwoPhaseMap[K, D]) GenerateMessage() *TwoPhaseMapState[K] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *TwoPhaseMapState[K]) Difference(state *TwoPhaseMapState[K]) *TwoPhaseMapState[K] {
|
func (m *TwoPhaseMapState[K]) Difference(highestStale uint64, state *TwoPhaseMapState[K]) *TwoPhaseMapState[K] {
|
||||||
mapState := &TwoPhaseMapState[K]{
|
mapState := &TwoPhaseMapState[K]{
|
||||||
AddContents: make(map[uint64]uint64),
|
AddContents: make(map[uint64]uint64),
|
||||||
RemoveContents: make(map[uint64]uint64),
|
RemoveContents: make(map[uint64]uint64),
|
||||||
@ -160,7 +160,7 @@ func (m *TwoPhaseMapState[K]) Difference(state *TwoPhaseMapState[K]) *TwoPhaseMa
|
|||||||
for key, value := range state.AddContents {
|
for key, value := range state.AddContents {
|
||||||
otherValue, ok := m.AddContents[key]
|
otherValue, ok := m.AddContents[key]
|
||||||
|
|
||||||
if !ok || otherValue < value {
|
if value > highestStale && (!ok || otherValue < value) {
|
||||||
mapState.AddContents[key] = value
|
mapState.AddContents[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,7 +168,7 @@ func (m *TwoPhaseMapState[K]) Difference(state *TwoPhaseMapState[K]) *TwoPhaseMa
|
|||||||
for key, value := range state.RemoveContents {
|
for key, value := range state.RemoveContents {
|
||||||
otherValue, ok := m.RemoveContents[key]
|
otherValue, ok := m.RemoveContents[key]
|
||||||
|
|
||||||
if !ok || otherValue < value {
|
if value > highestStale && (!ok || otherValue < value) {
|
||||||
mapState.RemoveContents[key] = value
|
mapState.RemoveContents[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
|
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SyncState int
|
type SyncState int
|
||||||
@ -68,9 +68,16 @@ func prepare(syncer *TwoPhaseSyncer) ([]byte, bool) {
|
|||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Increment the clock here so the clock gets
|
||||||
|
// distributed to everyone else in the mesh
|
||||||
|
syncer.manager.store.Clock.IncrementClock()
|
||||||
|
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
enc := gob.NewEncoder(&buffer)
|
enc := gob.NewEncoder(&buffer)
|
||||||
|
|
||||||
|
mapState := syncer.manager.store.GenerateMessage()
|
||||||
|
|
||||||
|
syncer.mapState = mapState
|
||||||
err = enc.Encode(*syncer.mapState)
|
err = enc.Encode(*syncer.mapState)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -96,7 +103,7 @@ func present(syncer *TwoPhaseSyncer) ([]byte, bool) {
|
|||||||
logging.Log.WriteErrorf(err.Error())
|
logging.Log.WriteErrorf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
difference := syncer.mapState.Difference(&mapState)
|
difference := syncer.mapState.Difference(syncer.manager.store.Clock.GetStaleCount(), &mapState)
|
||||||
syncer.manager.store.Clock.Merge(mapState.Vectors)
|
syncer.manager.store.Clock.Merge(mapState.Vectors)
|
||||||
|
|
||||||
var sendBuffer bytes.Buffer
|
var sendBuffer bytes.Buffer
|
||||||
@ -164,9 +171,6 @@ func (t *TwoPhaseSyncer) RecvMessage(msg []byte) error {
|
|||||||
|
|
||||||
func (t *TwoPhaseSyncer) Complete() {
|
func (t *TwoPhaseSyncer) Complete() {
|
||||||
logging.Log.WriteInfof("SYNC COMPLETED")
|
logging.Log.WriteInfof("SYNC COMPLETED")
|
||||||
if t.state >= MERGE {
|
|
||||||
t.manager.store.Clock.IncrementClock()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTwoPhaseSyncer(manager *TwoPhaseStoreMeshManager) *TwoPhaseSyncer {
|
func NewTwoPhaseSyncer(manager *TwoPhaseStoreMeshManager) *TwoPhaseSyncer {
|
||||||
@ -181,7 +185,6 @@ func NewTwoPhaseSyncer(manager *TwoPhaseStoreMeshManager) *TwoPhaseSyncer {
|
|||||||
return &TwoPhaseSyncer{
|
return &TwoPhaseSyncer{
|
||||||
manager: manager,
|
manager: manager,
|
||||||
state: HASH,
|
state: HASH,
|
||||||
mapState: manager.store.GenerateMessage(),
|
|
||||||
generateMessageFSM: generateMessageFsm,
|
generateMessageFSM: generateMessageFsm,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
214
pkg/crdt/two_phase_map_test.go
Normal file
214
pkg/crdt/two_phase_map_test.go
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
package crdt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"hash/fnv"
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewMap(processId string) *TwoPhaseMap[string, string] {
|
||||||
|
theMap := NewTwoPhaseMap[string, string](processId, func(key string) uint64 {
|
||||||
|
hash := fnv.New64a()
|
||||||
|
hash.Write([]byte(key))
|
||||||
|
return hash.Sum64()
|
||||||
|
}, 1)
|
||||||
|
return theMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTwoPhaseMapEmpty(t *testing.T) {
|
||||||
|
theMap := NewMap("a")
|
||||||
|
|
||||||
|
if theMap.Contains("a") {
|
||||||
|
t.Fatalf(`a should not be present in the map`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTwoPhaseMapValuePresent(t *testing.T) {
|
||||||
|
theMap := NewMap("a")
|
||||||
|
theMap.Put("a", "")
|
||||||
|
|
||||||
|
if !theMap.Contains("a") {
|
||||||
|
t.Fatalf(`should be present within the map`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTwoPhaseMapValueNotPresent(t *testing.T) {
|
||||||
|
theMap := NewMap("a")
|
||||||
|
theMap.Put("b", "")
|
||||||
|
|
||||||
|
if theMap.Contains("a") {
|
||||||
|
t.Fatalf(`a should not be present in the map`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTwoPhaseMapPutThenRemove(t *testing.T) {
|
||||||
|
theMap := NewMap("a")
|
||||||
|
|
||||||
|
theMap.Put("a", "")
|
||||||
|
theMap.Remove("a")
|
||||||
|
|
||||||
|
if theMap.Contains("a") {
|
||||||
|
t.Fatalf(`a should not be present within the map`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTwoPhaseMapPutThenRemoveThenPut(t *testing.T) {
|
||||||
|
theMap := NewMap("a")
|
||||||
|
|
||||||
|
theMap.Put("a", "")
|
||||||
|
theMap.Remove("a")
|
||||||
|
theMap.Put("a", "")
|
||||||
|
|
||||||
|
if !theMap.Contains("a") {
|
||||||
|
t.Fatalf(`a should be present within the map`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarkMarksTheValueIn2PMap(t *testing.T) {
|
||||||
|
theMap := NewMap("a")
|
||||||
|
|
||||||
|
theMap.Put("a", "")
|
||||||
|
theMap.Mark("a")
|
||||||
|
|
||||||
|
if !theMap.IsMarked("a") {
|
||||||
|
t.Fatalf(`a should be marked`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAsListReturnsItemsInList(t *testing.T) {
|
||||||
|
theMap := NewMap("a")
|
||||||
|
|
||||||
|
theMap.Put("a", "bob")
|
||||||
|
theMap.Put("b", "dylan")
|
||||||
|
|
||||||
|
keys := theMap.AsList()
|
||||||
|
slices.Sort(keys)
|
||||||
|
|
||||||
|
if !slices.Equal([]string{"bob", "dylan"}, keys) {
|
||||||
|
t.Fatalf(`values should be bob, dylan`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSnapShotRemoveMapEmpty(t *testing.T) {
|
||||||
|
theMap := NewMap("a")
|
||||||
|
theMap.Put("a", "bob")
|
||||||
|
theMap.Put("b", "dylan")
|
||||||
|
|
||||||
|
snapshot := theMap.Snapshot()
|
||||||
|
|
||||||
|
if len(snapshot.Add) != 2 {
|
||||||
|
t.Fatalf(`add values length should be 2`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(snapshot.Remove) != 0 {
|
||||||
|
t.Fatalf(`remove map length should be 0`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSnapshotMapEmpty(t *testing.T) {
|
||||||
|
theMap := NewMap("a")
|
||||||
|
|
||||||
|
snapshot := theMap.Snapshot()
|
||||||
|
|
||||||
|
if len(snapshot.Add) != 0 || len(snapshot.Remove) != 0 {
|
||||||
|
t.Fatalf(`snapshot length should be 0`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSnapShotFromStateReturnsIntersection(t *testing.T) {
|
||||||
|
map1 := NewMap("a")
|
||||||
|
map1.Put("a", "heyy")
|
||||||
|
|
||||||
|
map2 := NewMap("b")
|
||||||
|
map2.Put("b", "hmmm")
|
||||||
|
|
||||||
|
message := map2.GenerateMessage()
|
||||||
|
|
||||||
|
snapShot := map1.SnapShotFromState(message)
|
||||||
|
|
||||||
|
if len(snapShot.Add) != 1 {
|
||||||
|
t.Fatalf(`add length should be 1`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(snapShot.Remove) != 0 {
|
||||||
|
t.Fatalf(`remove length should be 0`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetHashDifferentOnChange(t *testing.T) {
|
||||||
|
theMap := NewMap("a")
|
||||||
|
|
||||||
|
prevHash := theMap.GetHash()
|
||||||
|
|
||||||
|
theMap.Put("b", "hmmhmhmh")
|
||||||
|
|
||||||
|
if prevHash == theMap.GetHash() {
|
||||||
|
t.Fatalf(`hashes should not be the same`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateMessageReturnsClocks(t *testing.T) {
|
||||||
|
theMap := NewMap("a")
|
||||||
|
theMap.Put("a", "hmm")
|
||||||
|
theMap.Put("b", "hmm")
|
||||||
|
theMap.Remove("a")
|
||||||
|
|
||||||
|
message := theMap.GenerateMessage()
|
||||||
|
|
||||||
|
if len(message.AddContents) != 2 {
|
||||||
|
t.Fatalf(`two items added add should be 2`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(message.RemoveContents) != 1 {
|
||||||
|
t.Fatalf(`a was removed remove map should be length 1`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDifferenceReturnsDifferenceOfMaps(t *testing.T) {
|
||||||
|
map1 := NewMap("a")
|
||||||
|
map1.Put("a", "ssms")
|
||||||
|
map1.Put("b", "sdmdsmd")
|
||||||
|
|
||||||
|
map2 := NewMap("b")
|
||||||
|
map2.Put("d", "eek")
|
||||||
|
map2.Put("c", "meh")
|
||||||
|
|
||||||
|
message1 := map1.GenerateMessage()
|
||||||
|
message2 := map2.GenerateMessage()
|
||||||
|
|
||||||
|
difference := message1.Difference(0, message2)
|
||||||
|
|
||||||
|
if len(difference.AddContents) != 2 {
|
||||||
|
t.Fatalf(`d and c are not in map1 they should be in add contents`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(difference.RemoveContents) != 0 {
|
||||||
|
t.Fatalf(`remove should be empty`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeMergesValuesThatAreGreaterThanCurrentClock(t *testing.T) {
|
||||||
|
map1 := NewMap("a")
|
||||||
|
map1.Put("a", "ssms")
|
||||||
|
map1.Put("b", "sdmdsmd")
|
||||||
|
|
||||||
|
map2 := NewMap("b")
|
||||||
|
map2.Put("d", "eek")
|
||||||
|
map2.Put("c", "meh")
|
||||||
|
|
||||||
|
message1 := map1.GenerateMessage()
|
||||||
|
message2 := map2.GenerateMessage()
|
||||||
|
|
||||||
|
difference := message1.Difference(0, message2)
|
||||||
|
state := map2.SnapShotFromState(difference)
|
||||||
|
|
||||||
|
map1.Merge(*state)
|
||||||
|
|
||||||
|
if !map1.Contains("d") {
|
||||||
|
t.Fatalf(`d should be in the map`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !map2.Contains("c") {
|
||||||
|
t.Fatalf(`c should be in the map`)
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
type VectorBucket struct {
|
type VectorBucket struct {
|
||||||
@ -23,6 +23,8 @@ type VectorClock[K cmp.Ordered] struct {
|
|||||||
processID K
|
processID K
|
||||||
staleTime uint64
|
staleTime uint64
|
||||||
hashFunc func(K) uint64
|
hashFunc func(K) uint64
|
||||||
|
// highest update that's been garbage collected
|
||||||
|
highestStale uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// IncrementClock: increments the node's value in the vector clock
|
// IncrementClock: increments the node's value in the vector clock
|
||||||
@ -78,6 +80,7 @@ func (m *VectorClock[K]) getStale() []uint64 {
|
|||||||
for key, bucket := range m.vectors {
|
for key, bucket := range m.vectors {
|
||||||
if maxTimeStamp-bucket.lastUpdate > m.staleTime {
|
if maxTimeStamp-bucket.lastUpdate > m.staleTime {
|
||||||
toRemove = append(toRemove, key)
|
toRemove = append(toRemove, key)
|
||||||
|
m.highestStale = max(bucket.clock, m.highestStale)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,6 +88,15 @@ func (m *VectorClock[K]) getStale() []uint64 {
|
|||||||
return toRemove
|
return toRemove
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStaleCount: returns a vector clock which is considered to be stale.
|
||||||
|
// all updates must be greater than this
|
||||||
|
func (m *VectorClock[K]) GetStaleCount() uint64 {
|
||||||
|
m.lock.RLock()
|
||||||
|
staleCount := m.highestStale
|
||||||
|
m.lock.RUnlock()
|
||||||
|
return staleCount
|
||||||
|
}
|
||||||
|
|
||||||
func (m *VectorClock[K]) Prune() {
|
func (m *VectorClock[K]) Prune() {
|
||||||
stale := m.getStale()
|
stale := m.getStale()
|
||||||
|
|
||||||
@ -120,7 +132,9 @@ func (m *VectorClock[K]) put(key uint64, value uint64) {
|
|||||||
clockValue = bucket.clock
|
clockValue = bucket.clock
|
||||||
}
|
}
|
||||||
|
|
||||||
if value > clockValue {
|
// Make sure that entries that were garbage collected don't get
|
||||||
|
// addded back
|
||||||
|
if value > clockValue && value > m.highestStale {
|
||||||
newBucket := VectorBucket{
|
newBucket := VectorBucket{
|
||||||
clock: value,
|
clock: value,
|
||||||
lastUpdate: uint64(time.Now().Unix()),
|
lastUpdate: uint64(time.Now().Unix()),
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
package ctrlserver
|
package ctrlserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conn"
|
"github.com/tim-beatham/smegmesh/pkg/conn"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/crdt"
|
"github.com/tim-beatham/smegmesh/pkg/crdt"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ip"
|
"github.com/tim-beatham/smegmesh/pkg/ip"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/query"
|
"github.com/tim-beatham/smegmesh/pkg/query"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/rpc"
|
"github.com/tim-beatham/smegmesh/pkg/rpc"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/wg"
|
"github.com/tim-beatham/smegmesh/pkg/wg"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl"
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ func NewCtrlServer(params *NewCtrlServerParams) (*MeshCtrlServer, error) {
|
|||||||
nodeFactory := &crdt.MeshNodeFactory{
|
nodeFactory := &crdt.MeshNodeFactory{
|
||||||
Config: *params.Conf,
|
Config: *params.Conf,
|
||||||
}
|
}
|
||||||
idGenerator := &lib.IDNameGenerator{}
|
idGenerator := &lib.ShortIDGenerator{}
|
||||||
ipAllocator := &ip.ULABuilder{}
|
ipAllocator := &ip.ULABuilder{}
|
||||||
interfaceManipulator := wg.NewWgInterfaceManipulator(params.Client)
|
interfaceManipulator := wg.NewWgInterfaceManipulator(params.Client)
|
||||||
|
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
package ctrlserver
|
package ctrlserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"net"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conn"
|
"time"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/query"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
|
"github.com/tim-beatham/smegmesh/pkg/conn"
|
||||||
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
|
"github.com/tim-beatham/smegmesh/pkg/query"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl"
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
@ -14,6 +18,14 @@ type MeshRoute struct {
|
|||||||
Path []string
|
Path []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Represents the WireGuard configuration attached to the node
|
||||||
|
type WireGuardStats struct {
|
||||||
|
AllowedIPs []string
|
||||||
|
TransmitBytes int64
|
||||||
|
ReceivedBytes int64
|
||||||
|
PersistentKeepAliveInterval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
// Represents a WireGuard MeshNode
|
// Represents a WireGuard MeshNode
|
||||||
type MeshNode struct {
|
type MeshNode struct {
|
||||||
HostEndpoint string
|
HostEndpoint string
|
||||||
@ -25,6 +37,7 @@ type MeshNode struct {
|
|||||||
Description string
|
Description string
|
||||||
Alias string
|
Alias string
|
||||||
Services map[string]string
|
Services map[string]string
|
||||||
|
Stats WireGuardStats
|
||||||
}
|
}
|
||||||
|
|
||||||
// Represents a WireGuard Mesh
|
// Represents a WireGuard Mesh
|
||||||
@ -51,3 +64,53 @@ type MeshCtrlServer struct {
|
|||||||
Conf *conf.DaemonConfiguration
|
Conf *conf.DaemonConfiguration
|
||||||
Querier query.Querier
|
Querier query.Querier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewCtrlNode create an instance of a ctrl node to send over an
|
||||||
|
// IPC call
|
||||||
|
func NewCtrlNode(provider mesh.MeshProvider, node mesh.MeshNode) *MeshNode {
|
||||||
|
pubKey, _ := node.GetPublicKey()
|
||||||
|
|
||||||
|
ctrlNode := MeshNode{
|
||||||
|
HostEndpoint: node.GetHostEndpoint(),
|
||||||
|
WgEndpoint: node.GetWgEndpoint(),
|
||||||
|
PublicKey: pubKey.String(),
|
||||||
|
WgHost: node.GetWgHost().String(),
|
||||||
|
Timestamp: node.GetTimeStamp(),
|
||||||
|
Routes: lib.Map(node.GetRoutes(), func(r mesh.Route) MeshRoute {
|
||||||
|
return MeshRoute{
|
||||||
|
Destination: r.GetDestination().String(),
|
||||||
|
Path: r.GetPath(),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Description: node.GetDescription(),
|
||||||
|
Alias: node.GetAlias(),
|
||||||
|
Services: node.GetServices(),
|
||||||
|
}
|
||||||
|
|
||||||
|
device, err := provider.GetDevice()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return &ctrlNode
|
||||||
|
}
|
||||||
|
|
||||||
|
peers := lib.Filter(device.Peers, func(p wgtypes.Peer) bool {
|
||||||
|
return p.PublicKey.String() == pubKey.String()
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(peers) > 0 {
|
||||||
|
peer := peers[0]
|
||||||
|
|
||||||
|
stats := WireGuardStats{
|
||||||
|
AllowedIPs: lib.Map(peer.AllowedIPs, func(i net.IPNet) string {
|
||||||
|
return i.String()
|
||||||
|
}),
|
||||||
|
TransmitBytes: peer.TransmitBytes,
|
||||||
|
ReceivedBytes: peer.ReceiveBytes,
|
||||||
|
PersistentKeepAliveInterval: peer.PersistentKeepaliveInterval,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrlNode.Stats = stats
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ctrlNode
|
||||||
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package ctrlserver
|
package ctrlserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conn"
|
"github.com/tim-beatham/smegmesh/pkg/conn"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/query"
|
"github.com/tim-beatham/smegmesh/pkg/query"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl"
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -4,21 +4,18 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/rpc"
|
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ipc"
|
"github.com/tim-beatham/smegmesh/pkg/ipc"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/query"
|
"github.com/tim-beatham/smegmesh/pkg/query"
|
||||||
)
|
)
|
||||||
|
|
||||||
const SockAddr = "/tmp/wgmesh_ipc.sock"
|
|
||||||
|
|
||||||
const MeshRegularExpression = `(?P<meshId>.+)\.(?P<alias>.+)\.smeg\.`
|
const MeshRegularExpression = `(?P<meshId>.+)\.(?P<alias>.+)\.smeg\.`
|
||||||
|
|
||||||
type DNSHandler struct {
|
type DNSHandler struct {
|
||||||
client *rpc.Client
|
client *ipc.SmegmeshIpc
|
||||||
server *dns.Server
|
server *dns.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +24,7 @@ type DNSHandler struct {
|
|||||||
func (d *DNSHandler) queryMesh(meshId, alias string) net.IP {
|
func (d *DNSHandler) queryMesh(meshId, alias string) net.IP {
|
||||||
var reply string
|
var reply string
|
||||||
|
|
||||||
err := d.client.Call("IpcHandler.Query", &ipc.QueryMesh{
|
err := d.client.Query(ipc.QueryMesh{
|
||||||
MeshId: meshId,
|
MeshId: meshId,
|
||||||
Query: fmt.Sprintf("[?alias == '%s'] | [0]", alias),
|
Query: fmt.Sprintf("[?alias == '%s'] | [0]", alias),
|
||||||
}, &reply)
|
}, &reply)
|
||||||
@ -97,7 +94,7 @@ func (h *DNSHandler) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewDns(udpPort int) (*DNSHandler, error) {
|
func NewDns(udpPort int) (*DNSHandler, error) {
|
||||||
client, err := rpc.DialHTTP("unix", SockAddr)
|
client, err := ipc.NewClientIpc()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
227
pkg/dot/dot.go
Normal file
227
pkg/dot/dot.go
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
// Graph allows the definition of a DOT graph in golang
|
||||||
|
package graph
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"hash/fnv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GraphType string
|
||||||
|
type Shape string
|
||||||
|
|
||||||
|
const (
|
||||||
|
GRAPH GraphType = "graph"
|
||||||
|
DIGRAPH GraphType = "digraph"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CIRCLE Shape = "circle"
|
||||||
|
STAR Shape = "star"
|
||||||
|
HEXAGON Shape = "hexagon"
|
||||||
|
PARALLELOGRAM Shape = "parallelogram"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Graph interface {
|
||||||
|
Dottable
|
||||||
|
GetType() GraphType
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cluster struct {
|
||||||
|
Type GraphType
|
||||||
|
Name string
|
||||||
|
Label string
|
||||||
|
nodes map[string]*Node
|
||||||
|
edges map[string]Edge
|
||||||
|
}
|
||||||
|
|
||||||
|
type RootGraph struct {
|
||||||
|
Type GraphType
|
||||||
|
Label string
|
||||||
|
nodes map[string]*Node
|
||||||
|
clusters map[string]*Cluster
|
||||||
|
edges map[string]Edge
|
||||||
|
}
|
||||||
|
|
||||||
|
type Node struct {
|
||||||
|
Name string
|
||||||
|
Label string
|
||||||
|
Shape Shape
|
||||||
|
Size int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Edge interface {
|
||||||
|
Dottable
|
||||||
|
}
|
||||||
|
|
||||||
|
type DirectedEdge struct {
|
||||||
|
Name string
|
||||||
|
Label string
|
||||||
|
From string
|
||||||
|
To string
|
||||||
|
}
|
||||||
|
|
||||||
|
type UndirectedEdge struct {
|
||||||
|
Name string
|
||||||
|
Label string
|
||||||
|
From string
|
||||||
|
To string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dottable means an implementer can convert the struct to DOT representation
|
||||||
|
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
|
||||||
|
func (g *RootGraph) PutNode(name, label string, size int, shape Shape) error {
|
||||||
|
_, exists := g.nodes[name]
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
// If exists no need to add the ndoe
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
g.nodes[name] = &Node{Name: name, Label: label, Size: size, Shape: shape}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RootGraph) PutCluster(graph *Cluster) {
|
||||||
|
g.clusters[graph.Label] = graph
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeContituents[D Dottable](result *strings.Builder, elements ...D) error {
|
||||||
|
for _, node := range elements {
|
||||||
|
dot, err := node.GetDOT()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = result.WriteString(dot)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RootGraph) GetDOT() (string, error) {
|
||||||
|
var result strings.Builder
|
||||||
|
|
||||||
|
result.WriteString(fmt.Sprintf("%s {\n", g.Type))
|
||||||
|
result.WriteString("node [colorscheme=set312];\n")
|
||||||
|
result.WriteString("layout = fdp;\n")
|
||||||
|
nodes := lib.MapValues(g.nodes)
|
||||||
|
edges := lib.MapValues(g.edges)
|
||||||
|
writeContituents(&result, nodes...)
|
||||||
|
writeContituents(&result, edges...)
|
||||||
|
|
||||||
|
for _, cluster := range g.clusters {
|
||||||
|
clusterDOT, err := cluster.GetDOT()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
result.WriteString(clusterDOT)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.WriteString("}")
|
||||||
|
return result.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetType implements Graph.
|
||||||
|
func (r *RootGraph) GetType() GraphType {
|
||||||
|
return r.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func constructEdge(graph Graph, name, label, from, to string) Edge {
|
||||||
|
switch graph.GetType() {
|
||||||
|
case DIGRAPH:
|
||||||
|
return &DirectedEdge{Name: name, Label: label, From: from, To: to}
|
||||||
|
default:
|
||||||
|
return &UndirectedEdge{Name: name, Label: label, From: from, To: to}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEdge: adds an edge between two nodes in the 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
|
||||||
|
}
|
||||||
|
|
||||||
|
const numColours = 12
|
||||||
|
|
||||||
|
func (n *Node) hash() int {
|
||||||
|
h := fnv.New32a()
|
||||||
|
h.Write([]byte(n.Name))
|
||||||
|
return (int(h.Sum32()) % numColours) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DirectedEdge) GetDOT() (string, error) {
|
||||||
|
return fmt.Sprintf("\"%s\" -> \"%s\" [label=\"%s\"];\n", e.From, e.To, e.Label), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UndirectedEdge) GetDOT() (string, error) {
|
||||||
|
return fmt.Sprintf("\"%s\" -- \"%s\" [label=\"%s\"];\n", e.From, e.To, e.Label), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEdge: adds an edge between two nodes in the graph
|
||||||
|
func (g *Cluster) AddEdge(name string, label string, from string, to string) error {
|
||||||
|
g.edges[name] = constructEdge(g, name, label, from, to)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutNode: puts a node in the graph
|
||||||
|
func (g *Cluster) PutNode(name, label string, size int, shape Shape) error {
|
||||||
|
_, exists := g.nodes[name]
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
// If exists no need to add the ndoe
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
g.nodes[name] = &Node{Name: name, Label: label, Shape: shape, Size: size}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Cluster) GetDOT() (string, error) {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
builder.WriteString(fmt.Sprintf("subgraph \"cluster%s\" {\n", g.Label))
|
||||||
|
builder.WriteString(fmt.Sprintf("label = \"%s\"\n", g.Label))
|
||||||
|
nodes := lib.MapValues(g.nodes)
|
||||||
|
edges := lib.MapValues(g.edges)
|
||||||
|
writeContituents(&builder, nodes...)
|
||||||
|
writeContituents(&builder, edges...)
|
||||||
|
|
||||||
|
builder.WriteString("}\n")
|
||||||
|
return builder.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Cluster) GetType() GraphType {
|
||||||
|
return g.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSubGraph(name string, label string, graphType GraphType) *Cluster {
|
||||||
|
return &Cluster{
|
||||||
|
Label: name,
|
||||||
|
Type: graphType,
|
||||||
|
Name: name,
|
||||||
|
nodes: make(map[string]*Node),
|
||||||
|
edges: make(map[string]Edge),
|
||||||
|
}
|
||||||
|
}
|
116
pkg/dot/wg.go
Normal file
116
pkg/dot/wg.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package graph
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"github.com/tim-beatham/smegmesh/pkg/ctrlserver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MeshGraphConverter converts a mesh to a graph
|
||||||
|
type MeshGraphConverter interface {
|
||||||
|
// convert the mesh to textual form
|
||||||
|
Generate() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MeshDOTConverter struct {
|
||||||
|
meshes map[string][]ctrlserver.MeshNode
|
||||||
|
destinations map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MeshDOTConverter) Generate() (string, error) {
|
||||||
|
g := NewGraph("Smegmesh", GRAPH)
|
||||||
|
|
||||||
|
for meshId := range c.meshes {
|
||||||
|
err := c.generateMesh(g, meshId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for mesh := range c.meshes {
|
||||||
|
g.PutNode(mesh, mesh, 1, CIRCLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
for destination := range c.destinations {
|
||||||
|
g.PutNode(destination, destination, 1, HEXAGON)
|
||||||
|
}
|
||||||
|
|
||||||
|
return g.GetDOT()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MeshDOTConverter) generateMesh(g *RootGraph, meshId string) error {
|
||||||
|
nodes := c.meshes[meshId]
|
||||||
|
|
||||||
|
g.PutNode(meshId, meshId, 1, CIRCLE)
|
||||||
|
|
||||||
|
for _, node := range nodes {
|
||||||
|
c.graphNode(g, node, meshId)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, node := range nodes {
|
||||||
|
g.AddEdge(fmt.Sprintf("%s to %s", node.PublicKey, meshId), "", node.PublicKey, meshId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// graphNode: graphs a node within the mesh
|
||||||
|
func (c *MeshDOTConverter) graphNode(g *RootGraph, node ctrlserver.MeshNode, meshId string) {
|
||||||
|
alias := node.Alias
|
||||||
|
|
||||||
|
if alias == "" {
|
||||||
|
alias = node.WgHost[1:len(node.WgHost)-20] + "\\n" + node.WgHost[len(node.WgHost)-20:len(node.WgHost)]
|
||||||
|
}
|
||||||
|
|
||||||
|
g.PutNode(node.PublicKey, alias, 2, CIRCLE)
|
||||||
|
|
||||||
|
for _, route := range node.Routes {
|
||||||
|
if len(route.Path) == 0 {
|
||||||
|
g.AddEdge(route.Destination, "", node.PublicKey, route.Destination)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
reversedPath := slices.Clone(route.Path)
|
||||||
|
slices.Reverse(reversedPath)
|
||||||
|
|
||||||
|
g.AddEdge(fmt.Sprintf("%s to %s", node.PublicKey, reversedPath[0]), "", node.PublicKey, reversedPath[0])
|
||||||
|
|
||||||
|
for _, mesh := range route.Path {
|
||||||
|
if _, ok := c.meshes[mesh]; !ok {
|
||||||
|
c.destinations[mesh] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for index := range reversedPath[0 : len(reversedPath)-1] {
|
||||||
|
routeID := fmt.Sprintf("%s to %s", reversedPath[index], reversedPath[index+1])
|
||||||
|
g.AddEdge(routeID, "", reversedPath[index], reversedPath[index+1])
|
||||||
|
}
|
||||||
|
|
||||||
|
if route.Destination == "::/0" {
|
||||||
|
c.destinations[route.Destination] = struct{}{}
|
||||||
|
lastMesh := reversedPath[len(reversedPath)-1]
|
||||||
|
routeID := fmt.Sprintf("%s to %s", lastMesh, route.Destination)
|
||||||
|
g.AddEdge(routeID, "", lastMesh, route.Destination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for service := range node.Services {
|
||||||
|
c.putService(g, service, meshId, node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// putService: construct a service node and a link between the nodes
|
||||||
|
func (c *MeshDOTConverter) putService(g *RootGraph, key, meshId string, node ctrlserver.MeshNode) {
|
||||||
|
serviceID := fmt.Sprintf("%s%s%s", key, node.PublicKey, meshId)
|
||||||
|
g.PutNode(serviceID, key, 1, PARALLELOGRAM)
|
||||||
|
g.AddEdge(fmt.Sprintf("%s to %s", node.PublicKey, serviceID), "", node.PublicKey, serviceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMeshGraphConverter(meshes map[string][]ctrlserver.MeshNode) MeshGraphConverter {
|
||||||
|
return &MeshDOTConverter{
|
||||||
|
meshes: meshes,
|
||||||
|
destinations: make(map[string]interface{}),
|
||||||
|
}
|
||||||
|
}
|
@ -1,178 +0,0 @@
|
|||||||
// Graph allows the definition of a DOT graph in golang
|
|
||||||
package graph
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"hash/fnv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
|
||||||
)
|
|
||||||
|
|
||||||
type GraphType string
|
|
||||||
type Shape string
|
|
||||||
|
|
||||||
const (
|
|
||||||
GRAPH GraphType = "graph"
|
|
||||||
DIGRAPH = "digraph"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
CIRCLE Shape = "circle"
|
|
||||||
STAR Shape = "star"
|
|
||||||
HEXAGON Shape = "hexagon"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Graph struct {
|
|
||||||
Type GraphType
|
|
||||||
Label string
|
|
||||||
nodes map[string]*Node
|
|
||||||
edges []Edge
|
|
||||||
}
|
|
||||||
|
|
||||||
type Node struct {
|
|
||||||
Name string
|
|
||||||
Shape Shape
|
|
||||||
}
|
|
||||||
|
|
||||||
type Edge interface {
|
|
||||||
Dottable
|
|
||||||
}
|
|
||||||
|
|
||||||
type DirectedEdge struct {
|
|
||||||
Label string
|
|
||||||
From *Node
|
|
||||||
To *Node
|
|
||||||
}
|
|
||||||
|
|
||||||
type UndirectedEdge struct {
|
|
||||||
Label string
|
|
||||||
From *Node
|
|
||||||
To *Node
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dottable means an implementer can convert the struct to DOT representation
|
|
||||||
type Dottable interface {
|
|
||||||
GetDOT() (string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGraph(label string, graphType GraphType) *Graph {
|
|
||||||
return &Graph{Type: graphType, Label: label, nodes: make(map[string]*Node), edges: make([]Edge, 0)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PutNode: puts a node in the graph
|
|
||||||
func (g *Graph) PutNode(label string, shape Shape) error {
|
|
||||||
_, exists := g.nodes[label]
|
|
||||||
|
|
||||||
if exists {
|
|
||||||
// If exists no need to add the ndoe
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
g.nodes[label] = &Node{Name: label, Shape: shape}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeContituents[D Dottable](result *strings.Builder, elements ...D) error {
|
|
||||||
for _, node := range elements {
|
|
||||||
dot, err := node.GetDOT()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = result.WriteString(dot)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Graph) GetDOT() (string, error) {
|
|
||||||
var result strings.Builder
|
|
||||||
|
|
||||||
_, err := result.WriteString(fmt.Sprintf("%s {\n", g.Type))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = result.WriteString("node [colorscheme=set312];\n")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes := lib.MapValues(g.nodes)
|
|
||||||
|
|
||||||
err = writeContituents(&result, nodes...)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = writeContituents(&result, g.edges...)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = result.WriteString("}")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Graph) constructEdge(label string, from *Node, to *Node) Edge {
|
|
||||||
switch g.Type {
|
|
||||||
case DIGRAPH:
|
|
||||||
return &DirectedEdge{Label: label, From: from, To: to}
|
|
||||||
default:
|
|
||||||
return &UndirectedEdge{Label: label, From: from, To: to}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddEdge: adds an edge between two nodes in the graph
|
|
||||||
func (g *Graph) AddEdge(label string, from string, to string) error {
|
|
||||||
fromNode, exists := g.nodes[from]
|
|
||||||
|
|
||||||
if !exists {
|
|
||||||
return errors.New(fmt.Sprintf("Node %s does not exist", from))
|
|
||||||
}
|
|
||||||
|
|
||||||
toNode, exists := g.nodes[to]
|
|
||||||
|
|
||||||
if !exists {
|
|
||||||
return errors.New(fmt.Sprintf("Node %s does not exist", to))
|
|
||||||
}
|
|
||||||
|
|
||||||
g.edges = append(g.edges, g.constructEdge(label, fromNode, toNode))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const numColours = 12
|
|
||||||
|
|
||||||
func (n *Node) hash() int {
|
|
||||||
h := fnv.New32a()
|
|
||||||
h.Write([]byte(n.Name))
|
|
||||||
return (int(h.Sum32()) % numColours) + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *Node) GetDOT() (string, error) {
|
|
||||||
return fmt.Sprintf("node[shape=%s, style=\"filled\", fillcolor=%d] %s;\n",
|
|
||||||
n.Shape, n.hash(), n.Name), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *DirectedEdge) GetDOT() (string, error) {
|
|
||||||
return fmt.Sprintf("%s -> %s;\n", e.From.Name, e.To.Name), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *UndirectedEdge) GetDOT() (string, error) {
|
|
||||||
return fmt.Sprintf("%s -- %s;\n", e.From.Name, e.To.Name), nil
|
|
||||||
}
|
|
@ -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)}
|
|
||||||
}
|
|
@ -34,7 +34,7 @@ type CgaParameters struct {
|
|||||||
flag byte
|
flag byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCga(key wgtypes.Key, subnetPrefix [2 * InterfaceIdLen]byte) (*CgaParameters, error) {
|
func NewCga(key wgtypes.Key, collisionCount uint8, subnetPrefix [2 * InterfaceIdLen]byte) (*CgaParameters, error) {
|
||||||
var params CgaParameters
|
var params CgaParameters
|
||||||
|
|
||||||
_, err := rand.Read(params.Modifier[:])
|
_, err := rand.Read(params.Modifier[:])
|
||||||
@ -45,6 +45,7 @@ func NewCga(key wgtypes.Key, subnetPrefix [2 * InterfaceIdLen]byte) (*CgaParamet
|
|||||||
|
|
||||||
params.PublicKey = key
|
params.PublicKey = key
|
||||||
params.SubnetPrefix = subnetPrefix
|
params.SubnetPrefix = subnetPrefix
|
||||||
|
params.CollisionCount = collisionCount
|
||||||
return ¶ms, nil
|
return ¶ms, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +79,6 @@ func (c *CgaParameters) generateHash1() []byte {
|
|||||||
byteVal[hash1Length-1] = c.CollisionCount
|
byteVal[hash1Length-1] = c.CollisionCount
|
||||||
|
|
||||||
hash := sha1.Sum(byteVal[:])
|
hash := sha1.Sum(byteVal[:])
|
||||||
|
|
||||||
return hash[:Hash1Prefix]
|
return hash[:Hash1Prefix]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,9 +90,6 @@ func clearBit(num, pos int) byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *CgaParameters) generateInterface() []byte {
|
func (c *CgaParameters) generateInterface() []byte {
|
||||||
// TODO: On duplicate address detection increment collision.
|
|
||||||
// Also incorporate SEC
|
|
||||||
|
|
||||||
hash1 := c.generateHash1()
|
hash1 := c.generateHash1()
|
||||||
|
|
||||||
var interfaceId []byte = make([]byte, InterfaceIdLen)
|
var interfaceId []byte = make([]byte, InterfaceIdLen)
|
||||||
|
@ -7,5 +7,5 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type IPAllocator interface {
|
type IPAllocator interface {
|
||||||
GetIP(key wgtypes.Key, meshId string) (net.IP, error)
|
GetIP(key wgtypes.Key, meshId string, collisionCount uint8) (net.IP, error)
|
||||||
}
|
}
|
||||||
|
@ -39,10 +39,10 @@ func (u *ULABuilder) GetIPNet(meshId string) (*net.IPNet, error) {
|
|||||||
return net, nil
|
return net, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ULABuilder) GetIP(key wgtypes.Key, meshId string) (net.IP, error) {
|
func (u *ULABuilder) GetIP(key wgtypes.Key, meshId string, collisionCount uint8) (net.IP, error) {
|
||||||
ulaPrefix := getMeshPrefix(meshId)
|
ulaPrefix := getMeshPrefix(meshId)
|
||||||
|
|
||||||
c, err := NewCga(key, ulaPrefix)
|
c, err := NewCga(key, collisionCount, ulaPrefix)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
143
pkg/ipc/ipc.go
143
pkg/ipc/ipc.go
@ -5,38 +5,80 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/rpc"
|
"net/rpc"
|
||||||
|
ipcRpc "net/rpc"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ctrlserver"
|
"github.com/tim-beatham/smegmesh/pkg/ctrlserver"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NewMeshArgs struct {
|
const SockAddr = "/tmp/wgmesh_sock"
|
||||||
|
|
||||||
|
type MeshIpc interface {
|
||||||
|
CreateMesh(args *NewMeshArgs, reply *string) error
|
||||||
|
ListMeshes(name string, reply *ListMeshReply) error
|
||||||
|
JoinMesh(args *JoinMeshArgs, reply *string) error
|
||||||
|
LeaveMesh(meshId string, reply *string) error
|
||||||
|
GetMesh(meshId string, reply *GetMeshReply) error
|
||||||
|
Query(query QueryMesh, reply *string) error
|
||||||
|
PutDescription(args PutDescriptionArgs, reply *string) error
|
||||||
|
PutAlias(args PutAliasArgs, reply *string) error
|
||||||
|
PutService(args PutServiceArgs, reply *string) error
|
||||||
|
DeleteService(args DeleteServiceArgs, reply *string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// WireGuardArgs are provided args specific to WireGuard
|
||||||
|
type WireGuardArgs struct {
|
||||||
// WgPort is the WireGuard port to expose
|
// WgPort is the WireGuard port to expose
|
||||||
WgPort int
|
WgPort int
|
||||||
|
// KeepAliveWg is the number of seconds to keep alive
|
||||||
|
// for WireGuard NAT/firewall traversal
|
||||||
|
KeepAliveWg int
|
||||||
|
// AdvertiseRoutes whether or not to advertise routes to and from the
|
||||||
|
// mesh network
|
||||||
|
AdvertiseRoutes bool
|
||||||
|
// AdvertiseDefaultRoute whether or not to advertise the default route
|
||||||
|
// into the mesh network
|
||||||
|
AdvertiseDefaultRoute bool
|
||||||
// Endpoint is the routable alias of the machine. Can be an IP
|
// Endpoint is the routable alias of the machine. Can be an IP
|
||||||
// or DNS entry
|
// or DNS entry
|
||||||
Endpoint string
|
Endpoint string
|
||||||
Role string
|
// Role is the role of the individual in the mesh
|
||||||
|
Role string
|
||||||
|
}
|
||||||
|
|
||||||
|
type NewMeshArgs struct {
|
||||||
|
// WgArgs are specific WireGuard args to use
|
||||||
|
WgArgs WireGuardArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
type JoinMeshArgs struct {
|
type JoinMeshArgs struct {
|
||||||
// MeshId is the ID of the mesh to join
|
// MeshId is the ID of the mesh to join
|
||||||
MeshId string
|
MeshId string
|
||||||
// IpAddress is a routable IP in another mesh
|
// IpAddress is a routable IP in another mesh
|
||||||
IpAdress string
|
IpAddress string
|
||||||
// Port is the WireGuard port to expose
|
// WgArgs is the WireGuard parameters to use.
|
||||||
Port int
|
WgArgs WireGuardArgs
|
||||||
// Endpoint to use to override the default
|
|
||||||
Endpoint string
|
|
||||||
// Client specifies whether we should join as a client of the peer
|
|
||||||
// we are connecting to
|
|
||||||
Client bool
|
|
||||||
Role string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PutServiceArgs struct {
|
type PutServiceArgs struct {
|
||||||
Service string
|
Service string
|
||||||
Value string
|
Value string
|
||||||
|
MeshId string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteServiceArgs struct {
|
||||||
|
Service string
|
||||||
|
MeshId string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PutAliasArgs struct {
|
||||||
|
Alias string
|
||||||
|
MeshId string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PutDescriptionArgs struct {
|
||||||
|
Description string
|
||||||
|
MeshId string
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetMeshReply struct {
|
type GetMeshReply struct {
|
||||||
@ -52,27 +94,78 @@ type QueryMesh struct {
|
|||||||
Query string
|
Query string
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetNodeArgs struct {
|
type ClientIpc interface {
|
||||||
NodeId string
|
|
||||||
MeshId string
|
|
||||||
}
|
|
||||||
|
|
||||||
type MeshIpc interface {
|
|
||||||
CreateMesh(args *NewMeshArgs, reply *string) error
|
CreateMesh(args *NewMeshArgs, reply *string) error
|
||||||
ListMeshes(name string, reply *ListMeshReply) error
|
ListMeshes(args *ListMeshReply, reply *string) error
|
||||||
JoinMesh(args JoinMeshArgs, reply *string) error
|
JoinMesh(args JoinMeshArgs, reply *string) error
|
||||||
LeaveMesh(meshId string, reply *string) error
|
LeaveMesh(meshId string, reply *string) error
|
||||||
GetMesh(meshId string, reply *GetMeshReply) error
|
GetMesh(meshId string, reply *GetMeshReply) error
|
||||||
GetDOT(meshId string, reply *string) error
|
|
||||||
Query(query QueryMesh, reply *string) error
|
Query(query QueryMesh, reply *string) error
|
||||||
PutDescription(description string, reply *string) error
|
PutDescription(args PutDescriptionArgs, reply *string) error
|
||||||
PutAlias(alias string, reply *string) error
|
PutAlias(args PutAliasArgs, reply *string) error
|
||||||
PutService(args PutServiceArgs, reply *string) error
|
PutService(args PutServiceArgs, reply *string) error
|
||||||
GetNode(args GetNodeArgs, reply *string) error
|
DeleteService(args DeleteServiceArgs, reply *string) error
|
||||||
DeleteService(service string, reply *string) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const SockAddr = "/tmp/wgmesh_ipc.sock"
|
type SmegmeshIpc struct {
|
||||||
|
client *ipcRpc.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientIpc() (*SmegmeshIpc, error) {
|
||||||
|
client, err := ipcRpc.DialHTTP("unix", SockAddr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &SmegmeshIpc{
|
||||||
|
client: client,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SmegmeshIpc) CreateMesh(args *NewMeshArgs, reply *string) error {
|
||||||
|
return c.client.Call("IpcHandler.CreateMesh", args, reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SmegmeshIpc) ListMeshes(reply *ListMeshReply) error {
|
||||||
|
return c.client.Call("IpcHandler.ListMeshes", "", reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SmegmeshIpc) JoinMesh(args JoinMeshArgs, reply *string) error {
|
||||||
|
return c.client.Call("IpcHandler.JoinMesh", &args, reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SmegmeshIpc) LeaveMesh(meshId string, reply *string) error {
|
||||||
|
return c.client.Call("IpcHandler.LeaveMesh", &meshId, reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SmegmeshIpc) GetMesh(meshId string, reply *GetMeshReply) error {
|
||||||
|
return c.client.Call("IpcHandler.GetMesh", &meshId, reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SmegmeshIpc) Query(query QueryMesh, reply *string) error {
|
||||||
|
return c.client.Call("IpcHandler.Query", &query, reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SmegmeshIpc) PutDescription(args PutDescriptionArgs, reply *string) error {
|
||||||
|
return c.client.Call("IpcHandler.PutDescription", &args, reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SmegmeshIpc) PutAlias(args PutAliasArgs, reply *string) error {
|
||||||
|
return c.client.Call("IpcHandler.PutAlias", &args, reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SmegmeshIpc) PutService(args PutServiceArgs, reply *string) error {
|
||||||
|
return c.client.Call("IpcHandler.PutService", &args, reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SmegmeshIpc) DeleteService(args DeleteServiceArgs, reply *string) error {
|
||||||
|
return c.client.Call("IpcHandler.DeleteService", &args, reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SmegmeshIpc) Close() error {
|
||||||
|
return c.Close()
|
||||||
|
}
|
||||||
|
|
||||||
func RunIpcHandler(server MeshIpc) error {
|
func RunIpcHandler(server MeshIpc) error {
|
||||||
if err := os.RemoveAll(SockAddr); err != nil {
|
if err := os.RemoveAll(SockAddr); err != nil {
|
||||||
|
@ -3,6 +3,7 @@ package lib
|
|||||||
import (
|
import (
|
||||||
"github.com/anandvarma/namegen"
|
"github.com/anandvarma/namegen"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/lithammer/shortuuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IdGenerator generates unique ids
|
// IdGenerator generates unique ids
|
||||||
@ -19,6 +20,14 @@ func (g *UUIDGenerator) GetId() (string, error) {
|
|||||||
return id.String(), nil
|
return id.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ShortIDGenerator struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *ShortIDGenerator) GetId() (string, error) {
|
||||||
|
id := shortuuid.New()
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
type IDNameGenerator struct {
|
type IDNameGenerator struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/jsimonetti/rtnetlink"
|
"github.com/jsimonetti/rtnetlink"
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -225,8 +225,11 @@ type Route struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r1 Route) equal(r2 Route) bool {
|
func (r1 Route) equal(r2 Route) bool {
|
||||||
|
mask1Ones, _ := r1.Destination.Mask.Size()
|
||||||
|
mask2Ones, _ := r2.Destination.Mask.Size()
|
||||||
|
|
||||||
return r1.Gateway.String() == r2.Gateway.String() &&
|
return r1.Gateway.String() == r2.Gateway.String() &&
|
||||||
r1.Destination.String() == r2.Destination.String()
|
(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
|
||||||
@ -257,18 +260,11 @@ func (c *RtNetlinkConfig) DeleteRoutes(ifName string, family uint8, exclude ...R
|
|||||||
|
|
||||||
shouldExclude := func(r Route) bool {
|
shouldExclude := func(r Route) bool {
|
||||||
for _, route := range exclude {
|
for _, route := range exclude {
|
||||||
if route.equal(r) {
|
if r.equal(route) {
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if family == unix.AF_INET && route.Destination.IP.To4() == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if family == unix.AF_INET6 && route.Destination.IP.To16() == nil {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(),
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,10 +7,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ip"
|
"github.com/tim-beatham/smegmesh/pkg/ip"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/route"
|
"github.com/tim-beatham/smegmesh/pkg/route"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -91,7 +91,11 @@ func (m *WgMeshConfigApplyer) convertMeshNode(params convertMeshNodeParams) (*wg
|
|||||||
return p.PublicKey.String() == pubKey.String()
|
return p.PublicKey.String() == pubKey.String()
|
||||||
})
|
})
|
||||||
|
|
||||||
endpoint, err := net.ResolveUDPAddr("udp", params.node.GetWgEndpoint())
|
var endpoint *net.UDPAddr = nil
|
||||||
|
|
||||||
|
if params.node.GetType() == conf.PEER_ROLE {
|
||||||
|
endpoint, err = net.ResolveUDPAddr("udp", params.node.GetWgEndpoint())
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -115,7 +119,7 @@ func (m *WgMeshConfigApplyer) convertMeshNode(params convertMeshNodeParams) (*wg
|
|||||||
|
|
||||||
// getRoutes: finds the routes with the least hop distance. If more than one route exists
|
// getRoutes: finds the routes with the least hop distance. If more than one route exists
|
||||||
// consistently hash to evenly spread the distribution of traffic
|
// consistently hash to evenly spread the distribution of traffic
|
||||||
func (m *WgMeshConfigApplyer) getRoutes(meshProvider MeshProvider) map[string][]routeNode {
|
func (m *WgMeshConfigApplyer) getRoutes(meshProvider MeshProvider) (map[string][]routeNode, error) {
|
||||||
mesh, _ := meshProvider.GetMesh()
|
mesh, _ := meshProvider.GetMesh()
|
||||||
routes := make(map[string][]routeNode)
|
routes := make(map[string][]routeNode)
|
||||||
|
|
||||||
@ -134,10 +138,7 @@ func (m *WgMeshConfigApplyer) getRoutes(meshProvider MeshProvider) map[string][]
|
|||||||
|
|
||||||
for _, route := range node.GetRoutes() {
|
for _, route := range node.GetRoutes() {
|
||||||
if lib.Contains(meshPrefixes, func(prefix *net.IPNet) bool {
|
if lib.Contains(meshPrefixes, func(prefix *net.IPNet) bool {
|
||||||
v6Default, _, _ := net.ParseCIDR("::/0")
|
if prefix.IP.Equal(net.IPv6zero) && *meshProvider.GetConfiguration().AdvertiseDefaultRoute {
|
||||||
v4Default, _, _ := net.ParseCIDR("0.0.0.0/0")
|
|
||||||
|
|
||||||
if (prefix.IP.Equal(v6Default) || prefix.IP.Equal(v4Default)) && *meshProvider.GetConfiguration().AdvertiseDefaultRoute {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,17 +158,19 @@ func (m *WgMeshConfigApplyer) getRoutes(meshProvider MeshProvider) map[string][]
|
|||||||
// Client's only acessible by another peer
|
// Client's only acessible by another peer
|
||||||
if node.GetType() == conf.CLIENT_ROLE {
|
if node.GetType() == conf.CLIENT_ROLE {
|
||||||
peer := m.getCorrespondingPeer(peers, node)
|
peer := m.getCorrespondingPeer(peers, node)
|
||||||
self, _ := m.meshManager.GetSelf(meshProvider.GetMeshId())
|
self, err := meshProvider.GetNode(m.meshManager.GetPublicKey().String())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// If the node isn't the self use that peer as the gateway
|
|
||||||
if !NodeEquals(peer, self) {
|
if !NodeEquals(peer, self) {
|
||||||
peerPub, _ := peer.GetPublicKey()
|
peerPub, _ := peer.GetPublicKey()
|
||||||
rn.gateway = peerPub.String()
|
rn.gateway = peerPub.String()
|
||||||
rn.route = &RouteStub{
|
rn.route = &RouteStub{
|
||||||
Destination: rn.route.GetDestination(),
|
Destination: rn.route.GetDestination(),
|
||||||
HopCount: rn.route.GetHopCount() + 1,
|
HopCount: rn.route.GetHopCount() + 1,
|
||||||
// Append the path to this peer
|
Path: append(rn.route.GetPath(), peer.GetWgHost().IP.String()),
|
||||||
Path: append(rn.route.GetPath(), peer.GetWgHost().IP.String()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,7 +187,7 @@ func (m *WgMeshConfigApplyer) getRoutes(meshProvider MeshProvider) map[string][]
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return routes
|
return routes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getCorrespondignPeer: gets the peer corresponding to the client
|
// getCorrespondignPeer: gets the peer corresponding to the client
|
||||||
@ -218,7 +221,6 @@ type GetConfigParams struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *WgMeshConfigApplyer) getClientConfig(params *GetConfigParams) (*wgtypes.Config, error) {
|
func (m *WgMeshConfigApplyer) getClientConfig(params *GetConfigParams) (*wgtypes.Config, error) {
|
||||||
self, err := m.meshManager.GetSelf(params.mesh.GetMeshId())
|
|
||||||
ula := &ip.ULABuilder{}
|
ula := &ip.ULABuilder{}
|
||||||
meshNet, _ := ula.GetIPNet(params.mesh.GetMeshId())
|
meshNet, _ := ula.GetIPNet(params.mesh.GetMeshId())
|
||||||
|
|
||||||
@ -234,6 +236,8 @@ func (m *WgMeshConfigApplyer) getClientConfig(params *GetConfigParams) (*wgtypes
|
|||||||
})
|
})
|
||||||
routes = append(routes, *meshNet)
|
routes = append(routes, *meshNet)
|
||||||
|
|
||||||
|
self, err := params.mesh.GetNode(m.meshManager.GetPublicKey().String())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -301,7 +305,7 @@ func (m *WgMeshConfigApplyer) getPeerConfig(params *GetConfigParams) (*wgtypes.C
|
|||||||
peerToClients := make(map[string][]net.IPNet)
|
peerToClients := make(map[string][]net.IPNet)
|
||||||
installedRoutes := make([]lib.Route, 0)
|
installedRoutes := make([]lib.Route, 0)
|
||||||
peerConfigs := make([]wgtypes.PeerConfig, 0)
|
peerConfigs := make([]wgtypes.PeerConfig, 0)
|
||||||
self, err := m.meshManager.GetSelf(params.mesh.GetMeshId())
|
self, err := params.mesh.GetNode(m.meshManager.GetPublicKey().String())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -392,7 +396,7 @@ func (m *WgMeshConfigApplyer) updateWgConf(mesh MeshProvider, routes map[string]
|
|||||||
return mn.GetType() == conf.CLIENT_ROLE
|
return mn.GetType() == conf.CLIENT_ROLE
|
||||||
})
|
})
|
||||||
|
|
||||||
self, err := m.meshManager.GetSelf(mesh.GetMeshId())
|
self, err := mesh.GetNode(m.meshManager.GetPublicKey().String())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -431,11 +435,15 @@ func (m *WgMeshConfigApplyer) updateWgConf(mesh MeshProvider, routes map[string]
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *WgMeshConfigApplyer) getAllRoutes() map[string][]routeNode {
|
func (m *WgMeshConfigApplyer) getAllRoutes() (map[string][]routeNode, error) {
|
||||||
allRoutes := make(map[string][]routeNode)
|
allRoutes := make(map[string][]routeNode)
|
||||||
|
|
||||||
for _, mesh := range m.meshManager.GetMeshes() {
|
for _, mesh := range m.meshManager.GetMeshes() {
|
||||||
routes := m.getRoutes(mesh)
|
routes, err := m.getRoutes(mesh)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
for destination, route := range routes {
|
for destination, route := range routes {
|
||||||
_, ok := allRoutes[destination]
|
_, ok := allRoutes[destination]
|
||||||
@ -453,11 +461,15 @@ func (m *WgMeshConfigApplyer) getAllRoutes() map[string][]routeNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return allRoutes
|
return allRoutes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *WgMeshConfigApplyer) ApplyConfig() error {
|
func (m *WgMeshConfigApplyer) ApplyConfig() error {
|
||||||
allRoutes := m.getAllRoutes()
|
allRoutes, err := m.getAllRoutes()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
for _, mesh := range m.meshManager.GetMeshes() {
|
for _, mesh := range m.meshManager.GetMeshes() {
|
||||||
err := m.updateWgConf(mesh, allRoutes)
|
err := m.updateWgConf(mesh, allRoutes)
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
package mesh
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/graph"
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MeshGraphConverter converts a mesh to a graph
|
|
||||||
type MeshGraphConverter interface {
|
|
||||||
// convert the mesh to textual form
|
|
||||||
Generate(meshId string) (string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type MeshDOTConverter struct {
|
|
||||||
manager MeshManager
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *MeshDOTConverter) Generate(meshId string) (string, error) {
|
|
||||||
mesh := c.manager.GetMesh(meshId)
|
|
||||||
|
|
||||||
if mesh == nil {
|
|
||||||
return "", errors.New("mesh does not exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
g := graph.NewGraph(meshId, graph.GRAPH)
|
|
||||||
|
|
||||||
snapshot, err := mesh.GetMesh()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, node := range snapshot.GetNodes() {
|
|
||||||
c.graphNode(g, node, meshId)
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes := lib.MapValues(snapshot.GetNodes())
|
|
||||||
|
|
||||||
for i, node1 := range nodes[:len(nodes)-1] {
|
|
||||||
for _, node2 := range nodes[i+1:] {
|
|
||||||
if node1.GetWgEndpoint() == node2.GetWgEndpoint() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
node1Id := fmt.Sprintf("\"%s\"", node1.GetIdentifier())
|
|
||||||
node2Id := fmt.Sprintf("\"%s\"", node2.GetIdentifier())
|
|
||||||
g.AddEdge(fmt.Sprintf("%s to %s", node1Id, node2Id), node1Id, node2Id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return g.GetDOT()
|
|
||||||
}
|
|
||||||
|
|
||||||
// graphNode: graphs a node within the mesh
|
|
||||||
func (c *MeshDOTConverter) graphNode(g *graph.Graph, node MeshNode, meshId string) {
|
|
||||||
nodeId := fmt.Sprintf("\"%s\"", node.GetIdentifier())
|
|
||||||
g.PutNode(nodeId, graph.CIRCLE)
|
|
||||||
|
|
||||||
self, _ := c.manager.GetSelf(meshId)
|
|
||||||
|
|
||||||
if NodeEquals(self, node) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, route := range node.GetRoutes() {
|
|
||||||
routeId := fmt.Sprintf("\"%s\"", route)
|
|
||||||
g.PutNode(routeId, graph.HEXAGON)
|
|
||||||
g.AddEdge(fmt.Sprintf("%s to %s", nodeId, routeId), nodeId, routeId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMeshDotConverter(m MeshManager) MeshGraphConverter {
|
|
||||||
return &MeshDOTConverter{manager: m}
|
|
||||||
}
|
|
@ -3,13 +3,15 @@ package mesh
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/cmd"
|
"github.com/tim-beatham/smegmesh/pkg/cmd"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ip"
|
"github.com/tim-beatham/smegmesh/pkg/ip"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/wg"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
|
"github.com/tim-beatham/smegmesh/pkg/wg"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl"
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
@ -24,15 +26,14 @@ type MeshManager interface {
|
|||||||
LeaveMesh(meshId string) error
|
LeaveMesh(meshId string) error
|
||||||
GetSelf(meshId string) (MeshNode, error)
|
GetSelf(meshId string) (MeshNode, error)
|
||||||
ApplyConfig() error
|
ApplyConfig() error
|
||||||
SetDescription(description string) error
|
SetDescription(meshId, description string) error
|
||||||
SetAlias(alias string) error
|
SetAlias(meshId, alias string) error
|
||||||
SetService(service string, value string) error
|
SetService(meshId, service, value string) error
|
||||||
RemoveService(service string) error
|
RemoveService(meshId, service string) error
|
||||||
UpdateTimeStamp() error
|
UpdateTimeStamp() error
|
||||||
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 +53,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)
|
||||||
}
|
}
|
||||||
@ -63,29 +63,33 @@ func (m *MeshManagerImpl) GetRouteManager() RouteManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RemoveService implements MeshManager.
|
// RemoveService implements MeshManager.
|
||||||
func (m *MeshManagerImpl) RemoveService(service string) error {
|
func (m *MeshManagerImpl) RemoveService(meshId, service string) error {
|
||||||
for _, mesh := range m.Meshes {
|
mesh := m.GetMesh(meshId)
|
||||||
err := mesh.RemoveService(m.HostParameters.GetPublicKey(), service)
|
|
||||||
|
|
||||||
if err != nil {
|
if mesh == nil {
|
||||||
return err
|
return fmt.Errorf("mesh %s does not exist", meshId)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if !mesh.NodeExists(m.HostParameters.GetPublicKey()) {
|
||||||
|
return fmt.Errorf("node %s does not exist in the mesh", meshId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mesh.RemoveService(m.HostParameters.GetPublicKey(), service)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetService implements MeshManager.
|
// SetService implements MeshManager.
|
||||||
func (m *MeshManagerImpl) SetService(service string, value string) error {
|
func (m *MeshManagerImpl) SetService(meshId, service, value string) error {
|
||||||
for _, mesh := range m.Meshes {
|
mesh := m.GetMesh(meshId)
|
||||||
err := mesh.AddService(m.HostParameters.GetPublicKey(), service, value)
|
|
||||||
|
|
||||||
if err != nil {
|
if mesh == nil {
|
||||||
return err
|
return fmt.Errorf("mesh %s does not exist", meshId)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if !mesh.NodeExists(m.HostParameters.GetPublicKey()) {
|
||||||
|
return fmt.Errorf("node %s does not exist in the mesh", meshId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mesh.AddService(m.HostParameters.GetPublicKey(), service, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MeshManagerImpl) GetNode(meshid, nodeId string) MeshNode {
|
func (m *MeshManagerImpl) GetNode(meshid, nodeId string) MeshNode {
|
||||||
@ -104,11 +108,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
|
||||||
@ -141,6 +140,10 @@ func (m *MeshManagerImpl) CreateMesh(args *CreateMeshParams) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *meshConfiguration.Role == conf.CLIENT_ROLE {
|
||||||
|
return "", fmt.Errorf("cannot create mesh as a client")
|
||||||
|
}
|
||||||
|
|
||||||
meshId, err := m.idGenerator.GetId()
|
meshId, err := m.idGenerator.GetId()
|
||||||
|
|
||||||
var ifName string = ""
|
var ifName string = ""
|
||||||
@ -284,10 +287,36 @@ func (s *MeshManagerImpl) AddSelf(params *AddSelfParams) error {
|
|||||||
|
|
||||||
pubKey := s.HostParameters.PrivateKey.PublicKey()
|
pubKey := s.HostParameters.PrivateKey.PublicKey()
|
||||||
|
|
||||||
nodeIP, err := s.ipAllocator.GetIP(pubKey, params.MeshId)
|
collisionCount := uint8(0)
|
||||||
|
|
||||||
if err != nil {
|
var nodeIP net.IP
|
||||||
return err
|
|
||||||
|
// Perform Duplicate Address Detection with the nodes
|
||||||
|
// that are already in the network
|
||||||
|
for {
|
||||||
|
generatedIP, err := s.ipAllocator.GetIP(pubKey, params.MeshId, collisionCount)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot, err := mesh.GetMesh()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
proposition := func(node MeshNode) bool {
|
||||||
|
ipNet := node.GetWgHost()
|
||||||
|
return ipNet.IP.Equal(nodeIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
if lib.Contains(lib.MapValues(snapshot.GetNodes()), proposition) {
|
||||||
|
collisionCount++
|
||||||
|
} else {
|
||||||
|
nodeIP = generatedIP
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node := s.nodeFactory.Build(&MeshNodeFactoryParams{
|
node := s.nodeFactory.Build(&MeshNodeFactoryParams{
|
||||||
@ -327,7 +356,7 @@ func (s *MeshManagerImpl) LeaveMesh(meshId string) error {
|
|||||||
err := mesh.RemoveNode(s.HostParameters.GetPublicKey())
|
err := mesh.RemoveNode(s.HostParameters.GetPublicKey())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
logging.Log.WriteErrorf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.OnDelete != nil {
|
if s.OnDelete != nil {
|
||||||
@ -355,7 +384,6 @@ func (s *MeshManagerImpl) LeaveMesh(meshId string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.cmdRunner.RunCommands(s.conf.BaseConfiguration.PostDown...)
|
s.cmdRunner.RunCommands(s.conf.BaseConfiguration.PostDown...)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,43 +408,36 @@ func (s *MeshManagerImpl) ApplyConfig() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.configApplyer.ApplyConfig()
|
return s.configApplyer.ApplyConfig()
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MeshManagerImpl) SetDescription(description string) error {
|
func (s *MeshManagerImpl) SetDescription(meshId, description string) error {
|
||||||
meshes := s.GetMeshes()
|
mesh := s.GetMesh(meshId)
|
||||||
for _, mesh := range meshes {
|
|
||||||
if mesh.NodeExists(s.HostParameters.GetPublicKey()) {
|
|
||||||
err := mesh.SetDescription(s.HostParameters.GetPublicKey(), description)
|
|
||||||
|
|
||||||
if err != nil {
|
if mesh == nil {
|
||||||
return err
|
return fmt.Errorf("mesh %s does not exist", meshId)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if !mesh.NodeExists(s.HostParameters.GetPublicKey()) {
|
||||||
|
return fmt.Errorf("node %s does not exist in the mesh", meshId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mesh.SetDescription(s.HostParameters.GetPublicKey(), description)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAlias implements MeshManager.
|
// SetAlias implements MeshManager.
|
||||||
func (s *MeshManagerImpl) SetAlias(alias string) error {
|
func (s *MeshManagerImpl) SetAlias(meshId, alias string) error {
|
||||||
meshes := s.GetMeshes()
|
mesh := s.GetMesh(meshId)
|
||||||
for _, mesh := range meshes {
|
|
||||||
if mesh.NodeExists(s.HostParameters.GetPublicKey()) {
|
|
||||||
err := mesh.SetAlias(s.HostParameters.GetPublicKey(), alias)
|
|
||||||
|
|
||||||
if err != nil {
|
if mesh == nil {
|
||||||
return err
|
return fmt.Errorf("mesh %s does not exist", meshId)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
if !mesh.NodeExists(s.HostParameters.GetPublicKey()) {
|
||||||
|
return fmt.Errorf("node %s does not exist in the mesh", meshId)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
@ -510,7 +531,7 @@ func NewMeshManager(params *NewMeshManagerParams) MeshManager {
|
|||||||
m.RouteManager = params.RouteManager
|
m.RouteManager = params.RouteManager
|
||||||
|
|
||||||
if m.RouteManager == nil {
|
if m.RouteManager == nil {
|
||||||
m.RouteManager = NewRouteManager(m, ¶ms.Conf)
|
m.RouteManager = NewRouteManager(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.CommandRunner == nil {
|
if params.CommandRunner == nil {
|
||||||
@ -521,11 +542,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
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,39 @@ package mesh
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ip"
|
"github.com/tim-beatham/smegmesh/pkg/ip"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/wg"
|
"github.com/tim-beatham/smegmesh/pkg/wg"
|
||||||
)
|
)
|
||||||
|
|
||||||
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,
|
||||||
|
SyncTime: 2,
|
||||||
|
HeartBeat: 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,36 +213,80 @@ func TestLeaveMeshDeletesMesh(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetDescription(t *testing.T) {
|
func TestSetAliasUpdatesAliasOfNode(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(meshId, 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 TestSetDescriptionSetsTheDescriptionOfTheNode(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{},
|
||||||
|
})
|
||||||
|
|
||||||
manager.AddSelf(&AddSelfParams{
|
manager.AddSelf(&AddSelfParams{
|
||||||
MeshId: meshId1,
|
MeshId: meshId1,
|
||||||
WgPort: 5000,
|
WgPort: 5000,
|
||||||
Endpoint: "abc.com:8080",
|
Endpoint: "abc.com:8080",
|
||||||
})
|
})
|
||||||
manager.AddSelf(&AddSelfParams{
|
|
||||||
MeshId: meshId2,
|
|
||||||
WgPort: 5000,
|
|
||||||
Endpoint: "abc.com:8080",
|
|
||||||
})
|
|
||||||
|
|
||||||
err := manager.SetDescription(description)
|
err := manager.SetDescription(meshId1, description)
|
||||||
|
|
||||||
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
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,
|
||||||
@ -234,3 +305,68 @@ func TestUpdateTimeStampUpdatesAllMeshes(t *testing.T) {
|
|||||||
t.Fatalf(`failed to update the timestamp`)
|
t.Fatalf(`failed to update the timestamp`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAddServiceAddsServiceToTheMesh(t *testing.T) {
|
||||||
|
manager := getMeshManager()
|
||||||
|
|
||||||
|
meshId1, _ := manager.CreateMesh(&CreateMeshParams{
|
||||||
|
Port: 5000,
|
||||||
|
Conf: &conf.WgConfiguration{},
|
||||||
|
})
|
||||||
|
manager.AddSelf(&AddSelfParams{
|
||||||
|
MeshId: meshId1,
|
||||||
|
WgPort: 5000,
|
||||||
|
Endpoint: "abc.com:8080",
|
||||||
|
})
|
||||||
|
|
||||||
|
serviceName := "hello"
|
||||||
|
manager.SetService(meshId1, serviceName, "dave")
|
||||||
|
|
||||||
|
self, err := manager.GetSelf(meshId1)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`error thrown %s:`, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := self.GetServices()[serviceName]; !ok {
|
||||||
|
t.Fatalf(`service not added`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveServiceRemovesTheServiceFromTheMesh(t *testing.T) {
|
||||||
|
manager := getMeshManager()
|
||||||
|
|
||||||
|
meshId1, _ := manager.CreateMesh(&CreateMeshParams{
|
||||||
|
Port: 5000,
|
||||||
|
Conf: &conf.WgConfiguration{},
|
||||||
|
})
|
||||||
|
manager.AddSelf(&AddSelfParams{
|
||||||
|
MeshId: meshId1,
|
||||||
|
WgPort: 5000,
|
||||||
|
Endpoint: "abc.com:8080",
|
||||||
|
})
|
||||||
|
|
||||||
|
serviceName := "hello"
|
||||||
|
manager.SetService(meshId1, serviceName, "dave")
|
||||||
|
|
||||||
|
self, err := manager.GetSelf(meshId1)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`error thrown %s:`, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := self.GetServices()[serviceName]; !ok {
|
||||||
|
t.Fatalf(`service not added`)
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.RemoveService(meshId1, serviceName)
|
||||||
|
self, err = manager.GetSelf(meshId1)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`error thrown %s:`, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := self.GetServices()[serviceName]; ok {
|
||||||
|
t.Fatalf(`service still exists`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,9 +3,8 @@ package mesh
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/ip"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ip"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type RouteManager interface {
|
type RouteManager interface {
|
||||||
@ -14,7 +13,6 @@ type RouteManager interface {
|
|||||||
|
|
||||||
type RouteManagerImpl struct {
|
type RouteManagerImpl struct {
|
||||||
meshManager MeshManager
|
meshManager MeshManager
|
||||||
conf *conf.DaemonConfiguration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RouteManagerImpl) UpdateRoutes() error {
|
func (r *RouteManagerImpl) UpdateRoutes() error {
|
||||||
@ -22,7 +20,11 @@ func (r *RouteManagerImpl) UpdateRoutes() error {
|
|||||||
routes := make(map[string][]Route)
|
routes := make(map[string][]Route)
|
||||||
|
|
||||||
for _, mesh1 := range meshes {
|
for _, mesh1 := range meshes {
|
||||||
self, err := r.meshManager.GetSelf(mesh1.GetMeshId())
|
if !*mesh1.GetConfiguration().AdvertiseRoutes {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
self, err := mesh1.GetNode(r.meshManager.GetPublicKey().String())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -32,23 +34,25 @@ func (r *RouteManagerImpl) UpdateRoutes() error {
|
|||||||
routes[mesh1.GetMeshId()] = make([]Route, 0)
|
routes[mesh1.GetMeshId()] = make([]Route, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *mesh1.GetConfiguration().AdvertiseDefaultRoute {
|
||||||
|
_, ipv6Default, _ := net.ParseCIDR("::/0")
|
||||||
|
|
||||||
|
defaultRoute := &RouteStub{
|
||||||
|
Destination: ipv6Default,
|
||||||
|
HopCount: 0,
|
||||||
|
Path: []string{mesh1.GetMeshId()},
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh1.AddRoutes(NodeID(self), defaultRoute)
|
||||||
|
routes[mesh1.GetMeshId()] = append(routes[mesh1.GetMeshId()], defaultRoute)
|
||||||
|
}
|
||||||
|
|
||||||
routeMap, err := mesh1.GetRoutes(NodeID(self))
|
routeMap, err := mesh1.GetRoutes(NodeID(self))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if *r.conf.BaseConfiguration.AdvertiseDefaultRoute {
|
|
||||||
_, ipv6Default, _ := net.ParseCIDR("::/0")
|
|
||||||
|
|
||||||
mesh1.AddRoutes(NodeID(self),
|
|
||||||
&RouteStub{
|
|
||||||
Destination: ipv6Default,
|
|
||||||
HopCount: 0,
|
|
||||||
Path: make([]string, 0),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, mesh2 := range meshes {
|
for _, mesh2 := range meshes {
|
||||||
routeValues, ok := routes[mesh2.GetMeshId()]
|
routeValues, ok := routes[mesh2.GetMeshId()]
|
||||||
|
|
||||||
@ -75,8 +79,9 @@ func (r *RouteManagerImpl) UpdateRoutes() error {
|
|||||||
return s == mesh2.GetMeshId()
|
return s == mesh2.GetMeshId()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the route does not see it's own IP
|
// Remove any potential routing loops
|
||||||
return !r.GetDestination().IP.Equal(mesh2IpNet.IP) && !lib.Contains(r.GetPath()[1:], pathNotMesh)
|
return !r.GetDestination().IP.Equal(mesh2IpNet.IP) &&
|
||||||
|
!lib.Contains(r.GetPath()[1:], pathNotMesh)
|
||||||
})
|
})
|
||||||
|
|
||||||
routes[mesh2.GetMeshId()] = routeValues
|
routes[mesh2.GetMeshId()] = routeValues
|
||||||
@ -85,11 +90,20 @@ func (r *RouteManagerImpl) UpdateRoutes() error {
|
|||||||
|
|
||||||
// Calculate the set different of each, working out routes to remove and to keep.
|
// Calculate the set different of each, working out routes to remove and to keep.
|
||||||
for meshId, meshRoutes := range routes {
|
for meshId, meshRoutes := range routes {
|
||||||
mesh := r.meshManager.GetMesh(meshId)
|
mesh := meshes[meshId]
|
||||||
self, _ := r.meshManager.GetSelf(meshId)
|
|
||||||
toRemove := make([]Route, 0)
|
|
||||||
|
|
||||||
prevRoutes, _ := mesh.GetRoutes(NodeID(self))
|
self, err := mesh.GetNode(r.meshManager.GetPublicKey().String())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
toRemove := make([]Route, 0)
|
||||||
|
prevRoutes, err := mesh.GetRoutes(NodeID(self))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
for _, route := range prevRoutes {
|
for _, route := range prevRoutes {
|
||||||
if !lib.Contains(meshRoutes, func(r Route) bool {
|
if !lib.Contains(meshRoutes, func(r Route) bool {
|
||||||
@ -106,6 +120,6 @@ func (r *RouteManagerImpl) UpdateRoutes() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouteManager(m MeshManager, conf *conf.DaemonConfiguration) RouteManager {
|
func NewRouteManager(m MeshManager) RouteManager {
|
||||||
return &RouteManagerImpl{meshManager: m, conf: conf}
|
return &RouteManagerImpl{meshManager: m}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,8 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
|
"github.com/tim-beatham/smegmesh/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.
|
||||||
@ -27,13 +30,13 @@ func (*MeshNodeStub) GetType() conf.NodeType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetServices implements MeshNode.
|
// GetServices implements MeshNode.
|
||||||
func (*MeshNodeStub) GetServices() map[string]string {
|
func (m *MeshNodeStub) GetServices() map[string]string {
|
||||||
return make(map[string]string)
|
return m.services
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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,12 +243,13 @@ 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),
|
||||||
identifier: "abc",
|
identifier: "abc",
|
||||||
description: "A Mesh Node Stub",
|
description: "A Mesh Node Stub",
|
||||||
|
services: make(map[string]string),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,37 +272,32 @@ type MeshManagerStub struct {
|
|||||||
|
|
||||||
// GetRouteManager implements MeshManager.
|
// GetRouteManager implements MeshManager.
|
||||||
func (*MeshManagerStub) GetRouteManager() RouteManager {
|
func (*MeshManagerStub) GetRouteManager() RouteManager {
|
||||||
panic("unimplemented")
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNode implements MeshManager.
|
// GetNode implements MeshManager.
|
||||||
func (*MeshManagerStub) GetNode(string, string) MeshNode {
|
func (*MeshManagerStub) GetNode(meshId, nodeId string) MeshNode {
|
||||||
panic("unimplemented")
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveService implements MeshManager.
|
// RemoveService implements MeshManager.
|
||||||
func (*MeshManagerStub) RemoveService(service string) error {
|
func (*MeshManagerStub) RemoveService(meshId, service string) error {
|
||||||
panic("unimplemented")
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetService implements MeshManager.
|
// SetService implements MeshManager.
|
||||||
func (*MeshManagerStub) SetService(service string, value string) error {
|
func (*MeshManagerStub) SetService(meshId, service, value string) error {
|
||||||
panic("unimplemented")
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
// GetMonitor implements MeshManager.
|
|
||||||
func (*MeshManagerStub) GetMonitor() MeshMonitor {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAlias implements MeshManager.
|
// SetAlias implements MeshManager.
|
||||||
func (*MeshManagerStub) SetAlias(alias string) error {
|
func (*MeshManagerStub) SetAlias(meshId, alias string) error {
|
||||||
panic("unimplemented")
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close implements MeshManager.
|
// Close implements MeshManager.
|
||||||
func (*MeshManagerStub) Close() error {
|
func (*MeshManagerStub) Close() error {
|
||||||
panic("unimplemented")
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prune implements MeshManager.
|
// Prune implements MeshManager.
|
||||||
@ -319,7 +349,7 @@ func (m *MeshManagerStub) ApplyConfig() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MeshManagerStub) SetDescription(description string) error {
|
func (m *MeshManagerStub) SetDescription(meshId, description string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl"
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
@ -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() &&
|
||||||
@ -75,6 +81,10 @@ func NodeEquals(node1, node2 MeshNode) bool {
|
|||||||
key1, _ := node1.GetPublicKey()
|
key1, _ := node1.GetPublicKey()
|
||||||
key2, _ := node2.GetPublicKey()
|
key2, _ := node2.GetPublicKey()
|
||||||
|
|
||||||
|
if node1 == nil || node2 == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return key1.String() == key2.String()
|
return key1.String() == key2.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,9 +6,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jmespath/go-jmespath"
|
"github.com/jmespath/go-jmespath"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Querier queries a data store for the given data
|
// Querier queries a data store for the given data
|
||||||
|
@ -2,27 +2,24 @@ package robin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ctrlserver"
|
"github.com/tim-beatham/smegmesh/pkg/ctrlserver"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ipc"
|
"github.com/tim-beatham/smegmesh/pkg/ipc"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
"github.com/tim-beatham/smegmesh/pkg/rpc"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/query"
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/rpc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type IpcHandler struct {
|
type IpcHandler struct {
|
||||||
Server ctrlserver.CtrlServer
|
Server ctrlserver.CtrlServer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *IpcHandler) CreateMesh(args *ipc.NewMeshArgs, reply *string) error {
|
func getOverrideConfiguration(args *ipc.WireGuardArgs) conf.WgConfiguration {
|
||||||
overrideConf := &conf.WgConfiguration{}
|
overrideConf := conf.WgConfiguration{}
|
||||||
|
|
||||||
if args.Role != "" {
|
if args.Role != "" {
|
||||||
role := conf.NodeType(args.Role)
|
role := conf.NodeType(args.Role)
|
||||||
@ -33,13 +30,22 @@ func (n *IpcHandler) CreateMesh(args *ipc.NewMeshArgs, reply *string) error {
|
|||||||
overrideConf.Endpoint = &args.Endpoint
|
overrideConf.Endpoint = &args.Endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
if *overrideConf.Role == conf.CLIENT_ROLE {
|
if args.KeepAliveWg != 0 {
|
||||||
return fmt.Errorf("cannot create a mesh with no public endpoint")
|
keepAliveWg := args.KeepAliveWg
|
||||||
|
overrideConf.KeepAliveWg = &keepAliveWg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overrideConf.AdvertiseRoutes = &args.AdvertiseRoutes
|
||||||
|
overrideConf.AdvertiseDefaultRoute = &args.AdvertiseDefaultRoute
|
||||||
|
return overrideConf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *IpcHandler) CreateMesh(args *ipc.NewMeshArgs, reply *string) error {
|
||||||
|
overrideConf := getOverrideConfiguration(&args.WgArgs)
|
||||||
|
|
||||||
meshId, err := n.Server.GetMeshManager().CreateMesh(&mesh.CreateMeshParams{
|
meshId, err := n.Server.GetMeshManager().CreateMesh(&mesh.CreateMeshParams{
|
||||||
Port: args.WgPort,
|
Port: args.WgArgs.WgPort,
|
||||||
Conf: overrideConf,
|
Conf: &overrideConf,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -48,8 +54,8 @@ func (n *IpcHandler) CreateMesh(args *ipc.NewMeshArgs, reply *string) error {
|
|||||||
|
|
||||||
err = n.Server.GetMeshManager().AddSelf(&mesh.AddSelfParams{
|
err = n.Server.GetMeshManager().AddSelf(&mesh.AddSelfParams{
|
||||||
MeshId: meshId,
|
MeshId: meshId,
|
||||||
WgPort: args.WgPort,
|
WgPort: args.WgArgs.WgPort,
|
||||||
Endpoint: args.Endpoint,
|
Endpoint: args.WgArgs.Endpoint,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -73,19 +79,14 @@ func (n *IpcHandler) ListMeshes(_ string, reply *ipc.ListMeshReply) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *IpcHandler) JoinMesh(args ipc.JoinMeshArgs, reply *string) error {
|
func (n *IpcHandler) JoinMesh(args *ipc.JoinMeshArgs, reply *string) error {
|
||||||
overrideConf := &conf.WgConfiguration{}
|
overrideConf := getOverrideConfiguration(&args.WgArgs)
|
||||||
|
|
||||||
if args.Role != "" {
|
if n.Server.GetMeshManager().GetMesh(args.MeshId) != nil {
|
||||||
role := conf.NodeType(args.Role)
|
return fmt.Errorf("user is already apart of the mesh")
|
||||||
overrideConf.Role = &role
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.Endpoint != "" {
|
peerConnection, err := n.Server.GetConnectionManager().GetConnection(args.IpAddress)
|
||||||
overrideConf.Endpoint = &args.Endpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
peerConnection, err := n.Server.GetConnectionManager().GetConnection(args.IpAdress)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -116,9 +117,9 @@ func (n *IpcHandler) JoinMesh(args ipc.JoinMeshArgs, reply *string) error {
|
|||||||
|
|
||||||
err = n.Server.GetMeshManager().AddMesh(&mesh.AddMeshParams{
|
err = n.Server.GetMeshManager().AddMesh(&mesh.AddMeshParams{
|
||||||
MeshId: args.MeshId,
|
MeshId: args.MeshId,
|
||||||
WgPort: args.Port,
|
WgPort: args.WgArgs.WgPort,
|
||||||
MeshBytes: meshReply.Mesh,
|
MeshBytes: meshReply.Mesh,
|
||||||
Conf: overrideConf,
|
Conf: &overrideConf,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -127,8 +128,8 @@ func (n *IpcHandler) JoinMesh(args ipc.JoinMeshArgs, reply *string) error {
|
|||||||
|
|
||||||
err = n.Server.GetMeshManager().AddSelf(&mesh.AddSelfParams{
|
err = n.Server.GetMeshManager().AddSelf(&mesh.AddSelfParams{
|
||||||
MeshId: args.MeshId,
|
MeshId: args.MeshId,
|
||||||
WgPort: args.Port,
|
WgPort: args.WgArgs.WgPort,
|
||||||
Endpoint: args.Endpoint,
|
Endpoint: args.WgArgs.Endpoint,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -146,7 +147,6 @@ func (n *IpcHandler) LeaveMesh(meshId string, reply *string) error {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
*reply = fmt.Sprintf("Left Mesh %s", meshId)
|
*reply = fmt.Sprintf("Left Mesh %s", meshId)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,30 +171,9 @@ func (n *IpcHandler) GetMesh(meshId string, reply *ipc.GetMeshReply) error {
|
|||||||
|
|
||||||
i := 0
|
i := 0
|
||||||
for _, node := range meshSnapshot.GetNodes() {
|
for _, node := range meshSnapshot.GetNodes() {
|
||||||
pubKey, _ := node.GetPublicKey()
|
node := ctrlserver.NewCtrlNode(theMesh, node)
|
||||||
|
|
||||||
if err != nil {
|
nodes[i] = *node
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
node := ctrlserver.MeshNode{
|
|
||||||
HostEndpoint: node.GetHostEndpoint(),
|
|
||||||
WgEndpoint: node.GetWgEndpoint(),
|
|
||||||
PublicKey: pubKey.String(),
|
|
||||||
WgHost: node.GetWgHost().String(),
|
|
||||||
Timestamp: node.GetTimeStamp(),
|
|
||||||
Routes: lib.Map(node.GetRoutes(), func(r mesh.Route) ctrlserver.MeshRoute {
|
|
||||||
return ctrlserver.MeshRoute{
|
|
||||||
Destination: r.GetDestination().String(),
|
|
||||||
Path: r.GetPath(),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Description: node.GetDescription(),
|
|
||||||
Alias: node.GetAlias(),
|
|
||||||
Services: node.GetServices(),
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes[i] = node
|
|
||||||
i += 1
|
i += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,19 +181,6 @@ func (n *IpcHandler) GetMesh(meshId string, reply *ipc.GetMeshReply) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *IpcHandler) GetDOT(meshId string, reply *string) error {
|
|
||||||
g := mesh.NewMeshDotConverter(n.Server.GetMeshManager())
|
|
||||||
|
|
||||||
result, err := g.Generate(meshId)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*reply = result
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *IpcHandler) Query(params ipc.QueryMesh, reply *string) error {
|
func (n *IpcHandler) Query(params ipc.QueryMesh, reply *string) error {
|
||||||
queryResponse, err := n.Server.GetQuerier().Query(params.MeshId, params.Query)
|
queryResponse, err := n.Server.GetQuerier().Query(params.MeshId, params.Query)
|
||||||
|
|
||||||
@ -226,30 +192,34 @@ func (n *IpcHandler) Query(params ipc.QueryMesh, reply *string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *IpcHandler) PutDescription(description string, reply *string) error {
|
func (n *IpcHandler) PutDescription(args ipc.PutDescriptionArgs, reply *string) error {
|
||||||
err := n.Server.GetMeshManager().SetDescription(description)
|
err := n.Server.GetMeshManager().SetDescription(args.MeshId, args.Description)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
*reply = fmt.Sprintf("Set description to %s", description)
|
*reply = fmt.Sprintf("set description to %s for %s", args.Description, args.MeshId)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *IpcHandler) PutAlias(alias string, reply *string) error {
|
func (n *IpcHandler) PutAlias(args ipc.PutAliasArgs, reply *string) error {
|
||||||
err := n.Server.GetMeshManager().SetAlias(alias)
|
if args.Alias == "" {
|
||||||
|
return fmt.Errorf("alias not provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := n.Server.GetMeshManager().SetAlias(args.MeshId, args.Alias)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
*reply = fmt.Sprintf("Set alias to %s", alias)
|
*reply = fmt.Sprintf("Set alias to %s", args.Alias)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *IpcHandler) PutService(service ipc.PutServiceArgs, reply *string) error {
|
func (n *IpcHandler) PutService(service ipc.PutServiceArgs, reply *string) error {
|
||||||
err := n.Server.GetMeshManager().SetService(service.Service, service.Value)
|
err := n.Server.GetMeshManager().SetService(service.MeshId, service.Service, service.Value)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -259,8 +229,8 @@ func (n *IpcHandler) PutService(service ipc.PutServiceArgs, reply *string) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *IpcHandler) DeleteService(service string, reply *string) error {
|
func (n *IpcHandler) DeleteService(service ipc.DeleteServiceArgs, reply *string) error {
|
||||||
err := n.Server.GetMeshManager().RemoveService(service)
|
err := n.Server.GetMeshManager().RemoveService(service.MeshId, service.Service)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -270,27 +240,6 @@ func (n *IpcHandler) DeleteService(service string, reply *string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *IpcHandler) GetNode(args ipc.GetNodeArgs, reply *string) error {
|
|
||||||
node := n.Server.GetMeshManager().GetNode(args.MeshId, args.NodeId)
|
|
||||||
|
|
||||||
if node == nil {
|
|
||||||
*reply = "nil"
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
queryNode := query.MeshNodeToQueryNode(node)
|
|
||||||
|
|
||||||
bytes, err := json.Marshal(queryNode)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
*reply = err.Error()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
*reply = string(bytes)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type RobinIpcParams struct {
|
type RobinIpcParams struct {
|
||||||
CtrlServer ctrlserver.CtrlServer
|
CtrlServer ctrlserver.CtrlServer
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,10 @@ package robin
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ctrlserver"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ipc"
|
"github.com/tim-beatham/smegmesh/pkg/ctrlserver"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
"github.com/tim-beatham/smegmesh/pkg/ipc"
|
||||||
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getRequester() *IpcHandler {
|
func getRequester() *IpcHandler {
|
||||||
@ -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)
|
||||||
|
@ -4,8 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ctrlserver"
|
"github.com/tim-beatham/smegmesh/pkg/ctrlserver"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/rpc"
|
"github.com/tim-beatham/smegmesh/pkg/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WgRpc struct {
|
type WgRpc struct {
|
||||||
|
@ -1 +0,0 @@
|
|||||||
package robin
|
|
@ -1,7 +1,7 @@
|
|||||||
package route
|
package route
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
package sync
|
package sync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
"github.com/tim-beatham/smegmesh/pkg/conf"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/conn"
|
"github.com/tim-beatham/smegmesh/pkg/conn"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Syncer: picks random nodes from the meshs
|
// Syncer: picks random nodes from the meshs
|
||||||
type Syncer interface {
|
type Syncer interface {
|
||||||
Sync(meshId string) error
|
Sync(theMesh mesh.MeshProvider) error
|
||||||
SyncMeshes() error
|
SyncMeshes() error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,48 +27,71 @@ type SyncerImpl struct {
|
|||||||
syncCount int
|
syncCount int
|
||||||
cluster conn.ConnCluster
|
cluster conn.ConnCluster
|
||||||
conf *conf.DaemonConfiguration
|
conf *conf.DaemonConfiguration
|
||||||
lastSync uint64
|
lastSync map[string]int64
|
||||||
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync: Sync random nodes
|
// Sync: Sync with random nodes
|
||||||
func (s *SyncerImpl) Sync(meshId string) error {
|
func (s *SyncerImpl) Sync(correspondingMesh mesh.MeshProvider) error {
|
||||||
// Self can be nil if the node is removed
|
if correspondingMesh == nil {
|
||||||
self, _ := s.manager.GetSelf(meshId)
|
return fmt.Errorf("mesh provided was nil cannot sync nil mesh")
|
||||||
|
}
|
||||||
|
|
||||||
correspondingMesh := s.manager.GetMesh(meshId)
|
// 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())
|
||||||
|
}
|
||||||
|
|
||||||
correspondingMesh.Prune()
|
correspondingMesh.Prune()
|
||||||
|
|
||||||
if self != nil && self.GetType() == conf.PEER_ROLE && !s.manager.HasChanges(meshId) && s.infectionCount == 0 {
|
if correspondingMesh.HasChanges() {
|
||||||
logging.Log.WriteInfof("No changes for %s", meshId)
|
logging.Log.WriteInfof("meshes %s has changes", correspondingMesh.GetMeshId())
|
||||||
|
}
|
||||||
|
|
||||||
|
// If removed sync with other nodes to gossip the node is removed
|
||||||
|
if self != nil && self.GetType() == conf.PEER_ROLE && !correspondingMesh.HasChanges() && s.infectionCount == 0 {
|
||||||
|
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) {
|
||||||
|
return s.Pull(self, correspondingMesh)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
before := time.Now()
|
before := time.Now()
|
||||||
s.manager.GetRouteManager().UpdateRoutes()
|
err = s.manager.GetRouteManager().UpdateRoutes()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logging.Log.WriteErrorf(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
publicKey := s.manager.GetPublicKey()
|
publicKey := s.manager.GetPublicKey()
|
||||||
|
|
||||||
logging.Log.WriteInfof(publicKey.String())
|
|
||||||
|
|
||||||
nodeNames := correspondingMesh.GetPeers()
|
nodeNames := correspondingMesh.GetPeers()
|
||||||
|
|
||||||
if self != nil {
|
nodeNames = lib.Filter(nodeNames, func(s string) bool {
|
||||||
nodeNames = lib.Filter(nodeNames, func(s string) bool {
|
// Filter our only public key out so we dont sync with ourself
|
||||||
return s != mesh.NodeID(self)
|
return s != publicKey.String()
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
var gossipNodes []string
|
var gossipNodes []string
|
||||||
|
|
||||||
// Clients always pings its peer for configuration
|
// Clients always pings its peer for configuration
|
||||||
if self != nil && self.GetType() == conf.CLIENT_ROLE {
|
if self != nil && self.GetType() == conf.CLIENT_ROLE && len(nodeNames) > 1 {
|
||||||
keyFunc := lib.HashString
|
neighbours := s.cluster.GetNeighbours(nodeNames, publicKey.String())
|
||||||
bucketFunc := lib.HashString
|
|
||||||
|
|
||||||
neighbour := lib.ConsistentHash(nodeNames, publicKey.String(), keyFunc, bucketFunc)
|
if len(neighbours) == 0 {
|
||||||
gossipNodes = make([]string, 1)
|
return nil
|
||||||
gossipNodes[0] = neighbour
|
}
|
||||||
|
|
||||||
|
// Peer with 2 nodes so that there is redundnacy in
|
||||||
|
// the situation the node leaves pre-emptively
|
||||||
|
redundancyLength := min(len(neighbours), 2)
|
||||||
|
gossipNodes = neighbours[:redundancyLength]
|
||||||
} else {
|
} else {
|
||||||
neighbours := s.cluster.GetNeighbours(nodeNames, publicKey.String())
|
neighbours := s.cluster.GetNeighbours(nodeNames, publicKey.String())
|
||||||
gossipNodes = lib.RandomSubsetOfLength(neighbours, s.conf.BranchRate)
|
gossipNodes = lib.RandomSubsetOfLength(neighbours, s.conf.BranchRate)
|
||||||
@ -80,58 +105,103 @@ func (s *SyncerImpl) Sync(meshId string) error {
|
|||||||
|
|
||||||
// Do this synchronously to conserve bandwidth
|
// Do this synchronously to conserve bandwidth
|
||||||
for _, node := range gossipNodes {
|
for _, node := range gossipNodes {
|
||||||
correspondingPeer := s.manager.GetNode(meshId, node)
|
correspondingPeer, err := correspondingMesh.GetNode(node)
|
||||||
|
|
||||||
if correspondingPeer == nil {
|
if correspondingPeer == nil || err != nil {
|
||||||
logging.Log.WriteErrorf("node %s does not exist", node)
|
logging.Log.WriteErrorf("node %s does not exist", node)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.requester.SyncMesh(meshId, correspondingPeer)
|
err = s.requester.SyncMesh(correspondingMesh.GetMeshId(), correspondingPeer)
|
||||||
|
|
||||||
if err == nil || err == io.EOF {
|
if err == nil || err == io.EOF {
|
||||||
succeeded = true
|
succeeded = true
|
||||||
} else {
|
|
||||||
// If the synchronisation operation has failed them mark a gravestone
|
|
||||||
// preventing the peer from being re-contacted until it has updated
|
|
||||||
// itself
|
|
||||||
s.manager.GetMesh(meshId).Mark(node)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
s.syncCount++
|
|
||||||
logging.Log.WriteInfof("SYNC TIME: %v", time.Since(before))
|
|
||||||
logging.Log.WriteInfof("SYNC COUNT: %d", s.syncCount)
|
|
||||||
|
|
||||||
s.infectionCount = ((s.conf.InfectionCount + s.infectionCount - 1) % s.conf.InfectionCount)
|
|
||||||
|
|
||||||
if !succeeded {
|
|
||||||
// If could not gossip with anyone then repeat.
|
|
||||||
s.infectionCount++
|
|
||||||
}
|
|
||||||
|
|
||||||
s.manager.GetMesh(meshId).SaveChanges()
|
|
||||||
s.lastSync = uint64(time.Now().Unix())
|
|
||||||
|
|
||||||
logging.Log.WriteInfof("UPDATING WG CONF")
|
|
||||||
err := s.manager.ApplyConfig()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
logging.Log.WriteInfof("Failed to update config %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SyncMeshes: Sync all meshes
|
|
||||||
func (s *SyncerImpl) SyncMeshes() error {
|
|
||||||
for meshId := range s.manager.GetMeshes() {
|
|
||||||
err := s.Sync(meshId)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Log.WriteErrorf(err.Error())
|
logging.Log.WriteErrorf(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
if !succeeded {
|
||||||
|
s.infectionCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
correspondingMesh.SaveChanges()
|
||||||
|
|
||||||
|
s.lock.Lock()
|
||||||
|
s.lastSync[correspondingMesh.GetMeshId()] = time.Now().Unix()
|
||||||
|
s.lock.Unlock()
|
||||||
|
return 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 {
|
||||||
|
peers := mesh.GetPeers()
|
||||||
|
pubKey, _ := self.GetPublicKey()
|
||||||
|
|
||||||
|
neighbours := s.cluster.GetNeighbours(peers, pubKey.String())
|
||||||
|
neighbour := lib.RandomSubsetOfLength(neighbours, 1)
|
||||||
|
|
||||||
|
if len(neighbour) == 0 {
|
||||||
|
logging.Log.WriteInfof("no neighbours")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logging.Log.WriteInfof("pulling from node %s", neighbour[0])
|
||||||
|
|
||||||
|
pullNode, err := mesh.GetNode(neighbour[0])
|
||||||
|
|
||||||
|
if err != nil || pullNode == nil {
|
||||||
|
return fmt.Errorf("node %s does not exist in the mesh", neighbour[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.requester.SyncMesh(mesh.GetMeshId(), pullNode)
|
||||||
|
|
||||||
|
if err == nil || err == io.EOF {
|
||||||
|
s.lastSync[mesh.GetMeshId()] = time.Now().Unix()
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.syncCount++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncMeshes: Sync all meshes
|
||||||
|
func (s *SyncerImpl) SyncMeshes() error {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
for _, mesh := range s.manager.GetMeshes() {
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
sync := func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
err := s.Sync(mesh)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logging.Log.WriteErrorf(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go sync()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
logging.Log.WriteInfof("updating the WireGuard configuration")
|
||||||
|
err := s.manager.ApplyConfig()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logging.Log.WriteInfof("failed to update config %w", err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,5 +213,6 @@ func NewSyncer(m mesh.MeshManager, conf *conf.DaemonConfiguration, r SyncRequest
|
|||||||
requester: r,
|
requester: r,
|
||||||
infectionCount: 0,
|
infectionCount: 0,
|
||||||
syncCount: 0,
|
syncCount: 0,
|
||||||
cluster: cluster}
|
cluster: cluster,
|
||||||
|
lastSync: make(map[string]int64)}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package sync
|
package sync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
"github.com/tim-beatham/smegmesh/pkg/conn"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
@ -15,11 +16,34 @@ type SyncErrorHandler interface {
|
|||||||
// SyncErrorHandlerImpl Is an implementation of the SyncErrorHandler
|
// SyncErrorHandlerImpl Is an implementation of the SyncErrorHandler
|
||||||
type SyncErrorHandlerImpl struct {
|
type SyncErrorHandlerImpl struct {
|
||||||
meshManager mesh.MeshManager
|
meshManager mesh.MeshManager
|
||||||
|
connManager conn.ConnectionManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SyncErrorHandlerImpl) handleFailed(meshId string, nodeId string) bool {
|
func (s *SyncErrorHandlerImpl) handleFailed(meshId string, nodeId string) bool {
|
||||||
mesh := s.meshManager.GetMesh(meshId)
|
mesh := s.meshManager.GetMesh(meshId)
|
||||||
mesh.Mark(nodeId)
|
mesh.Mark(nodeId)
|
||||||
|
node, err := mesh.GetNode(nodeId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.connManager.RemoveConnection(node.GetHostEndpoint())
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SyncErrorHandlerImpl) handleDeadlineExceeded(meshId string, nodeId string) bool {
|
||||||
|
mesh := s.meshManager.GetMesh(meshId)
|
||||||
|
|
||||||
|
if mesh == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
node, err := mesh.GetNode(nodeId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.connManager.RemoveConnection(node.GetHostEndpoint())
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,13 +53,15 @@ func (s *SyncErrorHandlerImpl) Handle(meshId string, nodeId string, err error) b
|
|||||||
logging.Log.WriteInfof("Handled gRPC error: %s", errStatus.Message())
|
logging.Log.WriteInfof("Handled gRPC error: %s", errStatus.Message())
|
||||||
|
|
||||||
switch errStatus.Code() {
|
switch errStatus.Code() {
|
||||||
case codes.Unavailable, codes.Unknown, codes.DeadlineExceeded, codes.Internal, codes.NotFound:
|
case codes.Unavailable, codes.Unknown, codes.Internal, codes.NotFound:
|
||||||
return s.handleFailed(meshId, nodeId)
|
return s.handleFailed(meshId, nodeId)
|
||||||
|
case codes.DeadlineExceeded:
|
||||||
|
return s.handleDeadlineExceeded(meshId, nodeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSyncErrorHandler(m mesh.MeshManager) SyncErrorHandler {
|
func NewSyncErrorHandler(m mesh.MeshManager, conn conn.ConnectionManager) SyncErrorHandler {
|
||||||
return &SyncErrorHandlerImpl{meshManager: m}
|
return &SyncErrorHandlerImpl{meshManager: m, connManager: conn}
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ctrlserver"
|
"github.com/tim-beatham/smegmesh/pkg/ctrlserver"
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/rpc"
|
"github.com/tim-beatham/smegmesh/pkg/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SyncRequester: coordinates the syncing of meshes
|
// SyncRequester: coordinates the syncing of meshes
|
||||||
@ -91,7 +91,7 @@ func (s *SyncRequesterImpl) SyncMesh(meshId string, meshNode mesh.MeshNode) erro
|
|||||||
|
|
||||||
c := rpc.NewSyncServiceClient(client)
|
c := rpc.NewSyncServiceClient(client)
|
||||||
|
|
||||||
syncTimeOut := float64(s.server.Conf.SyncRate) * float64(time.Second)
|
syncTimeOut := float64(s.server.Conf.SyncTime) * float64(time.Second)
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(syncTimeOut))
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(syncTimeOut))
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@ -99,11 +99,11 @@ func (s *SyncRequesterImpl) SyncMesh(meshId string, meshNode mesh.MeshNode) erro
|
|||||||
err = s.syncMesh(mesh, ctx, c)
|
err = s.syncMesh(mesh, ctx, c)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return s.handleErr(meshId, pubKey.String(), err)
|
s.handleErr(meshId, 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, meshId)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SyncRequesterImpl) syncMesh(mesh mesh.MeshProvider, ctx context.Context, client rpc.SyncServiceClient) error {
|
func (s *SyncRequesterImpl) syncMesh(mesh mesh.MeshProvider, ctx context.Context, client rpc.SyncServiceClient) error {
|
||||||
@ -151,6 +151,6 @@ func (s *SyncRequesterImpl) syncMesh(mesh mesh.MeshProvider, ctx context.Context
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewSyncRequester(s *ctrlserver.MeshCtrlServer) SyncRequester {
|
func NewSyncRequester(s *ctrlserver.MeshCtrlServer) SyncRequester {
|
||||||
errorHdlr := NewSyncErrorHandler(s.MeshManager)
|
errorHdlr := NewSyncErrorHandler(s.MeshManager, s.ConnectionManager)
|
||||||
return &SyncRequesterImpl{server: s, errorHdlr: errorHdlr}
|
return &SyncRequesterImpl{server: s, errorHdlr: errorHdlr}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package sync
|
package sync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ctrlserver"
|
"github.com/tim-beatham/smegmesh/pkg/ctrlserver"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Run implements SyncScheduler.
|
// Run implements SyncScheduler.
|
||||||
@ -14,5 +14,5 @@ func syncFunction(syncer Syncer) lib.TimerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewSyncScheduler(s *ctrlserver.MeshCtrlServer, syncRequester SyncRequester, syncer Syncer) *lib.Timer {
|
func NewSyncScheduler(s *ctrlserver.MeshCtrlServer, syncRequester SyncRequester, syncer Syncer) *lib.Timer {
|
||||||
return lib.NewTimer(syncFunction(syncer), s.Conf.SyncRate)
|
return lib.NewTimer(syncFunction(syncer), s.Conf.SyncTime)
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,9 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ctrlserver"
|
"github.com/tim-beatham/smegmesh/pkg/ctrlserver"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
"github.com/tim-beatham/smegmesh/pkg/mesh"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/rpc"
|
"github.com/tim-beatham/smegmesh/pkg/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SyncServiceImpl struct {
|
type SyncServiceImpl struct {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package timer
|
package timer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tim-beatham/wgmesh/pkg/ctrlserver"
|
"github.com/tim-beatham/smegmesh/pkg/ctrlserver"
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewTimestampScheduler(ctrlServer *ctrlserver.MeshCtrlServer) lib.Timer {
|
func NewTimestampScheduler(ctrlServer *ctrlserver.MeshCtrlServer) lib.Timer {
|
||||||
@ -11,5 +11,5 @@ func NewTimestampScheduler(ctrlServer *ctrlserver.MeshCtrlServer) lib.Timer {
|
|||||||
logging.Log.WriteInfof("Updated Timestamp")
|
logging.Log.WriteInfof("Updated Timestamp")
|
||||||
return ctrlServer.MeshManager.UpdateTimeStamp()
|
return ctrlServer.MeshManager.UpdateTimeStamp()
|
||||||
}
|
}
|
||||||
return *lib.NewTimer(timerFunc, ctrlServer.Conf.KeepAliveTime)
|
return *lib.NewTimer(timerFunc, ctrlServer.Conf.HeartBeat)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -5,8 +5,8 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
||||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
logging "github.com/tim-beatham/smegmesh/pkg/log"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl"
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user