From 330fa74ef4e2dc50dac731adc0ffaacfdbf6be8d Mon Sep 17 00:00:00 2001 From: Tim Beatham Date: Mon, 20 Nov 2023 15:22:32 +0000 Subject: [PATCH 1/2] IPv6 What 8 Words what 8 words for ipv6 started --- cmd/api/main.go | 2 +- pkg/api/types.go | 4 ++-- pkg/conf/conf.go | 2 ++ pkg/what8words/what8words.go | 2 ++ 4 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 pkg/what8words/what8words.go diff --git a/cmd/api/main.go b/cmd/api/main.go index 76fcab1..b4e757d 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -13,5 +13,5 @@ func main() { log.Fatal(err.Error()) } - apiServer.Run(":40000") + apiServer.Run(":8080") } diff --git a/pkg/api/types.go b/pkg/api/types.go index 11d26a2..6eb4bf9 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -18,11 +18,11 @@ type SmegMesh struct { } type CreateMeshRequest struct { - WgPort int `json:"port" binding:"gte=1024,lt=65535"` + WgPort int `json:"port" binding:"omitempty,gte=1024,lt=65535"` } type JoinMeshRequest struct { - WgPort int `json:"port" binding:"gte=1024,lt=65535"` + WgPort int `json:"port" binding:"omitempty,gte=1024,lt=65535"` Bootstrap string `json:"bootstrap" binding:"required"` MeshId string `json:"meshid" binding:"required"` } diff --git a/pkg/conf/conf.go b/pkg/conf/conf.go index ad68721..ae07339 100644 --- a/pkg/conf/conf.go +++ b/pkg/conf/conf.go @@ -53,6 +53,8 @@ type WgMeshConfiguration struct { Profile bool `yaml:"profile"` // StubWg whether or not to stub the WireGuard types StubWg bool `yaml:"stubWg"` + // What8Words file path for the what 8 words word list. + What8Words string `yaml:"what8Words"` } func ValidateConfiguration(c *WgMeshConfiguration) error { diff --git a/pkg/what8words/what8words.go b/pkg/what8words/what8words.go new file mode 100644 index 0000000..355f98d --- /dev/null +++ b/pkg/what8words/what8words.go @@ -0,0 +1,2 @@ +// Package to convert an IPV6 addres into 8 words +package what8words From 95f4495b0b82bfb09124092fab3bcfa70646f3cb Mon Sep 17 00:00:00 2001 From: Tim Beatham Date: Mon, 20 Nov 2023 18:07:52 +0000 Subject: [PATCH 2/2] 21-phonetic-words-ipv6 Simple what 8 words implementation --- cmd/api/main.go | 4 +- pkg/api/apiserver.go | 50 ++++++++++++++++---- pkg/api/types.go | 11 ++++- pkg/what8words/what8words.go | 90 ++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 10 deletions(-) diff --git a/cmd/api/main.go b/cmd/api/main.go index b4e757d..19a2c0f 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -7,7 +7,9 @@ import ( ) func main() { - apiServer, err := api.NewSmegServer() + apiServer, err := api.NewSmegServer(api.ApiServerConf{ + WordsFile: "./cmd/api/words.txt", + }) if err != nil { log.Fatal(err.Error()) diff --git a/pkg/api/apiserver.go b/pkg/api/apiserver.go index 655543f..cd132a3 100644 --- a/pkg/api/apiserver.go +++ b/pkg/api/apiserver.go @@ -10,6 +10,7 @@ import ( "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/what8words" ) const SockAddr = "/tmp/wgmesh_ipc.sock" @@ -22,33 +23,59 @@ type ApiServer interface { type SmegServer struct { router *gin.Engine client *ipcRpc.Client + words *what8words.What8Words } -func meshNodeToAPIMeshNode(meshNode ctrlserver.MeshNode) *SmegNode { +func (s *SmegServer) routeToApiRoute(meshNode ctrlserver.MeshNode) []Route { + routes := make([]Route, len(meshNode.Routes)) + + for index, route := range meshNode.Routes { + word, err := s.words.Convert(route) + + if err != nil { + fmt.Println(err.Error()) + } + + routes[index] = Route{ + Prefix: route, + RouteId: word, + } + } + + return routes +} + +func (s *SmegServer) meshNodeToAPIMeshNode(meshNode ctrlserver.MeshNode) *SmegNode { if meshNode.Routes == nil { meshNode.Routes = make([]string, 0) } + alias := meshNode.Alias + + if alias == "" { + alias, _ = s.words.ConvertIdentifier(meshNode.WgHost) + } + return &SmegNode{ WgHost: meshNode.WgHost, WgEndpoint: meshNode.WgEndpoint, Endpoint: meshNode.HostEndpoint, Timestamp: int(meshNode.Timestamp), Description: meshNode.Description, - Routes: meshNode.Routes, + Routes: s.routeToApiRoute(meshNode), PublicKey: meshNode.PublicKey, - Alias: meshNode.Alias, + Alias: alias, Services: meshNode.Services, } } -func meshToAPIMesh(meshId string, nodes []ctrlserver.MeshNode) SmegMesh { +func (s *SmegServer) 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) + smegMesh.Nodes[node.WgHost] = *s.meshNodeToAPIMeshNode(node) } return smegMesh @@ -138,7 +165,7 @@ func (s *SmegServer) GetMesh(c *gin.Context) { return } - mesh := meshToAPIMesh(meshidParam, getMeshReply.Nodes) + mesh := s.meshToAPIMesh(meshidParam, getMeshReply.Nodes) c.JSON(http.StatusOK, mesh) } @@ -167,7 +194,7 @@ func (s *SmegServer) GetMeshes(c *gin.Context) { return } - meshes = append(meshes, meshToAPIMesh(mesh, getMeshReply.Nodes)) + meshes = append(meshes, s.meshToAPIMesh(mesh, getMeshReply.Nodes)) } c.JSON(http.StatusOK, meshes) @@ -178,13 +205,19 @@ func (s *SmegServer) Run(addr string) error { return s.router.Run(addr) } -func NewSmegServer() (ApiServer, error) { +func NewSmegServer(conf ApiServerConf) (ApiServer, error) { client, err := ipcRpc.DialHTTP("unix", SockAddr) if err != nil { return nil, err } + words, err := what8words.NewWhat8Words(conf.WordsFile) + + if err != nil { + return nil, err + } + router := gin.Default() router.Use(gin.LoggerWithConfig(gin.LoggerConfig{ @@ -194,6 +227,7 @@ func NewSmegServer() (ApiServer, error) { smegServer := &SmegServer{ router: router, client: client, + words: words, } router.GET("/meshes", smegServer.GetMeshes) diff --git a/pkg/api/types.go b/pkg/api/types.go index 6eb4bf9..6439814 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -1,5 +1,10 @@ package api +type Route struct { + RouteId string `json:"routeId"` + Prefix string `json:"prefix"` +} + type SmegNode struct { Alias string `json:"alias"` WgHost string `json:"wgHost"` @@ -8,7 +13,7 @@ type SmegNode struct { Timestamp int `json:"timestamp"` Description string `json:"description"` PublicKey string `json:"publicKey"` - Routes []string `json:"routes"` + Routes []Route `json:"routes"` Services map[string]string `json:"services"` } @@ -26,3 +31,7 @@ type JoinMeshRequest struct { Bootstrap string `json:"bootstrap" binding:"required"` MeshId string `json:"meshid" binding:"required"` } + +type ApiServerConf struct { + WordsFile string +} diff --git a/pkg/what8words/what8words.go b/pkg/what8words/what8words.go index 355f98d..a20553b 100644 --- a/pkg/what8words/what8words.go +++ b/pkg/what8words/what8words.go @@ -1,2 +1,92 @@ // Package to convert an IPV6 addres into 8 words package what8words + +import ( + "bufio" + "bytes" + "fmt" + "net" + "os" + "strings" +) + +type What8Words struct { + words []string +} + +// Convert implements What8Words. +func (w *What8Words) Convert(ipStr string) (string, error) { + ip, ipNet, err := net.ParseCIDR(ipStr) + + if err != nil { + return "", err + } + + ip16 := ip.To16() + + if ip16 == nil { + return "", fmt.Errorf("cannot convert ip to 16 representation") + } + + representation := make([]string, 7) + + for i := 2; i <= net.IPv6len-2; i += 2 { + word1 := w.words[ip16[i]] + word2 := w.words[ip16[i+1]] + + representation[i/2-1] = fmt.Sprintf("%s-%s", word1, word2) + } + + prefixSize, _ := ipNet.Mask.Size() + return strings.Join(representation[:prefixSize/16-1], "."), nil +} + +// Convert implements What8Words. +func (w *What8Words) ConvertIdentifier(ipStr string) (string, error) { + ip, err := w.Convert(ipStr) + + if err != nil { + return "", err + } + + constituents := strings.Split(ip, ".") + + return strings.Join(constituents[3:], "."), nil +} +func NewWhat8Words(pathToWords string) (*What8Words, error) { + words, err := ReadWords(pathToWords) + + if err != nil { + return nil, err + } + + return &What8Words{words: words}, nil +} + +// ReadWords reads the what 8 words txt file +func ReadWords(wordFile string) ([]string, error) { + f, err := os.ReadFile(wordFile) + + if err != nil { + return nil, err + } + + words := make([]string, 257) + + reader := bufio.NewScanner(bytes.NewReader(f)) + + counter := 0 + + for reader.Scan() && counter <= len(words) { + text := reader.Text() + + words[counter] = text + counter++ + + if reader.Err() != nil { + return nil, reader.Err() + } + } + + return words, nil +}