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 diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 0f830bb2..8455a1cd 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,12 +35,12 @@ 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 { +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. @@ -79,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 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 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") + } } } // Sanitise the config @@ -177,6 +170,31 @@ 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") + } + + if *normaliseconf { + setLogLevel("error", logger) + } else { + setLogLevel(*loglevel, logger) + } + var cfg *config.NodeConfig var err error switch { @@ -190,7 +208,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 @@ -223,8 +241,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 } @@ -248,30 +266,10 @@ 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. - 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 { @@ -283,32 +281,34 @@ 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) + 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.(*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) + 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.(*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() 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..91923392 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") @@ -288,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) } diff --git a/contrib/semver/version.sh b/contrib/semver/version.sh index db96e339..c677e96b 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 [[ $* == *--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 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."` 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 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 -} diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 8f8054dd..59f64dc7 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,8 @@ func (m *Multicast) _announce() { if a, err := net.ResolveTCPAddr("tcp6", lladdr); err == nil { a.Zone = "" destAddr.Zone = iface.Name - msg := []byte(a.String()) + msg := append([]byte(nil), m.core.GetSelf().Key...) + msg = append(msg, a.String()...) _, _ = m.sock.WriteTo(msg, nil, destAddr) } if info.interval.Seconds() < 15 { @@ -342,7 +345,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 +365,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) } 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 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 -}