From 414c100125ba0096bcf05a1882c4c8aceffff93e Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 5 Jun 2021 05:07:04 -0500 Subject: [PATCH 01/15] add public keys to multicast, public key pinning to multicast peering --- src/multicast/multicast.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 8f8054dd..97596380 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -2,6 +2,8 @@ package multicast import ( "context" + "crypto/ed25519" + "encoding/hex" "fmt" "net" "net/url" @@ -303,7 +305,12 @@ func (m *Multicast) _announce() { if a, err := net.ResolveTCPAddr("tcp6", lladdr); err == nil { a.Zone = "" destAddr.Zone = iface.Name - msg := []byte(a.String()) + key, err := hex.DecodeString(m.config.PublicKey) + if err != nil { + panic(err) + } + msg := append([]byte(nil), key...) + msg = append(msg, a.String()...) _, _ = m.sock.WriteTo(msg, nil, destAddr) } if info.interval.Seconds() < 15 { @@ -342,7 +349,12 @@ func (m *Multicast) listen() { continue } } - anAddr := string(bs[:nBytes]) + if len(bs) < ed25519.PublicKeySize { + continue + } + var key ed25519.PublicKey + key = append(key, bs[:ed25519.PublicKeySize]...) + anAddr := string(bs[ed25519.PublicKeySize:nBytes]) addr, err := net.ResolveTCPAddr("tcp6", anAddr) if err != nil { continue @@ -357,7 +369,8 @@ func (m *Multicast) listen() { }) if _, ok := interfaces[from.Zone]; ok { addr.Zone = "" - u, err := url.Parse("tcp://" + addr.String()) + pin := fmt.Sprintf("/?ed25519=%s", hex.EncodeToString(key)) + u, err := url.Parse("tcp://" + addr.String() + pin) if err != nil { m.log.Debugln("Call from multicast failed, parse error:", addr.String(), err) } From e67ee9232d80fc77b02ffbeb56e7e4c8eff02c44 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sat, 5 Jun 2021 06:00:33 -0500 Subject: [PATCH 02/15] fix nil pointer when attempting to access node config --- cmd/yggdrasil/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 0f830bb2..6e8580c8 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -271,7 +271,7 @@ func main() { // Setup the Yggdrasil node itself. The node{} type includes a Core, so we // don't need to create this manually. - n := node{} + 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 { From cb536a7322dc40f8c4050fb4d5ae1bb8ea7739c7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jun 2021 20:55:08 +0100 Subject: [PATCH 03/15] Clean up util package --- src/util/util.go | 93 ------------------------------------------ src/util/workerpool.go | 29 ------------- 2 files changed, 122 deletions(-) delete mode 100644 src/util/workerpool.go diff --git a/src/util/util.go b/src/util/util.go index 06c2d2d4..507426d0 100644 --- a/src/util/util.go +++ b/src/util/util.go @@ -5,35 +5,9 @@ package util // These are misc. utility functions that didn't really fit anywhere else import ( - "runtime" - "strconv" - "strings" "time" ) -// Yield just executes runtime.Gosched(), and is included so we don't need to explicitly import runtime elsewhere. -func Yield() { - runtime.Gosched() -} - -// LockThread executes runtime.LockOSThread(), and is included so we don't need to explicitly import runtime elsewhere. -func LockThread() { - runtime.LockOSThread() -} - -// UnlockThread executes runtime.UnlockOSThread(), and is included so we don't need to explicitly import runtime elsewhere. -func UnlockThread() { - runtime.UnlockOSThread() -} - -// ResizeBytes returns a slice of the specified length. If the provided slice has sufficient capacity, it will be resized and returned rather than allocating a new slice. -func ResizeBytes(bs []byte, length int) []byte { - if cap(bs) >= length { - return bs[:length] - } - return make([]byte, length) -} - // TimerStop stops a timer and makes sure the channel is drained, returns true if the timer was stopped before firing. func TimerStop(t *time.Timer) bool { stopped := t.Stop() @@ -61,70 +35,3 @@ func FuncTimeout(timeout time.Duration, f func()) bool { return false } } - -// Difference loops over two strings and returns the elements of A which do not appear in B. -// This is somewhat useful when needing to determine which elements of a configuration file have changed. -func Difference(a, b []string) []string { - ab := []string{} - mb := map[string]bool{} - for _, x := range b { - mb[x] = true - } - for _, x := range a { - if !mb[x] { - ab = append(ab, x) - } - } - return ab -} - -// DecodeCoordString decodes a string representing coordinates in [1 2 3] format -// and returns a []uint64. -func DecodeCoordString(in string) (out []uint64) { - s := strings.Trim(in, "[]") - t := strings.Split(s, " ") - for _, a := range t { - if u, err := strconv.ParseUint(a, 0, 64); err == nil { - out = append(out, u) - } - } - return out -} - -// GetFlowKey takes an IP packet as an argument and returns some information about the traffic flow. -// For IPv4 packets, this is derived from the source and destination protocol and port numbers. -// For IPv6 packets, this is derived from the FlowLabel field of the packet if this was set, otherwise it's handled like IPv4. -// The FlowKey is then used internally by Yggdrasil for congestion control. -func GetFlowKey(bs []byte) uint64 { - // Work out the flowkey - this is used to determine which switch queue - // traffic will be pushed to in the event of congestion - var flowkey uint64 - // Get the IP protocol version from the packet - switch bs[0] & 0xf0 { - case 0x40: // IPv4 packet - ihl := (bs[0] & 0x0f) * 4 // whole IPv4 header length (min 20) - // 8 is minimum UDP packet length - if ihl >= 20 && len(bs)-int(ihl) >= 8 { - switch bs[9] /* protocol */ { - case 0x06 /* TCP */, 0x11 /* UDP */, 0x84 /* SCTP */ : - flowkey = uint64(bs[9])<<32 /* proto */ | - uint64(bs[ihl+0])<<24 | uint64(bs[ihl+1])<<16 /* sport */ | - uint64(bs[ihl+2])<<8 | uint64(bs[ihl+3]) /* dport */ - } - } - case 0x60: // IPv6 packet - // Check if the flowlabel was specified in the packet header - flowkey = uint64(bs[1]&0x0f)<<16 | uint64(bs[2])<<8 | uint64(bs[3]) - // If the flowlabel isn't present, make protokey from proto | sport | dport - // if the packet meets minimum UDP packet length - if flowkey == 0 && len(bs) >= 48 { - switch bs[9] /* protocol */ { - case 0x06 /* TCP */, 0x11 /* UDP */, 0x84 /* SCTP */ : - flowkey = uint64(bs[6])<<32 /* proto */ | - uint64(bs[40])<<24 | uint64(bs[41])<<16 /* sport */ | - uint64(bs[42])<<8 | uint64(bs[43]) /* dport */ - } - } - } - return flowkey -} diff --git a/src/util/workerpool.go b/src/util/workerpool.go deleted file mode 100644 index fd37f397..00000000 --- a/src/util/workerpool.go +++ /dev/null @@ -1,29 +0,0 @@ -package util - -import "runtime" - -var workerPool chan func() - -func init() { - maxProcs := runtime.GOMAXPROCS(0) - if maxProcs < 1 { - maxProcs = 1 - } - workerPool = make(chan func(), maxProcs) - for idx := 0; idx < maxProcs; idx++ { - go func() { - for f := range workerPool { - f() - } - }() - } -} - -// WorkerGo submits a job to a pool of GOMAXPROCS worker goroutines. -// This is meant for short non-blocking functions f() where you could just go f(), -// but you want some kind of backpressure to prevent spawning endless goroutines. -// WorkerGo returns as soon as the function is queued to run, not when it finishes. -// In Yggdrasil, these workers are used for certain cryptographic operations. -func WorkerGo(f func()) { - workerPool <- f -} From 99973b2757bbf3729fb2c4b005ee5b029ba7abf3 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jun 2021 20:57:03 +0100 Subject: [PATCH 04/15] Remove module package, it didn't really give us anything anyway --- cmd/yggdrasil/main.go | 15 +++++++-------- src/module/module.go | 19 ------------------- 2 files changed, 7 insertions(+), 27 deletions(-) delete mode 100644 src/module/module.go diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 6e8580c8..6619a956 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -27,7 +27,6 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/core" - "github.com/yggdrasil-network/yggdrasil-go/src/module" "github.com/yggdrasil-network/yggdrasil-go/src/multicast" "github.com/yggdrasil-network/yggdrasil-go/src/tuntap" "github.com/yggdrasil-network/yggdrasil-go/src/version" @@ -36,9 +35,9 @@ import ( type node struct { core core.Core config *config.NodeConfig - tuntap module.Module // tuntap.TunAdapter - multicast module.Module // multicast.Multicast - admin module.Module // admin.AdminSocket + tuntap *tuntap.TunAdapter + multicast *multicast.Multicast + admin *admin.AdminSocket } func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *config.NodeConfig { @@ -283,28 +282,28 @@ func main() { n.admin = &admin.AdminSocket{} n.multicast = &multicast.Multicast{} n.tuntap = &tuntap.TunAdapter{} - n.tuntap.(*tuntap.TunAdapter).SetSessionGatekeeper(n.sessionFirewall) + n.tuntap.SetSessionGatekeeper(n.sessionFirewall) // Start the admin socket if err := n.admin.Init(&n.core, cfg, logger, nil); err != nil { logger.Errorln("An error occured 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.(*admin.AdminSocket)) + 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 occured initialising multicast:", err) } else if err := n.multicast.Start(); err != nil { logger.Errorln("An error occurred starting multicast:", err) } - n.multicast.SetupAdminHandlers(n.admin.(*admin.AdminSocket)) + n.multicast.SetupAdminHandlers(n.admin) // Start the TUN/TAP interface if err := n.tuntap.Init(&n.core, cfg, logger, nil); err != nil { logger.Errorln("An error occurred initialising TUN/TAP:", err) } else if err := n.tuntap.Start(); err != nil { logger.Errorln("An error occurred starting TUN/TAP:", err) } - n.tuntap.SetupAdminHandlers(n.admin.(*admin.AdminSocket)) + n.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() diff --git a/src/module/module.go b/src/module/module.go deleted file mode 100644 index 8f915005..00000000 --- a/src/module/module.go +++ /dev/null @@ -1,19 +0,0 @@ -package module - -import ( - "github.com/gologme/log" - - "github.com/yggdrasil-network/yggdrasil-go/src/admin" - "github.com/yggdrasil-network/yggdrasil-go/src/config" - "github.com/yggdrasil-network/yggdrasil-go/src/core" -) - -// Module is an interface that defines which functions must be supported by a -// given Yggdrasil module. -type Module interface { - Init(core *core.Core, state *config.NodeConfig, log *log.Logger, options interface{}) error - Start() error - Stop() error - SetupAdminHandlers(a *admin.AdminSocket) - IsStarted() bool -} From ea15eeee7ed5ebd5138773d1453615561421dac6 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jun 2021 21:32:04 +0100 Subject: [PATCH 05/15] Ensure PublicKey option is unused, map old config options --- cmd/yggdrasil/main.go | 87 ++++++++++++++++++-------------------- cmd/yggdrasilctl/main.go | 7 ++- src/multicast/multicast.go | 6 +-- src/tuntap/iface.go | 2 +- src/tuntap/tun_darwin.go | 4 +- 5 files changed, 51 insertions(+), 55 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 6619a956..95502aac 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -40,7 +40,7 @@ type node struct { admin *admin.AdminSocket } -func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *config.NodeConfig { +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. @@ -78,27 +78,21 @@ func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *config if err := hjson.Unmarshal(conf, &dat); err != nil { panic(err) } - // Check for fields that have changed type recently, e.g. the Listen config - // option is now a []string rather than a string - if listen, ok := dat["Listen"].(string); ok { - dat["Listen"] = []string{listen} + // Check if we have old field names + if _, ok := dat["TunnelRouting"]; ok { + log.Warnln("WARNING: Tunnel routing is no longer supported") } - if tunnelrouting, ok := dat["TunnelRouting"].(map[string]interface{}); ok { - if c, ok := tunnelrouting["IPv4Sources"]; ok { - delete(tunnelrouting, "IPv4Sources") - tunnelrouting["IPv4LocalSubnets"] = c + if _, ok := dat["SigningPrivateKey"]; ok { + log.Warnln("WARNING: The configuration file is out of date, please take a backup and then use -normaliseconf") + log.Warnln("WARNING: The \"SigningPrivateKey\" configuration option has been renamed to \"PrivateKey\"") + if _, ok := dat["PrivateKey"]; !ok { + dat["PrivateKey"] = dat["SigningPrivateKey"] } - if c, ok := tunnelrouting["IPv6Sources"]; ok { - delete(tunnelrouting, "IPv6Sources") - tunnelrouting["IPv6LocalSubnets"] = c - } - if c, ok := tunnelrouting["IPv4Destinations"]; ok { - delete(tunnelrouting, "IPv4Destinations") - tunnelrouting["IPv4RemoteSubnets"] = c - } - if c, ok := tunnelrouting["IPv6Destinations"]; ok { - delete(tunnelrouting, "IPv6Destinations") - tunnelrouting["IPv6RemoteSubnets"] = c + } + if _, ok := dat["SigningPublicKey"]; ok { + log.Warnln("WARNING: The \"SigningPrivateKey\" configuration option has been renamed to \"PrivateKey\"") + if _, ok := dat["PublicKey"]; !ok { + dat["PublicKey"] = dat["SigningPublicKey"] } } // Sanitise the config @@ -176,6 +170,27 @@ func main() { loglevel := flag.String("loglevel", "info", "loglevel to enable") flag.Parse() + // Create a new logger that logs output to stdout. + var logger *log.Logger + switch *logto { + case "stdout": + logger = log.New(os.Stdout, "", log.Flags()) + case "syslog": + if syslogger, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, "DAEMON", version.BuildName()); err == nil { + logger = log.New(syslogger, "", log.Flags()) + } + default: + if logfd, err := os.OpenFile(*logto, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err == nil { + logger = log.New(logfd, "", log.Flags()) + } + } + if logger == nil { + logger = log.New(os.Stdout, "", log.Flags()) + logger.Warnln("Logging defaulting to stdout") + } + + setLogLevel(*loglevel, logger) + var cfg *config.NodeConfig var err error switch { @@ -189,7 +204,7 @@ func main() { cfg = config.GenerateConfig() case *useconffile != "" || *useconf: // Read the configuration from either stdin or from the filesystem - cfg = readConfig(useconf, useconffile, normaliseconf) + cfg = readConfig(logger, useconf, useconffile, 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 @@ -222,8 +237,8 @@ func main() { } // 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.PublicKey); err == nil { - return ed25519.PublicKey(pubkey) + if pubkey, err := hex.DecodeString(cfg.PrivateKey); err == nil { + return ed25519.PrivateKey(pubkey).Public().(ed25519.PublicKey) } return nil } @@ -247,26 +262,6 @@ func main() { return default: } - // Create a new logger that logs output to stdout. - var logger *log.Logger - switch *logto { - case "stdout": - logger = log.New(os.Stdout, "", log.Flags()) - case "syslog": - if syslogger, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, "DAEMON", version.BuildName()); err == nil { - logger = log.New(syslogger, "", log.Flags()) - } - default: - if logfd, err := os.OpenFile(*logto, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err == nil { - logger = log.New(logfd, "", log.Flags()) - } - } - if logger == nil { - logger = log.New(os.Stdout, "", log.Flags()) - logger.Warnln("Logging defaulting to stdout") - } - - setLogLevel(*loglevel, logger) // Setup the Yggdrasil node itself. The node{} type includes a Core, so we // don't need to create this manually. @@ -285,14 +280,14 @@ func main() { n.tuntap.SetSessionGatekeeper(n.sessionFirewall) // Start the admin socket if err := n.admin.Init(&n.core, cfg, logger, nil); err != nil { - logger.Errorln("An error occured initialising admin socket:", err) + 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 occured initialising multicast:", err) + logger.Errorln("An error occurred initialising multicast:", err) } else if err := n.multicast.Start(); err != nil { logger.Errorln("An error occurred starting multicast:", err) } @@ -308,6 +303,8 @@ func main() { // This is just logged to stdout for the user. address := n.core.Address() subnet := n.core.Subnet() + public := n.core.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. diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 884656db..f7f4a69f 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -47,8 +47,11 @@ func run() int { fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] command [key=value] [key=value] ...\n\n", os.Args[0]) fmt.Println("Options:") flag.PrintDefaults() - fmt.Println("\nPlease note that options must always specified BEFORE the command\non the command line or they will be ignored.\n") // nolint:govet - fmt.Println("Commands:\n - Use \"list\" for a list of available commands\n") // nolint:govet + fmt.Println() + fmt.Println("Please note that options must always specified BEFORE the command\non the command line or they will be ignored.") + fmt.Println() + fmt.Println("Commands:\n - Use \"list\" for a list of available commands") + fmt.Println() fmt.Println("Examples:") fmt.Println(" - ", os.Args[0], "list") fmt.Println(" - ", os.Args[0], "getPeers") diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 97596380..59f64dc7 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -305,11 +305,7 @@ func (m *Multicast) _announce() { if a, err := net.ResolveTCPAddr("tcp6", lladdr); err == nil { a.Zone = "" destAddr.Zone = iface.Name - key, err := hex.DecodeString(m.config.PublicKey) - if err != nil { - panic(err) - } - msg := append([]byte(nil), key...) + msg := append([]byte(nil), m.core.GetSelf().Key...) msg = append(msg, a.String()...) _, _ = m.sock.WriteTo(msg, nil, destAddr) } diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go index da66e9d2..be9b6eba 100644 --- a/src/tuntap/iface.go +++ b/src/tuntap/iface.go @@ -48,7 +48,7 @@ func (tun *TunAdapter) read() { copy(srcSubnet[:], bs[8:]) copy(dstSubnet[:], bs[24:]) if srcAddr != tun.addr && srcSubnet != tun.subnet { - continue // Wrong soruce address + continue // Wrong source address } bs = buf[begin-1 : end] bs[0] = typeSessionTraffic diff --git a/src/tuntap/tun_darwin.go b/src/tuntap/tun_darwin.go index 060ad144..75938881 100644 --- a/src/tuntap/tun_darwin.go +++ b/src/tuntap/tun_darwin.go @@ -90,7 +90,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { ar.ifra_prefixmask.sin6_len = uint8(unsafe.Sizeof(ar.ifra_prefixmask)) b := make([]byte, 16) binary.LittleEndian.PutUint16(b, uint16(0xFE00)) - ar.ifra_prefixmask.sin6_addr[0] = uint16(binary.BigEndian.Uint16(b)) + ar.ifra_prefixmask.sin6_addr[0] = binary.BigEndian.Uint16(b) ar.ifra_addr.sin6_len = uint8(unsafe.Sizeof(ar.ifra_addr)) ar.ifra_addr.sin6_family = unix.AF_INET6 @@ -99,7 +99,7 @@ func (tun *TunAdapter) setupAddress(addr string) error { addr, _ := strconv.ParseUint(parts[i], 16, 16) b := make([]byte, 16) binary.LittleEndian.PutUint16(b, uint16(addr)) - ar.ifra_addr.sin6_addr[i] = uint16(binary.BigEndian.Uint16(b)) + ar.ifra_addr.sin6_addr[i] = binary.BigEndian.Uint16(b) } ar.ifra_flags |= darwin_IN6_IFF_NODAD From 05ad5df8ab1cca26fc8f7d3822d6d0e0e2869225 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jun 2021 21:32:12 +0100 Subject: [PATCH 06/15] Run tests in CI --- .circleci/config.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8a486c38..6e41648c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,6 +16,11 @@ jobs: go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.31.0 golangci-lint run + - run: + name: Run Go tests + command: | + go test ./... + build-linux: docker: - image: circleci/golang:1.16 From 5cede61a34870ae214e93a3ba8a3329422b4ab0e Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jun 2021 21:32:18 +0100 Subject: [PATCH 07/15] Use git describe output for versions --- contrib/semver/version.sh | 47 +++------------------------------------ 1 file changed, 3 insertions(+), 44 deletions(-) diff --git a/contrib/semver/version.sh b/contrib/semver/version.sh index db96e339..7473c41f 100644 --- a/contrib/semver/version.sh +++ b/contrib/semver/version.sh @@ -1,46 +1,5 @@ #!/bin/sh -# Get the last tag -TAG=$(git describe --abbrev=0 --tags --match="v[0-9]*\.[0-9]*\.[0-9]*" 2>/dev/null) - -# Did getting the tag succeed? -if [ $? != 0 ] || [ -z "$TAG" ]; then - printf -- "unknown" - exit 0 -fi - -# Get the current branch -BRANCH=$(git symbolic-ref -q HEAD --short 2>/dev/null) - -# Did getting the branch succeed? -if [ $? != 0 ] || [ -z "$BRANCH" ]; then - BRANCH="master" -fi - -# Split out into major, minor and patch numbers -MAJOR=$(echo $TAG | cut -c 2- | cut -d "." -f 1) -MINOR=$(echo $TAG | cut -c 2- | cut -d "." -f 2) -PATCH=$(echo $TAG | cut -c 2- | cut -d "." -f 3) - -# Output in the desired format -if [ $((PATCH)) -eq 0 ]; then - printf '%s%d.%d' "$PREPEND" "$((MAJOR))" "$((MINOR))" -else - printf '%s%d.%d.%d' "$PREPEND" "$((MAJOR))" "$((MINOR))" "$((PATCH))" -fi - -# Add the build tag on non-master branches -if [ "$BRANCH" != "master" ]; then - BUILD=$(git rev-list --count $TAG..HEAD 2>/dev/null) - - # Did getting the count of commits since the tag succeed? - if [ $? != 0 ] || [ -z "$BUILD" ]; then - printf -- "-unknown" - exit 0 - fi - - # Is the build greater than zero? - if [ $((BUILD)) -gt 0 ]; then - printf -- "-%04d" "$((BUILD))" - fi -fi +# We'll just use the `git describe` version since it's reasonably +# easy to refer to a tag or commit using the describe output. +git describe --match="v[0-9]*\.[0-9]*\.[0-9]*" 2>/dev/null From 54cced0b89135b9f98227cec4c1a1e9204befc4c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jun 2021 21:40:58 +0100 Subject: [PATCH 08/15] Ensure PublicKey is correct when extracting from old config --- cmd/yggdrasil/main.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 95502aac..cf8710cc 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -82,17 +82,15 @@ func readConfig(log *log.Logger, useconf *bool, useconffile *string, normaliseco if _, ok := dat["TunnelRouting"]; ok { log.Warnln("WARNING: Tunnel routing is no longer supported") } - if _, ok := dat["SigningPrivateKey"]; ok { - log.Warnln("WARNING: The configuration file is out of date, please take a backup and then use -normaliseconf") + if old, ok := dat["SigningPrivateKey"]; ok { log.Warnln("WARNING: The \"SigningPrivateKey\" configuration option has been renamed to \"PrivateKey\"") if _, ok := dat["PrivateKey"]; !ok { - dat["PrivateKey"] = dat["SigningPrivateKey"] - } - } - if _, ok := dat["SigningPublicKey"]; ok { - log.Warnln("WARNING: The \"SigningPrivateKey\" configuration option has been renamed to \"PrivateKey\"") - if _, ok := dat["PublicKey"]; !ok { - dat["PublicKey"] = dat["SigningPublicKey"] + 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[:]) + } } } // Sanitise the config From 4a684e7caf82d5db553b4cb1bf4d50f581071173 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jun 2021 21:48:20 +0100 Subject: [PATCH 09/15] Don't add mutex to config output --- src/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/config.go b/src/config/config.go index af47face..87d1af98 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -28,7 +28,7 @@ import ( // options that are necessary for an Yggdrasil node to run. You will need to // supply one of these structs to the Yggdrasil core when starting a node. type NodeConfig struct { - sync.RWMutex + sync.RWMutex `json:"-"` Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."` InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ tcp://a.b.c.d:e ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."` Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntcp://0.0.0.0:0 or tcp://[::]:0 to listen on all interfaces."` From 80b6bf0c78abeaa34760ae7b0cc0e26fa4bc9d65 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jun 2021 21:49:11 +0100 Subject: [PATCH 10/15] Further tweaks to transition handling --- cmd/yggdrasil/main.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index cf8710cc..8455a1cd 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -90,6 +90,8 @@ func readConfig(log *log.Logger, useconf *bool, useconffile *string, normaliseco 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") } } } @@ -187,7 +189,11 @@ func main() { logger.Warnln("Logging defaulting to stdout") } - setLogLevel(*loglevel, logger) + if *normaliseconf { + setLogLevel("error", logger) + } else { + setLogLevel(*loglevel, logger) + } var cfg *config.NodeConfig var err error From d46a8830200e25beb5da28fa614f5a81ab0a0950 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jun 2021 21:54:05 +0100 Subject: [PATCH 11/15] Include public key in yggdrasilctl getSelf output for v0.4 nodes --- cmd/yggdrasilctl/main.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index f7f4a69f..91923392 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -291,6 +291,9 @@ func run() int { if subnet, ok := v.(map[string]interface{})["subnet"].(string); ok { fmt.Println("IPv6 subnet:", subnet) } + if boxSigKey, ok := v.(map[string]interface{})["key"].(string); ok { + fmt.Println("Public key:", boxSigKey) + } if coords, ok := v.(map[string]interface{})["coords"].(string); ok { fmt.Println("Coords:", coords) } From 2e2566d248b4678da958f5e047677b5e8dec8fd9 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jun 2021 21:56:31 +0100 Subject: [PATCH 12/15] Remove src/core/doc.go --- src/core/doc.go | 176 ------------------------------------------------ 1 file changed, 176 deletions(-) delete mode 100644 src/core/doc.go diff --git a/src/core/doc.go b/src/core/doc.go deleted file mode 100644 index 595af229..00000000 --- a/src/core/doc.go +++ /dev/null @@ -1,176 +0,0 @@ -/* -Package core implements the core functionality of the Yggdrasil Network. - -Introduction - -Yggdrasil is a proof-of-concept mesh network which provides end-to-end encrypted -communication between nodes in a decentralised fashion. The network is arranged -using a globally-agreed spanning tree which provides each node with a locator -(coordinates relative to the root) and a distributed hash table (DHT) mechanism -for finding other nodes. - -Each node also implements a router, which is responsible for encryption of -traffic, searches and connections, and a switch, which is responsible ultimately -for forwarding traffic across the network. - -While many Yggdrasil nodes in existence today are IP nodes - that is, they are -transporting IPv6 packets, like a kind of mesh VPN - it is also possible to -integrate Yggdrasil into your own applications and use it as a generic data -transport, similar to UDP. - -This library is what you need to integrate and use Yggdrasil in your own -application. - -Basics - -In order to start an Yggdrasil node, you should start by generating node -configuration, which amongst other things, includes encryption keypairs which -are used to generate the node's identity, and supply a logger which Yggdrasil's -output will be written to. - -This may look something like this: - - import ( - "os" - "github.com/gologme/log" - "github.com/yggdrasil-network/yggdrasil-go/src/config" - "github.com/yggdrasil-network/yggdrasil-go/src/core" - ) - - type node struct { - core core.Core - config *config.NodeConfig - log *log.Logger - } - -You then can supply node configuration and a logger: - - n := node{} - n.log = log.New(os.Stdout, "", log.Flags()) - n.config = config.GenerateConfig() - -In the above example, we ask the config package to supply new configuration each -time, which results in fresh encryption keys and therefore a new identity. It is -normally preferable in most cases to persist node configuration onto the -filesystem or into some configuration store so that the node's identity does not -change each time that the program starts. Note that Yggdrasil will automatically -fill in any missing configuration items with sane defaults. - -Once you have supplied a logger and some node configuration, you can then start -the node: - - n.core.Start(n.config, n.log) - -Add some peers to connect to the network: - - n.core.AddPeer("tcp://some-host.net:54321", "") - n.core.AddPeer("tcp://[2001::1:2:3]:54321", "") - n.core.AddPeer("tcp://1.2.3.4:54321", "") - -You can also ask the API for information about our node: - - n.log.Println("My node ID is", n.core.NodeID()) - n.log.Println("My public key is", n.core.EncryptionPublicKey()) - n.log.Println("My coords are", n.core.Coords()) - -Incoming Connections - -Once your node is started, you can then listen for connections from other nodes -by asking the API for a Listener: - - listener, err := n.core.ConnListen() - if err != nil { - // ... - } - -The Listener has a blocking Accept function which will wait for incoming -connections from remote nodes. It will return a Conn when a connection is -received. If the node never receives any incoming connections then this function -can block forever, so be prepared for that, perhaps by listening in a separate -goroutine. - -Assuming that you have defined a myConnectionHandler function to deal with -incoming connections: - - for { - conn, err := listener.Accept() - if err != nil { - // ... - } - - // We've got a new connection - go myConnectionHandler(conn) - } - -Outgoing Connections - -If you know the node ID of the remote node that you want to talk to, you can -dial an outbound connection to it. To do this, you should first ask the API for -a Dialer: - - dialer, err := n.core.ConnDialer() - if err != nil { - // ... - } - -You can then dial using the node's public key in hexadecimal format, for example: - - conn, err := dialer.Dial("curve25519", "55071be281f50d0abbda63aadc59755624280c44b2f1f47684317aa4e0325604") - if err != nil { - // ... - } - -Using Connections - -Conn objects are implementations of io.ReadWriteCloser, and as such, you can -Read, Write and Close them as necessary. - -Each Read or Write operation can deal with a buffer with a maximum size of 65535 -bytes - any bigger than this and the operation will return an error. - -For example, to write to the Conn from the supplied buffer: - - buf := []byte{1, 2, 3, 4, 5} - w, err := conn.Write(buf) - if err != nil { - // ... - } else { - // written w bytes - } - -Reading from the Conn into the supplied buffer: - - buf := make([]byte, 65535) - r, err := conn.Read(buf) - if err != nil { - // ... - } else { - // read r bytes - } - -When you are happy that a connection is no longer required, you can discard it: - - err := conn.Close() - if err != nil { - // ... - } - -Limitations - -You should be aware of the following limitations when working with the Yggdrasil -library: - -Individual messages written through Yggdrasil connections can not exceed 65535 -bytes in size. Yggdrasil has no concept of fragmentation, so if you try to send -a message that exceeds 65535 bytes in size, it will be dropped altogether and -an error will be returned. - -Yggdrasil connections are unreliable by nature. Messages are delivered on a -best-effort basis, and employs congestion control where appropriate to ensure -that congestion does not affect message transport, but Yggdrasil will not -retransmit any messages that have been lost. If reliable delivery is important -then you should manually implement acknowledgement and retransmission of -messages. - -*/ -package core From e827e5d313252936e292fc8b1b63599b6f4ea031 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jun 2021 22:09:15 +0100 Subject: [PATCH 13/15] Go back to old semver version for now --- contrib/semver/version.sh | 47 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/contrib/semver/version.sh b/contrib/semver/version.sh index 7473c41f..db96e339 100644 --- a/contrib/semver/version.sh +++ b/contrib/semver/version.sh @@ -1,5 +1,46 @@ #!/bin/sh -# We'll just use the `git describe` version since it's reasonably -# easy to refer to a tag or commit using the describe output. -git describe --match="v[0-9]*\.[0-9]*\.[0-9]*" 2>/dev/null +# Get the last tag +TAG=$(git describe --abbrev=0 --tags --match="v[0-9]*\.[0-9]*\.[0-9]*" 2>/dev/null) + +# Did getting the tag succeed? +if [ $? != 0 ] || [ -z "$TAG" ]; then + printf -- "unknown" + exit 0 +fi + +# Get the current branch +BRANCH=$(git symbolic-ref -q HEAD --short 2>/dev/null) + +# Did getting the branch succeed? +if [ $? != 0 ] || [ -z "$BRANCH" ]; then + BRANCH="master" +fi + +# Split out into major, minor and patch numbers +MAJOR=$(echo $TAG | cut -c 2- | cut -d "." -f 1) +MINOR=$(echo $TAG | cut -c 2- | cut -d "." -f 2) +PATCH=$(echo $TAG | cut -c 2- | cut -d "." -f 3) + +# Output in the desired format +if [ $((PATCH)) -eq 0 ]; then + printf '%s%d.%d' "$PREPEND" "$((MAJOR))" "$((MINOR))" +else + printf '%s%d.%d.%d' "$PREPEND" "$((MAJOR))" "$((MINOR))" "$((PATCH))" +fi + +# Add the build tag on non-master branches +if [ "$BRANCH" != "master" ]; then + BUILD=$(git rev-list --count $TAG..HEAD 2>/dev/null) + + # Did getting the count of commits since the tag succeed? + if [ $? != 0 ] || [ -z "$BUILD" ]; then + printf -- "-unknown" + exit 0 + fi + + # Is the build greater than zero? + if [ $((BUILD)) -gt 0 ]; then + printf -- "-%04d" "$((BUILD))" + fi +fi From c5529a3a388ef2b7d46129e326bf8366828d0926 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jun 2021 22:28:29 +0100 Subject: [PATCH 14/15] Use git describe again --- contrib/semver/version.sh | 46 ++++----------------------------------- 1 file changed, 4 insertions(+), 42 deletions(-) diff --git a/contrib/semver/version.sh b/contrib/semver/version.sh index db96e339..1a5c2fd9 100644 --- a/contrib/semver/version.sh +++ b/contrib/semver/version.sh @@ -1,46 +1,8 @@ #!/bin/sh -# Get the last tag -TAG=$(git describe --abbrev=0 --tags --match="v[0-9]*\.[0-9]*\.[0-9]*" 2>/dev/null) - -# Did getting the tag succeed? -if [ $? != 0 ] || [ -z "$TAG" ]; then - printf -- "unknown" - exit 0 -fi - -# Get the current branch -BRANCH=$(git symbolic-ref -q HEAD --short 2>/dev/null) - -# Did getting the branch succeed? -if [ $? != 0 ] || [ -z "$BRANCH" ]; then - BRANCH="master" -fi - -# Split out into major, minor and patch numbers -MAJOR=$(echo $TAG | cut -c 2- | cut -d "." -f 1) -MINOR=$(echo $TAG | cut -c 2- | cut -d "." -f 2) -PATCH=$(echo $TAG | cut -c 2- | cut -d "." -f 3) - -# Output in the desired format -if [ $((PATCH)) -eq 0 ]; then - printf '%s%d.%d' "$PREPEND" "$((MAJOR))" "$((MINOR))" +if [ "$1" == "--bare" ]; then + # Remove the "v" prefix + git describe --tags --match="v[0-9]*\.[0-9]*\.[0-9]*" | cut -c 2- else - printf '%s%d.%d.%d' "$PREPEND" "$((MAJOR))" "$((MINOR))" "$((PATCH))" -fi - -# Add the build tag on non-master branches -if [ "$BRANCH" != "master" ]; then - BUILD=$(git rev-list --count $TAG..HEAD 2>/dev/null) - - # Did getting the count of commits since the tag succeed? - if [ $? != 0 ] || [ -z "$BUILD" ]; then - printf -- "-unknown" - exit 0 - fi - - # Is the build greater than zero? - if [ $((BUILD)) -gt 0 ]; then - printf -- "-%04d" "$((BUILD))" - fi + git describe --tags --match="v[0-9]*\.[0-9]*\.[0-9]*" fi From 2b6aa3e2d74b7a927109b98c5252479e5c4a10d2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 5 Jun 2021 22:38:37 +0100 Subject: [PATCH 15/15] Semver version fix --- contrib/semver/version.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/semver/version.sh b/contrib/semver/version.sh index 1a5c2fd9..c677e96b 100644 --- a/contrib/semver/version.sh +++ b/contrib/semver/version.sh @@ -1,6 +1,6 @@ #!/bin/sh -if [ "$1" == "--bare" ]; then +if [[ $* == *--bare* ]]; then # Remove the "v" prefix git describe --tags --match="v[0-9]*\.[0-9]*\.[0-9]*" | cut -c 2- else