300 lines
8.2 KiB
Go
300 lines
8.2 KiB
Go
/* SPDX-License-Identifier: MIT
|
|
*
|
|
* Copyright (C) 2017-2021 Kusakabe Si. All Rights Reserved.
|
|
*/
|
|
|
|
package gencfg
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"math"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/KusakabeSi/EtherGuard-VPN/conn"
|
|
"github.com/KusakabeSi/EtherGuard-VPN/device"
|
|
"github.com/KusakabeSi/EtherGuard-VPN/mtypes"
|
|
"github.com/KusakabeSi/EtherGuard-VPN/tap"
|
|
yaml "gopkg.in/yaml.v2"
|
|
)
|
|
|
|
var gencfg_reader *bufio.Reader
|
|
|
|
func readFLn(promptF string, checkFn func(string) error, defaultAns func() string, args ...interface{}) string {
|
|
defaultans := defaultAns()
|
|
if defaultans != "" {
|
|
fmt.Printf(promptF+" ("+defaultans+") :", args...)
|
|
} else {
|
|
fmt.Printf(promptF+" :", args...)
|
|
}
|
|
text, err := gencfg_reader.ReadString('\n')
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
text = strings.Replace(text, "\n", "", -1)
|
|
if text == "" {
|
|
text = defaultans
|
|
}
|
|
if err := checkFn(text); err != nil {
|
|
fmt.Println(err)
|
|
return readFLn(promptF, checkFn, defaultAns, args...)
|
|
}
|
|
return text
|
|
}
|
|
|
|
func ParseIDs(s string) ([]int, int, int, error) {
|
|
ret := make([]int, 0)
|
|
if len(s) <= 3 {
|
|
return ret, 0, 0, fmt.Errorf("Parse Error: %v", s)
|
|
}
|
|
if s[0] != '[' {
|
|
return ret, 0, 0, fmt.Errorf("Parse Error: %v", s)
|
|
}
|
|
if s[len(s)-1] != ']' {
|
|
return ret, 0, 0, fmt.Errorf("Parse Error: %v", s)
|
|
}
|
|
s = s[1 : len(s)-1]
|
|
as := strings.Split(s, ",")
|
|
min := math.MaxUint16
|
|
max := 0
|
|
for i, es := range as {
|
|
if strings.Contains(es, "~") {
|
|
esl := strings.SplitN(es, "~", 2)
|
|
si, err := strconv.ParseInt(esl[0], 10, 16)
|
|
if err != nil {
|
|
return ret, min, max, err
|
|
}
|
|
ei, err := strconv.ParseInt(esl[1], 10, 16)
|
|
if err != nil {
|
|
return ret, min, max, err
|
|
}
|
|
if si >= ei {
|
|
return ret, min, max, fmt.Errorf("end %v must > start %v", ei, si)
|
|
}
|
|
if int(si) < 0 {
|
|
return ret, min, max, fmt.Errorf("node ID < 0 at element %v", i)
|
|
}
|
|
if min > int(si) {
|
|
min = int(si)
|
|
}
|
|
if int(si) < max {
|
|
return ret, min, max, fmt.Errorf("list out of order at the %vth element: %v", i, es)
|
|
} else if int(si) == max {
|
|
return ret, min, max, fmt.Errorf("duplicate id in the %vth element: %v", i, es)
|
|
}
|
|
max = int(ei)
|
|
for ; si <= ei; si++ {
|
|
ret = append(ret, int(si))
|
|
}
|
|
} else {
|
|
si, err := strconv.ParseInt(es, 10, 16)
|
|
if err != nil {
|
|
return ret, min, max, err
|
|
}
|
|
if int(si) < max {
|
|
return ret, min, max, fmt.Errorf("List out of order at the %vth element!", i)
|
|
} else if int(si) == max {
|
|
return ret, min, max, fmt.Errorf("duplicate id in the %vth element", i)
|
|
}
|
|
if min > int(si) {
|
|
min = int(si)
|
|
}
|
|
max = int(si)
|
|
ret = append(ret, int(si))
|
|
}
|
|
}
|
|
return ret, min, max, nil
|
|
}
|
|
|
|
func printExampleSMCfg() {
|
|
tconfig := SMCfg{}
|
|
toprint, _ := yaml.Marshal(tconfig)
|
|
fmt.Print(string(toprint))
|
|
}
|
|
|
|
func GenSuperCfg(SMCinfigPath string, printExample bool) (err error) {
|
|
SMCfg := SMCfg{}
|
|
if printExample {
|
|
printExampleSMCfg()
|
|
return
|
|
}
|
|
err = mtypes.ReadYaml(SMCinfigPath, &SMCfg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
os.Chdir(filepath.Dir(SMCinfigPath))
|
|
|
|
err = os.MkdirAll(SMCfg.ConfigOutputDir, 0o700)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var fileWriter bulkFileWriter
|
|
fileWriter.files = make(map[string]fileWriterfile)
|
|
fileWriter.ow = SMCfg.ConfigOutputDirOW
|
|
|
|
if SMCfg.SuperConfigTemplate != "" {
|
|
var sconfig mtypes.SuperConfig
|
|
err = mtypes.ReadYaml(SMCfg.SuperConfigTemplate, &sconfig)
|
|
if err != nil {
|
|
fmt.Printf("Error read config: %v\t%v\n", SMCfg.SuperConfigTemplate, err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
if SMCfg.EdgeConfigTemplate != "" {
|
|
var econfig mtypes.EdgeConfig
|
|
err = mtypes.ReadYaml(SMCfg.EdgeConfigTemplate, &econfig)
|
|
if err != nil {
|
|
fmt.Printf("Error read config: %v\t%v\n", SMCfg.EdgeConfigTemplate, err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
sconfig, _ := GetExampleSuperConf(SMCfg.SuperConfigTemplate, false)
|
|
|
|
if len(SMCfg.NetworkName) > 10 {
|
|
return fmt.Errorf("Name too long")
|
|
}
|
|
allowed := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-"
|
|
for _, c := range []byte(SMCfg.NetworkName) {
|
|
if strings.Contains(allowed, string(c)) == false {
|
|
return fmt.Errorf("Name can only contain %v", allowed)
|
|
}
|
|
}
|
|
|
|
ListenPort := fmt.Sprintf("%v", SMCfg.Supernode.ListenPort)
|
|
|
|
API_Prefix := SMCfg.Supernode.EdgeAPI_Prefix
|
|
EndpointV4 := SMCfg.Supernode.EndpointV4
|
|
if EndpointV4 != "" {
|
|
_, _, err = conn.LookupIP(EndpointV4+":"+ListenPort, conn.EnabledAf4, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
}
|
|
|
|
EndpointV6 := SMCfg.Supernode.EndpointV6
|
|
if EndpointV6 != "" {
|
|
if strings.Contains(EndpointV6, ":") && (EndpointV6[0] != '[' || EndpointV6[len(EndpointV6)-1] != ']') {
|
|
return fmt.Errorf("Invalid IPv6 format, please use [%v] instead", EndpointV6)
|
|
}
|
|
_, _, err = conn.LookupIP(EndpointV6+":"+ListenPort, conn.EnabledAf6, 0)
|
|
if err != nil {
|
|
return
|
|
}
|
|
} else if EndpointV4 == "" {
|
|
return fmt.Errorf("Muse provide at lease v4 v6 address")
|
|
}
|
|
|
|
EndpointEdgeAPIUrl := SMCfg.Supernode.Endpoint_EdgeAPI
|
|
|
|
sconfig.NodeName = SMCfg.NetworkName + "SN"
|
|
sconfig.API_Prefix = API_Prefix
|
|
sconfig.ListenPort, _ = strconv.Atoi(ListenPort)
|
|
sconfig.ListenPort_EdgeAPI = ListenPort
|
|
sconfig.ListenPort_ManageAPI = ListenPort
|
|
sconfig.EdgeTemplate = SMCfg.EdgeConfigTemplate
|
|
|
|
NodeIDs, _, ModeIDmax, err := ParseIDs(SMCfg.EdgeNode.NodeIDs)
|
|
if err != nil {
|
|
return
|
|
}
|
|
MacPrefix := SMCfg.EdgeNode.MacPrefix
|
|
|
|
if MacPrefix != "" {
|
|
_, err = tap.GetMacAddr(MacPrefix, uint32(ModeIDmax))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
pbyte := mtypes.RandomBytes(4, []byte{0xaa, 0xbb, 0xcc, 0xdd})
|
|
pbyte[0] &^= 0b00000001
|
|
pbyte[0] |= 0b00000010
|
|
MacPrefix = fmt.Sprintf("%02X:%02X:%02X:%02X", pbyte[0], pbyte[1], pbyte[2], pbyte[3])
|
|
}
|
|
|
|
IPv4Block := SMCfg.EdgeNode.IPv4Range
|
|
if IPv4Block != "" {
|
|
_, _, err = tap.GetIP(4, IPv4Block, uint32(ModeIDmax))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
IPv6Block := SMCfg.EdgeNode.IPv6Range
|
|
if IPv6Block != "" {
|
|
_, _, err = tap.GetIP(6, IPv6Block, uint32(ModeIDmax))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
IPv6LLBlock := SMCfg.EdgeNode.IPv6LLRange
|
|
if IPv6LLBlock != "" {
|
|
_, _, err = tap.GetIP(6, IPv6LLBlock, uint32(ModeIDmax))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
SuperPeerInfo := make([]mtypes.SuperPeerInfo, 0, ModeIDmax)
|
|
PrivKeyS4, PubKeyS4 := device.RandomKeyPair()
|
|
PrivKeyS6, PubKeyS6 := device.RandomKeyPair()
|
|
sconfig.PrivKeyV4 = PrivKeyS4.ToString()
|
|
sconfig.PrivKeyV6 = PrivKeyS6.ToString()
|
|
allec := make(map[mtypes.Vertex]mtypes.EdgeConfig)
|
|
peerceconf, _ := GetExampleEdgeConf(sconfig.EdgeTemplate, false)
|
|
for _, ii := range NodeIDs {
|
|
i := mtypes.Vertex(ii)
|
|
PSKeyE := device.RandomPSK()
|
|
PrivKeyE, PubKeyE := device.RandomKeyPair()
|
|
idstr := fmt.Sprintf("%0"+strconv.Itoa(len(strconv.Itoa(ModeIDmax)))+"d", i)
|
|
|
|
allec[i] = peerceconf
|
|
if EndpointV4 != "" {
|
|
peerceconf.DynamicRoute.SuperNode.EndpointV4 = EndpointV4 + ":" + ListenPort
|
|
}
|
|
if EndpointV6 != "" {
|
|
peerceconf.DynamicRoute.SuperNode.EndpointV6 = EndpointV6 + ":" + ListenPort
|
|
}
|
|
peerceconf.DynamicRoute.SuperNode.EndpointEdgeAPIUrl = EndpointEdgeAPIUrl
|
|
peerceconf.NodeName = SMCfg.NetworkName
|
|
peerceconf.Interface.Name = SMCfg.NetworkName
|
|
if SMCfg.NetworkIFNameID {
|
|
peerceconf.NodeName += idstr
|
|
peerceconf.Interface.Name += idstr
|
|
}
|
|
peerceconf.Interface.MacAddrPrefix = MacPrefix
|
|
peerceconf.Interface.IPv4CIDR = IPv4Block
|
|
peerceconf.Interface.IPv6CIDR = IPv6Block
|
|
peerceconf.Interface.IPv6LLPrefix = IPv6LLBlock
|
|
|
|
peerceconf.NodeID = i
|
|
peerceconf.DynamicRoute.SuperNode.PubKeyV4 = PubKeyS4.ToString()
|
|
peerceconf.DynamicRoute.SuperNode.PubKeyV6 = PubKeyS6.ToString()
|
|
peerceconf.DynamicRoute.SuperNode.PSKey = PSKeyE.ToString()
|
|
peerceconf.PrivKey = PrivKeyE.ToString()
|
|
|
|
SuperPeerInfo = append(SuperPeerInfo, mtypes.SuperPeerInfo{
|
|
NodeID: i,
|
|
Name: SMCfg.NetworkName + idstr,
|
|
PubKey: PubKeyE.ToString(),
|
|
PSKey: PSKeyE.ToString(),
|
|
AdditionalCost: peerceconf.DynamicRoute.AdditionalCost,
|
|
SkipLocalIP: peerceconf.DynamicRoute.SuperNode.SkipLocalIP,
|
|
})
|
|
mtypesBytes, _ := yaml.Marshal(peerceconf)
|
|
fileWriter.WriteFile(filepath.Join(SMCfg.ConfigOutputDir, SMCfg.NetworkName+"_edge"+idstr+".yaml"), mtypesBytes, 0o600)
|
|
}
|
|
sconfig.Peers = SuperPeerInfo
|
|
mtypesBytes, _ := yaml.Marshal(sconfig)
|
|
fileWriter.WriteFile(filepath.Join(SMCfg.ConfigOutputDir, SMCfg.NetworkName+"_super.yaml"), mtypesBytes, 0o600)
|
|
err = fileWriter.Commit()
|
|
return err
|
|
}
|