forked from extern/smegmesh
Implemented the forwarding of packets between meshes
This commit is contained in:
parent
180f5e226c
commit
c205be6748
@ -5,6 +5,7 @@ import (
|
||||
"log"
|
||||
ipcRpc "net/rpc"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/akamensky/argparse"
|
||||
@ -80,6 +81,10 @@ func getMesh(client *ipcRpc.Client, meshId string) {
|
||||
fmt.Println("WireGuard Endpoint: " + node.WgEndpoint)
|
||||
fmt.Println("Wg IP: " + node.WgHost)
|
||||
fmt.Println(fmt.Sprintf("Timestamp: %s", time.Unix(node.Timestamp, 0).String()))
|
||||
|
||||
advertiseRoutes := strings.Join(node.Routes, ",")
|
||||
fmt.Printf("Routes: %s\n", advertiseRoutes)
|
||||
|
||||
fmt.Println("---")
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
certificatePath: "../../cert/cert.pem"
|
||||
privateKeyPath: "../../cert/key.pem"
|
||||
skipCertVerification: true
|
||||
ifName: "wgmesh"
|
||||
wgPort: 51820
|
||||
gRPCPort: "8080"
|
||||
secret: "abc123"
|
||||
advertiseRoutes: true
|
@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/automerge/automerge-go"
|
||||
"github.com/tim-beatham/wgmesh/pkg/conf"
|
||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
||||
"github.com/tim-beatham/wgmesh/pkg/wg"
|
||||
"golang.zx2c4.com/wireguard/wgctrl"
|
||||
@ -22,6 +23,7 @@ type CrdtNodeManager struct {
|
||||
Client *wgctrl.Client
|
||||
doc *automerge.Doc
|
||||
LastHash automerge.ChangeHash
|
||||
conf *conf.WgMeshConfiguration
|
||||
}
|
||||
|
||||
const maxFails = 5
|
||||
@ -30,6 +32,8 @@ func (c *CrdtNodeManager) AddNode(crdt MeshNodeCrdt) {
|
||||
crdt.FailedMap = automerge.NewMap()
|
||||
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())
|
||||
}
|
||||
|
||||
func (c *CrdtNodeManager) ApplyWg() error {
|
||||
@ -66,13 +70,14 @@ func (c *CrdtNodeManager) Save() []byte {
|
||||
}
|
||||
|
||||
// NewCrdtNodeManager: Create a new crdt node manager
|
||||
func NewCrdtNodeManager(meshId, hostId, devName string, port int, client *wgctrl.Client) (*CrdtNodeManager, error) {
|
||||
func NewCrdtNodeManager(meshId, hostId, devName string, port int, conf conf.WgMeshConfiguration, client *wgctrl.Client) (*CrdtNodeManager, error) {
|
||||
var manager CrdtNodeManager
|
||||
manager.MeshId = meshId
|
||||
manager.doc = automerge.New()
|
||||
manager.IfName = devName
|
||||
manager.Client = client
|
||||
manager.NodeId = hostId
|
||||
manager.conf = &conf
|
||||
|
||||
err := wg.CreateWgInterface(client, devName, port)
|
||||
|
||||
@ -105,6 +110,11 @@ func (m *CrdtNodeManager) convertMeshNode(node MeshNodeCrdt) (*wgtypes.PeerConfi
|
||||
|
||||
allowedIps[0] = *ipnet
|
||||
|
||||
for route, _ := range node.Routes {
|
||||
_, ipnet, _ := net.ParseCIDR(route)
|
||||
allowedIps = append(allowedIps, *ipnet)
|
||||
}
|
||||
|
||||
peerConfig := wgtypes.PeerConfig{
|
||||
PublicKey: peerPublic,
|
||||
Remove: m.HasFailed(node.HostEndpoint),
|
||||
@ -290,6 +300,31 @@ func (m *CrdtNodeManager) updateWgConf(devName string, nodes map[string]MeshNode
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRoutes: adds routes to the specific nodeId
|
||||
func (m *CrdtNodeManager) AddRoutes(routes ...string) error {
|
||||
nodeVal, err := m.doc.Path("nodes").Map().Get(m.NodeId)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
routeMap, err := nodeVal.Map().Get("routes")
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, route := range routes {
|
||||
err = routeMap.Map().Set(route, struct{}{})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CrdtNodeManager) GetSyncer() *AutomergeSync {
|
||||
return NewAutomergeSync(m)
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package crdt
|
||||
|
||||
import "github.com/automerge/automerge-go"
|
||||
|
||||
// MeshNodeCrdt: Represents a CRDT for a mesh nodes
|
||||
type MeshNodeCrdt struct {
|
||||
HostEndpoint string `automerge:"hostEndpoint"`
|
||||
WgEndpoint string `automerge:"wgEndpoint"`
|
||||
@ -9,8 +10,10 @@ type MeshNodeCrdt struct {
|
||||
WgHost string `automerge:"wgHost"`
|
||||
Timestamp int64 `automerge:"timestamp"`
|
||||
FailedMap *automerge.Map `automerge:"failedMap"`
|
||||
Routes map[string]interface{} `automerge:"routes"`
|
||||
}
|
||||
|
||||
// MeshCrdt: Represents the mesh network as a whole
|
||||
type MeshCrdt struct {
|
||||
Nodes map[string]MeshNodeCrdt `automerge:"nodes"`
|
||||
}
|
||||
|
@ -12,10 +12,8 @@ type WgMeshConfiguration struct {
|
||||
CertificatePath string `yaml:"certificatePath"`
|
||||
PrivateKeyPath string `yaml:"privateKeyPath"`
|
||||
SkipCertVerification bool `yaml:"skipCertVerification"`
|
||||
IfName string `yaml:"ifName"`
|
||||
WgPort int `yaml:"wgPort"`
|
||||
GrpcPort string `yaml:"gRPCPort"`
|
||||
Secret string `yaml:"secret"`
|
||||
AdvertiseRoutes bool `yaml:"advertiseRoutes"`
|
||||
}
|
||||
|
||||
func ParseConfiguration(filePath string) (*WgMeshConfiguration, error) {
|
||||
|
@ -18,6 +18,7 @@ type MeshNode struct {
|
||||
WgHost string
|
||||
Failed bool
|
||||
Timestamp int64
|
||||
Routes []string
|
||||
}
|
||||
|
||||
type Mesh struct {
|
||||
|
@ -20,7 +20,6 @@ const (
|
||||
Hash2Prefix = 14
|
||||
Hash1Prefix = 8
|
||||
InterfaceIdLen = 8
|
||||
SubnetPrefixLen = 8
|
||||
)
|
||||
|
||||
/*
|
||||
@ -28,14 +27,14 @@ const (
|
||||
*/
|
||||
type CgaParameters struct {
|
||||
Modifier [ModifierLength]byte
|
||||
SubnetPrefix [SubnetPrefixLen]byte
|
||||
SubnetPrefix [2 * InterfaceIdLen]byte
|
||||
CollisionCount uint8
|
||||
PublicKey wgtypes.Key
|
||||
interfaceId [2 * InterfaceIdLen]byte
|
||||
flag byte
|
||||
}
|
||||
|
||||
func NewCga(key wgtypes.Key, subnetPrefix [SubnetPrefixLen]byte) (*CgaParameters, error) {
|
||||
func NewCga(key wgtypes.Key, subnetPrefix [2 * InterfaceIdLen]byte) (*CgaParameters, error) {
|
||||
var params CgaParameters
|
||||
|
||||
_, err := rand.Read(params.Modifier[:])
|
||||
|
@ -2,6 +2,7 @@ package ip
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
@ -9,8 +10,8 @@ import (
|
||||
|
||||
type ULABuilder struct{}
|
||||
|
||||
func getULAPrefix(meshId string) [8]byte {
|
||||
var ulaPrefix [8]byte
|
||||
func getMeshPrefix(meshId string) [16]byte {
|
||||
var ulaPrefix [16]byte
|
||||
|
||||
ulaPrefix[0] = 0xfd
|
||||
|
||||
@ -24,8 +25,22 @@ func getULAPrefix(meshId string) [8]byte {
|
||||
return ulaPrefix
|
||||
}
|
||||
|
||||
func (u *ULABuilder) GetIPNet(meshId string) (*net.IPNet, error) {
|
||||
meshBytes := getMeshPrefix(meshId)
|
||||
var meshIP net.IP = meshBytes[:]
|
||||
|
||||
ip := fmt.Sprintf("%s/%d", meshIP.String(), 64)
|
||||
_, net, err := net.ParseCIDR(ip)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return net, nil
|
||||
}
|
||||
|
||||
func (u *ULABuilder) GetIP(key wgtypes.Key, meshId string) (net.IP, error) {
|
||||
ulaPrefix := getULAPrefix(meshId)
|
||||
ulaPrefix := getMeshPrefix(meshId)
|
||||
|
||||
c, err := NewCga(key, ulaPrefix)
|
||||
|
||||
|
@ -25,3 +25,15 @@ func MapValuesWithExclude[K comparable, V any](m map[K]V, exclude map[K]struct{}
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
func MapKeys[K comparable, V any](m map[K]V) []K {
|
||||
values := make([]K, len(m))
|
||||
|
||||
i := 0
|
||||
for k, _ := range m {
|
||||
values[i] = k
|
||||
i++
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ func (c *MeshDOTConverter) Generate(meshId string) (string, error) {
|
||||
}
|
||||
|
||||
for _, node2 := range nodes[i+1:] {
|
||||
if node1 == node2 || mesh.HasFailed(node2.HostEndpoint) {
|
||||
if node1.WgEndpoint == node2.WgEndpoint || mesh.HasFailed(node2.HostEndpoint) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
|
||||
type MeshManger struct {
|
||||
Meshes map[string]*crdt.CrdtNodeManager
|
||||
RouteManager RouteManager
|
||||
Client *wgctrl.Client
|
||||
HostEndpoint string
|
||||
conf *conf.WgMeshConfiguration
|
||||
@ -32,19 +33,20 @@ func (m *MeshManger) CreateMesh(devName string, port int) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
nodeManager, err := crdt.NewCrdtNodeManager(key.String(), m.HostEndpoint, devName, port, m.Client)
|
||||
nodeManager, err := crdt.NewCrdtNodeManager(key.String(), m.HostEndpoint, devName, port, *m.conf, m.Client)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
m.Meshes[key.String()] = nodeManager
|
||||
return key.String(), nil
|
||||
|
||||
return key.String(), err
|
||||
}
|
||||
|
||||
// AddMesh: Add the mesh to the list of meshes
|
||||
func (m *MeshManger) AddMesh(meshId string, devName string, port int, meshBytes []byte) error {
|
||||
mesh, err := crdt.NewCrdtNodeManager(meshId, m.HostEndpoint, devName, port, m.Client)
|
||||
mesh, err := crdt.NewCrdtNodeManager(meshId, m.HostEndpoint, devName, port, *m.conf, m.Client)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@ -63,6 +65,10 @@ func (m *MeshManger) AddMesh(meshId string, devName string, port int, meshBytes
|
||||
// AddMeshNode: Add a mesh node
|
||||
func (m *MeshManger) AddMeshNode(meshId string, node crdt.MeshNodeCrdt) {
|
||||
m.Meshes[meshId].AddNode(node)
|
||||
|
||||
if m.conf.AdvertiseRoutes {
|
||||
m.RouteManager.UpdateRoutes()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MeshManger) HasChanges(meshId string) bool {
|
||||
@ -100,7 +106,13 @@ func (s *MeshManger) EnableInterface(meshId string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return wg.EnableInterface(mesh.IfName, node.WgHost)
|
||||
err = wg.EnableInterface(mesh.IfName, node.WgHost)
|
||||
|
||||
if s.conf.AdvertiseRoutes {
|
||||
s.RouteManager.ApplyWg(mesh)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPublicKey: Gets the public key of the WireGuard mesh
|
||||
@ -135,11 +147,13 @@ func (s *MeshManger) UpdateTimeStamp() error {
|
||||
|
||||
func NewMeshManager(conf conf.WgMeshConfiguration, client *wgctrl.Client) *MeshManger {
|
||||
ip := lib.GetOutboundIP()
|
||||
|
||||
return &MeshManger{
|
||||
m := &MeshManger{
|
||||
Meshes: make(map[string]*crdt.CrdtNodeManager),
|
||||
HostEndpoint: fmt.Sprintf("%s:%s", ip.String(), conf.GrpcPort),
|
||||
Client: client,
|
||||
conf: &conf,
|
||||
}
|
||||
|
||||
m.RouteManager = NewRouteManager(m)
|
||||
return m
|
||||
}
|
||||
|
78
pkg/mesh/route_manager.go
Normal file
78
pkg/mesh/route_manager.go
Normal file
@ -0,0 +1,78 @@
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
crdt "github.com/tim-beatham/wgmesh/pkg/automerge"
|
||||
"github.com/tim-beatham/wgmesh/pkg/ip"
|
||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
||||
"github.com/tim-beatham/wgmesh/pkg/route"
|
||||
)
|
||||
|
||||
type RouteManager interface {
|
||||
UpdateRoutes() error
|
||||
ApplyWg(mesh *crdt.CrdtNodeManager) error
|
||||
}
|
||||
|
||||
type RouteManagerImpl struct {
|
||||
meshManager *MeshManger
|
||||
routeInstaller route.RouteInstaller
|
||||
}
|
||||
|
||||
func (r *RouteManagerImpl) UpdateRoutes() error {
|
||||
meshes := r.meshManager.Meshes
|
||||
ulaBuilder := new(ip.ULABuilder)
|
||||
|
||||
for _, mesh1 := range meshes {
|
||||
for _, mesh2 := range meshes {
|
||||
if mesh1 == mesh2 {
|
||||
continue
|
||||
}
|
||||
|
||||
ipNet, err := ulaBuilder.GetIPNet(mesh2.MeshId)
|
||||
|
||||
if err != nil {
|
||||
logging.Log.WriteErrorf(err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
mesh1.AddRoutes(ipNet.String())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RouteManagerImpl) ApplyWg(mesh *crdt.CrdtNodeManager) error {
|
||||
snapshot, err := mesh.GetCrdt()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, node := range snapshot.Nodes {
|
||||
if node.HostEndpoint == r.meshManager.HostEndpoint {
|
||||
continue
|
||||
}
|
||||
|
||||
for route, _ := range node.Routes {
|
||||
_, netIP, err := net.ParseCIDR(route)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = r.routeInstaller.InstallRoutes(mesh.IfName, netIP)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewRouteManager(m *MeshManger) RouteManager {
|
||||
return &RouteManagerImpl{meshManager: m, routeInstaller: route.NewRouteInstaller()}
|
||||
}
|
@ -51,6 +51,7 @@ func (n *RobinIpc) CreateMesh(args *ipc.NewMeshArgs, reply *string) error {
|
||||
PublicKey: pubKey.String(),
|
||||
WgEndpoint: fmt.Sprintf("%s:%d", outBoundIp.String(), args.WgPort),
|
||||
WgHost: nodeIP.String() + "/128",
|
||||
Routes: map[string]interface{}{},
|
||||
}
|
||||
|
||||
n.Server.MeshManager.AddMeshNode(meshId, meshNode)
|
||||
@ -127,6 +128,7 @@ func (n *RobinIpc) JoinMesh(args ipc.JoinMeshArgs, reply *string) error {
|
||||
WgEndpoint: fmt.Sprintf("%s:%d", outBoundIP.String(), args.Port),
|
||||
PublicKey: pubKey.String(),
|
||||
WgHost: ipAddr.String() + "/128",
|
||||
Routes: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
n.Server.MeshManager.AddMeshNode(args.MeshId, node)
|
||||
@ -154,6 +156,7 @@ func (n *RobinIpc) GetMesh(meshId string, reply *ipc.GetMeshReply) error {
|
||||
WgHost: node.WgHost,
|
||||
Failed: mesh.HasFailed(node.HostEndpoint),
|
||||
Timestamp: node.Timestamp,
|
||||
Routes: lib.MapKeys(node.Routes),
|
||||
}
|
||||
|
||||
nodes[i] = node
|
||||
|
47
pkg/route/route.go
Normal file
47
pkg/route/route.go
Normal file
@ -0,0 +1,47 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os/exec"
|
||||
|
||||
logging "github.com/tim-beatham/wgmesh/pkg/log"
|
||||
)
|
||||
|
||||
type RouteInstaller interface {
|
||||
InstallRoutes(devName string, routes ...*net.IPNet) error
|
||||
}
|
||||
|
||||
type RouteInstallerImpl struct{}
|
||||
|
||||
// InstallRoutes: installs a route into the routing table
|
||||
func (r *RouteInstallerImpl) InstallRoutes(devName string, routes ...*net.IPNet) error {
|
||||
for _, route := range routes {
|
||||
err := r.installRoute(devName, route)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// installRoute: installs a route into the linux table
|
||||
func (r *RouteInstallerImpl) installRoute(devName string, route *net.IPNet) error {
|
||||
// TODO: Find a library that automates this
|
||||
cmd := exec.Command("/usr/bin/ip", "-6", "route", "add", route.String(), "dev", devName)
|
||||
|
||||
logging.Log.WriteInfof("%s %s", route.String(), devName)
|
||||
|
||||
if msg, err := cmd.CombinedOutput(); err != nil {
|
||||
logging.Log.WriteErrorf(err.Error())
|
||||
logging.Log.WriteErrorf(string(msg))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewRouteInstaller() RouteInstaller {
|
||||
return &RouteInstallerImpl{}
|
||||
}
|
Loading…
Reference in New Issue
Block a user