mirror of
https://github.com/tim-beatham/smegmesh.git
synced 2025-07-02 21:40:10 +02:00
Compare commits
9 Commits
9-add-ci-s
...
15-add-res
Author | SHA1 | Date | |
---|---|---|---|
5f176e731f | |||
44f119b45c | |||
5215d5d54d | |||
1a864b7c80 | |||
4c19ebd81f | |||
acbeb689b5 | |||
bc6cd4fdd5 | |||
c88012cf71 | |||
4dc85f3861 |
11
Containerfile
Normal file
11
Containerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM docker.io/library/golang:bookworm
|
||||
COPY ./ /wgmesh
|
||||
RUN apt-get update && apt-get install -y \
|
||||
wireguard \
|
||||
wireguard-tools \
|
||||
iproute2 \
|
||||
iputils-ping \
|
||||
tmux \
|
||||
vim
|
||||
WORKDIR /wgmesh
|
||||
RUN go build -o /usr/local/bin ./...
|
1
Dockerfile
Symbolic link
1
Dockerfile
Symbolic link
@ -0,0 +1 @@
|
||||
Containerfile
|
17
cmd/api/main.go
Normal file
17
cmd/api/main.go
Normal file
@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/tim-beatham/wgmesh/pkg/api"
|
||||
)
|
||||
|
||||
func main() {
|
||||
apiServer, err := api.NewSmegServer()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
apiServer.Run(":8080")
|
||||
}
|
@ -2,6 +2,7 @@ certificatePath: "/wgmesh/cert/cert.pem"
|
||||
privateKeyPath: "/wgmesh/cert/priv.pem"
|
||||
caCertificatePath: "/wgmesh/cert/cacert.pem"
|
||||
skipCertVerification: true
|
||||
timeout: 5
|
||||
gRPCPort: "21906"
|
||||
advertiseRoutes: true
|
||||
clusterSize: 32
|
||||
@ -9,4 +10,5 @@ syncRate: 1
|
||||
interClusterChance: 0.15
|
||||
branchRate: 3
|
||||
infectionCount: 3
|
||||
keepAliveRate: 60
|
||||
keepAliveTime: 10
|
||||
pruneTime: 20
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
ctrlserver "github.com/tim-beatham/wgmesh/pkg/ctrlserver"
|
||||
"github.com/tim-beatham/wgmesh/pkg/ipc"
|
||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
||||
"github.com/tim-beatham/wgmesh/pkg/robin"
|
||||
"github.com/tim-beatham/wgmesh/pkg/sync"
|
||||
"github.com/tim-beatham/wgmesh/pkg/timestamp"
|
||||
@ -44,12 +45,13 @@ func main() {
|
||||
SyncProvider: &syncProvider,
|
||||
Client: client,
|
||||
}
|
||||
|
||||
ctrlServer, err := ctrlserver.NewCtrlServer(&ctrlServerParams)
|
||||
|
||||
syncProvider.Server = ctrlServer
|
||||
syncRequester := sync.NewSyncRequester(ctrlServer)
|
||||
syncScheduler := sync.NewSyncScheduler(ctrlServer, syncRequester)
|
||||
timestampScheduler := timestamp.NewTimestampScheduler(ctrlServer)
|
||||
pruneScheduler := mesh.NewPruner(ctrlServer.MeshManager, *conf)
|
||||
|
||||
robinIpcParams := robin.RobinIpcParams{
|
||||
CtrlServer: ctrlServer,
|
||||
@ -68,6 +70,7 @@ func main() {
|
||||
go ipc.RunIpcHandler(&robinIpc)
|
||||
go syncScheduler.Run()
|
||||
go timestampScheduler.Run()
|
||||
go pruneScheduler.Run()
|
||||
|
||||
closeResources := func() {
|
||||
logging.Log.WriteInfof("Closing resources")
|
||||
|
95
examples/meshtomesh/docker-compose.yaml
Normal file
95
examples/meshtomesh/docker-compose.yaml
Normal file
@ -0,0 +1,95 @@
|
||||
version: '3'
|
||||
networks:
|
||||
net-1:
|
||||
driver: bridge
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: 10.89.0.0/17
|
||||
net-2:
|
||||
driver: bridge
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: 10.89.155.0/17
|
||||
services:
|
||||
wg-1:
|
||||
image: wg-mesh-base:latest
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
tty: true
|
||||
networks:
|
||||
- net-1
|
||||
volumes:
|
||||
- ./shared:/shared
|
||||
command: "wgmeshd /shared/configuration.yaml"
|
||||
wg-2:
|
||||
image: wg-mesh-base:latest
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
tty: true
|
||||
networks:
|
||||
- net-1
|
||||
volumes:
|
||||
- ./shared:/shared
|
||||
command: "wgmeshd /shared/configuration.yaml"
|
||||
wg-3:
|
||||
image: wg-mesh-base:latest
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
tty: true
|
||||
networks:
|
||||
- net-1
|
||||
volumes:
|
||||
- ./shared:/shared
|
||||
command: "wgmeshd /shared/configuration.yaml"
|
||||
wg-4:
|
||||
image: wg-mesh-base:latest
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
tty: true
|
||||
sysctls:
|
||||
- net.ipv6.conf.all.forwarding=1
|
||||
networks:
|
||||
- net-1
|
||||
- net-2
|
||||
volumes:
|
||||
- ./shared:/shared
|
||||
command: "wgmeshd /shared/configuration.yaml"
|
||||
wg-5:
|
||||
image: wg-mesh-base:latest
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
tty: true
|
||||
networks:
|
||||
- net-2
|
||||
volumes:
|
||||
- ./shared:/shared
|
||||
command: "wgmeshd /shared/configuration.yaml"
|
||||
wg-6:
|
||||
image: wg-mesh-base:latest
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
tty: true
|
||||
networks:
|
||||
- net-2
|
||||
volumes:
|
||||
- ./shared:/shared
|
||||
command: "wgmeshd /shared/configuration.yaml"
|
||||
wg-7:
|
||||
image: wg-mesh-base:latest
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
tty: true
|
||||
networks:
|
||||
- net-2
|
||||
volumes:
|
||||
- ./shared:/shared
|
||||
command: "wgmeshd /shared/configuration.yaml"
|
14
examples/meshtomesh/shared/configuration.yaml
Normal file
14
examples/meshtomesh/shared/configuration.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
certificatePath: "/wgmesh/cert/cert.pem"
|
||||
privateKeyPath: "/wgmesh/cert/priv.pem"
|
||||
caCertificatePath: "/wgmesh/cert/cacert.pem"
|
||||
skipCertVerification: true
|
||||
timeout: 5
|
||||
gRPCPort: "21906"
|
||||
advertiseRoutes: true
|
||||
clusterSize: 32
|
||||
syncRate: 1
|
||||
interClusterChance: 0.15
|
||||
branchRate: 3
|
||||
infectionCount: 3
|
||||
keepAliveTime: 10
|
||||
pruneTime: 20
|
42
examples/simple/docker-compose.yaml
Normal file
42
examples/simple/docker-compose.yaml
Normal file
@ -0,0 +1,42 @@
|
||||
version: '3'
|
||||
networks:
|
||||
net-1:
|
||||
driver: bridge
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: 10.89.0.0/17
|
||||
services:
|
||||
wg-1:
|
||||
image: wg-mesh-base:latest
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
tty: true
|
||||
networks:
|
||||
- net-1
|
||||
volumes:
|
||||
- ./shared:/shared
|
||||
command: "wgmeshd /shared/configuration.yaml"
|
||||
wg-2:
|
||||
image: wg-mesh-base:latest
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
tty: true
|
||||
networks:
|
||||
- net-1
|
||||
volumes:
|
||||
- ./shared:/shared
|
||||
command: "wgmeshd /shared/configuration.yaml"
|
||||
wg-3:
|
||||
image: wg-mesh-base:latest
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
tty: true
|
||||
networks:
|
||||
- net-1
|
||||
volumes:
|
||||
- ./shared:/shared
|
||||
command: "wgmeshd /shared/configuration.yaml"
|
14
examples/simple/shared/configuration.yaml
Normal file
14
examples/simple/shared/configuration.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
certificatePath: "/wgmesh/cert/cert.pem"
|
||||
privateKeyPath: "/wgmesh/cert/priv.pem"
|
||||
caCertificatePath: "/wgmesh/cert/cacert.pem"
|
||||
skipCertVerification: true
|
||||
timeout: 5
|
||||
gRPCPort: "21906"
|
||||
advertiseRoutes: true
|
||||
clusterSize: 32
|
||||
syncRate: 1
|
||||
interClusterChance: 0.15
|
||||
branchRate: 3
|
||||
infectionCount: 3
|
||||
keepAliveTime: 10
|
||||
pruneTime: 20
|
21
go.mod
21
go.mod
@ -15,12 +15,33 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.9.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/validator/v10 v10.14.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/jsimonetti/rtnetlink v1.3.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mdlayher/genetlink v1.3.2 // indirect
|
||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||
github.com/mdlayher/socket v0.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.13.0 // indirect
|
||||
golang.org/x/net v0.15.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
|
203
pkg/api/apiserver.go
Normal file
203
pkg/api/apiserver.go
Normal file
@ -0,0 +1,203 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
ipcRpc "net/rpc"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/tim-beatham/wgmesh/pkg/ctrlserver"
|
||||
"github.com/tim-beatham/wgmesh/pkg/ipc"
|
||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
||||
)
|
||||
|
||||
const SockAddr = "/tmp/wgmesh_ipc.sock"
|
||||
|
||||
type ApiServer interface {
|
||||
GetMeshes(c *gin.Context)
|
||||
Run(addr string) error
|
||||
}
|
||||
|
||||
type SmegServer struct {
|
||||
router *gin.Engine
|
||||
client *ipcRpc.Client
|
||||
}
|
||||
|
||||
func meshNodeToAPIMeshNode(meshNode ctrlserver.MeshNode) *SmegNode {
|
||||
if meshNode.Routes == nil {
|
||||
meshNode.Routes = make([]string, 0)
|
||||
}
|
||||
|
||||
return &SmegNode{
|
||||
WgHost: meshNode.WgHost,
|
||||
WgEndpoint: meshNode.WgEndpoint,
|
||||
Endpoint: meshNode.HostEndpoint,
|
||||
Timestamp: int(meshNode.Timestamp),
|
||||
Description: meshNode.Description,
|
||||
Routes: meshNode.Routes,
|
||||
PublicKey: meshNode.PublicKey,
|
||||
}
|
||||
}
|
||||
|
||||
func meshToAPIMesh(meshId string, nodes []ctrlserver.MeshNode) SmegMesh {
|
||||
var smegMesh SmegMesh
|
||||
smegMesh.MeshId = meshId
|
||||
smegMesh.Nodes = make(map[string]SmegNode)
|
||||
|
||||
for _, node := range nodes {
|
||||
smegMesh.Nodes[node.WgHost] = *meshNodeToAPIMeshNode(node)
|
||||
}
|
||||
|
||||
return smegMesh
|
||||
}
|
||||
|
||||
// CreateMesh: creates a mesh network
|
||||
func (s *SmegServer) CreateMesh(c *gin.Context) {
|
||||
var createMesh CreateMeshRequest
|
||||
|
||||
if err := c.ShouldBindJSON(&createMesh); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ipcRequest := ipc.NewMeshArgs{
|
||||
IfName: createMesh.IfName,
|
||||
WgPort: createMesh.WgPort,
|
||||
}
|
||||
|
||||
var reply string
|
||||
|
||||
err := s.client.Call("IpcHandler.CreateMesh", &ipcRequest, &reply)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &gin.H{
|
||||
"meshid": reply,
|
||||
})
|
||||
}
|
||||
|
||||
// JoinMesh: joins a mesh network
|
||||
func (s *SmegServer) JoinMesh(c *gin.Context) {
|
||||
var joinMesh JoinMeshRequest
|
||||
|
||||
if err := c.ShouldBindJSON(&joinMesh); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ipcRequest := ipc.JoinMeshArgs{
|
||||
MeshId: joinMesh.MeshId,
|
||||
IpAdress: joinMesh.Bootstrap,
|
||||
IfName: joinMesh.IfName,
|
||||
Port: joinMesh.WgPort,
|
||||
}
|
||||
|
||||
var reply string
|
||||
|
||||
err := s.client.Call("IpcHandler.JoinMesh", &ipcRequest, &reply)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &gin.H{
|
||||
"status": "success",
|
||||
})
|
||||
}
|
||||
|
||||
// GetMesh: given a meshId returns the corresponding mesh
|
||||
// network.
|
||||
func (s *SmegServer) GetMesh(c *gin.Context) {
|
||||
meshidParam := c.Param("meshid")
|
||||
|
||||
var meshid string = meshidParam
|
||||
|
||||
getMeshReply := new(ipc.GetMeshReply)
|
||||
|
||||
err := s.client.Call("IpcHandler.GetMesh", &meshid, &getMeshReply)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound,
|
||||
&gin.H{
|
||||
"error": fmt.Sprintf("could not find mesh %s", meshidParam),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
mesh := meshToAPIMesh(meshidParam, getMeshReply.Nodes)
|
||||
|
||||
c.JSON(http.StatusOK, mesh)
|
||||
}
|
||||
|
||||
func (s *SmegServer) GetMeshes(c *gin.Context) {
|
||||
listMeshesReply := new(ipc.ListMeshReply)
|
||||
|
||||
err := s.client.Call("IpcHandler.ListMeshes", "", &listMeshesReply)
|
||||
|
||||
if err != nil {
|
||||
logging.Log.WriteErrorf(err.Error())
|
||||
c.JSON(http.StatusBadRequest, nil)
|
||||
return
|
||||
}
|
||||
|
||||
meshes := make([]SmegMesh, 0)
|
||||
|
||||
for _, mesh := range listMeshesReply.Meshes {
|
||||
getMeshReply := new(ipc.GetMeshReply)
|
||||
|
||||
err := s.client.Call("IpcHandler.GetMesh", &mesh, &getMeshReply)
|
||||
|
||||
if err != nil {
|
||||
logging.Log.WriteErrorf(err.Error())
|
||||
c.JSON(http.StatusBadRequest, nil)
|
||||
return
|
||||
}
|
||||
|
||||
meshes = append(meshes, meshToAPIMesh(mesh, getMeshReply.Nodes))
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, meshes)
|
||||
}
|
||||
|
||||
func (s *SmegServer) Run(addr string) error {
|
||||
logging.Log.WriteInfof("Running API server")
|
||||
return s.router.Run(addr)
|
||||
}
|
||||
|
||||
func NewSmegServer() (ApiServer, error) {
|
||||
client, err := ipcRpc.DialHTTP("unix", SockAddr)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
router := gin.Default()
|
||||
|
||||
router.Use(gin.LoggerWithConfig(gin.LoggerConfig{
|
||||
Output: logging.Log.Writer(),
|
||||
}))
|
||||
|
||||
smegServer := &SmegServer{
|
||||
router: router,
|
||||
client: client,
|
||||
}
|
||||
|
||||
router.GET("/meshes", smegServer.GetMeshes)
|
||||
router.GET("/mesh/:meshid", smegServer.GetMesh)
|
||||
router.POST("/mesh/create", smegServer.CreateMesh)
|
||||
router.POST("/mesh/join", smegServer.JoinMesh)
|
||||
return smegServer, nil
|
||||
}
|
28
pkg/api/types.go
Normal file
28
pkg/api/types.go
Normal file
@ -0,0 +1,28 @@
|
||||
package api
|
||||
|
||||
type SmegNode struct {
|
||||
WgHost string `json:"wgHost"`
|
||||
WgEndpoint string `json:"wgEndpoint"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
Timestamp int `json:"timestamp"`
|
||||
Description string `json:"description"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
Routes []string `json:"routes"`
|
||||
}
|
||||
|
||||
type SmegMesh struct {
|
||||
MeshId string `json:"meshid"`
|
||||
Nodes map[string]SmegNode `json:"nodes"`
|
||||
}
|
||||
|
||||
type CreateMeshRequest struct {
|
||||
IfName string `json:"ifName" binding:"required"`
|
||||
WgPort int `json:"port" binding:"required,gte=1024,lt=65535"`
|
||||
}
|
||||
|
||||
type JoinMeshRequest struct {
|
||||
IfName string `json:"ifName" binding:"required"`
|
||||
WgPort int `json:"port" binding:"required,gte=1024,lt=65535"`
|
||||
Bootstrap string `json:"bootstrap" binding:"required"`
|
||||
MeshId string `json:"meshid" binding:"required"`
|
||||
}
|
@ -34,10 +34,10 @@ func (c *CrdtMeshManager) AddNode(node mesh.MeshNode) {
|
||||
panic("node must be of type *MeshNodeCrdt")
|
||||
}
|
||||
|
||||
crdt.Routes = make(map[string]interface{})
|
||||
|
||||
crdt.Timestamp = time.Now().Unix()
|
||||
c.doc.Path("nodes").Map().Set(crdt.HostEndpoint, crdt)
|
||||
nodeVal, _ := c.doc.Path("nodes").Map().Get(crdt.HostEndpoint)
|
||||
nodeVal.Map().Set("routes", automerge.NewMap())
|
||||
}
|
||||
|
||||
// GetMesh(): Converts the document into a struct
|
||||
@ -204,14 +204,90 @@ func (m *CrdtMeshManager) AddRoutes(nodeId string, routes ...string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteRoutes deletes the specified routes
|
||||
func (m *CrdtMeshManager) RemoveRoutes(nodeId string, routes ...string) error {
|
||||
nodeVal, err := m.doc.Path("nodes").Map().Get(nodeId)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if nodeVal.Kind() != automerge.KindMap {
|
||||
return fmt.Errorf("node is not a map")
|
||||
}
|
||||
|
||||
routeMap, err := nodeVal.Map().Get("routes")
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, route := range routes {
|
||||
err = routeMap.Map().Delete(route)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *CrdtMeshManager) GetSyncer() mesh.MeshSyncer {
|
||||
return NewAutomergeSync(m)
|
||||
}
|
||||
|
||||
func (m *CrdtMeshManager) Prune(pruneTime int) error {
|
||||
nodes, err := m.doc.Path("nodes").Get()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if nodes.Kind() != automerge.KindMap {
|
||||
return errors.New("node must be a map")
|
||||
}
|
||||
|
||||
values, err := nodes.Map().Values()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deletionNodes := make([]string, 0)
|
||||
|
||||
for nodeId, node := range values {
|
||||
if node.Kind() != automerge.KindMap {
|
||||
return errors.New("node must be a map")
|
||||
}
|
||||
|
||||
nodeMap := node.Map()
|
||||
|
||||
timeStamp, err := nodeMap.Get("timestamp")
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if timeStamp.Kind() != automerge.KindInt64 {
|
||||
return errors.New("timestamp is not int64")
|
||||
}
|
||||
|
||||
timeValue := timeStamp.Int64()
|
||||
nowValue := time.Now().Unix()
|
||||
|
||||
if nowValue-timeValue >= int64(pruneTime) {
|
||||
deletionNodes = append(deletionNodes, nodeId)
|
||||
}
|
||||
}
|
||||
|
||||
for _, node := range deletionNodes {
|
||||
logging.Log.WriteInfof("Pruning %s", node)
|
||||
nodes.Map().Delete(node)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m1 *MeshNodeCrdt) Compare(m2 *MeshNodeCrdt) int {
|
||||
return strings.Compare(m1.PublicKey, m2.PublicKey)
|
||||
}
|
||||
|
@ -32,13 +32,23 @@ type WgMeshConfiguration struct {
|
||||
AdvertiseRoutes bool `yaml:"advertiseRoutes"`
|
||||
// Endpoint is the IP in which this computer is publicly reachable.
|
||||
// usecase is when the node has multiple IP addresses
|
||||
Endpoint string `yaml:"publicEndpoint"`
|
||||
ClusterSize int `yaml:"clusterSize"`
|
||||
SyncRate float64 `yaml:"syncRate"`
|
||||
Endpoint string `yaml:"publicEndpoint"`
|
||||
// ClusterSize size of the cluster to split on
|
||||
ClusterSize int `yaml:"clusterSize"`
|
||||
// SyncRate number of times per second to perform a sync
|
||||
SyncRate float64 `yaml:"syncRate"`
|
||||
// InterClusterChance proability of inter-cluster communication in a sync round
|
||||
InterClusterChance float64 `yaml:"interClusterChance"`
|
||||
BranchRate int `yaml:"branchRate"`
|
||||
InfectionCount int `yaml:"infectionCount"`
|
||||
KeepAliveRate int `yaml:"keepAliveRate"`
|
||||
// BranchRate number of nodes to randomly communicate with
|
||||
BranchRate int `yaml:"branchRate"`
|
||||
// InfectionCount number of times we sync before we can no longer catch the udpate
|
||||
InfectionCount int `yaml:"infectionCount"`
|
||||
// KeepAliveTime number of seconds before we update node indicating that we are still alive
|
||||
KeepAliveTime int `yaml:"keepAliveTime"`
|
||||
// Timeout number of seconds before we consider the node as dead
|
||||
Timeout int `yaml:"timeout"`
|
||||
// PruneTime number of seconds before we consider the 'node' as dead
|
||||
PruneTime int `yaml:"pruneTime"`
|
||||
}
|
||||
|
||||
func ValidateConfiguration(c *WgMeshConfiguration) error {
|
||||
@ -90,7 +100,7 @@ func ValidateConfiguration(c *WgMeshConfiguration) error {
|
||||
}
|
||||
}
|
||||
|
||||
if c.KeepAliveRate <= 0 {
|
||||
if c.KeepAliveTime <= 0 {
|
||||
return &WgMeshConfigurationError{
|
||||
msg: "KeepAliveRate cannot be less than negative",
|
||||
}
|
||||
@ -102,6 +112,24 @@ func ValidateConfiguration(c *WgMeshConfiguration) error {
|
||||
}
|
||||
}
|
||||
|
||||
if c.Timeout < 1 {
|
||||
return &WgMeshConfigurationError{
|
||||
msg: "Timeout should be greater than or equal to 1",
|
||||
}
|
||||
}
|
||||
|
||||
if c.PruneTime <= 1 {
|
||||
return &WgMeshConfigurationError{
|
||||
msg: "Prune time cannot be <= 1",
|
||||
}
|
||||
}
|
||||
|
||||
if c.KeepAliveTime <= 1 {
|
||||
return &WgMeshConfigurationError{
|
||||
msg: "Prune time cannot be less than keep alive time",
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,10 @@ func getExampleConfiguration() *WgMeshConfiguration {
|
||||
SyncRate: 1,
|
||||
InterClusterChance: 0.1,
|
||||
BranchRate: 2,
|
||||
KeepAliveRate: 1,
|
||||
KeepAliveTime: 4,
|
||||
InfectionCount: 1,
|
||||
Timeout: 2,
|
||||
PruneTime: 20,
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,7 +112,7 @@ func InfectionCountZero(t *testing.T) {
|
||||
|
||||
func KeepAliveRateZero(t *testing.T) {
|
||||
conf := getExampleConfiguration()
|
||||
conf.KeepAliveRate = 0
|
||||
conf.KeepAliveTime = 0
|
||||
|
||||
err := ValidateConfiguration(conf)
|
||||
|
||||
@ -128,3 +130,36 @@ func TestValidCOnfiguration(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeout(t *testing.T) {
|
||||
conf := getExampleConfiguration()
|
||||
conf.Timeout = 0
|
||||
|
||||
err := ValidateConfiguration(conf)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal(`error should be thrown`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPruneTimeZero(t *testing.T) {
|
||||
conf := getExampleConfiguration()
|
||||
conf.PruneTime = 0
|
||||
|
||||
err := ValidateConfiguration(conf)
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf(`Error should be thrown`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPruneTimeLessThanKeepAliveTime(t *testing.T) {
|
||||
conf := getExampleConfiguration()
|
||||
conf.PruneTime = 1
|
||||
|
||||
err := ValidateConfiguration(conf)
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf(`Error should be thrown`)
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ func NewCtrlServer(params *NewCtrlServerParams) (*MeshCtrlServer, error) {
|
||||
ipAllocator := &ip.ULABuilder{}
|
||||
interfaceManipulator := wg.NewWgInterfaceManipulator(params.Client)
|
||||
|
||||
configApplyer := mesh.NewWgMeshConfigApplyer()
|
||||
|
||||
meshManagerParams := &mesh.NewMeshManagerParams{
|
||||
Conf: *params.Conf,
|
||||
Client: params.Client,
|
||||
@ -43,9 +45,11 @@ func NewCtrlServer(params *NewCtrlServerParams) (*MeshCtrlServer, error) {
|
||||
IdGenerator: idGenerator,
|
||||
IPAllocator: ipAllocator,
|
||||
InterfaceManipulator: interfaceManipulator,
|
||||
ConfigApplyer: mesh.NewWgMeshConfigApplyer(ctrlServer.MeshManager),
|
||||
ConfigApplyer: configApplyer,
|
||||
}
|
||||
|
||||
ctrlServer.MeshManager = mesh.NewMeshManager(meshManagerParams)
|
||||
configApplyer.SetMeshManager(ctrlServer.MeshManager)
|
||||
|
||||
ctrlServer.Conf = params.Conf
|
||||
connManagerParams := conn.NewConnectionManagerParams{
|
||||
@ -107,6 +111,10 @@ func (s *MeshCtrlServer) Close() error {
|
||||
logging.Log.WriteErrorf(err.Error())
|
||||
}
|
||||
|
||||
if err := s.MeshManager.Close(); err != nil {
|
||||
logging.Log.WriteErrorf(err.Error())
|
||||
}
|
||||
|
||||
if err := s.ConnectionServer.Close(); err != nil {
|
||||
logging.Log.WriteErrorf(err.Error())
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ type MeshNode struct {
|
||||
WgHost string
|
||||
Timestamp int64
|
||||
Routes []string
|
||||
Description string
|
||||
}
|
||||
|
||||
// Represents a WireGuard Mesh
|
||||
|
@ -63,7 +63,7 @@ const SockAddr = "/tmp/wgmesh_ipc.sock"
|
||||
|
||||
func RunIpcHandler(server MeshIpc) error {
|
||||
if err := os.RemoveAll(SockAddr); err != nil {
|
||||
return errors.New("Could not find to address")
|
||||
return errors.New("could not find to address")
|
||||
}
|
||||
|
||||
rpc.Register(server)
|
||||
|
300
pkg/lib/rtnetlink.go
Normal file
300
pkg/lib/rtnetlink.go
Normal file
@ -0,0 +1,300 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/jsimonetti/rtnetlink"
|
||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type RtNetlinkConfig struct {
|
||||
conn *rtnetlink.Conn
|
||||
}
|
||||
|
||||
func NewRtNetlinkConfig() (*RtNetlinkConfig, error) {
|
||||
conn, err := rtnetlink.Dial(nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &RtNetlinkConfig{conn: conn}, nil
|
||||
}
|
||||
|
||||
const WIREGUARD_MTU = 1420
|
||||
|
||||
// Create a netlink interface if it does not exist. ifName is the name of the netlink interface
|
||||
func (c *RtNetlinkConfig) CreateLink(ifName string) error {
|
||||
_, err := net.InterfaceByName(ifName)
|
||||
|
||||
if err == nil {
|
||||
return fmt.Errorf("interface %s already exists", ifName)
|
||||
}
|
||||
|
||||
err = c.conn.Link.New(&rtnetlink.LinkMessage{
|
||||
Family: unix.AF_UNSPEC,
|
||||
Flags: unix.IFF_UP,
|
||||
Attributes: &rtnetlink.LinkAttributes{
|
||||
Name: ifName,
|
||||
Info: &rtnetlink.LinkInfo{Kind: "wireguard"},
|
||||
MTU: uint32(WIREGUARD_MTU),
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create wireguard interface: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete link delete the specified interface
|
||||
func (c *RtNetlinkConfig) DeleteLink(ifName string) error {
|
||||
iface, err := net.InterfaceByName(ifName)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get interface %s %w", ifName, err)
|
||||
}
|
||||
|
||||
err = c.conn.Link.Delete(uint32(iface.Index))
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete wg interface %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddAddress adds an address to the given interface.
|
||||
func (c *RtNetlinkConfig) AddAddress(ifName string, address string) error {
|
||||
iface, err := net.InterfaceByName(ifName)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get interface %s error: %w", ifName, err)
|
||||
}
|
||||
|
||||
addr, cidr, err := net.ParseCIDR(address)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse CIDR %s error: %w", addr, err)
|
||||
}
|
||||
|
||||
family := unix.AF_INET6
|
||||
|
||||
ipv4 := cidr.IP.To4()
|
||||
|
||||
if ipv4 != nil {
|
||||
family = unix.AF_INET
|
||||
}
|
||||
|
||||
// Calculate the prefix length
|
||||
ones, _ := cidr.Mask.Size()
|
||||
|
||||
// Calculate the broadcast IP
|
||||
// Only used when family is AF_INET
|
||||
var brd net.IP
|
||||
if ipv4 != nil {
|
||||
brd = make(net.IP, len(ipv4))
|
||||
binary.BigEndian.PutUint32(brd, binary.BigEndian.Uint32(ipv4)|^binary.BigEndian.Uint32(net.IP(cidr.Mask).To4()))
|
||||
}
|
||||
|
||||
err = c.conn.Address.New(&rtnetlink.AddressMessage{
|
||||
Family: uint8(family),
|
||||
PrefixLength: uint8(ones),
|
||||
Scope: unix.RT_SCOPE_UNIVERSE,
|
||||
Index: uint32(iface.Index),
|
||||
Attributes: &rtnetlink.AddressAttributes{
|
||||
Address: addr,
|
||||
Local: addr,
|
||||
Broadcast: brd,
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to add address to link %w", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// AddRoute: adds a route to the routing table.
|
||||
// ifName is the intrface to add the route to
|
||||
// gateway is the IP of the gateway device to hop to
|
||||
// dst is the network prefix of the advertised destination
|
||||
func (c *RtNetlinkConfig) AddRoute(ifName string, route Route) error {
|
||||
iface, err := net.InterfaceByName(ifName)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed accessing interface %s error %w", ifName, err)
|
||||
}
|
||||
|
||||
gw := route.Gateway
|
||||
dst := route.Destination
|
||||
|
||||
var family uint8 = unix.AF_INET6
|
||||
|
||||
if dst.IP.To4() != nil {
|
||||
family = unix.AF_INET
|
||||
}
|
||||
|
||||
attr := rtnetlink.RouteAttributes{
|
||||
Dst: dst.IP,
|
||||
OutIface: uint32(iface.Index),
|
||||
Gateway: gw,
|
||||
}
|
||||
|
||||
ones, _ := dst.Mask.Size()
|
||||
|
||||
err = c.conn.Route.Replace(&rtnetlink.RouteMessage{
|
||||
Family: family,
|
||||
Table: unix.RT_TABLE_MAIN,
|
||||
Protocol: unix.RTPROT_BOOT,
|
||||
Scope: unix.RT_SCOPE_LINK,
|
||||
Type: unix.RTN_UNICAST,
|
||||
DstLength: uint8(ones),
|
||||
Attributes: attr,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add route %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteRoute deletes routes with the gateway and destination
|
||||
func (c *RtNetlinkConfig) DeleteRoute(ifName string, route Route) error {
|
||||
iface, err := net.InterfaceByName(ifName)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed accessing interface %s error %w", ifName, err)
|
||||
}
|
||||
|
||||
gw := route.Gateway
|
||||
dst := route.Destination
|
||||
|
||||
var family uint8 = unix.AF_INET6
|
||||
|
||||
if dst.IP.To4() != nil {
|
||||
family = unix.AF_INET
|
||||
}
|
||||
|
||||
attr := rtnetlink.RouteAttributes{
|
||||
Dst: dst.IP,
|
||||
OutIface: uint32(iface.Index),
|
||||
Gateway: gw,
|
||||
}
|
||||
|
||||
ones, _ := dst.Mask.Size()
|
||||
|
||||
err = c.conn.Route.Delete(&rtnetlink.RouteMessage{
|
||||
Family: family,
|
||||
Table: unix.RT_TABLE_MAIN,
|
||||
Protocol: unix.RTPROT_BOOT,
|
||||
Scope: unix.RT_SCOPE_LINK,
|
||||
Type: unix.RTN_UNICAST,
|
||||
DstLength: uint8(ones),
|
||||
Attributes: attr,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete route %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Route struct {
|
||||
Gateway net.IP
|
||||
Destination net.IPNet
|
||||
}
|
||||
|
||||
func (r1 Route) equal(r2 Route) bool {
|
||||
return r1.Gateway.String() == r2.Gateway.String() &&
|
||||
r1.Destination.String() == r2.Destination.String()
|
||||
}
|
||||
|
||||
// DeleteRoutes deletes all routes not in exclude
|
||||
func (c *RtNetlinkConfig) DeleteRoutes(ifName string, family uint8, exclude ...Route) error {
|
||||
routes := make([]rtnetlink.RouteMessage, 0)
|
||||
|
||||
if len(exclude) != 0 {
|
||||
lRoutes, err := c.listRoutes(ifName, family, exclude[0].Gateway)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
routes = lRoutes
|
||||
}
|
||||
|
||||
ifRoutes := make([]Route, 0)
|
||||
|
||||
for _, rtRoute := range routes {
|
||||
logging.Log.WriteInfof("Routes: %s", rtRoute.Attributes.Dst.String())
|
||||
maskSize := 128
|
||||
|
||||
if family == unix.AF_INET {
|
||||
maskSize = 32
|
||||
}
|
||||
|
||||
cidr := net.CIDRMask(int(rtRoute.DstLength), maskSize)
|
||||
route := Route{
|
||||
Gateway: rtRoute.Attributes.Gateway,
|
||||
Destination: net.IPNet{IP: rtRoute.Attributes.Dst, Mask: cidr},
|
||||
}
|
||||
|
||||
ifRoutes = append(ifRoutes, route)
|
||||
}
|
||||
|
||||
shouldExclude := func(r Route) bool {
|
||||
for _, route := range exclude {
|
||||
if route.equal(r) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
toDelete := Filter(ifRoutes, shouldExclude)
|
||||
|
||||
for _, route := range toDelete {
|
||||
logging.Log.WriteInfof("Deleting route %s", route.Destination.String())
|
||||
err := c.DeleteRoute(ifName, route)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// listRoutes lists all routes on the interface
|
||||
func (c *RtNetlinkConfig) listRoutes(ifName string, family uint8, gateway net.IP) ([]rtnetlink.RouteMessage, error) {
|
||||
iface, err := net.InterfaceByName(ifName)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed accessing interface %s error %w", ifName, err)
|
||||
}
|
||||
|
||||
routes, err := c.conn.Route.List()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get route %w", err)
|
||||
}
|
||||
|
||||
filterFunc := func(r rtnetlink.RouteMessage) bool {
|
||||
return r.Attributes.Gateway.Equal(gateway) && r.Attributes.OutIface == uint32(iface.Index)
|
||||
}
|
||||
|
||||
routes = Filter(routes, filterFunc)
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
func (c *RtNetlinkConfig) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
42
pkg/lib/timer.go
Normal file
42
pkg/lib/timer.go
Normal file
@ -0,0 +1,42 @@
|
||||
package lib
|
||||
|
||||
import "time"
|
||||
|
||||
type TimerFunc = func() error
|
||||
|
||||
type Timer struct {
|
||||
f TimerFunc
|
||||
quit chan struct{}
|
||||
updateRate int
|
||||
}
|
||||
|
||||
func (t *Timer) Run() error {
|
||||
ticker := time.NewTicker(time.Duration(t.updateRate) * time.Second)
|
||||
|
||||
t.quit = make(chan struct{})
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
err := t.f()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case <-t.quit:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Timer) Stop() error {
|
||||
close(t.quit)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewTimer(f TimerFunc, updateRate int) *Timer {
|
||||
return &Timer{
|
||||
f: f,
|
||||
updateRate: updateRate,
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -15,6 +16,7 @@ type Logger interface {
|
||||
WriteInfof(msg string, args ...interface{})
|
||||
WriteErrorf(msg string, args ...interface{})
|
||||
WriteWarnf(msg string, args ...interface{})
|
||||
Writer() io.Writer
|
||||
}
|
||||
|
||||
type LogrusLogger struct {
|
||||
@ -33,6 +35,10 @@ func (l *LogrusLogger) WriteWarnf(msg string, args ...interface{}) {
|
||||
l.logger.Warnf(msg, args...)
|
||||
}
|
||||
|
||||
func (l *LogrusLogger) Writer() io.Writer {
|
||||
return l.logger.Writer()
|
||||
}
|
||||
|
||||
func NewLogrusLogger() *LogrusLogger {
|
||||
logger := logrus.New()
|
||||
logger.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})
|
||||
|
@ -1,7 +1,6 @@
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
@ -12,6 +11,7 @@ import (
|
||||
type MeshConfigApplyer interface {
|
||||
ApplyConfig() error
|
||||
RemovePeers(meshId string) error
|
||||
SetMeshManager(manager MeshManager)
|
||||
}
|
||||
|
||||
// WgMeshConfigApplyer applies WireGuard configuration
|
||||
@ -101,7 +101,7 @@ func (m *WgMeshConfigApplyer) RemovePeers(meshId string) error {
|
||||
mesh := m.meshManager.GetMesh(meshId)
|
||||
|
||||
if mesh == nil {
|
||||
return errors.New(fmt.Sprintf("mesh %s does not exist", meshId))
|
||||
return fmt.Errorf("mesh %s does not exist", meshId)
|
||||
}
|
||||
|
||||
dev, err := mesh.GetDevice()
|
||||
@ -118,6 +118,10 @@ func (m *WgMeshConfigApplyer) RemovePeers(meshId string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewWgMeshConfigApplyer(manager MeshManager) MeshConfigApplyer {
|
||||
return &WgMeshConfigApplyer{meshManager: manager}
|
||||
func (m *WgMeshConfigApplyer) SetMeshManager(manager MeshManager) {
|
||||
m.meshManager = manager
|
||||
}
|
||||
|
||||
func NewWgMeshConfigApplyer() MeshConfigApplyer {
|
||||
return &WgMeshConfigApplyer{}
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ type MeshManager interface {
|
||||
UpdateTimeStamp() error
|
||||
GetClient() *wgctrl.Client
|
||||
GetMeshes() map[string]MeshProvider
|
||||
Prune() error
|
||||
Close() error
|
||||
}
|
||||
|
||||
type MeshManagerImpl struct {
|
||||
@ -46,6 +48,19 @@ type MeshManagerImpl struct {
|
||||
interfaceManipulator wg.WgInterfaceManipulator
|
||||
}
|
||||
|
||||
// Prune implements MeshManager.
|
||||
func (m *MeshManagerImpl) Prune() error {
|
||||
for _, mesh := range m.Meshes {
|
||||
err := mesh.Prune(m.conf.PruneTime)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateMesh: Creates a new mesh, stores it and returns the mesh id
|
||||
func (m *MeshManagerImpl) CreateMesh(devName string, port int) (string, error) {
|
||||
meshId, err := m.idGenerator.GetId()
|
||||
@ -63,7 +78,7 @@ func (m *MeshManagerImpl) CreateMesh(devName string, port int) (string, error) {
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", fmt.Errorf("error creating mesh: %w", err)
|
||||
}
|
||||
|
||||
err = m.interfaceManipulator.CreateInterface(&wg.CreateInterfaceParams{
|
||||
@ -72,16 +87,10 @@ func (m *MeshManagerImpl) CreateMesh(devName string, port int) (string, error) {
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", nil
|
||||
return "", fmt.Errorf("error creating mesh: %w", err)
|
||||
}
|
||||
|
||||
m.Meshes[meshId] = nodeManager
|
||||
err = m.configApplyer.RemovePeers(meshId)
|
||||
|
||||
if err != nil {
|
||||
logging.Log.WriteErrorf(err.Error())
|
||||
}
|
||||
|
||||
return meshId, nil
|
||||
}
|
||||
|
||||
@ -139,25 +148,13 @@ func (s *MeshManagerImpl) EnableInterface(meshId string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
meshNode, err := s.GetSelf(meshId)
|
||||
err = s.RouteManager.InstallRoutes()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mesh := s.GetMesh(meshId)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dev, err := mesh.GetDevice()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.interfaceManipulator.EnableInterface(dev.Name, meshNode.GetWgHost().String())
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPublicKey: Gets the public key of the WireGuard mesh
|
||||
@ -189,6 +186,12 @@ type AddSelfParams struct {
|
||||
|
||||
// AddSelf adds this host to the mesh
|
||||
func (s *MeshManagerImpl) AddSelf(params *AddSelfParams) error {
|
||||
mesh := s.GetMesh(params.MeshId)
|
||||
|
||||
if mesh == nil {
|
||||
return fmt.Errorf("addself: mesh %s does not exist", params.MeshId)
|
||||
}
|
||||
|
||||
pubKey, err := s.GetPublicKey(params.MeshId)
|
||||
|
||||
if err != nil {
|
||||
@ -208,28 +211,52 @@ func (s *MeshManagerImpl) AddSelf(params *AddSelfParams) error {
|
||||
Endpoint: params.Endpoint,
|
||||
})
|
||||
|
||||
device, err := mesh.GetDevice()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get device %w", err)
|
||||
}
|
||||
|
||||
err = s.interfaceManipulator.AddAddress(device.Name, fmt.Sprintf("%s/64", nodeIP))
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("addSelf: failed to add address to dev %w", err)
|
||||
}
|
||||
|
||||
s.Meshes[params.MeshId].AddNode(node)
|
||||
return s.RouteManager.UpdateRoutes()
|
||||
}
|
||||
|
||||
// LeaveMesh leaves the mesh network
|
||||
func (s *MeshManagerImpl) LeaveMesh(meshId string) error {
|
||||
_, exists := s.Meshes[meshId]
|
||||
mesh, exists := s.Meshes[meshId]
|
||||
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("mesh %s does not exist", meshId))
|
||||
return fmt.Errorf("mesh %s does not exist", meshId)
|
||||
}
|
||||
|
||||
// For now just delete the mesh with the ID.
|
||||
err := s.RouteManager.RemoveRoutes(meshId)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
device, err := mesh.GetDevice()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.interfaceManipulator.RemoveInterface(device.Name)
|
||||
delete(s.Meshes, meshId)
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *MeshManagerImpl) GetSelf(meshId string) (MeshNode, error) {
|
||||
meshInstance, ok := s.Meshes[meshId]
|
||||
|
||||
if !ok {
|
||||
return nil, errors.New(fmt.Sprintf("mesh %s does not exist", meshId))
|
||||
return nil, fmt.Errorf("mesh %s does not exist", meshId)
|
||||
}
|
||||
|
||||
snapshot, err := meshInstance.GetMesh()
|
||||
@ -248,7 +275,13 @@ func (s *MeshManagerImpl) GetSelf(meshId string) (MeshNode, error) {
|
||||
}
|
||||
|
||||
func (s *MeshManagerImpl) ApplyConfig() error {
|
||||
return s.configApplyer.ApplyConfig()
|
||||
err := s.configApplyer.ApplyConfig()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.RouteManager.InstallRoutes()
|
||||
}
|
||||
|
||||
func (s *MeshManagerImpl) SetDescription(description string) error {
|
||||
@ -294,6 +327,24 @@ func (s *MeshManagerImpl) GetMeshes() map[string]MeshProvider {
|
||||
return s.Meshes
|
||||
}
|
||||
|
||||
func (s *MeshManagerImpl) Close() error {
|
||||
for _, mesh := range s.Meshes {
|
||||
dev, err := mesh.GetDevice()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.interfaceManipulator.RemoveInterface(dev.Name)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewMeshManagerParams params required to create an instance of a mesh manager
|
||||
type NewMeshManagerParams struct {
|
||||
Conf conf.WgMeshConfiguration
|
||||
@ -304,6 +355,7 @@ type NewMeshManagerParams struct {
|
||||
IPAllocator ip.IPAllocator
|
||||
InterfaceManipulator wg.WgInterfaceManipulator
|
||||
ConfigApplyer MeshConfigApplyer
|
||||
RouteManager RouteManager
|
||||
}
|
||||
|
||||
// Creates a new instance of a mesh manager with the given parameters
|
||||
@ -327,8 +379,14 @@ func NewMeshManager(params *NewMeshManagerParams) *MeshManagerImpl {
|
||||
Client: params.Client,
|
||||
conf: ¶ms.Conf,
|
||||
}
|
||||
|
||||
m.configApplyer = params.ConfigApplyer
|
||||
m.RouteManager = NewRouteManager(m)
|
||||
m.RouteManager = params.RouteManager
|
||||
|
||||
if m.RouteManager == nil {
|
||||
m.RouteManager = NewRouteManager(m)
|
||||
}
|
||||
|
||||
m.idGenerator = params.IdGenerator
|
||||
m.ipAllocator = params.IPAllocator
|
||||
m.interfaceManipulator = params.InterfaceManipulator
|
||||
|
@ -18,7 +18,7 @@ func getMeshConfiguration() *conf.WgMeshConfiguration {
|
||||
BranchRate: 3,
|
||||
InterClusterChance: 0.15,
|
||||
InfectionCount: 2,
|
||||
KeepAliveRate: 60,
|
||||
KeepAliveTime: 60,
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,6 +32,7 @@ func getMeshManager() *MeshManagerImpl {
|
||||
IPAllocator: &ip.ULABuilder{},
|
||||
InterfaceManipulator: &wg.WgInterfaceManipulatorStub{},
|
||||
ConfigApplyer: &MeshConfigApplyerStub{},
|
||||
RouteManager: &RouteManagerStub{},
|
||||
})
|
||||
|
||||
return manager
|
||||
@ -186,7 +187,7 @@ func TestLeaveMeshDeletesMesh(t *testing.T) {
|
||||
err = manager.LeaveMesh(meshId)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.Fatalf("%s", err.Error())
|
||||
}
|
||||
|
||||
_, exists := manager.Meshes[meshId]
|
||||
|
16
pkg/mesh/pruner.go
Normal file
16
pkg/mesh/pruner.go
Normal file
@ -0,0 +1,16 @@
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
||||
)
|
||||
|
||||
func pruneFunction(m MeshManager) lib.TimerFunc {
|
||||
return func() error {
|
||||
return m.Prune()
|
||||
}
|
||||
}
|
||||
|
||||
func NewPruner(m MeshManager, conf conf.WgMeshConfiguration) *lib.Timer {
|
||||
return lib.NewTimer(pruneFunction(m), conf.PruneTime/2)
|
||||
}
|
@ -1,13 +1,20 @@
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/tim-beatham/wgmesh/pkg/ip"
|
||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
||||
"github.com/tim-beatham/wgmesh/pkg/route"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type RouteManager interface {
|
||||
UpdateRoutes() error
|
||||
InstallRoutes() error
|
||||
RemoveRoutes(meshId string) error
|
||||
}
|
||||
|
||||
type RouteManagerImpl struct {
|
||||
@ -49,6 +56,129 @@ func (r *RouteManagerImpl) UpdateRoutes() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// removeRoutes: removes all meshes we are no longer a part of
|
||||
func (r *RouteManagerImpl) RemoveRoutes(meshId string) error {
|
||||
ulaBuilder := new(ip.ULABuilder)
|
||||
meshes := r.meshManager.GetMeshes()
|
||||
|
||||
ipNet, err := ulaBuilder.GetIPNet(meshId)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, mesh1 := range meshes {
|
||||
self, err := r.meshManager.GetSelf(meshId)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mesh1.RemoveRoutes(self.GetHostEndpoint(), ipNet.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRoute adds a route to the given interface
|
||||
func (m *RouteManagerImpl) addRoute(ifName string, meshPrefix string, routes ...lib.Route) error {
|
||||
rtnl, err := lib.NewRtNetlinkConfig()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create config: %w", err)
|
||||
}
|
||||
defer rtnl.Close()
|
||||
|
||||
// Delete any routes that may be vacant
|
||||
err = rtnl.DeleteRoutes(ifName, unix.AF_INET6, routes...)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, route := range routes {
|
||||
if route.Destination.String() == meshPrefix {
|
||||
continue
|
||||
}
|
||||
|
||||
err = rtnl.AddRoute(ifName, route)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *RouteManagerImpl) installRoute(ifName string, meshid string, node MeshNode) error {
|
||||
routeMapFunc := func(route string) lib.Route {
|
||||
_, cidr, _ := net.ParseCIDR(route)
|
||||
|
||||
r := lib.Route{
|
||||
Destination: *cidr,
|
||||
Gateway: node.GetWgHost().IP,
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
ipBuilder := &ip.ULABuilder{}
|
||||
ipNet, err := ipBuilder.GetIPNet(meshid)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
routes := lib.Map(append(node.GetRoutes(), ipNet.String()), routeMapFunc)
|
||||
return m.addRoute(ifName, ipNet.String(), routes...)
|
||||
}
|
||||
|
||||
func (m *RouteManagerImpl) installRoutes(meshProvider MeshProvider) error {
|
||||
mesh, err := meshProvider.GetMesh()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dev, err := meshProvider.GetDevice()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self, err := m.meshManager.GetSelf(meshProvider.GetMeshId())
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, node := range mesh.GetNodes() {
|
||||
if self.GetHostEndpoint() == node.GetHostEndpoint() {
|
||||
continue
|
||||
}
|
||||
|
||||
err = m.installRoute(dev.Name, meshProvider.GetMeshId(), node)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InstallRoutes installs all routes to the RIB
|
||||
func (r *RouteManagerImpl) InstallRoutes() error {
|
||||
for _, mesh := range r.meshManager.GetMeshes() {
|
||||
err := r.installRoutes(mesh)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewRouteManager(m MeshManager) RouteManager {
|
||||
return &RouteManagerImpl{meshManager: m, routeInstaller: route.NewRouteInstaller()}
|
||||
}
|
||||
|
16
pkg/mesh/route_stub.go
Normal file
16
pkg/mesh/route_stub.go
Normal file
@ -0,0 +1,16 @@
|
||||
package mesh
|
||||
|
||||
type RouteManagerStub struct {
|
||||
}
|
||||
|
||||
func (r *RouteManagerStub) UpdateRoutes() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RouteManagerStub) InstallRoutes() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RouteManagerStub) RemoveRoutes(meshId string) error {
|
||||
return nil
|
||||
}
|
@ -66,6 +66,16 @@ type MeshProviderStub struct {
|
||||
snapshot *MeshSnapshotStub
|
||||
}
|
||||
|
||||
// RemoveRoutes implements MeshProvider.
|
||||
func (*MeshProviderStub) RemoveRoutes(nodeId string, route ...string) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Prune implements MeshProvider.
|
||||
func (*MeshProviderStub) Prune(pruneAmount int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateTimeStamp implements MeshProvider.
|
||||
func (*MeshProviderStub) UpdateTimeStamp(nodeId string) error {
|
||||
return nil
|
||||
@ -154,10 +164,23 @@ func (a *MeshConfigApplyerStub) RemovePeers(meshId string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *MeshConfigApplyerStub) SetMeshManager(manager MeshManager) {
|
||||
}
|
||||
|
||||
type MeshManagerStub struct {
|
||||
meshes map[string]MeshProvider
|
||||
}
|
||||
|
||||
// Close implements MeshManager.
|
||||
func (*MeshManagerStub) Close() error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Prune implements MeshManager.
|
||||
func (*MeshManagerStub) Prune() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewMeshManagerStub() MeshManager {
|
||||
return &MeshManagerStub{meshes: make(map[string]MeshProvider)}
|
||||
}
|
||||
|
@ -58,20 +58,26 @@ type MeshProvider interface {
|
||||
GetDevice() (*wgtypes.Device, error)
|
||||
// HasChanges returns true if we have changes since last time we synced
|
||||
HasChanges() bool
|
||||
// Record that we have changges and save the corresponding changes
|
||||
// Record that we have changes and save the corresponding changes
|
||||
SaveChanges()
|
||||
// UpdateTimeStamp: update the timestamp of the given node
|
||||
UpdateTimeStamp(nodeId string) error
|
||||
// AddRoutes: adds routes to the given node
|
||||
AddRoutes(nodeId string, route ...string) error
|
||||
// DeleteRoutes: deletes the routes from the node
|
||||
RemoveRoutes(nodeId string, route ...string) error
|
||||
// GetSyncer: returns the automerge syncer for sync
|
||||
GetSyncer() MeshSyncer
|
||||
// SetDescription: sets the description of this automerge data type
|
||||
SetDescription(nodeId string, description string) error
|
||||
// Prune: prunes all nodes that have not updated their timestamp in
|
||||
// pruneAmount seconds
|
||||
Prune(pruneAmount int) error
|
||||
}
|
||||
|
||||
// HostParameters contains the IDs of a node
|
||||
type HostParameters struct {
|
||||
HostEndpoint string
|
||||
// TODO: Contain the WireGungracefullyuard identifier in this
|
||||
}
|
||||
|
||||
// MeshProviderFactoryParams parameters required to build a mesh provider
|
||||
|
@ -30,6 +30,10 @@ func (n *IpcHandler) CreateMesh(args *ipc.NewMeshArgs, reply *string) error {
|
||||
Endpoint: args.Endpoint,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*reply = meshId
|
||||
return err
|
||||
}
|
||||
@ -122,6 +126,7 @@ func (n *IpcHandler) GetMesh(meshId string, reply *ipc.GetMeshReply) error {
|
||||
if mesh == nil {
|
||||
return errors.New("mesh does not exist")
|
||||
}
|
||||
|
||||
nodes := make([]ctrlserver.MeshNode, len(meshSnapshot.GetNodes()))
|
||||
|
||||
i := 0
|
||||
@ -139,6 +144,7 @@ func (n *IpcHandler) GetMesh(meshId string, reply *ipc.GetMeshReply) error {
|
||||
WgHost: node.GetWgHost().String(),
|
||||
Timestamp: node.GetTimeStamp(),
|
||||
Routes: node.GetRoutes(),
|
||||
Description: node.GetDescription(),
|
||||
}
|
||||
|
||||
nodes[i] = node
|
||||
|
@ -31,7 +31,11 @@ type SyncerImpl struct {
|
||||
// Sync: Sync random nodes
|
||||
func (s *SyncerImpl) Sync(meshId string) error {
|
||||
logging.Log.WriteInfof("UPDATING WG CONF")
|
||||
s.manager.ApplyConfig()
|
||||
err := s.manager.ApplyConfig()
|
||||
|
||||
if err != nil {
|
||||
logging.Log.WriteInfof("Failed to update config %w", err)
|
||||
}
|
||||
|
||||
if !s.manager.HasChanges(meshId) && s.infectionCount == 0 {
|
||||
logging.Log.WriteInfof("No changes for %s", meshId)
|
||||
@ -107,7 +111,6 @@ func (s *SyncerImpl) Sync(meshId string) error {
|
||||
logging.Log.WriteInfof("SYNC COUNT: %d", s.syncCount)
|
||||
|
||||
s.infectionCount = ((s.conf.InfectionCount + s.infectionCount - 1) % s.conf.InfectionCount)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,13 @@ func (s *SyncErrorHandlerImpl) incrementFailedCount(meshId string, endpoint stri
|
||||
return false
|
||||
}
|
||||
|
||||
// self, err := s.meshManager.GetSelf(meshId)
|
||||
|
||||
// if err != nil {
|
||||
// return false
|
||||
// }
|
||||
|
||||
// mesh.DecrementHealth(endpoint, self.GetHostEndpoint())
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -89,10 +89,12 @@ func (s *SyncRequesterImpl) SyncMesh(meshId, endpoint string) error {
|
||||
|
||||
c := rpc.NewSyncServiceClient(client)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
syncTimeOut := s.server.Conf.SyncRate * float64(time.Second)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(syncTimeOut))
|
||||
defer cancel()
|
||||
|
||||
err = syncMesh(mesh, ctx, c)
|
||||
err = s.syncMesh(mesh, ctx, c)
|
||||
|
||||
if err != nil {
|
||||
return s.handleErr(meshId, endpoint, err)
|
||||
@ -102,7 +104,7 @@ func (s *SyncRequesterImpl) SyncMesh(meshId, endpoint string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func 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 {
|
||||
stream, err := client.SyncMesh(ctx)
|
||||
|
||||
syncer := mesh.GetSyncer()
|
||||
|
@ -1,10 +1,8 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/tim-beatham/wgmesh/pkg/ctrlserver"
|
||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
||||
)
|
||||
|
||||
// SyncScheduler: Loops through all nodes in the mesh and runs a schedule to
|
||||
@ -22,34 +20,13 @@ type SyncSchedulerImpl struct {
|
||||
}
|
||||
|
||||
// Run implements SyncScheduler.
|
||||
func (s *SyncSchedulerImpl) Run() error {
|
||||
ticker := time.NewTicker(time.Duration(s.server.Conf.SyncRate) * time.Second)
|
||||
|
||||
quit := make(chan struct{})
|
||||
s.quit = quit
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
err := s.syncer.SyncMeshes()
|
||||
|
||||
if err != nil {
|
||||
logging.Log.WriteErrorf(err.Error())
|
||||
}
|
||||
break
|
||||
case <-quit:
|
||||
break
|
||||
}
|
||||
func syncFunction(syncer Syncer) lib.TimerFunc {
|
||||
return func() error {
|
||||
return syncer.SyncMeshes()
|
||||
}
|
||||
}
|
||||
|
||||
// Stop implements SyncScheduler.
|
||||
func (s *SyncSchedulerImpl) Stop() error {
|
||||
close(s.quit)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewSyncScheduler(s *ctrlserver.MeshCtrlServer, syncRequester SyncRequester) SyncScheduler {
|
||||
func NewSyncScheduler(s *ctrlserver.MeshCtrlServer, syncRequester SyncRequester) *lib.Timer {
|
||||
syncer := NewSyncer(s.MeshManager, s.Conf, syncRequester)
|
||||
return &SyncSchedulerImpl{server: s, syncer: syncer}
|
||||
return lib.NewTimer(syncFunction(syncer), int(s.Conf.SyncRate))
|
||||
}
|
||||
|
@ -1,51 +1,14 @@
|
||||
package timestamp
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/tim-beatham/wgmesh/pkg/ctrlserver"
|
||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
||||
"github.com/tim-beatham/wgmesh/pkg/mesh"
|
||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
||||
)
|
||||
|
||||
type TimestampScheduler interface {
|
||||
Run() error
|
||||
Stop() error
|
||||
}
|
||||
|
||||
type TimeStampSchedulerImpl struct {
|
||||
meshManager mesh.MeshManager
|
||||
updateRate int
|
||||
quit chan struct{}
|
||||
}
|
||||
|
||||
func (s *TimeStampSchedulerImpl) Run() error {
|
||||
ticker := time.NewTicker(time.Duration(s.updateRate) * time.Second)
|
||||
|
||||
s.quit = make(chan struct{})
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
err := s.meshManager.UpdateTimeStamp()
|
||||
|
||||
if err != nil {
|
||||
logging.Log.WriteErrorf("Update Timestamp Error: %s", err.Error())
|
||||
}
|
||||
case <-s.quit:
|
||||
break
|
||||
}
|
||||
func NewTimestampScheduler(ctrlServer *ctrlserver.MeshCtrlServer) lib.Timer {
|
||||
timerFunc := func() error {
|
||||
return ctrlServer.MeshManager.UpdateTimeStamp()
|
||||
}
|
||||
}
|
||||
|
||||
func NewTimestampScheduler(ctrlServer *ctrlserver.MeshCtrlServer) TimestampScheduler {
|
||||
return &TimeStampSchedulerImpl{
|
||||
meshManager: ctrlServer.MeshManager,
|
||||
updateRate: ctrlServer.Conf.KeepAliveRate,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TimeStampSchedulerImpl) Stop() error {
|
||||
close(s.quit)
|
||||
return nil
|
||||
return *lib.NewTimer(timerFunc, ctrlServer.Conf.KeepAliveTime)
|
||||
}
|
||||
|
@ -6,6 +6,10 @@ func (i *WgInterfaceManipulatorStub) CreateInterface(params *CreateInterfacePara
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *WgInterfaceManipulatorStub) EnableInterface(ifName string, ip string) error {
|
||||
func (i *WgInterfaceManipulatorStub) AddAddress(ifName string, addr string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *WgInterfaceManipulatorStub) RemoveInterface(ifName string) error {
|
||||
return nil
|
||||
}
|
||||
|
@ -16,7 +16,8 @@ type CreateInterfaceParams struct {
|
||||
type WgInterfaceManipulator interface {
|
||||
// CreateInterface creates a WireGuard interface
|
||||
CreateInterface(params *CreateInterfaceParams) error
|
||||
// Enable interface enables the given interface with
|
||||
// the IP. It overrides the IP at the interface
|
||||
EnableInterface(ifName string, ip string) error
|
||||
// AddAddress adds an address to the given interface name
|
||||
AddAddress(ifName string, addr string) error
|
||||
// RemoveInterface removes the specified interface
|
||||
RemoveInterface(ifName string) error
|
||||
}
|
||||
|
102
pkg/wg/wg.go
102
pkg/wg/wg.go
@ -1,50 +1,37 @@
|
||||
package wg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os/exec"
|
||||
|
||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
||||
"golang.zx2c4.com/wireguard/wgctrl"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
// createInterface uses ip link to create an interface. If the interface exists
|
||||
// it returns an error
|
||||
func createInterface(ifName string) error {
|
||||
_, err := net.InterfaceByName(ifName)
|
||||
|
||||
if err == nil {
|
||||
err = flushInterface(ifName)
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if the interface exists
|
||||
cmd := exec.Command("/usr/bin/ip", "link", "add", "dev", ifName, "type", "wireguard")
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type WgInterfaceManipulatorImpl struct {
|
||||
client *wgctrl.Client
|
||||
}
|
||||
|
||||
// CreateInterface creates a WireGuard interface
|
||||
func (m *WgInterfaceManipulatorImpl) CreateInterface(params *CreateInterfaceParams) error {
|
||||
err := createInterface(params.IfName)
|
||||
rtnl, err := lib.NewRtNetlinkConfig()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to access link: %w", err)
|
||||
}
|
||||
defer rtnl.Close()
|
||||
|
||||
err = rtnl.CreateLink(params.IfName)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create link: %w", err)
|
||||
}
|
||||
|
||||
privateKey, err := wgtypes.GeneratePrivateKey()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to create private key: %w", err)
|
||||
}
|
||||
|
||||
var cfg wgtypes.Config = wgtypes.Config{
|
||||
@ -52,59 +39,44 @@ func (m *WgInterfaceManipulatorImpl) CreateInterface(params *CreateInterfacePara
|
||||
ListenPort: ¶ms.Port,
|
||||
}
|
||||
|
||||
m.client.ConfigureDevice(params.IfName, cfg)
|
||||
err = m.client.ConfigureDevice(params.IfName, cfg)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to configure dev: %w", err)
|
||||
}
|
||||
|
||||
logging.Log.WriteInfof("ip link set up dev %s type wireguard", params.IfName)
|
||||
return nil
|
||||
}
|
||||
|
||||
// flushInterface flushes the specified interface
|
||||
func flushInterface(ifName string) error {
|
||||
_, err := net.InterfaceByName(ifName)
|
||||
// Add an address to the given interface
|
||||
func (m *WgInterfaceManipulatorImpl) AddAddress(ifName string, addr string) error {
|
||||
rtnl, err := lib.NewRtNetlinkConfig()
|
||||
|
||||
if err != nil {
|
||||
return &WgError{msg: fmt.Sprintf("Interface %s does not exist cannot flush", ifName)}
|
||||
return fmt.Errorf("failed to create config: %w", err)
|
||||
}
|
||||
defer rtnl.Close()
|
||||
|
||||
err = rtnl.AddAddress(ifName, addr)
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to add address: %w", err)
|
||||
}
|
||||
|
||||
cmd := exec.Command("/usr/bin/ip", "addr", "flush", "dev", ifName)
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
logging.Log.WriteErrorf(fmt.Sprintf("%s error flushing interface %s", err.Error(), ifName))
|
||||
return &WgError{msg: fmt.Sprintf("Failed to flush interface %s", ifName)}
|
||||
}
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// EnableInterface flushes the interface and sets the ip address of the
|
||||
// interface
|
||||
func (m *WgInterfaceManipulatorImpl) EnableInterface(ifName string, ip string) error {
|
||||
if len(ifName) == 0 {
|
||||
return errors.New("ifName not provided")
|
||||
}
|
||||
|
||||
err := flushInterface(ifName)
|
||||
// RemoveInterface implements WgInterfaceManipulator.
|
||||
func (*WgInterfaceManipulatorImpl) RemoveInterface(ifName string) error {
|
||||
rtnl, err := lib.NewRtNetlinkConfig()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to create config: %w", err)
|
||||
}
|
||||
defer rtnl.Close()
|
||||
|
||||
cmd := exec.Command("/usr/bin/ip", "link", "set", "up", "dev", ifName)
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostIp, _, err := net.ParseCIDR(ip)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd = exec.Command("/usr/bin/ip", "addr", "add", hostIp.String()+"/64", "dev", ifName)
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return rtnl.DeleteLink(ifName)
|
||||
}
|
||||
|
||||
func NewWgInterfaceManipulator(client *wgctrl.Client) WgInterfaceManipulator {
|
||||
|
Reference in New Issue
Block a user