mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2025-04-29 06:35:07 +03:00
Refactor node setup, config, defaults
This commit is contained in:
parent
559e31c502
commit
8be919abba
13 changed files with 395 additions and 331 deletions
|
@ -1,235 +1,36 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
|
||||
"github.com/gologme/log"
|
||||
gsyslog "github.com/hashicorp/go-syslog"
|
||||
"github.com/hjson/hjson-go"
|
||||
"github.com/kardianos/minwinsvc"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/admin"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/setup"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/tuntap"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/version"
|
||||
)
|
||||
|
||||
type node struct {
|
||||
core core.Core
|
||||
config *config.NodeConfig
|
||||
tuntap *tuntap.TunAdapter
|
||||
multicast *multicast.Multicast
|
||||
admin *admin.AdminSocket
|
||||
}
|
||||
|
||||
func readConfig(log *log.Logger, useconf bool, useconffile string, normaliseconf bool) *config.NodeConfig {
|
||||
// Use a configuration file. If -useconf, the configuration will be read
|
||||
// from stdin. If -useconffile, the configuration will be read from the
|
||||
// filesystem.
|
||||
var conf []byte
|
||||
var err error
|
||||
if useconffile != "" {
|
||||
// Read the file from the filesystem
|
||||
conf, err = ioutil.ReadFile(useconffile)
|
||||
} else {
|
||||
// Read the file from stdin.
|
||||
conf, err = ioutil.ReadAll(os.Stdin)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// If there's a byte order mark - which Windows 10 is now incredibly fond of
|
||||
// throwing everywhere when it's converting things into UTF-16 for the hell
|
||||
// of it - remove it and decode back down into UTF-8. This is necessary
|
||||
// because hjson doesn't know what to do with UTF-16 and will panic
|
||||
if bytes.Equal(conf[0:2], []byte{0xFF, 0xFE}) ||
|
||||
bytes.Equal(conf[0:2], []byte{0xFE, 0xFF}) {
|
||||
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
|
||||
decoder := utf.NewDecoder()
|
||||
conf, err = decoder.Bytes(conf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
// Generate a new configuration - this gives us a set of sane defaults -
|
||||
// then parse the configuration we loaded above on top of it. The effect
|
||||
// of this is that any configuration item that is missing from the provided
|
||||
// configuration will use a sane default.
|
||||
cfg := defaults.GenerateConfig()
|
||||
var dat map[string]interface{}
|
||||
if err := hjson.Unmarshal(conf, &dat); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Check if we have old field names
|
||||
if _, ok := dat["TunnelRouting"]; ok {
|
||||
log.Warnln("WARNING: Tunnel routing is no longer supported")
|
||||
}
|
||||
if old, ok := dat["SigningPrivateKey"]; ok {
|
||||
log.Warnln("WARNING: The \"SigningPrivateKey\" configuration option has been renamed to \"PrivateKey\"")
|
||||
if _, ok := dat["PrivateKey"]; !ok {
|
||||
if privstr, err := hex.DecodeString(old.(string)); err == nil {
|
||||
priv := ed25519.PrivateKey(privstr)
|
||||
pub := priv.Public().(ed25519.PublicKey)
|
||||
dat["PrivateKey"] = hex.EncodeToString(priv[:])
|
||||
dat["PublicKey"] = hex.EncodeToString(pub[:])
|
||||
} else {
|
||||
log.Warnln("WARNING: The \"SigningPrivateKey\" configuration option contains an invalid value and will be ignored")
|
||||
}
|
||||
}
|
||||
}
|
||||
if oldmc, ok := dat["MulticastInterfaces"]; ok {
|
||||
if oldmcvals, ok := oldmc.([]interface{}); ok {
|
||||
var newmc []config.MulticastInterfaceConfig
|
||||
for _, oldmcval := range oldmcvals {
|
||||
if str, ok := oldmcval.(string); ok {
|
||||
newmc = append(newmc, config.MulticastInterfaceConfig{
|
||||
Regex: str,
|
||||
Beacon: true,
|
||||
Listen: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
if newmc != nil {
|
||||
if oldport, ok := dat["LinkLocalTCPPort"]; ok {
|
||||
// numbers parse to float64 by default
|
||||
if port, ok := oldport.(float64); ok {
|
||||
for idx := range newmc {
|
||||
newmc[idx].Port = uint16(port)
|
||||
}
|
||||
}
|
||||
}
|
||||
dat["MulticastInterfaces"] = newmc
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sanitise the config
|
||||
confJson, err := json.Marshal(dat)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := json.Unmarshal(confJson, &cfg); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Overlay our newly mapped configuration onto the autoconf node config that
|
||||
// we generated above.
|
||||
if err = mapstructure.Decode(dat, &cfg); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Generates a new configuration and returns it in HJSON format. This is used
|
||||
// with -genconf.
|
||||
func doGenconf(isjson bool) string {
|
||||
cfg := defaults.GenerateConfig()
|
||||
var bs []byte
|
||||
var err error
|
||||
if isjson {
|
||||
bs, err = json.MarshalIndent(cfg, "", " ")
|
||||
} else {
|
||||
bs, err = hjson.Marshal(cfg)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(bs)
|
||||
}
|
||||
|
||||
func setLogLevel(loglevel string, logger *log.Logger) {
|
||||
levels := [...]string{"error", "warn", "info", "debug", "trace"}
|
||||
loglevel = strings.ToLower(loglevel)
|
||||
|
||||
contains := func() bool {
|
||||
for _, l := range levels {
|
||||
if l == loglevel {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if !contains() { // set default log level
|
||||
logger.Infoln("Loglevel parse failed. Set default level(info)")
|
||||
loglevel = "info"
|
||||
}
|
||||
|
||||
for _, l := range levels {
|
||||
logger.EnableLevel(l)
|
||||
if l == loglevel {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type yggArgs struct {
|
||||
genconf bool
|
||||
useconf bool
|
||||
normaliseconf bool
|
||||
confjson bool
|
||||
autoconf bool
|
||||
ver bool
|
||||
getaddr bool
|
||||
getsnet bool
|
||||
useconffile string
|
||||
logto string
|
||||
loglevel string
|
||||
}
|
||||
|
||||
func getArgs() yggArgs {
|
||||
genconf := flag.Bool("genconf", false, "print a new config to stdout")
|
||||
useconf := flag.Bool("useconf", false, "read HJSON/JSON config from stdin")
|
||||
useconffile := flag.String("useconffile", "", "read HJSON/JSON config from specified file path")
|
||||
normaliseconf := flag.Bool("normaliseconf", false, "use in combination with either -useconf or -useconffile, outputs your configuration normalised")
|
||||
confjson := flag.Bool("json", false, "print configuration from -genconf or -normaliseconf as JSON instead of HJSON")
|
||||
autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)")
|
||||
ver := flag.Bool("version", false, "prints the version of this build")
|
||||
logto := flag.String("logto", "stdout", "file path to log to, \"syslog\" or \"stdout\"")
|
||||
getaddr := flag.Bool("address", false, "returns the IPv6 address as derived from the supplied configuration")
|
||||
getsnet := flag.Bool("subnet", false, "returns the IPv6 subnet as derived from the supplied configuration")
|
||||
loglevel := flag.String("loglevel", "info", "loglevel to enable")
|
||||
flag.Parse()
|
||||
return yggArgs{
|
||||
genconf: *genconf,
|
||||
useconf: *useconf,
|
||||
useconffile: *useconffile,
|
||||
normaliseconf: *normaliseconf,
|
||||
confjson: *confjson,
|
||||
autoconf: *autoconf,
|
||||
ver: *ver,
|
||||
logto: *logto,
|
||||
getaddr: *getaddr,
|
||||
getsnet: *getsnet,
|
||||
loglevel: *loglevel,
|
||||
}
|
||||
}
|
||||
|
||||
// The main function is responsible for configuring and starting Yggdrasil.
|
||||
func run(args yggArgs, ctx context.Context, done chan struct{}) {
|
||||
defer close(done)
|
||||
func main() {
|
||||
args := setup.ParseArguments()
|
||||
|
||||
// Create a new logger that logs output to stdout.
|
||||
var logger *log.Logger
|
||||
switch args.logto {
|
||||
switch args.LogTo {
|
||||
case "stdout":
|
||||
logger = log.New(os.Stdout, "", log.Flags())
|
||||
case "syslog":
|
||||
|
@ -237,7 +38,7 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) {
|
|||
logger = log.New(syslogger, "", log.Flags())
|
||||
}
|
||||
default:
|
||||
if logfd, err := os.OpenFile(args.logto, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err == nil {
|
||||
if logfd, err := os.OpenFile(args.LogTo, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err == nil {
|
||||
logger = log.New(logfd, "", log.Flags())
|
||||
}
|
||||
}
|
||||
|
@ -246,33 +47,27 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) {
|
|||
logger.Warnln("Logging defaulting to stdout")
|
||||
}
|
||||
|
||||
if args.normaliseconf {
|
||||
setLogLevel("error", logger)
|
||||
} else {
|
||||
setLogLevel(args.loglevel, logger)
|
||||
}
|
||||
|
||||
var cfg *config.NodeConfig
|
||||
var err error
|
||||
switch {
|
||||
case args.ver:
|
||||
case args.Version:
|
||||
fmt.Println("Build name:", version.BuildName())
|
||||
fmt.Println("Build version:", version.BuildVersion())
|
||||
return
|
||||
case args.autoconf:
|
||||
case args.AutoConf:
|
||||
// Use an autoconf-generated config, this will give us random keys and
|
||||
// port numbers, and will use an automatically selected TUN/TAP interface.
|
||||
cfg = defaults.GenerateConfig()
|
||||
case args.useconffile != "" || args.useconf:
|
||||
cfg = config.GenerateConfig()
|
||||
case args.UseConfFile != "" || args.UseConf:
|
||||
// Read the configuration from either stdin or from the filesystem
|
||||
cfg = readConfig(logger, args.useconf, args.useconffile, args.normaliseconf)
|
||||
cfg = setup.ReadConfig(logger, args.UseConf, args.UseConfFile, args.NormaliseConf)
|
||||
// If the -normaliseconf option was specified then remarshal the above
|
||||
// configuration and print it back to stdout. This lets the user update
|
||||
// their configuration file with newly mapped names (like above) or to
|
||||
// convert from plain JSON to commented HJSON.
|
||||
if args.normaliseconf {
|
||||
if args.NormaliseConf {
|
||||
var bs []byte
|
||||
if args.confjson {
|
||||
if args.ConfJSON {
|
||||
bs, err = json.MarshalIndent(cfg, "", " ")
|
||||
} else {
|
||||
bs, err = hjson.Marshal(cfg)
|
||||
|
@ -283,9 +78,9 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) {
|
|||
fmt.Println(string(bs))
|
||||
return
|
||||
}
|
||||
case args.genconf:
|
||||
case args.GenConf:
|
||||
// Generate a new configuration and print it to stdout.
|
||||
fmt.Println(doGenconf(args.confjson))
|
||||
fmt.Println(config.GenerateConfigJSON(args.ConfJSON))
|
||||
return
|
||||
default:
|
||||
// No flags were provided, therefore print the list of flags to stdout.
|
||||
|
@ -297,6 +92,11 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) {
|
|||
if cfg == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Create a new standalone node
|
||||
n := setup.NewNode(cfg, logger)
|
||||
n.SetLogLevel(args.LogLevel)
|
||||
|
||||
// Have we been asked for the node address yet? If so, print it and then stop.
|
||||
getNodeKey := func() ed25519.PublicKey {
|
||||
if pubkey, err := hex.DecodeString(cfg.PrivateKey); err == nil {
|
||||
|
@ -305,14 +105,14 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) {
|
|||
return nil
|
||||
}
|
||||
switch {
|
||||
case args.getaddr:
|
||||
case args.GetAddr:
|
||||
if key := getNodeKey(); key != nil {
|
||||
addr := address.AddrForKey(key)
|
||||
ip := net.IP(addr[:])
|
||||
fmt.Println(ip.String())
|
||||
}
|
||||
return
|
||||
case args.getsnet:
|
||||
case args.GetSubnet:
|
||||
if key := getNodeKey(); key != nil {
|
||||
snet := address.SubnetForKey(key)
|
||||
ipnet := net.IPNet{
|
||||
|
@ -325,84 +125,39 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) {
|
|||
default:
|
||||
}
|
||||
|
||||
// Setup the Yggdrasil node itself. The node{} type includes a Core, so we
|
||||
// don't need to create this manually.
|
||||
n := node{config: cfg}
|
||||
// Now start Yggdrasil - this starts the DHT, router, switch and other core
|
||||
// components needed for Yggdrasil to operate
|
||||
if err = n.core.Start(cfg, logger); err != nil {
|
||||
logger.Errorln("An error occurred during startup")
|
||||
panic(err)
|
||||
if err = n.Run(args); err != nil {
|
||||
logger.Fatalln(err)
|
||||
}
|
||||
// Register the session firewall gatekeeper function
|
||||
// Allocate our modules
|
||||
n.admin = &admin.AdminSocket{}
|
||||
n.multicast = &multicast.Multicast{}
|
||||
n.tuntap = &tuntap.TunAdapter{}
|
||||
// Start the admin socket
|
||||
if err := n.admin.Init(&n.core, cfg, logger, nil); err != nil {
|
||||
logger.Errorln("An error occurred initialising admin socket:", err)
|
||||
} else if err := n.admin.Start(); err != nil {
|
||||
logger.Errorln("An error occurred starting admin socket:", err)
|
||||
}
|
||||
n.admin.SetupAdminHandlers(n.admin)
|
||||
// Start the multicast interface
|
||||
if err := n.multicast.Init(&n.core, cfg, logger, nil); err != nil {
|
||||
logger.Errorln("An error occurred initialising multicast:", err)
|
||||
} else if err := n.multicast.Start(); err != nil {
|
||||
logger.Errorln("An error occurred starting multicast:", err)
|
||||
}
|
||||
n.multicast.SetupAdminHandlers(n.admin)
|
||||
|
||||
// Start the TUN/TAP interface
|
||||
rwc := ipv6rwc.NewReadWriteCloser(&n.core)
|
||||
if err := n.tuntap.Init(rwc, cfg, logger, nil); err != nil {
|
||||
tuntap := &tuntap.TunAdapter{}
|
||||
rwc := ipv6rwc.NewReadWriteCloser(&n.Core)
|
||||
if err := tuntap.Init(rwc, cfg, logger, nil); err != nil {
|
||||
logger.Errorln("An error occurred initialising TUN/TAP:", err)
|
||||
} else if err := n.tuntap.Start(); err != nil {
|
||||
} else if err := tuntap.Start(); err != nil {
|
||||
logger.Errorln("An error occurred starting TUN/TAP:", err)
|
||||
}
|
||||
n.tuntap.SetupAdminHandlers(n.admin)
|
||||
tuntap.SetupAdminHandlers(n.Admin())
|
||||
|
||||
// Make some nice output that tells us what our IPv6 address and subnet are.
|
||||
// This is just logged to stdout for the user.
|
||||
address := n.core.Address()
|
||||
subnet := n.core.Subnet()
|
||||
public := n.core.GetSelf().Key
|
||||
address := n.Address()
|
||||
subnet := n.Subnet()
|
||||
public := n.GetSelf().Key
|
||||
logger.Infof("Your public key is %s", hex.EncodeToString(public[:]))
|
||||
logger.Infof("Your IPv6 address is %s", address.String())
|
||||
logger.Infof("Your IPv6 subnet is %s", subnet.String())
|
||||
// Catch interrupts from the operating system to exit gracefully.
|
||||
<-ctx.Done()
|
||||
// Capture the service being stopped on Windows.
|
||||
minwinsvc.SetOnExit(n.shutdown)
|
||||
n.shutdown()
|
||||
}
|
||||
|
||||
func (n *node) shutdown() {
|
||||
_ = n.admin.Stop()
|
||||
_ = n.multicast.Stop()
|
||||
_ = n.tuntap.Stop()
|
||||
n.core.Stop()
|
||||
}
|
||||
|
||||
func main() {
|
||||
args := getArgs()
|
||||
hup := make(chan os.Signal, 1)
|
||||
//signal.Notify(hup, os.Interrupt, syscall.SIGHUP)
|
||||
term := make(chan os.Signal, 1)
|
||||
signal.Notify(term, os.Interrupt, syscall.SIGTERM)
|
||||
for {
|
||||
done := make(chan struct{})
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go run(args, ctx, done)
|
||||
select {
|
||||
case <-hup:
|
||||
cancel()
|
||||
<-done
|
||||
case <-term:
|
||||
cancel()
|
||||
<-done
|
||||
return
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case <-n.Done():
|
||||
case <-term:
|
||||
}
|
||||
|
||||
n.Close()
|
||||
_ = tuntap.Stop()
|
||||
}
|
||||
|
|
3
go.mod
3
go.mod
|
@ -9,6 +9,7 @@ require (
|
|||
github.com/cheggaaa/pb/v3 v3.0.8
|
||||
github.com/fatih/color v1.12.0 // indirect
|
||||
github.com/gologme/log v1.2.0
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/hashicorp/go-syslog v1.0.0
|
||||
github.com/hjson/hjson-go v3.1.0+incompatible
|
||||
github.com/kardianos/minwinsvc v1.0.0
|
||||
|
@ -21,6 +22,8 @@ require (
|
|||
golang.org/x/net v0.0.0-20211101193420-4a448f8816b3
|
||||
golang.org/x/sys v0.0.0-20211102192858-4dd72447c267
|
||||
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b
|
||||
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a
|
||||
golang.zx2c4.com/wireguard/windows v0.4.12
|
||||
inet.af/netstack v0.0.0-20211120045802-8aa80cf23d3c
|
||||
)
|
||||
|
|
6
go.sum
6
go.sum
|
@ -13,6 +13,8 @@ github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
|||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/gologme/log v1.2.0 h1:Ya5Ip/KD6FX7uH0S31QO87nCCSucKtF44TLbTtO7V4c=
|
||||
github.com/gologme/log v1.2.0/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U=
|
||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hjson/hjson-go v3.1.0+incompatible h1:DY/9yE8ey8Zv22bY+mHV1uk2yRy0h8tKhZ77hEdi0Aw=
|
||||
|
@ -89,6 +91,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b h1:NXqSWXSRUSCaFuvitrWtU169I3876zRTalMRbfd6LL0=
|
||||
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
||||
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M=
|
||||
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
|
@ -104,3 +108,5 @@ golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a h1:tTbyylK9/D3u/wE
|
|||
golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a/go.mod h1:id8Oh3eCCmpj9uVGWVjsUAl6UPX5ysMLzu6QxJU2UOU=
|
||||
golang.zx2c4.com/wireguard/windows v0.4.12 h1:CUmbdWKVNzTSsVb4yUAiEwL3KsabdJkEPdDjCHxBlhA=
|
||||
golang.zx2c4.com/wireguard/windows v0.4.12/go.mod h1:PW4y+d9oY83XU9rRwRwrJDwEMuhVjMxu2gfD1cfzS7w=
|
||||
inet.af/netstack v0.0.0-20211120045802-8aa80cf23d3c h1:nr31qYr+91rWD8klUkPx3eGTZzumCC414UJG1QRKZTc=
|
||||
inet.af/netstack v0.0.0-20211120045802-8aa80cf23d3c/go.mod h1:KOJdAzQzMLKzwFEdOOnrnSrLIhaFVB+NQoME/e5wllA=
|
||||
|
|
|
@ -19,7 +19,12 @@ package config
|
|||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"github.com/hjson/hjson-go"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||
)
|
||||
|
||||
// NodeConfig is the main configuration structure, containing configuration
|
||||
|
@ -59,3 +64,106 @@ func (cfg *NodeConfig) NewKeys() {
|
|||
cfg.PublicKey = hex.EncodeToString(spub[:])
|
||||
cfg.PrivateKey = hex.EncodeToString(spriv[:])
|
||||
}
|
||||
|
||||
// Generates default configuration and returns a pointer to the resulting
|
||||
// NodeConfig. This is used when outputting the -genconf parameter and also when
|
||||
// using -autoconf.
|
||||
func GenerateConfig() *NodeConfig {
|
||||
defaults := defaults.GetDefaults()
|
||||
cfg := &NodeConfig{}
|
||||
cfg.NewKeys()
|
||||
cfg.Listen = []string{}
|
||||
cfg.AdminListen = defaults.DefaultAdminListen
|
||||
cfg.Peers = []string{}
|
||||
cfg.InterfacePeers = map[string][]string{}
|
||||
cfg.AllowedPublicKeys = []string{}
|
||||
for _, regex := range defaults.DefaultMulticastInterfaces {
|
||||
cfg.MulticastInterfaces = append(cfg.MulticastInterfaces, MulticastInterfaceConfig{
|
||||
Regex: regex,
|
||||
Beacon: true,
|
||||
Listen: true,
|
||||
})
|
||||
}
|
||||
cfg.IfName = defaults.DefaultIfName
|
||||
cfg.IfMTU = defaults.DefaultIfMTU
|
||||
cfg.NodeInfoPrivacy = false
|
||||
return cfg
|
||||
}
|
||||
|
||||
func GenerateConfigJSON(isjson bool) []byte {
|
||||
// Generates a new configuration and returns it in HJSON or JSON format.
|
||||
cfg := GenerateConfig()
|
||||
var bs []byte
|
||||
var err error
|
||||
if isjson {
|
||||
bs, err = json.MarshalIndent(cfg, "", " ")
|
||||
} else {
|
||||
bs, err = hjson.Marshal(cfg)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func ReadConfig(conf []byte) *NodeConfig {
|
||||
// Generate a new configuration - this gives us a set of sane defaults -
|
||||
// then parse the configuration we loaded above on top of it. The effect
|
||||
// of this is that any configuration item that is missing from the provided
|
||||
// configuration will use a sane default.
|
||||
cfg := GenerateConfig()
|
||||
var dat map[string]interface{}
|
||||
if err := hjson.Unmarshal(conf, &dat); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Check if we have old field names
|
||||
if old, ok := dat["SigningPrivateKey"]; ok {
|
||||
if _, ok := dat["PrivateKey"]; !ok {
|
||||
if privstr, err := hex.DecodeString(old.(string)); err == nil {
|
||||
priv := ed25519.PrivateKey(privstr)
|
||||
pub := priv.Public().(ed25519.PublicKey)
|
||||
dat["PrivateKey"] = hex.EncodeToString(priv[:])
|
||||
dat["PublicKey"] = hex.EncodeToString(pub[:])
|
||||
}
|
||||
}
|
||||
}
|
||||
if oldmc, ok := dat["MulticastInterfaces"]; ok {
|
||||
if oldmcvals, ok := oldmc.([]interface{}); ok {
|
||||
var newmc []MulticastInterfaceConfig
|
||||
for _, oldmcval := range oldmcvals {
|
||||
if str, ok := oldmcval.(string); ok {
|
||||
newmc = append(newmc, MulticastInterfaceConfig{
|
||||
Regex: str,
|
||||
Beacon: true,
|
||||
Listen: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
if newmc != nil {
|
||||
if oldport, ok := dat["LinkLocalTCPPort"]; ok {
|
||||
// numbers parse to float64 by default
|
||||
if port, ok := oldport.(float64); ok {
|
||||
for idx := range newmc {
|
||||
newmc[idx].Port = uint16(port)
|
||||
}
|
||||
}
|
||||
}
|
||||
dat["MulticastInterfaces"] = newmc
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sanitise the config
|
||||
confJson, err := json.Marshal(dat)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := json.Unmarshal(confJson, &cfg); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Overlay our newly mapped configuration onto the autoconf node config that
|
||||
// we generated above.
|
||||
if err = mapstructure.Decode(dat, &cfg); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
|
|
@ -11,12 +11,11 @@ import (
|
|||
"github.com/gologme/log"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||
)
|
||||
|
||||
// GenerateConfig produces default configuration with suitable modifications for tests.
|
||||
func GenerateConfig() *config.NodeConfig {
|
||||
cfg := defaults.GenerateConfig()
|
||||
cfg := config.GenerateConfig()
|
||||
cfg.AdminListen = "none"
|
||||
cfg.Listen = []string{"tcp://127.0.0.1:0"}
|
||||
cfg.IfName = "none"
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package defaults
|
||||
|
||||
import "github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||
|
||||
type MulticastInterfaceConfig = config.MulticastInterfaceConfig
|
||||
type MulticastInterfaceConfig struct {
|
||||
Regex string
|
||||
Beacon bool
|
||||
Listen bool
|
||||
Port uint16
|
||||
}
|
||||
|
||||
// Defines which parameters are expected by default for configuration on a
|
||||
// specific platform. These values are populated in the relevant defaults_*.go
|
||||
|
@ -15,30 +18,10 @@ type platformDefaultParameters struct {
|
|||
DefaultConfigFile string
|
||||
|
||||
// Multicast interfaces
|
||||
DefaultMulticastInterfaces []MulticastInterfaceConfig
|
||||
DefaultMulticastInterfaces []string
|
||||
|
||||
// TUN/TAP
|
||||
MaximumIfMTU uint64
|
||||
DefaultIfMTU uint64
|
||||
DefaultIfName string
|
||||
}
|
||||
|
||||
// Generates default configuration and returns a pointer to the resulting
|
||||
// NodeConfig. This is used when outputting the -genconf parameter and also when
|
||||
// using -autoconf.
|
||||
func GenerateConfig() *config.NodeConfig {
|
||||
// Create a node configuration and populate it.
|
||||
cfg := new(config.NodeConfig)
|
||||
cfg.NewKeys()
|
||||
cfg.Listen = []string{}
|
||||
cfg.AdminListen = GetDefaults().DefaultAdminListen
|
||||
cfg.Peers = []string{}
|
||||
cfg.InterfacePeers = map[string][]string{}
|
||||
cfg.AllowedPublicKeys = []string{}
|
||||
cfg.MulticastInterfaces = GetDefaults().DefaultMulticastInterfaces
|
||||
cfg.IfName = GetDefaults().DefaultIfName
|
||||
cfg.IfMTU = GetDefaults().DefaultIfMTU
|
||||
cfg.NodeInfoPrivacy = false
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
|
|
@ -14,10 +14,7 @@ func GetDefaults() platformDefaultParameters {
|
|||
DefaultConfigFile: "/etc/yggdrasil.conf",
|
||||
|
||||
// Multicast interfaces
|
||||
DefaultMulticastInterfaces: []MulticastInterfaceConfig{
|
||||
{Regex: "en.*", Beacon: true, Listen: true},
|
||||
{Regex: "bridge.*", Beacon: true, Listen: true},
|
||||
},
|
||||
DefaultMulticastInterfaces: []string{"en.*", "bridge.*"},
|
||||
|
||||
// TUN/TAP
|
||||
MaximumIfMTU: 65535,
|
||||
|
|
|
@ -14,9 +14,7 @@ func GetDefaults() platformDefaultParameters {
|
|||
DefaultConfigFile: "/usr/local/etc/yggdrasil.conf",
|
||||
|
||||
// Multicast interfaces
|
||||
DefaultMulticastInterfaces: []MulticastInterfaceConfig{
|
||||
{Regex: ".*", Beacon: true, Listen: true},
|
||||
},
|
||||
DefaultMulticastInterfaces: []string{".*"},
|
||||
|
||||
// TUN/TAP
|
||||
MaximumIfMTU: 32767,
|
||||
|
|
|
@ -14,9 +14,7 @@ func GetDefaults() platformDefaultParameters {
|
|||
DefaultConfigFile: "/etc/yggdrasil.conf",
|
||||
|
||||
// Multicast interfaces
|
||||
DefaultMulticastInterfaces: []MulticastInterfaceConfig{
|
||||
{Regex: ".*", Beacon: true, Listen: true},
|
||||
},
|
||||
DefaultMulticastInterfaces: []string{".*"},
|
||||
|
||||
// TUN/TAP
|
||||
MaximumIfMTU: 65535,
|
||||
|
|
|
@ -14,9 +14,7 @@ func GetDefaults() platformDefaultParameters {
|
|||
DefaultConfigFile: "/etc/yggdrasil.conf",
|
||||
|
||||
// Multicast interfaces
|
||||
DefaultMulticastInterfaces: []MulticastInterfaceConfig{
|
||||
{Regex: ".*", Beacon: true, Listen: true},
|
||||
},
|
||||
DefaultMulticastInterfaces: []string{".*"},
|
||||
|
||||
// TUN/TAP
|
||||
MaximumIfMTU: 16384,
|
||||
|
|
|
@ -14,9 +14,7 @@ func GetDefaults() platformDefaultParameters {
|
|||
DefaultConfigFile: "/etc/yggdrasil.conf",
|
||||
|
||||
// Multicast interfaces
|
||||
DefaultMulticastInterfaces: []MulticastInterfaceConfig{
|
||||
{Regex: ".*", Beacon: true, Listen: true},
|
||||
},
|
||||
DefaultMulticastInterfaces: []string{".*"},
|
||||
|
||||
// TUN/TAP
|
||||
MaximumIfMTU: 65535,
|
||||
|
|
|
@ -14,9 +14,7 @@ func GetDefaults() platformDefaultParameters {
|
|||
DefaultConfigFile: "C:\\Program Files\\Yggdrasil\\yggdrasil.conf",
|
||||
|
||||
// Multicast interfaces
|
||||
DefaultMulticastInterfaces: []MulticastInterfaceConfig{
|
||||
{Regex: ".*", Beacon: true, Listen: true},
|
||||
},
|
||||
DefaultMulticastInterfaces: []string{".*"},
|
||||
|
||||
// TUN/TAP
|
||||
MaximumIfMTU: 65535,
|
||||
|
|
223
src/setup/setup.go
Normal file
223
src/setup/setup.go
Normal file
|
@ -0,0 +1,223 @@
|
|||
package setup
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gologme/log"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/admin"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
core.Core
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
logger *log.Logger
|
||||
config *config.NodeConfig
|
||||
multicast *multicast.Multicast
|
||||
admin *admin.AdminSocket
|
||||
}
|
||||
|
||||
func NewNode(cfg *config.NodeConfig, logger *log.Logger) *Node {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
return &Node{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
logger: logger,
|
||||
config: cfg,
|
||||
multicast: &multicast.Multicast{},
|
||||
admin: &admin.AdminSocket{},
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Node) Close() {
|
||||
n.cancel()
|
||||
_ = n.multicast.Stop()
|
||||
_ = n.admin.Stop()
|
||||
_ = n.Core.Close()
|
||||
}
|
||||
|
||||
func (n *Node) Done() <-chan struct{} {
|
||||
return n.ctx.Done()
|
||||
}
|
||||
|
||||
func (n *Node) Admin() *admin.AdminSocket {
|
||||
return n.admin
|
||||
}
|
||||
|
||||
// The main function is responsible for configuring and starting Yggdrasil.
|
||||
func (n *Node) Run(args Arguments) error {
|
||||
// Have we got a working configuration? If we don't then it probably means
|
||||
// that neither -autoconf, -useconf or -useconffile were set above. Stop
|
||||
// if we don't.
|
||||
if n.config == nil {
|
||||
return fmt.Errorf("no configuration supplied")
|
||||
}
|
||||
// Have we been asked for the node address yet? If so, print it and then stop.
|
||||
getNodeKey := func() ed25519.PublicKey {
|
||||
if pubkey, err := hex.DecodeString(n.config.PrivateKey); err == nil {
|
||||
return ed25519.PrivateKey(pubkey).Public().(ed25519.PublicKey)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
switch {
|
||||
case args.GetAddr:
|
||||
if key := getNodeKey(); key != nil {
|
||||
addr := address.AddrForKey(key)
|
||||
ip := net.IP(addr[:])
|
||||
fmt.Println(ip.String())
|
||||
}
|
||||
return nil
|
||||
case args.GetSubnet:
|
||||
if key := getNodeKey(); key != nil {
|
||||
snet := address.SubnetForKey(key)
|
||||
ipnet := net.IPNet{
|
||||
IP: append(snet[:], 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
Mask: net.CIDRMask(len(snet)*8, 128),
|
||||
}
|
||||
fmt.Println(ipnet.String())
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
// Now start Yggdrasil - this starts the DHT, router, switch and other core
|
||||
// components needed for Yggdrasil to operate
|
||||
if err := n.Core.Start(n.config, n.logger); err != nil {
|
||||
return fmt.Errorf("n.core.Start: %w", err)
|
||||
}
|
||||
// Register the session firewall gatekeeper function
|
||||
// Allocate our modules
|
||||
|
||||
// Start the admin socket
|
||||
n.admin = &admin.AdminSocket{}
|
||||
if err := n.admin.Init(&n.Core, n.config, n.logger, nil); err != nil {
|
||||
return fmt.Errorf("n.admin.Init: %w", err)
|
||||
} else if err := n.admin.Start(); err != nil {
|
||||
return fmt.Errorf("n.admin.Start: %w", err)
|
||||
}
|
||||
n.admin.SetupAdminHandlers(n.admin)
|
||||
|
||||
// Start the multicast interface
|
||||
if err := n.multicast.Init(&n.Core, n.config, n.logger, nil); err != nil {
|
||||
return fmt.Errorf("n.multicast.Init: %w", err)
|
||||
} else if err := n.multicast.Start(); err != nil {
|
||||
return fmt.Errorf("n.admin.Start: %w", err)
|
||||
}
|
||||
n.multicast.SetupAdminHandlers(n.admin)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Node) SetLogLevel(loglevel string) {
|
||||
levels := [...]string{"error", "warn", "info", "debug", "trace"}
|
||||
loglevel = strings.ToLower(loglevel)
|
||||
|
||||
contains := func() bool {
|
||||
for _, l := range levels {
|
||||
if l == loglevel {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if !contains() { // set default log level
|
||||
n.logger.Infoln("Loglevel parse failed. Set default level(info)")
|
||||
loglevel = "info"
|
||||
}
|
||||
|
||||
for _, l := range levels {
|
||||
n.logger.EnableLevel(l)
|
||||
if l == loglevel {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ReadConfig(log *log.Logger, useconf bool, useconffile string, normaliseconf bool) *config.NodeConfig {
|
||||
// Use a configuration file. If -useconf, the configuration will be read
|
||||
// from stdin. If -useconffile, the configuration will be read from the
|
||||
// filesystem.
|
||||
var conf []byte
|
||||
var err error
|
||||
if useconffile != "" {
|
||||
// Read the file from the filesystem
|
||||
conf, err = ioutil.ReadFile(useconffile)
|
||||
} else {
|
||||
// Read the file from stdin.
|
||||
conf, err = ioutil.ReadAll(os.Stdin)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// If there's a byte order mark - which Windows 10 is now incredibly fond of
|
||||
// throwing everywhere when it's converting things into UTF-16 for the hell
|
||||
// of it - remove it and decode back down into UTF-8. This is necessary
|
||||
// because hjson doesn't know what to do with UTF-16 and will panic
|
||||
if bytes.Equal(conf[0:2], []byte{0xFF, 0xFE}) ||
|
||||
bytes.Equal(conf[0:2], []byte{0xFE, 0xFF}) {
|
||||
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
|
||||
decoder := utf.NewDecoder()
|
||||
conf, err = decoder.Bytes(conf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return config.ReadConfig(conf)
|
||||
}
|
||||
|
||||
type Arguments struct {
|
||||
GenConf bool
|
||||
UseConf bool
|
||||
NormaliseConf bool
|
||||
ConfJSON bool
|
||||
AutoConf bool
|
||||
Version bool
|
||||
GetAddr bool
|
||||
GetSubnet bool
|
||||
UseConfFile string
|
||||
LogTo string
|
||||
LogLevel string
|
||||
}
|
||||
|
||||
func ParseArguments() Arguments {
|
||||
genconf := flag.Bool("genconf", false, "print a new config to stdout")
|
||||
useconf := flag.Bool("useconf", false, "read HJSON/JSON config from stdin")
|
||||
useconffile := flag.String("useconffile", "", "read HJSON/JSON config from specified file path")
|
||||
normaliseconf := flag.Bool("normaliseconf", false, "use in combination with either -useconf or -useconffile, outputs your configuration normalised")
|
||||
confjson := flag.Bool("json", false, "print configuration from -genconf or -normaliseconf as JSON instead of HJSON")
|
||||
autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)")
|
||||
ver := flag.Bool("version", false, "prints the version of this build")
|
||||
logto := flag.String("logto", "stdout", "file path to log to, \"syslog\" or \"stdout\"")
|
||||
getaddr := flag.Bool("address", false, "returns the IPv6 address as derived from the supplied configuration")
|
||||
getsnet := flag.Bool("subnet", false, "returns the IPv6 subnet as derived from the supplied configuration")
|
||||
loglevel := flag.String("loglevel", "info", "loglevel to enable")
|
||||
flag.Parse()
|
||||
return Arguments{
|
||||
GenConf: *genconf,
|
||||
UseConf: *useconf,
|
||||
UseConfFile: *useconffile,
|
||||
NormaliseConf: *normaliseconf,
|
||||
ConfJSON: *confjson,
|
||||
AutoConf: *autoconf,
|
||||
Version: *ver,
|
||||
LogTo: *logto,
|
||||
GetAddr: *getaddr,
|
||||
GetSubnet: *getsnet,
|
||||
LogLevel: *loglevel,
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue