mirror of
				https://github.com/yggdrasil-network/yggdrasil-go.git
				synced 2025-11-04 03:05:07 +03:00 
			
		
		
		
	Port "Refactor node creation"
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
This commit is contained in:
		
							parent
							
								
									a0b3897278
								
							
						
					
					
						commit
						7b41cec27f
					
				
					 3 changed files with 435 additions and 232 deletions
				
			
		| 
						 | 
				
			
			@ -2,16 +2,11 @@ package main
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"crypto/ed25519"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/signal"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"syscall"
 | 
			
		||||
 | 
			
		||||
	"github.com/gologme/log"
 | 
			
		||||
| 
						 | 
				
			
			@ -19,40 +14,14 @@ import (
 | 
			
		|||
	"github.com/hjson/hjson-go/v4"
 | 
			
		||||
	"github.com/kardianos/minwinsvc"
 | 
			
		||||
 | 
			
		||||
	"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/ipv6rwc"
 | 
			
		||||
 | 
			
		||||
	"github.com/yggdrasil-network/yggdrasil-go/src/core"
 | 
			
		||||
	"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
 | 
			
		||||
	"github.com/yggdrasil-network/yggdrasil-go/src/tun"
 | 
			
		||||
	"github.com/yggdrasil-network/yggdrasil-go/src/setup"
 | 
			
		||||
	"github.com/yggdrasil-network/yggdrasil-go/src/version"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type node struct {
 | 
			
		||||
	core      *core.Core
 | 
			
		||||
	tun       *tun.TunAdapter
 | 
			
		||||
	multicast *multicast.Multicast
 | 
			
		||||
	admin     *admin.AdminSocket
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The main function is responsible for configuring and starting Yggdrasil.
 | 
			
		||||
func main() {
 | 
			
		||||
	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")
 | 
			
		||||
	exportkey := flag.Bool("exportkey", false, "use in combination with either -useconf or -useconffile, outputs your private key in PEM format")
 | 
			
		||||
	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, "use in combination with either -useconf or -useconffile, outputs your IPv6 address")
 | 
			
		||||
	getsnet := flag.Bool("subnet", false, "use in combination with either -useconf or -useconffile, outputs your IPv6 subnet")
 | 
			
		||||
	getpkey := flag.Bool("publickey", false, "use in combination with either -useconf or -useconffile, outputs your public key")
 | 
			
		||||
	loglevel := flag.String("loglevel", "info", "loglevel to enable")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
	args := setup.ParseArguments()
 | 
			
		||||
 | 
			
		||||
	// Catch interrupts from the operating system to exit gracefully.
 | 
			
		||||
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +31,7 @@ func main() {
 | 
			
		|||
 | 
			
		||||
	// Create a new logger that logs output to stdout.
 | 
			
		||||
	var logger *log.Logger
 | 
			
		||||
	switch *logto {
 | 
			
		||||
	switch args.LogTo {
 | 
			
		||||
	case "stdout":
 | 
			
		||||
		logger = log.New(os.Stdout, "", log.Flags())
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +41,7 @@ func main() {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		if logfd, err := os.OpenFile(*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())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -80,227 +49,70 @@ func main() {
 | 
			
		|||
		logger = log.New(os.Stdout, "", log.Flags())
 | 
			
		||||
		logger.Warnln("Logging defaulting to stdout")
 | 
			
		||||
	}
 | 
			
		||||
	if *normaliseconf {
 | 
			
		||||
		setLogLevel("error", logger)
 | 
			
		||||
	} else {
 | 
			
		||||
		setLogLevel(*loglevel, logger)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cfg := config.GenerateConfig()
 | 
			
		||||
	var cfg *config.NodeConfig
 | 
			
		||||
	var err error
 | 
			
		||||
	switch {
 | 
			
		||||
	case *ver:
 | 
			
		||||
	case args.Version:
 | 
			
		||||
		fmt.Println("Build name:", version.BuildName())
 | 
			
		||||
		fmt.Println("Build version:", version.BuildVersion())
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
	case *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 interface.
 | 
			
		||||
		// port numbers, and will use an automatically selected TUN/TAP interface.
 | 
			
		||||
		cfg = config.GenerateConfig()
 | 
			
		||||
 | 
			
		||||
	case *useconf:
 | 
			
		||||
		if _, err := cfg.ReadFrom(os.Stdin); err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case *useconffile != "":
 | 
			
		||||
		f, err := os.Open(*useconffile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
		if _, err := cfg.ReadFrom(f); err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
		_ = f.Close()
 | 
			
		||||
 | 
			
		||||
	case *genconf:
 | 
			
		||||
		cfg.AdminListen = ""
 | 
			
		||||
		var bs []byte
 | 
			
		||||
		if *confjson {
 | 
			
		||||
			bs, err = json.MarshalIndent(cfg, "", "  ")
 | 
			
		||||
		} else {
 | 
			
		||||
			bs, err = hjson.Marshal(cfg)
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Println(string(bs))
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		fmt.Println("Usage:")
 | 
			
		||||
		flag.PrintDefaults()
 | 
			
		||||
 | 
			
		||||
		if *getaddr || *getsnet {
 | 
			
		||||
			fmt.Println("\nError: You need to specify some config data using -useconf or -useconffile.")
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	privateKey := ed25519.PrivateKey(cfg.PrivateKey)
 | 
			
		||||
	publicKey := privateKey.Public().(ed25519.PublicKey)
 | 
			
		||||
 | 
			
		||||
	switch {
 | 
			
		||||
	case *getaddr:
 | 
			
		||||
		addr := address.AddrForKey(publicKey)
 | 
			
		||||
		ip := net.IP(addr[:])
 | 
			
		||||
		fmt.Println(ip.String())
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
	case *getsnet:
 | 
			
		||||
		snet := address.SubnetForKey(publicKey)
 | 
			
		||||
		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
 | 
			
		||||
 | 
			
		||||
	case *getpkey:
 | 
			
		||||
		fmt.Println(hex.EncodeToString(publicKey))
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
	case *normaliseconf:
 | 
			
		||||
		cfg.AdminListen = ""
 | 
			
		||||
		var bs []byte
 | 
			
		||||
		if *confjson {
 | 
			
		||||
			bs, err = json.MarshalIndent(cfg, "", "  ")
 | 
			
		||||
		} else {
 | 
			
		||||
			bs, err = hjson.Marshal(cfg)
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Println(string(bs))
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
	case *exportkey:
 | 
			
		||||
		pem, err := cfg.MarshalPEMPrivateKey()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Println(string(pem))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	n := &node{}
 | 
			
		||||
 | 
			
		||||
	// Setup the Yggdrasil node itself.
 | 
			
		||||
	{
 | 
			
		||||
		options := []core.SetupOption{
 | 
			
		||||
			core.NodeInfo(cfg.NodeInfo),
 | 
			
		||||
			core.NodeInfoPrivacy(cfg.NodeInfoPrivacy),
 | 
			
		||||
		}
 | 
			
		||||
		for _, addr := range cfg.Listen {
 | 
			
		||||
			options = append(options, core.ListenAddress(addr))
 | 
			
		||||
		}
 | 
			
		||||
		for _, peer := range cfg.Peers {
 | 
			
		||||
			options = append(options, core.Peer{URI: peer})
 | 
			
		||||
		}
 | 
			
		||||
		for intf, peers := range cfg.InterfacePeers {
 | 
			
		||||
			for _, peer := range peers {
 | 
			
		||||
				options = append(options, core.Peer{URI: peer, SourceInterface: intf})
 | 
			
		||||
	case args.UseConfFile != "" || args.UseConf:
 | 
			
		||||
		// Read the configuration from either stdin or from the filesystem
 | 
			
		||||
		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 {
 | 
			
		||||
			var bs []byte
 | 
			
		||||
			if args.ConfJSON {
 | 
			
		||||
				bs, err = json.MarshalIndent(cfg, "", "  ")
 | 
			
		||||
			} else {
 | 
			
		||||
				bs, err = hjson.Marshal(cfg)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for _, allowed := range cfg.AllowedPublicKeys {
 | 
			
		||||
			k, err := hex.DecodeString(allowed)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				panic(err)
 | 
			
		||||
			}
 | 
			
		||||
			options = append(options, core.AllowedPublicKey(k[:]))
 | 
			
		||||
			fmt.Println(string(bs))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if n.core, err = core.New(cfg.Certificate, logger, options...); err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
		address, subnet := n.core.Address(), n.core.Subnet()
 | 
			
		||||
		logger.Infof("Your public key is %s", hex.EncodeToString(n.core.PublicKey()))
 | 
			
		||||
		logger.Infof("Your IPv6 address is %s", address.String())
 | 
			
		||||
		logger.Infof("Your IPv6 subnet is %s", subnet.String())
 | 
			
		||||
 | 
			
		||||
	case args.GenConf:
 | 
			
		||||
		// Generate a new configuration and print it to stdout.
 | 
			
		||||
		fmt.Printf("%s\n", config.GenerateConfigJSON(args.ConfJSON))
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		// No flags were provided, therefore print the list of flags to stdout.
 | 
			
		||||
		fmt.Println("Usage:")
 | 
			
		||||
		flag.PrintDefaults()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Setup the admin socket.
 | 
			
		||||
	{
 | 
			
		||||
		options := []admin.SetupOption{
 | 
			
		||||
			admin.ListenAddress(cfg.AdminListen),
 | 
			
		||||
		}
 | 
			
		||||
		if cfg.LogLookups {
 | 
			
		||||
			options = append(options, admin.LogLookups{})
 | 
			
		||||
		}
 | 
			
		||||
		if n.admin, err = admin.New(n.core, logger, options...); err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
		if n.admin != nil {
 | 
			
		||||
			n.admin.SetupAdminHandlers()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Create a new standalone node
 | 
			
		||||
	n := setup.NewNode(cfg, logger)
 | 
			
		||||
	n.SetLogLevel(args.LogLevel)
 | 
			
		||||
 | 
			
		||||
	// Setup the multicast module.
 | 
			
		||||
	{
 | 
			
		||||
		options := []multicast.SetupOption{}
 | 
			
		||||
		for _, intf := range cfg.MulticastInterfaces {
 | 
			
		||||
			options = append(options, multicast.MulticastInterface{
 | 
			
		||||
				Regex:    regexp.MustCompile(intf.Regex),
 | 
			
		||||
				Beacon:   intf.Beacon,
 | 
			
		||||
				Listen:   intf.Listen,
 | 
			
		||||
				Port:     intf.Port,
 | 
			
		||||
				Priority: uint8(intf.Priority),
 | 
			
		||||
				Password: intf.Password,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		if n.multicast, err = multicast.New(n.core, logger, options...); err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
		if n.admin != nil && n.multicast != nil {
 | 
			
		||||
			n.multicast.SetupAdminHandlers(n.admin)
 | 
			
		||||
		}
 | 
			
		||||
	// Now start Yggdrasil - this starts the router, switch and other core
 | 
			
		||||
	// components needed for Yggdrasil to operate
 | 
			
		||||
	if err = n.Run(args); err != nil {
 | 
			
		||||
		logger.Fatalln(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Setup the TUN module.
 | 
			
		||||
	{
 | 
			
		||||
		options := []tun.SetupOption{
 | 
			
		||||
			tun.InterfaceName(cfg.IfName),
 | 
			
		||||
			tun.InterfaceMTU(cfg.IfMTU),
 | 
			
		||||
		}
 | 
			
		||||
		if n.tun, err = tun.New(ipv6rwc.NewReadWriteCloser(n.core), logger, options...); err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
		if n.admin != nil && n.tun != nil {
 | 
			
		||||
			n.tun.SetupAdminHandlers(n.admin)
 | 
			
		||||
		}
 | 
			
		||||
	if err = n.SetupTun(); err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Block until we are told to shut down.
 | 
			
		||||
	<-ctx.Done()
 | 
			
		||||
 | 
			
		||||
	// Shut down the node.
 | 
			
		||||
	_ = n.admin.Stop()
 | 
			
		||||
	_ = n.multicast.Stop()
 | 
			
		||||
	_ = n.tun.Stop()
 | 
			
		||||
	n.core.Stop()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	n.Close()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,7 @@ import (
 | 
			
		|||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/hjson/hjson-go/v4"
 | 
			
		||||
	"github.com/mitchellh/mapstructure"
 | 
			
		||||
	"golang.org/x/text/encoding/unicode"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -89,6 +90,22 @@ func GenerateConfig() *NodeConfig {
 | 
			
		|||
	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 (cfg *NodeConfig) ReadFrom(r io.Reader) (int64, error) {
 | 
			
		||||
	conf, err := io.ReadAll(r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -258,3 +275,68 @@ func (k *KeyBytes) UnmarshalJSON(b []byte) error {
 | 
			
		|||
	*k, err = hex.DecodeString(s)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
	}
 | 
			
		||||
	if err := cfg.postprocessConfig(); err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return cfg
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										309
									
								
								src/setup/setup.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										309
									
								
								src/setup/setup.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,309 @@
 | 
			
		|||
package setup
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"context"
 | 
			
		||||
	"crypto/ed25519"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net"
 | 
			
		||||
	"os"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"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/ipv6rwc"
 | 
			
		||||
	"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
 | 
			
		||||
	"github.com/yggdrasil-network/yggdrasil-go/src/tun"
 | 
			
		||||
	"golang.org/x/text/encoding/unicode"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Node struct {
 | 
			
		||||
	core      *core.Core
 | 
			
		||||
	ctx       context.Context
 | 
			
		||||
	cancel    context.CancelFunc
 | 
			
		||||
	logger    *log.Logger
 | 
			
		||||
	config    *config.NodeConfig
 | 
			
		||||
	tun       *tun.TunAdapter
 | 
			
		||||
	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,
 | 
			
		||||
		tun:       &tun.TunAdapter{},
 | 
			
		||||
		multicast: &multicast.Multicast{},
 | 
			
		||||
		admin:     &admin.AdminSocket{},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (n *Node) Close() {
 | 
			
		||||
	n.cancel()
 | 
			
		||||
	_ = n.admin.Stop()
 | 
			
		||||
	_ = n.multicast.Stop()
 | 
			
		||||
	_ = n.tun.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 {
 | 
			
		||||
	var err error = nil
 | 
			
		||||
	// 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 {
 | 
			
		||||
		return ed25519.PrivateKey(n.config.PrivateKey).Public().(ed25519.PublicKey)
 | 
			
		||||
	}
 | 
			
		||||
	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
 | 
			
		||||
	case args.GetPKey:
 | 
			
		||||
		if key := getNodeKey(); key != nil {
 | 
			
		||||
			fmt.Println(hex.EncodeToString(key))
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	case args.ExportKey:
 | 
			
		||||
		pem, err := n.config.MarshalPEMPrivateKey()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Println(string(pem))
 | 
			
		||||
		return nil
 | 
			
		||||
	default:
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Setup the Yggdrasil node itself.
 | 
			
		||||
	{
 | 
			
		||||
		options := []core.SetupOption{
 | 
			
		||||
			core.NodeInfo(n.config.NodeInfo),
 | 
			
		||||
			core.NodeInfoPrivacy(n.config.NodeInfoPrivacy),
 | 
			
		||||
		}
 | 
			
		||||
		for _, addr := range n.config.Listen {
 | 
			
		||||
			options = append(options, core.ListenAddress(addr))
 | 
			
		||||
		}
 | 
			
		||||
		for _, peer := range n.config.Peers {
 | 
			
		||||
			options = append(options, core.Peer{URI: peer})
 | 
			
		||||
		}
 | 
			
		||||
		for intf, peers := range n.config.InterfacePeers {
 | 
			
		||||
			for _, peer := range peers {
 | 
			
		||||
				options = append(options, core.Peer{URI: peer, SourceInterface: intf})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for _, allowed := range n.config.AllowedPublicKeys {
 | 
			
		||||
			k, err := hex.DecodeString(allowed)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			options = append(options, core.AllowedPublicKey(k[:]))
 | 
			
		||||
		}
 | 
			
		||||
		if n.core, err = core.New(n.config.Certificate, n.logger, options...); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		address, subnet := n.core.Address(), n.core.Subnet()
 | 
			
		||||
		n.logger.Infof("Your public key is %s", hex.EncodeToString(n.core.PublicKey()))
 | 
			
		||||
		n.logger.Infof("Your IPv6 address is %s", address.String())
 | 
			
		||||
		n.logger.Infof("Your IPv6 subnet is %s", subnet.String())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Setup the admin socket.
 | 
			
		||||
	{
 | 
			
		||||
		options := []admin.SetupOption{
 | 
			
		||||
			admin.ListenAddress(n.config.AdminListen),
 | 
			
		||||
		}
 | 
			
		||||
		if n.config.LogLookups {
 | 
			
		||||
			options = append(options, admin.LogLookups{})
 | 
			
		||||
		}
 | 
			
		||||
		if n.admin, err = admin.New(n.core, n.logger, options...); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if n.admin != nil {
 | 
			
		||||
			n.admin.SetupAdminHandlers()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Setup the multicast module.
 | 
			
		||||
	{
 | 
			
		||||
		options := []multicast.SetupOption{}
 | 
			
		||||
		for _, intf := range n.config.MulticastInterfaces {
 | 
			
		||||
			options = append(options, multicast.MulticastInterface{
 | 
			
		||||
				Regex:    regexp.MustCompile(intf.Regex),
 | 
			
		||||
				Beacon:   intf.Beacon,
 | 
			
		||||
				Listen:   intf.Listen,
 | 
			
		||||
				Port:     intf.Port,
 | 
			
		||||
				Priority: uint8(intf.Priority),
 | 
			
		||||
				Password: intf.Password,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		if n.multicast, err = multicast.New(n.core, n.logger, options...); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if n.admin != nil && n.multicast != nil {
 | 
			
		||||
			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 (n *Node) SetupTun() error {
 | 
			
		||||
	var err error = nil
 | 
			
		||||
 | 
			
		||||
	options := []tun.SetupOption{
 | 
			
		||||
		tun.InterfaceName(n.config.IfName),
 | 
			
		||||
		tun.InterfaceMTU(n.config.IfMTU),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if n.tun, err = tun.New(ipv6rwc.NewReadWriteCloser(n.core), n.logger, options...); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if n.admin != nil && n.tun != nil {
 | 
			
		||||
		n.tun.SetupAdminHandlers(n.admin)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
	ExportKey     bool
 | 
			
		||||
	ConfJSON      bool
 | 
			
		||||
	AutoConf      bool
 | 
			
		||||
	Version       bool
 | 
			
		||||
	GetAddr       bool
 | 
			
		||||
	GetSubnet     bool
 | 
			
		||||
	GetPKey       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")
 | 
			
		||||
	exportkey := flag.Bool("exportkey", false, "use in combination with either -useconf or -useconffile, outputs your private key in PEM format")
 | 
			
		||||
	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")
 | 
			
		||||
	getpkey := flag.Bool("publickey", false, "use in combination with either -useconf or -useconffile, outputs your public key")
 | 
			
		||||
	loglevel := flag.String("loglevel", "info", "loglevel to enable")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
	return Arguments{
 | 
			
		||||
		GenConf:       *genconf,
 | 
			
		||||
		UseConf:       *useconf,
 | 
			
		||||
		UseConfFile:   *useconffile,
 | 
			
		||||
		NormaliseConf: *normaliseconf,
 | 
			
		||||
		ExportKey:     *exportkey,
 | 
			
		||||
		ConfJSON:      *confjson,
 | 
			
		||||
		AutoConf:      *autoconf,
 | 
			
		||||
		Version:       *ver,
 | 
			
		||||
		LogTo:         *logto,
 | 
			
		||||
		GetAddr:       *getaddr,
 | 
			
		||||
		GetSubnet:     *getsnet,
 | 
			
		||||
		GetPKey:       *getpkey,
 | 
			
		||||
		LogLevel:      *loglevel,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue