Merge pull request #4 from vikulin/future

Future merge
This commit is contained in:
Vadym Vikulin 2021-06-06 10:31:07 +03:00 committed by GitHub
commit 6c33805027
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 88 additions and 423 deletions

View file

@ -16,6 +16,11 @@ jobs:
go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.31.0 go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.31.0
golangci-lint run golangci-lint run
- run:
name: Run Go tests
command: |
go test ./...
build-linux: build-linux:
docker: docker:
- image: circleci/golang:1.16 - image: circleci/golang:1.16

View file

@ -27,7 +27,6 @@ import (
"github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/core" "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/multicast"
"github.com/yggdrasil-network/yggdrasil-go/src/tuntap" "github.com/yggdrasil-network/yggdrasil-go/src/tuntap"
"github.com/yggdrasil-network/yggdrasil-go/src/version" "github.com/yggdrasil-network/yggdrasil-go/src/version"
@ -36,12 +35,12 @@ import (
type node struct { type node struct {
core core.Core core core.Core
config *config.NodeConfig config *config.NodeConfig
tuntap module.Module // tuntap.TunAdapter tuntap *tuntap.TunAdapter
multicast module.Module // multicast.Multicast multicast *multicast.Multicast
admin module.Module // admin.AdminSocket 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 // Use a configuration file. If -useconf, the configuration will be read
// from stdin. If -useconffile, the configuration will be read from the // from stdin. If -useconffile, the configuration will be read from the
// filesystem. // filesystem.
@ -79,27 +78,21 @@ func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *config
if err := hjson.Unmarshal(conf, &dat); err != nil { if err := hjson.Unmarshal(conf, &dat); err != nil {
panic(err) panic(err)
} }
// Check for fields that have changed type recently, e.g. the Listen config // Check if we have old field names
// option is now a []string rather than a string if _, ok := dat["TunnelRouting"]; ok {
if listen, ok := dat["Listen"].(string); ok { log.Warnln("WARNING: Tunnel routing is no longer supported")
dat["Listen"] = []string{listen}
} }
if tunnelrouting, ok := dat["TunnelRouting"].(map[string]interface{}); ok { if old, ok := dat["SigningPrivateKey"]; ok {
if c, ok := tunnelrouting["IPv4Sources"]; ok { log.Warnln("WARNING: The \"SigningPrivateKey\" configuration option has been renamed to \"PrivateKey\"")
delete(tunnelrouting, "IPv4Sources") if _, ok := dat["PrivateKey"]; !ok {
tunnelrouting["IPv4LocalSubnets"] = c if privstr, err := hex.DecodeString(old.(string)); err == nil {
} priv := ed25519.PrivateKey(privstr)
if c, ok := tunnelrouting["IPv6Sources"]; ok { pub := priv.Public().(ed25519.PublicKey)
delete(tunnelrouting, "IPv6Sources") dat["PrivateKey"] = hex.EncodeToString(priv[:])
tunnelrouting["IPv6LocalSubnets"] = c dat["PublicKey"] = hex.EncodeToString(pub[:])
} } else {
if c, ok := tunnelrouting["IPv4Destinations"]; ok { log.Warnln("WARNING: The \"SigningPrivateKey\" configuration option contains an invalid value and will be ignored")
delete(tunnelrouting, "IPv4Destinations") }
tunnelrouting["IPv4RemoteSubnets"] = c
}
if c, ok := tunnelrouting["IPv6Destinations"]; ok {
delete(tunnelrouting, "IPv6Destinations")
tunnelrouting["IPv6RemoteSubnets"] = c
} }
} }
// Sanitise the config // Sanitise the config
@ -177,6 +170,31 @@ func main() {
loglevel := flag.String("loglevel", "info", "loglevel to enable") loglevel := flag.String("loglevel", "info", "loglevel to enable")
flag.Parse() 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 cfg *config.NodeConfig
var err error var err error
switch { switch {
@ -190,7 +208,7 @@ func main() {
cfg = config.GenerateConfig() cfg = config.GenerateConfig()
case *useconffile != "" || *useconf: case *useconffile != "" || *useconf:
// Read the configuration from either stdin or from the filesystem // 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 // If the -normaliseconf option was specified then remarshal the above
// configuration and print it back to stdout. This lets the user update // configuration and print it back to stdout. This lets the user update
// their configuration file with newly mapped names (like above) or to // 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. // Have we been asked for the node address yet? If so, print it and then stop.
getNodeKey := func() ed25519.PublicKey { getNodeKey := func() ed25519.PublicKey {
if pubkey, err := hex.DecodeString(cfg.PublicKey); err == nil { if pubkey, err := hex.DecodeString(cfg.PrivateKey); err == nil {
return ed25519.PublicKey(pubkey) return ed25519.PrivateKey(pubkey).Public().(ed25519.PublicKey)
} }
return nil return nil
} }
@ -248,30 +266,10 @@ func main() {
return return
default: 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 // Setup the Yggdrasil node itself. The node{} type includes a Core, so we
// don't need to create this manually. // 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 // Now start Yggdrasil - this starts the DHT, router, switch and other core
// components needed for Yggdrasil to operate // components needed for Yggdrasil to operate
if err = n.core.Start(cfg, logger); err != nil { if err = n.core.Start(cfg, logger); err != nil {
@ -283,32 +281,34 @@ func main() {
n.admin = &admin.AdminSocket{} n.admin = &admin.AdminSocket{}
n.multicast = &multicast.Multicast{} n.multicast = &multicast.Multicast{}
n.tuntap = &tuntap.TunAdapter{} n.tuntap = &tuntap.TunAdapter{}
n.tuntap.(*tuntap.TunAdapter).SetSessionGatekeeper(n.sessionFirewall) n.tuntap.SetSessionGatekeeper(n.sessionFirewall)
// Start the admin socket // Start the admin socket
if err := n.admin.Init(&n.core, cfg, logger, nil); err != nil { 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 { } else if err := n.admin.Start(); err != nil {
logger.Errorln("An error occurred starting admin socket:", err) 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 // Start the multicast interface
if err := n.multicast.Init(&n.core, cfg, logger, nil); err != nil { 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 { } else if err := n.multicast.Start(); err != nil {
logger.Errorln("An error occurred starting multicast:", err) 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 // Start the TUN/TAP interface
if err := n.tuntap.Init(&n.core, cfg, logger, nil); err != nil { if err := n.tuntap.Init(&n.core, cfg, logger, nil); err != nil {
logger.Errorln("An error occurred initialising TUN/TAP:", err) logger.Errorln("An error occurred initialising TUN/TAP:", err)
} else if err := n.tuntap.Start(); err != nil { } else if err := n.tuntap.Start(); err != nil {
logger.Errorln("An error occurred starting TUN/TAP:", err) 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. // Make some nice output that tells us what our IPv6 address and subnet are.
// This is just logged to stdout for the user. // This is just logged to stdout for the user.
address := n.core.Address() address := n.core.Address()
subnet := n.core.Subnet() 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 address is %s", address.String())
logger.Infof("Your IPv6 subnet is %s", subnet.String()) logger.Infof("Your IPv6 subnet is %s", subnet.String())
// Catch interrupts from the operating system to exit gracefully. // Catch interrupts from the operating system to exit gracefully.

View file

@ -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.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] command [key=value] [key=value] ...\n\n", os.Args[0])
fmt.Println("Options:") fmt.Println("Options:")
flag.PrintDefaults() 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()
fmt.Println("Commands:\n - Use \"list\" for a list of available commands\n") // nolint:govet 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("Examples:")
fmt.Println(" - ", os.Args[0], "list") fmt.Println(" - ", os.Args[0], "list")
fmt.Println(" - ", os.Args[0], "getPeers") fmt.Println(" - ", os.Args[0], "getPeers")
@ -288,6 +291,9 @@ func run() int {
if subnet, ok := v.(map[string]interface{})["subnet"].(string); ok { if subnet, ok := v.(map[string]interface{})["subnet"].(string); ok {
fmt.Println("IPv6 subnet:", subnet) 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 { if coords, ok := v.(map[string]interface{})["coords"].(string); ok {
fmt.Println("Coords:", coords) fmt.Println("Coords:", coords)
} }

View file

@ -1,46 +1,8 @@
#!/bin/sh #!/bin/sh
# Get the last tag if [[ $* == *--bare* ]]; then
TAG=$(git describe --abbrev=0 --tags --match="v[0-9]*\.[0-9]*\.[0-9]*" 2>/dev/null) # Remove the "v" prefix
git describe --tags --match="v[0-9]*\.[0-9]*\.[0-9]*" | cut -c 2-
# 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 else
printf '%s%d.%d.%d' "$PREPEND" "$((MAJOR))" "$((MINOR))" "$((PATCH))" git describe --tags --match="v[0-9]*\.[0-9]*\.[0-9]*"
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 fi

View file

@ -28,7 +28,7 @@ import (
// options that are necessary for an Yggdrasil node to run. You will need to // 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. // supply one of these structs to the Yggdrasil core when starting a node.
type NodeConfig struct { 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."` 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."` 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."` 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."`

View file

@ -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

View file

@ -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
}

View file

@ -2,6 +2,8 @@ package multicast
import ( import (
"context" "context"
"crypto/ed25519"
"encoding/hex"
"fmt" "fmt"
"net" "net"
"net/url" "net/url"
@ -303,7 +305,8 @@ func (m *Multicast) _announce() {
if a, err := net.ResolveTCPAddr("tcp6", lladdr); err == nil { if a, err := net.ResolveTCPAddr("tcp6", lladdr); err == nil {
a.Zone = "" a.Zone = ""
destAddr.Zone = iface.Name 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) _, _ = m.sock.WriteTo(msg, nil, destAddr)
} }
if info.interval.Seconds() < 15 { if info.interval.Seconds() < 15 {
@ -342,7 +345,12 @@ func (m *Multicast) listen() {
continue 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) addr, err := net.ResolveTCPAddr("tcp6", anAddr)
if err != nil { if err != nil {
continue continue
@ -357,7 +365,8 @@ func (m *Multicast) listen() {
}) })
if _, ok := interfaces[from.Zone]; ok { if _, ok := interfaces[from.Zone]; ok {
addr.Zone = "" 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 { if err != nil {
m.log.Debugln("Call from multicast failed, parse error:", addr.String(), err) m.log.Debugln("Call from multicast failed, parse error:", addr.String(), err)
} }

View file

@ -48,7 +48,7 @@ func (tun *TunAdapter) read() {
copy(srcSubnet[:], bs[8:]) copy(srcSubnet[:], bs[8:])
copy(dstSubnet[:], bs[24:]) copy(dstSubnet[:], bs[24:])
if srcAddr != tun.addr && srcSubnet != tun.subnet { if srcAddr != tun.addr && srcSubnet != tun.subnet {
continue // Wrong soruce address continue // Wrong source address
} }
bs = buf[begin-1 : end] bs = buf[begin-1 : end]
bs[0] = typeSessionTraffic bs[0] = typeSessionTraffic

View file

@ -90,7 +90,7 @@ func (tun *TunAdapter) setupAddress(addr string) error {
ar.ifra_prefixmask.sin6_len = uint8(unsafe.Sizeof(ar.ifra_prefixmask)) ar.ifra_prefixmask.sin6_len = uint8(unsafe.Sizeof(ar.ifra_prefixmask))
b := make([]byte, 16) b := make([]byte, 16)
binary.LittleEndian.PutUint16(b, uint16(0xFE00)) 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_len = uint8(unsafe.Sizeof(ar.ifra_addr))
ar.ifra_addr.sin6_family = unix.AF_INET6 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) addr, _ := strconv.ParseUint(parts[i], 16, 16)
b := make([]byte, 16) b := make([]byte, 16)
binary.LittleEndian.PutUint16(b, uint16(addr)) 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 ar.ifra_flags |= darwin_IN6_IFF_NODAD

View file

@ -5,35 +5,9 @@ package util
// These are misc. utility functions that didn't really fit anywhere else // These are misc. utility functions that didn't really fit anywhere else
import ( import (
"runtime"
"strconv"
"strings"
"time" "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. // 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 { func TimerStop(t *time.Timer) bool {
stopped := t.Stop() stopped := t.Stop()
@ -61,70 +35,3 @@ func FuncTimeout(timeout time.Duration, f func()) bool {
return false 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
}

View file

@ -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
}