mirror of
https://github.com/cwinfo/yggdrasil-go.git
synced 2024-11-22 10:40:27 +00:00
commit
1f1ba3bab8
@ -48,14 +48,12 @@ jobs:
|
||||
command: |
|
||||
rm -f {yggdrasil,yggdrasilctl}
|
||||
GOOS=darwin GOARCH=amd64 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-darwin-amd64 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-darwin-amd64;
|
||||
GOOS=darwin GOARCH=386 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-darwin-i386 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-darwin-i386;
|
||||
|
||||
- run:
|
||||
name: Build for macOS (.pkg format)
|
||||
command: |
|
||||
rm -rf {yggdrasil,yggdrasilctl}
|
||||
GOOS=darwin GOARCH=amd64 ./build && PKGARCH=amd64 sh contrib/macos/create-pkg.sh && mv *.pkg /tmp/upload/
|
||||
GOOS=darwin GOARCH=386 ./build && PKGARCH=i386 sh contrib/macos/create-pkg.sh && mv *.pkg /tmp/upload/
|
||||
|
||||
- run:
|
||||
name: Build for OpenBSD
|
||||
|
21
CHANGELOG.md
21
CHANGELOG.md
@ -25,6 +25,27 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
- in case of vulnerabilities.
|
||||
-->
|
||||
|
||||
## [0.3.3] - 2018-02-18
|
||||
### Added
|
||||
- Dynamic reconfiguration, which allows reloading the configuration file to make changes during runtime by sending a `SIGHUP` signal (note: this only works with `-useconffile` and not `-useconf` and currently reconfiguring TUN/TAP is not supported)
|
||||
- Support for building Yggdrasil as an iOS or Android framework if the appropriate tools (e.g. `gomobile`/`gobind` + SDKs) are available
|
||||
- Connection contexts used for TCP connections which allow more exotic socket options to be set, e.g.
|
||||
- Reusing the multicast socket to allow multiple running Yggdrasil instances without having to disable multicast
|
||||
- Allowing supported Macs to peer with other nearby Macs that aren't even on the same Wi-Fi network using AWDL
|
||||
- Flexible logging support, which allows for logging at different levels of verbosity
|
||||
|
||||
### Changed
|
||||
- Switch changes to improve parent selection
|
||||
- Node configuration is now stored centrally, rather than having fragments/copies distributed at startup time
|
||||
- Significant refactoring in various areas, including for link types (TCP, AWDL etc), generic streams and adapters
|
||||
- macOS builds through CircleCI are now 64-bit only
|
||||
|
||||
### Fixed
|
||||
- Simplified `systemd` service now in `contrib`
|
||||
|
||||
### Removed
|
||||
- `ReadTimeout` option is now deprecated
|
||||
|
||||
## [0.3.2] - 2018-12-26
|
||||
### Added
|
||||
- The admin socket is now multithreaded, greatly improving performance of the crawler and allowing concurrent lookups to take place
|
||||
|
36
build
36
build
@ -1,17 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -ef
|
||||
|
||||
PKGSRC=${PKGSRC:-github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil}
|
||||
PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)}
|
||||
PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)}
|
||||
|
||||
LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER"
|
||||
|
||||
while getopts "udtc:l:" option
|
||||
while getopts "udaitc:l:" option
|
||||
do
|
||||
case "${option}"
|
||||
in
|
||||
u) UPX=true;;
|
||||
d) DEBUG=true;;
|
||||
i) IOS=true;;
|
||||
a) ANDROID=true;;
|
||||
t) TABLES=true;;
|
||||
c) GCFLAGS="$GCFLAGS $OPTARG";;
|
||||
l) LDFLAGS="$LDFLAGS $OPTARG";;
|
||||
@ -22,15 +26,23 @@ if [ -z $TABLES ]; then
|
||||
STRIP="-s -w"
|
||||
fi
|
||||
|
||||
for CMD in `ls cmd/` ; do
|
||||
echo "Building: $CMD"
|
||||
if [ $IOS ]; then
|
||||
echo "Building framework for iOS"
|
||||
gomobile bind -target ios -tags mobile -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil
|
||||
elif [ $ANDROID ]; then
|
||||
echo "Building aar for Android"
|
||||
gomobile bind -target android -tags mobile -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil
|
||||
else
|
||||
for CMD in `ls cmd/` ; do
|
||||
echo "Building: $CMD"
|
||||
|
||||
if [ $DEBUG ]; then
|
||||
go build -ldflags="$LDFLAGS" -gcflags="$GCFLAGS" -tags debug -v ./cmd/$CMD
|
||||
else
|
||||
go build -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" -v ./cmd/$CMD
|
||||
fi
|
||||
if [ $UPX ]; then
|
||||
upx --brute $CMD
|
||||
fi
|
||||
done
|
||||
if [ $DEBUG ]; then
|
||||
go build -ldflags="$LDFLAGS" -gcflags="$GCFLAGS" -tags debug -v ./cmd/$CMD
|
||||
else
|
||||
go build -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" -v ./cmd/$CMD
|
||||
fi
|
||||
if [ $UPX ]; then
|
||||
upx --brute $CMD
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
@ -2,28 +2,23 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/signal"
|
||||
"regexp"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
|
||||
"github.com/gologme/log"
|
||||
"github.com/hjson/hjson-go"
|
||||
"github.com/kardianos/minwinsvc"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil"
|
||||
)
|
||||
|
||||
@ -34,52 +29,124 @@ type node struct {
|
||||
core Core
|
||||
}
|
||||
|
||||
// Generates default configuration. This is used when outputting the -genconf
|
||||
// parameter and also when using -autoconf. The isAutoconf flag is used to
|
||||
// determine whether the operating system should select a free port by itself
|
||||
// (which guarantees that there will not be a conflict with any other services)
|
||||
// or whether to generate a random port number. The only side effect of setting
|
||||
// isAutoconf is that the TCP and UDP ports will likely end up with different
|
||||
// port numbers.
|
||||
func generateConfig(isAutoconf bool) *nodeConfig {
|
||||
// Create a new core.
|
||||
core := Core{}
|
||||
// Generate encryption keys.
|
||||
bpub, bpriv := core.NewEncryptionKeys()
|
||||
spub, spriv := core.NewSigningKeys()
|
||||
// Create a node configuration and populate it.
|
||||
cfg := nodeConfig{}
|
||||
if isAutoconf {
|
||||
cfg.Listen = "[::]:0"
|
||||
func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *nodeConfig {
|
||||
// Use a configuration file. If -useconf, the configuration will be read
|
||||
// from stdin. If -useconffile, the configuration will be read from the
|
||||
// filesystem.
|
||||
var conf []byte
|
||||
var err error
|
||||
if *useconffile != "" {
|
||||
// Read the file from the filesystem
|
||||
conf, err = ioutil.ReadFile(*useconffile)
|
||||
} else {
|
||||
r1 := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
cfg.Listen = fmt.Sprintf("[::]:%d", r1.Intn(65534-32768)+32768)
|
||||
// Read the file from stdin.
|
||||
conf, err = ioutil.ReadAll(os.Stdin)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// If there's a byte order mark - which Windows 10 is now incredibly fond of
|
||||
// throwing everywhere when it's converting things into UTF-16 for the hell
|
||||
// of it - remove it and decode back down into UTF-8. This is necessary
|
||||
// because hjson doesn't know what to do with UTF-16 and will panic
|
||||
if bytes.Compare(conf[0:2], []byte{0xFF, 0xFE}) == 0 ||
|
||||
bytes.Compare(conf[0:2], []byte{0xFE, 0xFF}) == 0 {
|
||||
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
|
||||
decoder := utf.NewDecoder()
|
||||
conf, err = decoder.Bytes(conf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
// Generate a new configuration - this gives us a set of sane defaults -
|
||||
// then parse the configuration we loaded above on top of it. The effect
|
||||
// of this is that any configuration item that is missing from the provided
|
||||
// configuration will use a sane default.
|
||||
cfg := config.GenerateConfig(false)
|
||||
var dat map[string]interface{}
|
||||
if err := hjson.Unmarshal(conf, &dat); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
confJson, err := json.Marshal(dat)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
json.Unmarshal(confJson, &cfg)
|
||||
// For now we will do a little bit to help the user adjust their
|
||||
// configuration to match the new configuration format, as some of the key
|
||||
// names have changed recently.
|
||||
changes := map[string]string{
|
||||
"Multicast": "",
|
||||
"ReadTimeout": "",
|
||||
"LinkLocal": "MulticastInterfaces",
|
||||
"BoxPub": "EncryptionPublicKey",
|
||||
"BoxPriv": "EncryptionPrivateKey",
|
||||
"SigPub": "SigningPublicKey",
|
||||
"SigPriv": "SigningPrivateKey",
|
||||
"AllowedBoxPubs": "AllowedEncryptionPublicKeys",
|
||||
}
|
||||
// Loop over the mappings aove and see if we have anything to fix.
|
||||
for from, to := range changes {
|
||||
if _, ok := dat[from]; ok {
|
||||
if to == "" {
|
||||
if !*normaliseconf {
|
||||
log.Println("Warning: Config option", from, "is deprecated")
|
||||
}
|
||||
} else {
|
||||
if !*normaliseconf {
|
||||
log.Println("Warning: Config option", from, "has been renamed - please change to", to)
|
||||
}
|
||||
// If the configuration file doesn't already contain a line with the
|
||||
// new name then set it to the old value. This makes sure that we
|
||||
// don't overwrite something that was put there intentionally.
|
||||
if _, ok := dat[to]; !ok {
|
||||
dat[to] = dat[from]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check to see if the peers are in a parsable format, if not then default
|
||||
// them to the TCP scheme
|
||||
if peers, ok := dat["Peers"].([]interface{}); ok {
|
||||
for index, peer := range peers {
|
||||
uri := peer.(string)
|
||||
if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(uri, "tcp:") {
|
||||
uri = uri[4:]
|
||||
}
|
||||
(dat["Peers"].([]interface{}))[index] = "tcp://" + uri
|
||||
}
|
||||
}
|
||||
// Now do the same with the interface peers
|
||||
if interfacepeers, ok := dat["InterfacePeers"].(map[string]interface{}); ok {
|
||||
for intf, peers := range interfacepeers {
|
||||
for index, peer := range peers.([]interface{}) {
|
||||
uri := peer.(string)
|
||||
if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(uri, "tcp:") {
|
||||
uri = uri[4:]
|
||||
}
|
||||
((dat["InterfacePeers"].(map[string]interface{}))[intf]).([]interface{})[index] = "tcp://" + uri
|
||||
}
|
||||
}
|
||||
}
|
||||
// Overlay our newly mapped configuration onto the autoconf node config that
|
||||
// we generated above.
|
||||
if err = mapstructure.Decode(dat, &cfg); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cfg.AdminListen = defaults.GetDefaults().DefaultAdminListen
|
||||
cfg.EncryptionPublicKey = hex.EncodeToString(bpub[:])
|
||||
cfg.EncryptionPrivateKey = hex.EncodeToString(bpriv[:])
|
||||
cfg.SigningPublicKey = hex.EncodeToString(spub[:])
|
||||
cfg.SigningPrivateKey = hex.EncodeToString(spriv[:])
|
||||
cfg.Peers = []string{}
|
||||
cfg.InterfacePeers = map[string][]string{}
|
||||
cfg.AllowedEncryptionPublicKeys = []string{}
|
||||
cfg.MulticastInterfaces = []string{".*"}
|
||||
cfg.IfName = defaults.GetDefaults().DefaultIfName
|
||||
cfg.IfMTU = defaults.GetDefaults().DefaultIfMTU
|
||||
cfg.IfTAPMode = defaults.GetDefaults().DefaultIfTAPMode
|
||||
cfg.SessionFirewall.Enable = false
|
||||
cfg.SessionFirewall.AllowFromDirect = true
|
||||
cfg.SessionFirewall.AllowFromRemote = true
|
||||
cfg.SwitchOptions.MaxTotalQueueSize = yggdrasil.SwitchQueueTotalMinSize
|
||||
cfg.NodeInfoPrivacy = false
|
||||
|
||||
return &cfg
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Generates a new configuration and returns it in HJSON format. This is used
|
||||
// with -genconf.
|
||||
func doGenconf(isjson bool) string {
|
||||
cfg := generateConfig(false)
|
||||
cfg := config.GenerateConfig(false)
|
||||
var bs []byte
|
||||
var err error
|
||||
if isjson {
|
||||
@ -103,9 +170,11 @@ func main() {
|
||||
confjson := flag.Bool("json", false, "print configuration from -genconf or -normaliseconf as JSON instead of HJSON")
|
||||
autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)")
|
||||
version := flag.Bool("version", false, "prints the version of this build")
|
||||
logging := flag.String("logging", "info,warn,error", "comma-separated list of logging levels to enable")
|
||||
flag.Parse()
|
||||
|
||||
var cfg *nodeConfig
|
||||
var err error
|
||||
switch {
|
||||
case *version:
|
||||
fmt.Println("Build name:", yggdrasil.GetBuildName())
|
||||
@ -114,116 +183,10 @@ func main() {
|
||||
case *autoconf:
|
||||
// Use an autoconf-generated config, this will give us random keys and
|
||||
// port numbers, and will use an automatically selected TUN/TAP interface.
|
||||
cfg = generateConfig(true)
|
||||
cfg = config.GenerateConfig(true)
|
||||
case *useconffile != "" || *useconf:
|
||||
// Use a configuration file. If -useconf, the configuration will be read
|
||||
// from stdin. If -useconffile, the configuration will be read from the
|
||||
// filesystem.
|
||||
var config []byte
|
||||
var err error
|
||||
if *useconffile != "" {
|
||||
// Read the file from the filesystem
|
||||
config, err = ioutil.ReadFile(*useconffile)
|
||||
} else {
|
||||
// Read the file from stdin.
|
||||
config, err = ioutil.ReadAll(os.Stdin)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// If there's a byte order mark - which Windows 10 is now incredibly fond of
|
||||
// throwing everywhere when it's converting things into UTF-16 for the hell
|
||||
// of it - remove it and decode back down into UTF-8. This is necessary
|
||||
// because hjson doesn't know what to do with UTF-16 and will panic
|
||||
if bytes.Compare(config[0:2], []byte{0xFF, 0xFE}) == 0 ||
|
||||
bytes.Compare(config[0:2], []byte{0xFE, 0xFF}) == 0 {
|
||||
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
|
||||
decoder := utf.NewDecoder()
|
||||
config, err = decoder.Bytes(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
// Generate a new configuration - this gives us a set of sane defaults -
|
||||
// then parse the configuration we loaded above on top of it. The effect
|
||||
// of this is that any configuration item that is missing from the provided
|
||||
// configuration will use a sane default.
|
||||
cfg = generateConfig(false)
|
||||
var dat map[string]interface{}
|
||||
if err := hjson.Unmarshal(config, &dat); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
confJson, err := json.Marshal(dat)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
json.Unmarshal(confJson, &cfg)
|
||||
// For now we will do a little bit to help the user adjust their
|
||||
// configuration to match the new configuration format, as some of the key
|
||||
// names have changed recently.
|
||||
changes := map[string]string{
|
||||
"Multicast": "",
|
||||
"LinkLocal": "MulticastInterfaces",
|
||||
"BoxPub": "EncryptionPublicKey",
|
||||
"BoxPriv": "EncryptionPrivateKey",
|
||||
"SigPub": "SigningPublicKey",
|
||||
"SigPriv": "SigningPrivateKey",
|
||||
"AllowedBoxPubs": "AllowedEncryptionPublicKeys",
|
||||
}
|
||||
// Loop over the mappings aove and see if we have anything to fix.
|
||||
for from, to := range changes {
|
||||
if _, ok := dat[from]; ok {
|
||||
if to == "" {
|
||||
if !*normaliseconf {
|
||||
log.Println("Warning: Deprecated config option", from, "- please remove")
|
||||
}
|
||||
} else {
|
||||
if !*normaliseconf {
|
||||
log.Println("Warning: Deprecated config option", from, "- please rename to", to)
|
||||
}
|
||||
// If the configuration file doesn't already contain a line with the
|
||||
// new name then set it to the old value. This makes sure that we
|
||||
// don't overwrite something that was put there intentionally.
|
||||
if _, ok := dat[to]; !ok {
|
||||
dat[to] = dat[from]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check to see if the peers are in a parsable format, if not then default
|
||||
// them to the TCP scheme
|
||||
if peers, ok := dat["Peers"].([]interface{}); ok {
|
||||
for index, peer := range peers {
|
||||
uri := peer.(string)
|
||||
if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(uri, "tcp:") {
|
||||
uri = uri[4:]
|
||||
}
|
||||
(dat["Peers"].([]interface{}))[index] = "tcp://" + uri
|
||||
}
|
||||
}
|
||||
// Now do the same with the interface peers
|
||||
if interfacepeers, ok := dat["InterfacePeers"].(map[string]interface{}); ok {
|
||||
for intf, peers := range interfacepeers {
|
||||
for index, peer := range peers.([]interface{}) {
|
||||
uri := peer.(string)
|
||||
if strings.HasPrefix(uri, "tcp://") || strings.HasPrefix(uri, "socks://") {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(uri, "tcp:") {
|
||||
uri = uri[4:]
|
||||
}
|
||||
((dat["InterfacePeers"].(map[string]interface{}))[intf]).([]interface{})[index] = "tcp://" + uri
|
||||
}
|
||||
}
|
||||
}
|
||||
// Overlay our newly mapped configuration onto the autoconf node config that
|
||||
// we generated above.
|
||||
if err = mapstructure.Decode(dat, &cfg); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Read the configuration from either stdin or from the filesystem
|
||||
cfg = readConfig(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
|
||||
@ -256,51 +219,30 @@ func main() {
|
||||
}
|
||||
// Create a new logger that logs output to stdout.
|
||||
logger := log.New(os.Stdout, "", log.Flags())
|
||||
//logger.EnableLevel("error")
|
||||
//logger.EnableLevel("warn")
|
||||
//logger.EnableLevel("info")
|
||||
if levels := strings.Split(*logging, ","); len(levels) > 0 {
|
||||
for _, level := range levels {
|
||||
l := strings.TrimSpace(level)
|
||||
switch l {
|
||||
case "error", "warn", "info", "trace", "debug":
|
||||
logger.EnableLevel(l)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
// Setup the Yggdrasil node itself. The node{} type includes a Core, so we
|
||||
// don't need to create this manually.
|
||||
n := node{}
|
||||
// Check to see if any multicast interface expressions were provided in the
|
||||
// config. If they were then set them now.
|
||||
for _, ll := range cfg.MulticastInterfaces {
|
||||
ifceExpr, err := regexp.Compile(ll)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
n.core.AddMulticastInterfaceExpr(ifceExpr)
|
||||
}
|
||||
// Now that we have a working configuration, we can now actually start
|
||||
// Yggdrasil. This will start the router, switch, DHT node, TCP and UDP
|
||||
// sockets, TUN/TAP adapter and multicast discovery port.
|
||||
if err := n.core.Start(cfg, logger); err != nil {
|
||||
logger.Println("An error occurred during startup")
|
||||
logger.Errorln("An error occurred during startup")
|
||||
panic(err)
|
||||
}
|
||||
// Check to see if any allowed encryption keys were provided in the config.
|
||||
// If they were then set them now.
|
||||
for _, pBoxStr := range cfg.AllowedEncryptionPublicKeys {
|
||||
n.core.AddAllowedEncryptionPublicKey(pBoxStr)
|
||||
}
|
||||
// If any static peers were provided in the configuration above then we should
|
||||
// configure them. The loop ensures that disconnected peers will eventually
|
||||
// be reconnected with.
|
||||
go func() {
|
||||
if len(cfg.Peers) == 0 && len(cfg.InterfacePeers) == 0 {
|
||||
return
|
||||
}
|
||||
for {
|
||||
for _, peer := range cfg.Peers {
|
||||
n.core.AddPeer(peer, "")
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
for intf, intfpeers := range cfg.InterfacePeers {
|
||||
for _, peer := range intfpeers {
|
||||
n.core.AddPeer(peer, intf)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
time.Sleep(time.Minute)
|
||||
}
|
||||
}()
|
||||
// The Stop function ensures that the TUN/TAP adapter is correctly shut down
|
||||
// before the program exits.
|
||||
defer func() {
|
||||
@ -310,11 +252,13 @@ func main() {
|
||||
// This is just logged to stdout for the user.
|
||||
address := n.core.GetAddress()
|
||||
subnet := n.core.GetSubnet()
|
||||
logger.Printf("Your IPv6 address is %s", address.String())
|
||||
logger.Printf("Your IPv6 subnet is %s", subnet.String())
|
||||
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.
|
||||
c := make(chan os.Signal, 1)
|
||||
r := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
signal.Notify(r, os.Interrupt, syscall.SIGHUP)
|
||||
// Create a function to capture the service being stopped on Windows.
|
||||
winTerminate := func() {
|
||||
c <- os.Interrupt
|
||||
@ -322,5 +266,18 @@ func main() {
|
||||
minwinsvc.SetOnExit(winTerminate)
|
||||
// Wait for the terminate/interrupt signal. Once a signal is received, the
|
||||
// deferred Stop function above will run which will shut down TUN/TAP.
|
||||
<-c
|
||||
for {
|
||||
select {
|
||||
case _ = <-r:
|
||||
if *useconffile != "" {
|
||||
cfg = readConfig(useconf, useconffile, normaliseconf)
|
||||
n.core.UpdateConfig(cfg)
|
||||
} else {
|
||||
logger.Errorln("Reloading config at runtime is only possible with -useconffile")
|
||||
}
|
||||
case _ = <-c:
|
||||
goto exit
|
||||
}
|
||||
}
|
||||
exit:
|
||||
}
|
||||
|
125
contrib/ansible/genkeys.go
Normal file
125
contrib/ansible/genkeys.go
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
|
||||
This file generates crypto keys for [ansible-yggdrasil](https://github.com/jcgruenhage/ansible-yggdrasil/)
|
||||
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||
)
|
||||
|
||||
var numHosts = flag.Int("hosts", 1, "number of host vars to generate")
|
||||
var keyTries = flag.Int("tries", 1000, "number of tries before taking the best keys")
|
||||
|
||||
type keySet struct {
|
||||
priv []byte
|
||||
pub []byte
|
||||
id []byte
|
||||
ip string
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if *numHosts > *keyTries {
|
||||
println("Can't generate less keys than hosts.")
|
||||
return
|
||||
}
|
||||
|
||||
var encryptionKeys []keySet
|
||||
for i := 0; i < *numHosts+1; i++ {
|
||||
encryptionKeys = append(encryptionKeys, newBoxKey())
|
||||
}
|
||||
encryptionKeys = sortKeySetArray(encryptionKeys)
|
||||
for i := 0; i < *keyTries-*numHosts-1; i++ {
|
||||
encryptionKeys[0] = newBoxKey()
|
||||
encryptionKeys = bubbleUpTo(encryptionKeys, 0)
|
||||
}
|
||||
|
||||
var signatureKeys []keySet
|
||||
for i := 0; i < *numHosts+1; i++ {
|
||||
signatureKeys = append(signatureKeys, newSigKey())
|
||||
}
|
||||
signatureKeys = sortKeySetArray(signatureKeys)
|
||||
for i := 0; i < *keyTries-*numHosts-1; i++ {
|
||||
signatureKeys[0] = newSigKey()
|
||||
signatureKeys = bubbleUpTo(signatureKeys, 0)
|
||||
}
|
||||
|
||||
os.MkdirAll("host_vars", 0755)
|
||||
|
||||
for i := 1; i <= *numHosts; i++ {
|
||||
os.MkdirAll(fmt.Sprintf("host_vars/%x", i), 0755)
|
||||
file, err := os.Create(fmt.Sprintf("host_vars/%x/vars", i))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
file.WriteString(fmt.Sprintf("yggdrasil_encryption_public_key: %v\n", hex.EncodeToString(encryptionKeys[i].pub)))
|
||||
file.WriteString("yggdrasil_encryption_private_key: \"{{ vault_yggdrasil_encryption_private_key }}\"\n")
|
||||
file.WriteString(fmt.Sprintf("yggdrasil_signing_public_key: %v\n", hex.EncodeToString(signatureKeys[i].pub)))
|
||||
file.WriteString("yggdrasil_signing_private_key: \"{{ vault_yggdrasil_signing_private_key }}\"\n")
|
||||
file.WriteString(fmt.Sprintf("ansible_host: %v\n", encryptionKeys[i].ip))
|
||||
|
||||
file, err = os.Create(fmt.Sprintf("host_vars/%x/vault", i))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
file.WriteString(fmt.Sprintf("vault_yggdrasil_encryption_private_key: %v\n", hex.EncodeToString(encryptionKeys[i].priv)))
|
||||
file.WriteString(fmt.Sprintf("vault_yggdrasil_signing_private_key: %v\n", hex.EncodeToString(signatureKeys[i].priv)))
|
||||
}
|
||||
}
|
||||
|
||||
func newBoxKey() keySet {
|
||||
pub, priv := crypto.NewBoxKeys()
|
||||
id := crypto.GetNodeID(pub)
|
||||
ip := net.IP(address.AddrForNodeID(id)[:]).String()
|
||||
return keySet{priv[:], pub[:], id[:], ip}
|
||||
}
|
||||
|
||||
func newSigKey() keySet {
|
||||
pub, priv := crypto.NewSigKeys()
|
||||
id := crypto.GetTreeID(pub)
|
||||
return keySet{priv[:], pub[:], id[:], ""}
|
||||
}
|
||||
|
||||
func isBetter(oldID, newID []byte) bool {
|
||||
for idx := range oldID {
|
||||
if newID[idx] > oldID[idx] {
|
||||
return true
|
||||
}
|
||||
if newID[idx] < oldID[idx] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func sortKeySetArray(sets []keySet) []keySet {
|
||||
for i := 0; i < len(sets); i++ {
|
||||
sets = bubbleUpTo(sets, i)
|
||||
}
|
||||
return sets
|
||||
}
|
||||
|
||||
func bubbleUpTo(sets []keySet, num int) []keySet {
|
||||
for i := 0; i < len(sets)-num-1; i++ {
|
||||
if isBetter(sets[i+1].id, sets[i].id) {
|
||||
var tmp = sets[i]
|
||||
sets[i] = sets[i+1]
|
||||
sets[i+1] = tmp
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return sets
|
||||
}
|
@ -110,12 +110,10 @@ EOF
|
||||
cp yggdrasil /tmp/$PKGNAME/usr/bin/
|
||||
cp yggdrasilctl /tmp/$PKGNAME/usr/bin/
|
||||
cp contrib/systemd/yggdrasil.service /tmp/$PKGNAME/etc/systemd/system/
|
||||
cp contrib/systemd/yggdrasil-resume.service /tmp/$PKGNAME/etc/systemd/system/
|
||||
|
||||
tar -czvf /tmp/$PKGNAME/data.tar.gz -C /tmp/$PKGNAME/ \
|
||||
usr/bin/yggdrasil usr/bin/yggdrasilctl \
|
||||
etc/systemd/system/yggdrasil.service \
|
||||
etc/systemd/system/yggdrasil-resume.service
|
||||
etc/systemd/system/yggdrasil.service
|
||||
tar -czvf /tmp/$PKGNAME/control.tar.gz -C /tmp/$PKGNAME/debian .
|
||||
echo 2.0 > /tmp/$PKGNAME/debian-binary
|
||||
|
||||
|
157
contrib/logo/ygg-neilalexander.svg
Normal file
157
contrib/logo/ygg-neilalexander.svg
Normal file
@ -0,0 +1,157 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
sodipodi:docname="drawing.svg"
|
||||
inkscape:version="0.91 r13725"
|
||||
version="1.1"
|
||||
id="svg4240"
|
||||
viewBox="0 0 981.96461 321.60015"
|
||||
height="90.762711mm"
|
||||
width="277.13223mm">
|
||||
<defs
|
||||
id="defs4242" />
|
||||
<sodipodi:namedview
|
||||
fit-margin-bottom="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-top="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:window-y="1"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-height="1021"
|
||||
inkscape:window-width="2048"
|
||||
showgrid="false"
|
||||
inkscape:current-layer="layer2"
|
||||
inkscape:document-units="px"
|
||||
inkscape:cy="13.914395"
|
||||
inkscape:cx="751.6295"
|
||||
inkscape:zoom="0.66468037"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
borderopacity="1.0"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff"
|
||||
id="base" />
|
||||
<metadata
|
||||
id="metadata4245">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(383.92494,-160.49328)" />
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Layer 2"
|
||||
transform="translate(383.92494,-160.49328)">
|
||||
<path
|
||||
style="fill:#000000"
|
||||
d="m 352.74397,478.24119 c 0.92103,-3.76903 11.87131,-30.48993 21.5083,-52.48465 9.86344,-22.51152 9.67726,-21.6278 6.92943,-32.89221 -3.42997,-14.06075 -3.22164,-36.95243 0.44688,-49.10642 13.24423,-43.87864 47.63362,-73.61698 122.30718,-105.76556 24.32504,-10.47245 37.67777,-17.18807 47.80968,-24.04538 17.86083,-12.08828 36.4402,-33.06424 42.38057,-47.84736 1.25285,-3.11781 2.66096,-5.64051 3.12912,-5.60598 1.46014,0.10767 0.73701,44.30167 -0.9768,59.69719 -10.61597,95.36545 -42.95689,157.39345 -96.20598,184.51751 -30.73114,15.65385 -79.17559,21.45357 -101.74118,12.18037 -3.19081,-1.31125 -6.5492,-2.38408 -7.46311,-2.38408 -3.43636,0 -15.75824,32.89925 -19.29523,51.51802 -1.09802,5.78003 -2.76237,13.70787 -3.00898,14.91667 -5.50064,-0.0422 -0.35371,-0.0119 -8.18026,-0.0119 l -8.29605,0 z"
|
||||
id="path4918"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ssssssscssssscscs" />
|
||||
<g
|
||||
style="font-style:normal;font-weight:normal;font-size:150px;line-height:86.00000143%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="text4920"
|
||||
transform="translate(-2,0)">
|
||||
<path
|
||||
d="m -345.34994,319.70594 -36.575,-55.6875 21.725,0 23.925,38.775 24.2,-38.775 20.625,0 -36.575,55.6875 0,41.6625 -17.325,0 0,-41.6625 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:137.5px;line-height:86.00000143%;font-family:Avenir;-inkscape-font-specification:'Avenir Heavy';letter-spacing:1.35000002px"
|
||||
id="path5638"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m -202.55678,354.21844 q -18.0125,9.625 -40.2875,9.625 -11.275,0 -20.7625,-3.575 -9.35,-3.7125 -16.225,-10.3125 -6.7375,-6.7375 -10.5875,-16.0875 -3.85,-9.35 -3.85,-20.7625 0,-11.6875 3.85,-21.175 3.85,-9.625 10.5875,-16.3625 6.875,-6.7375 16.225,-10.3125 9.4875,-3.7125 20.7625,-3.7125 11.1375,0 20.9,2.75 9.7625,2.6125 17.4625,9.4875 l -12.7875,12.925 q -4.675,-4.5375 -11.4125,-7.0125 -6.6,-2.475 -14.025,-2.475 -7.5625,0 -13.75,2.75 -6.05,2.6125 -10.45,7.425 -4.4,4.675 -6.875,11 -2.3375,6.325 -2.3375,13.6125 0,7.8375 2.3375,14.4375 2.475,6.6 6.875,11.4125 4.4,4.8125 10.45,7.5625 6.1875,2.75 13.75,2.75 6.6,0 12.375,-1.2375 5.9125,-1.2375 10.45,-3.85 l 0,-22.9625 -19.9375,0 0,-15.675 37.2625,0 0,49.775 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:137.5px;line-height:86.00000143%;font-family:Avenir;-inkscape-font-specification:'Avenir Heavy';letter-spacing:1.35000002px"
|
||||
id="path5640"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m -102.05346,354.21844 q -18.0125,9.625 -40.2875,9.625 -11.275,0 -20.7625,-3.575 -9.35,-3.7125 -16.225,-10.3125 -6.7375,-6.7375 -10.5875,-16.0875 -3.85,-9.35 -3.85,-20.7625 0,-11.6875 3.85,-21.175 3.85,-9.625 10.5875,-16.3625 6.875,-6.7375 16.225,-10.3125 9.4875,-3.7125 20.7625,-3.7125 11.1375,0 20.9,2.75 9.7625,2.6125 17.4625,9.4875 l -12.7875,12.925 q -4.675,-4.5375 -11.4125,-7.0125 -6.6,-2.475 -14.025,-2.475 -7.5625,0 -13.75,2.75 -6.05,2.6125 -10.45,7.425 -4.4,4.675 -6.875,11 -2.3375,6.325 -2.3375,13.6125 0,7.8375 2.3375,14.4375 2.475,6.6 6.875,11.4125 4.4,4.8125 10.45,7.5625 6.1875,2.75 13.75,2.75 6.6,0 12.375,-1.2375 5.9125,-1.2375 10.45,-3.85 l 0,-22.9625 -19.9375,0 0,-15.675 37.2625,0 0,49.775 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:137.5px;line-height:86.00000143%;font-family:Avenir;-inkscape-font-specification:'Avenir Heavy';letter-spacing:1.35000002px"
|
||||
id="path5642"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m -90.037646,264.01844 38.3625,0 q 9.625,0 18.5625,3.025 8.9375,2.8875 15.8125,8.9375 6.875,6.05 11,15.2625 4.125,9.075 4.125,21.45 0,12.5125 -4.8125,21.725 -4.675,9.075 -12.2375,15.125 -7.425,5.9125 -16.6375,8.9375 -9.075,2.8875 -17.875,2.8875 l -36.3,0 0,-97.35 z m 30.25,81.675 q 8.1125,0 15.2625,-1.7875 7.2875,-1.925 12.65,-5.775 5.3625,-3.9875 8.3875,-10.175 3.1625,-6.325 3.1625,-15.2625 0,-8.8 -2.75,-15.125 -2.75,-6.325 -7.7,-10.175 -4.8125,-3.9875 -11.55,-5.775 -6.6,-1.925 -14.575,-1.925 l -15.8125,0 0,66 12.925,0 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:137.5px;line-height:86.00000143%;font-family:Avenir;-inkscape-font-specification:'Avenir Heavy';letter-spacing:1.35000002px"
|
||||
id="path5644"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 7.511578,264.01844 33.825,0 q 7.0125,0 13.475,1.375 6.6,1.2375 11.687502,4.4 5.0875,3.1625 8.1125,8.525 3.025,5.3625 3.025,13.6125 0,10.5875 -5.9125,17.7375 -5.775002,7.15 -16.637502,8.6625 l 25.850002,43.0375 -20.900002,0 -22.55,-41.25 -12.65,0 0,41.25 -17.325,0 0,-97.35 z m 30.8,41.25 q 3.7125,0 7.425,-0.275 3.7125,-0.4125 6.7375,-1.65 3.1625,-1.375 5.0875,-3.9875 1.925,-2.75 1.925,-7.5625 0,-4.2625 -1.7875,-6.875 -1.7875,-2.6125 -4.675,-3.85 -2.8875,-1.375 -6.4625,-1.7875 -3.4375,-0.4125 -6.7375,-0.4125 l -14.9875,0 0,26.4 13.475,0 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:137.5px;line-height:86.00000143%;font-family:Avenir;-inkscape-font-specification:'Avenir Heavy';letter-spacing:1.35000002px"
|
||||
id="path5646"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 126.82369,264.01844 14.9875,0 41.9375,97.35 -19.8,0 -9.075,-22.275 -42.2125,0 -8.8,22.275 -19.3875,0 42.35,-97.35 z m 22,60.225 -14.9875,-39.6 -15.2625,39.6 30.25,0 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:137.5px;line-height:86.00000143%;font-family:Avenir;-inkscape-font-specification:'Avenir Heavy';letter-spacing:1.35000002px"
|
||||
id="path5648"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 239.7639,284.91844 q -2.75,-3.9875 -7.425,-5.775 -4.5375,-1.925 -9.625,-1.925 -3.025,0 -5.9125,0.6875 -2.75,0.6875 -5.0875,2.2 -2.2,1.5125 -3.575,3.9875 -1.375,2.3375 -1.375,5.6375 0,4.95 3.4375,7.5625 3.4375,2.6125 8.525,4.5375 5.0875,1.925 11.1375,3.7125 6.05,1.7875 11.1375,4.95 5.0875,3.1625 8.525,8.3875 3.4375,5.225 3.4375,13.8875 0,7.8375 -2.8875,13.75 -2.8875,5.775 -7.8375,9.625 -4.8125,3.85 -11.275,5.775 -6.4625,1.925 -13.6125,1.925 -9.075,0 -17.4625,-3.025 -8.3875,-3.025 -14.4375,-10.175 l 13.0625,-12.65 q 3.1625,4.8125 8.25,7.5625 5.225,2.6125 11,2.6125 3.025,0 6.05,-0.825 3.025,-0.825 5.5,-2.475 2.475,-1.65 3.9875,-4.125 1.5125,-2.6125 1.5125,-5.9125 0,-5.3625 -3.4375,-8.25 -3.4375,-2.8875 -8.525,-4.8125 -5.0875,-2.0625 -11.1375,-3.85 -6.05,-1.7875 -11.1375,-4.8125 -5.0875,-3.1625 -8.525,-8.25 -3.4375,-5.225 -3.4375,-13.8875 0,-7.5625 3.025,-13.0625 3.1625,-5.5 8.1125,-9.075 5.0875,-3.7125 11.55,-5.5 6.4625,-1.7875 13.2,-1.7875 7.7,0 14.85,2.3375 7.2875,2.3375 13.0625,7.7 l -12.65,13.3375 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:137.5px;line-height:86.00000143%;font-family:Avenir;-inkscape-font-specification:'Avenir Heavy';letter-spacing:1.35000002px"
|
||||
id="path5650"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 264.21257,264.01844 17.325,0 0,97.35 -17.325,0 0,-97.35 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:137.5px;line-height:86.00000143%;font-family:Avenir;-inkscape-font-specification:'Avenir Heavy';letter-spacing:1.35000002px"
|
||||
id="path5652"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 296.37837,264.01844 17.325,0 0,81.675 41.3875,0 0,15.675 -58.7125,0 0,-97.35 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:137.5px;line-height:86.00000143%;font-family:Avenir;-inkscape-font-specification:'Avenir Heavy';letter-spacing:1.35000002px"
|
||||
id="path5654"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m -369.13744,382.26844 22.9625,0 47.1625,72.325 0.275,0 0,-72.325 17.325,0 0,97.35 -22,0 -48.125,-74.6625 -0.275,0 0,74.6625 -17.325,0 0,-97.35 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:137.5px;line-height:86.00000143%;font-family:Avenir;-inkscape-font-specification:'Avenir Heavy';letter-spacing:1.35000002px"
|
||||
id="path5656"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m -270.48568,382.26844 64.4875,0 0,15.675 -47.1625,0 0,23.925 44.6875,0 0,15.675 -44.6875,0 0,26.4 49.6375,0 0,15.675 -66.9625,0 0,-97.35 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:137.5px;line-height:86.00000143%;font-family:Avenir;-inkscape-font-specification:'Avenir Heavy';letter-spacing:1.35000002px"
|
||||
id="path5658"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m -165.14057,397.94344 -29.8375,0 0,-15.675 77,0 0,15.675 -29.8375,0 0,81.675 -17.325,0 0,-81.675 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:137.5px;line-height:86.00000143%;font-family:Avenir;-inkscape-font-specification:'Avenir Heavy';letter-spacing:1.35000002px"
|
||||
id="path5660"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m -111.36694,382.26844 18.974997,0 18.2875,70.125 0.275,0 21.8625,-70.125 17.05,0 21.45,70.125 0.275,0 19.1125,-70.125 17.6,0 -28.3250004,97.35 -16.4999996,0 -22.55,-74.1125 -0.275,0 -22.55,74.1125 -15.95,0 -28.737497,-97.35 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:137.5px;line-height:86.00000143%;font-family:Avenir;-inkscape-font-specification:'Avenir Heavy';letter-spacing:1.35000002px"
|
||||
id="path5662"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 24.435016,431.35594 q 0,-11.6875 3.85,-21.175 3.85,-9.625 10.5875,-16.3625 6.875,-6.7375 16.225,-10.3125 9.4875,-3.7125 20.7625,-3.7125 11.412504,-0.1375 20.900004,3.4375 9.4875,3.4375 16.3625,10.175 6.875,6.7375 10.725,16.225 3.85,9.4875 3.85,21.175 0,11.4125 -3.85,20.7625 -3.85,9.35 -10.725,16.0875 -6.875,6.7375 -16.3625,10.5875 -9.4875,3.7125 -20.900004,3.85 -11.275,0 -20.7625,-3.575 -9.35,-3.7125 -16.225,-10.3125 -6.7375,-6.7375 -10.5875,-16.0875 -3.85,-9.35 -3.85,-20.7625 z m 18.15,-1.1 q 0,7.8375 2.3375,14.4375 2.475,6.6 6.875,11.4125 4.4,4.8125 10.45,7.5625 6.1875,2.75 13.75,2.75 7.5625,0 13.750004,-2.75 6.1875,-2.75 10.5875,-7.5625 4.4,-4.8125 6.7375,-11.4125 2.475,-6.6 2.475,-14.4375 0,-7.2875 -2.475,-13.6125 -2.3375,-6.325 -6.7375,-11 -4.4,-4.8125 -10.5875,-7.425 -6.187504,-2.75 -13.750004,-2.75 -7.5625,0 -13.75,2.75 -6.05,2.6125 -10.45,7.425 -4.4,4.675 -6.875,11 -2.3375,6.325 -2.3375,13.6125 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:137.5px;line-height:86.00000143%;font-family:Avenir;-inkscape-font-specification:'Avenir Heavy';letter-spacing:1.35000002px"
|
||||
id="path5664"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 137.68287,382.26844 33.825,0 q 7.0125,0 13.475,1.375 6.6,1.2375 11.6875,4.4 5.0875,3.1625 8.1125,8.525 3.025,5.3625 3.025,13.6125 0,10.5875 -5.9125,17.7375 -5.775,7.15 -16.6375,8.6625 l 25.85,43.0375 -20.9,0 -22.55,-41.25 -12.65,0 0,41.25 -17.325,0 0,-97.35 z m 30.8,41.25 q 3.7125,0 7.425,-0.275 3.7125,-0.4125 6.7375,-1.65 3.1625,-1.375 5.0875,-3.9875 1.925,-2.75 1.925,-7.5625 0,-4.2625 -1.7875,-6.875 -1.7875,-2.6125 -4.675,-3.85 -2.8875,-1.375 -6.4625,-1.7875 -3.4375,-0.4125 -6.7375,-0.4125 l -14.9875,0 0,26.4 13.475,0 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:137.5px;line-height:86.00000143%;font-family:Avenir;-inkscape-font-specification:'Avenir Heavy';letter-spacing:1.35000002px"
|
||||
id="path5666"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 221.50746,382.26844 17.325,0 0,41.25 0.825,0 40.2875,-41.25 23.375,0 -45.5125,44.9625 48.5375,52.3875 -24.3375,0 -42.2125,-47.85 -0.9625,0 0,47.85 -17.325,0 0,-97.35 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:137.5px;line-height:86.00000143%;font-family:Avenir;-inkscape-font-specification:'Avenir Heavy';letter-spacing:1.35000002px"
|
||||
id="path5668"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 14 KiB |
@ -4,8 +4,8 @@
|
||||
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null)
|
||||
|
||||
# Complain if the git history is not available
|
||||
if [ $? != 0 ]; then
|
||||
printf "unknown"
|
||||
if [ $? != 0 ] || [ -z "$BRANCH" ]; then
|
||||
printf "yggdrasil"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
@ -1,63 +1,46 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Merge commits from this branch are counted
|
||||
DEVELOPBRANCH="yggdrasil-network/develop"
|
||||
|
||||
# Get the last tag
|
||||
TAG=$(git describe --abbrev=0 --tags --match="v[0-9]*\.[0-9]*\.0" 2>/dev/null)
|
||||
TAG=$(git describe --abbrev=0 --tags --match="v[0-9]*\.[0-9]*\.[0-9]*" 2>/dev/null)
|
||||
|
||||
# Get last merge to master
|
||||
MERGE=$(git rev-list $TAG..master --grep "from $DEVELOPBRANCH" 2>/dev/null | head -n 1)
|
||||
|
||||
# Get the number of merges since the last merge to master
|
||||
PATCH=$(git rev-list $TAG..master --count --merges --grep="from $DEVELOPBRANCH" --first-parent master 2>/dev/null)
|
||||
|
||||
# Decide whether we should prepend the version with "v" - the default is that
|
||||
# we do because we use it in git tags, but we might not always need it
|
||||
PREPEND="v"
|
||||
if [ "$1" = "--bare" ]; then
|
||||
PREPEND=""
|
||||
# Did getting the tag succeed?
|
||||
if [ $? != 0 ] || [ -z "$TAG" ]; then
|
||||
printf -- "unknown"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If it fails then there's no last tag - go from the first commit
|
||||
if [ $? != 0 ]; then
|
||||
PATCH=$(git rev-list HEAD --count 2>/dev/null)
|
||||
# Get the current branch
|
||||
BRANCH=$(git symbolic-ref -q HEAD --short 2>/dev/null)
|
||||
|
||||
# Complain if the git history is not available
|
||||
if [ $? != 0 ]; then
|
||||
printf 'unknown'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s0.0.%d' "$PREPEND" "$PATCH"
|
||||
exit 1
|
||||
# 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)
|
||||
|
||||
# Get the current checked out branch
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
PATCH=$(echo $TAG | cut -c 2- | cut -d "." -f 3)
|
||||
|
||||
# Output in the desired format
|
||||
if [ $PATCH = 0 ]; then
|
||||
if [ ! -z $FULL ]; then
|
||||
printf '%s%d.%d.0' "$PREPEND" "$MAJOR" "$MINOR"
|
||||
else
|
||||
printf '%s%d.%d' "$PREPEND" "$MAJOR" "$MINOR"
|
||||
fi
|
||||
if [ $((PATCH)) -eq 0 ]; then
|
||||
printf '%s%d.%d' "$PREPEND" "$((MAJOR))" "$((MINOR))"
|
||||
else
|
||||
printf '%s%d.%d.%d' "$PREPEND" "$MAJOR" "$MINOR" "$PATCH"
|
||||
printf '%s%d.%d.%d' "$PREPEND" "$((MAJOR))" "$((MINOR))" "$((PATCH))"
|
||||
fi
|
||||
|
||||
# Get the number of merges on the current branch since the last tag
|
||||
TAG=$(git describe --abbrev=0 --tags --match="v[0-9]*\.[0-9]*\.[0-9]*" --first-parent master 2>/dev/null)
|
||||
BUILD=$(git rev-list $TAG.. --count)
|
||||
|
||||
# Add the build tag on non-master branches
|
||||
if [ $BRANCH != "master" ]; then
|
||||
if [ $BUILD != 0 ]; then
|
||||
printf -- "-%04d" "$BUILD"
|
||||
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 1
|
||||
fi
|
||||
|
||||
# Is the build greater than zero?
|
||||
if [ $((BUILD)) -gt 0 ]; then
|
||||
printf -- "-%04d" "$((BUILD))"
|
||||
fi
|
||||
fi
|
||||
|
@ -1,10 +0,0 @@
|
||||
[Unit]
|
||||
Description=Restart yggdrasil on resume from sleep
|
||||
After=sleep.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/bin/systemctl restart yggdrasil
|
||||
|
||||
[Install]
|
||||
WantedBy=sleep.target
|
@ -14,8 +14,8 @@ ExecStartPre=/bin/sh -ec "if ! test -s /etc/yggdrasil.conf; \
|
||||
echo 'WARNING: A new /etc/yggdrasil.conf file has been generated.'; \
|
||||
fi"
|
||||
ExecStart=/usr/bin/yggdrasil -useconffile /etc/yggdrasil.conf
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Also=yggdrasil-resume.service
|
||||
|
1
go.mod
1
go.mod
@ -2,6 +2,7 @@ module github.com/yggdrasil-network/yggdrasil-go
|
||||
|
||||
require (
|
||||
github.com/docker/libcontainer v2.2.1+incompatible
|
||||
github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8
|
||||
github.com/hjson/hjson-go v0.0.0-20181010104306-a25ecf6bd222
|
||||
github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0
|
||||
github.com/mitchellh/mapstructure v1.1.2
|
||||
|
2
go.sum
2
go.sum
@ -1,5 +1,7 @@
|
||||
github.com/docker/libcontainer v2.2.1+incompatible h1:++SbbkCw+X8vAd4j2gOCzZ2Nn7s2xFALTf7LZKmM1/0=
|
||||
github.com/docker/libcontainer v2.2.1+incompatible/go.mod h1:osvj61pYsqhNCMLGX31xr7klUBhHb/ZBuXS0o1Fvwbw=
|
||||
github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 h1:WD8iJ37bRNwvETMfVTusVSAi0WdXTpfNVGY2aHycNKY=
|
||||
github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U=
|
||||
github.com/hjson/hjson-go v0.0.0-20181010104306-a25ecf6bd222 h1:xmvkbxXDeN1ffWq8kvrhyqVYAO2aXuRBsbpxVTR+JyU=
|
||||
github.com/hjson/hjson-go v0.0.0-20181010104306-a25ecf6bd222/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio=
|
||||
github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 h1:YnZmFjg0Nvk8851WTVWlqMC1ecJH07Ctz+Ezxx4u54g=
|
||||
|
@ -51,12 +51,12 @@ ip netns exec node4 ip link set lo up
|
||||
ip netns exec node5 ip link set lo up
|
||||
ip netns exec node6 ip link set lo up
|
||||
|
||||
ip netns exec node1 env PPROFLISTEN=localhost:6060 ./run --autoconf &> /dev/null &
|
||||
ip netns exec node2 env PPROFLISTEN=localhost:6060 ./run --autoconf &> /dev/null &
|
||||
ip netns exec node3 env PPROFLISTEN=localhost:6060 ./run --autoconf &> /dev/null &
|
||||
ip netns exec node4 env PPROFLISTEN=localhost:6060 ./run --autoconf &> /dev/null &
|
||||
ip netns exec node5 env PPROFLISTEN=localhost:6060 ./run --autoconf &> /dev/null &
|
||||
ip netns exec node6 env PPROFLISTEN=localhost:6060 ./run --autoconf &> /dev/null &
|
||||
echo '{AdminListen: "none"}' | ip netns exec node1 env PPROFLISTEN=localhost:6060 ./yggdrasil --useconf &> /dev/null &
|
||||
echo '{AdminListen: "none"}' | ip netns exec node2 env PPROFLISTEN=localhost:6060 ./yggdrasil --useconf &> /dev/null &
|
||||
echo '{AdminListen: "none"}' | ip netns exec node3 env PPROFLISTEN=localhost:6060 ./yggdrasil --useconf &> /dev/null &
|
||||
echo '{AdminListen: "none"}' | ip netns exec node4 env PPROFLISTEN=localhost:6060 ./yggdrasil --useconf &> /dev/null &
|
||||
echo '{AdminListen: "none"}' | ip netns exec node5 env PPROFLISTEN=localhost:6060 ./yggdrasil --useconf &> /dev/null &
|
||||
echo '{AdminListen: "none"}' | ip netns exec node6 env PPROFLISTEN=localhost:6060 ./yggdrasil --useconf &> /dev/null &
|
||||
|
||||
echo "Started, to continue you should (possibly w/ sudo):"
|
||||
echo "kill" $(jobs -p)
|
||||
|
33
misc/run-twolink-test
Executable file
33
misc/run-twolink-test
Executable file
@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Connects nodes in two namespaces by two links with different bandwidth (10mbit and 100mbit)
|
||||
|
||||
ip netns add node1
|
||||
ip netns add node2
|
||||
|
||||
ip link add veth11 type veth peer name veth21
|
||||
ip link set veth11 netns node1 up
|
||||
ip link set veth21 netns node2 up
|
||||
|
||||
ip link add veth12 type veth peer name veth22
|
||||
ip link set veth12 netns node1 up
|
||||
ip link set veth22 netns node2 up
|
||||
|
||||
ip netns exec node1 tc qdisc add dev veth11 root tbf rate 10mbit burst 8192 latency 1ms
|
||||
ip netns exec node2 tc qdisc add dev veth21 root tbf rate 10mbit burst 8192 latency 1ms
|
||||
|
||||
ip netns exec node1 tc qdisc add dev veth12 root tbf rate 100mbit burst 8192 latency 1ms
|
||||
ip netns exec node2 tc qdisc add dev veth22 root tbf rate 100mbit burst 8192 latency 1ms
|
||||
|
||||
echo '{AdminListen: "unix://node1.sock"}' | ip netns exec node1 env PPROFLISTEN=localhost:6060 ./yggdrasil -logging "info,warn,error,debug" -useconf &> node1.log &
|
||||
echo '{AdminListen: "unix://node2.sock"}' | ip netns exec node2 env PPROFLISTEN=localhost:6060 ./yggdrasil -logging "info,warn,error,debug" -useconf &> node2.log &
|
||||
|
||||
echo "Started, to continue you should (possibly w/ sudo):"
|
||||
echo "kill" $(jobs -p)
|
||||
wait
|
||||
|
||||
ip netns delete node1
|
||||
ip netns delete node2
|
||||
|
||||
ip link delete veth11
|
||||
ip link delete veth12
|
@ -1,12 +1,21 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||
)
|
||||
|
||||
// NodeConfig defines all configuration values needed to run a signle yggdrasil node
|
||||
type NodeConfig struct {
|
||||
Listen string `comment:"Listen address for peer connections. Default is to listen for all\nTCP connections over IPv4 and IPv6 with a random port."`
|
||||
AdminListen string `comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."`
|
||||
Peers []string `comment:"List of connection strings for static peers in URI format, e.g.\ntcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j."`
|
||||
InterfacePeers map[string][]string `comment:"List of connection strings for static peers in URI format, arranged\nby source interface, e.g. { \"eth0\": [ tcp://a.b.c.d:e ] }. Note that\nSOCKS peerings will NOT be affected by this option and should go in\nthe \"Peers\" section instead."`
|
||||
ReadTimeout int32 `comment:"Read timeout for connections, specified in milliseconds. If less\nthan 6000 and not negative, 6000 (the default) is used. If negative,\nreads won't time out."`
|
||||
AllowedEncryptionPublicKeys []string `comment:"List of peer encryption public keys to allow or incoming TCP\nconnections from. If left empty/undefined then all connections\nwill be allowed by default."`
|
||||
EncryptionPublicKey string `comment:"Your public encryption key. Your peers may ask you for this to put\ninto their AllowedEncryptionPublicKeys configuration."`
|
||||
EncryptionPrivateKey string `comment:"Your private encryption key. DO NOT share this with anyone!"`
|
||||
@ -53,3 +62,45 @@ type TunnelRouting struct {
|
||||
type SwitchOptions struct {
|
||||
MaxTotalQueueSize uint64 `comment:"Maximum size of all switch queues combined (in bytes)."`
|
||||
}
|
||||
|
||||
// Generates default configuration. This is used when outputting the -genconf
|
||||
// parameter and also when using -autoconf. The isAutoconf flag is used to
|
||||
// determine whether the operating system should select a free port by itself
|
||||
// (which guarantees that there will not be a conflict with any other services)
|
||||
// or whether to generate a random port number. The only side effect of setting
|
||||
// isAutoconf is that the TCP and UDP ports will likely end up with different
|
||||
// port numbers.
|
||||
func GenerateConfig(isAutoconf bool) *NodeConfig {
|
||||
// Create a new core.
|
||||
//core := Core{}
|
||||
// Generate encryption keys.
|
||||
bpub, bpriv := crypto.NewBoxKeys()
|
||||
spub, spriv := crypto.NewSigKeys()
|
||||
// Create a node configuration and populate it.
|
||||
cfg := NodeConfig{}
|
||||
if isAutoconf {
|
||||
cfg.Listen = "[::]:0"
|
||||
} else {
|
||||
r1 := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
cfg.Listen = fmt.Sprintf("[::]:%d", r1.Intn(65534-32768)+32768)
|
||||
}
|
||||
cfg.AdminListen = defaults.GetDefaults().DefaultAdminListen
|
||||
cfg.EncryptionPublicKey = hex.EncodeToString(bpub[:])
|
||||
cfg.EncryptionPrivateKey = hex.EncodeToString(bpriv[:])
|
||||
cfg.SigningPublicKey = hex.EncodeToString(spub[:])
|
||||
cfg.SigningPrivateKey = hex.EncodeToString(spriv[:])
|
||||
cfg.Peers = []string{}
|
||||
cfg.InterfacePeers = map[string][]string{}
|
||||
cfg.AllowedEncryptionPublicKeys = []string{}
|
||||
cfg.MulticastInterfaces = []string{".*"}
|
||||
cfg.IfName = defaults.GetDefaults().DefaultIfName
|
||||
cfg.IfMTU = defaults.GetDefaults().DefaultIfMTU
|
||||
cfg.IfTAPMode = defaults.GetDefaults().DefaultIfTAPMode
|
||||
cfg.SessionFirewall.Enable = false
|
||||
cfg.SessionFirewall.AllowFromDirect = true
|
||||
cfg.SessionFirewall.AllowFromRemote = true
|
||||
cfg.SwitchOptions.MaxTotalQueueSize = 4 * 1024 * 1024
|
||||
cfg.NodeInfoPrivacy = false
|
||||
|
||||
return &cfg
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package util
|
||||
// These are misc. utility functions that didn't really fit anywhere else
|
||||
|
||||
import "runtime"
|
||||
import "time"
|
||||
|
||||
// A wrapper around runtime.Gosched() so it doesn't need to be imported elsewhere.
|
||||
func Yield() {
|
||||
@ -44,3 +45,14 @@ func PutBytes(bs []byte) {
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// This is a workaround to go's broken timer implementation
|
||||
func TimerStop(t *time.Timer) bool {
|
||||
if !t.Stop() {
|
||||
select {
|
||||
case <-t.C:
|
||||
default:
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -1,20 +1,12 @@
|
||||
package yggdrasil
|
||||
|
||||
// Defines the minimum required functions for an adapter type.
|
||||
type AdapterInterface interface {
|
||||
init(core *Core, send chan<- []byte, recv <-chan []byte)
|
||||
read() error
|
||||
write() error
|
||||
close() error
|
||||
}
|
||||
|
||||
// Defines the minimum required struct members for an adapter type (this is
|
||||
// now the base type for tunAdapter in tun.go)
|
||||
type Adapter struct {
|
||||
AdapterInterface
|
||||
core *Core
|
||||
send chan<- []byte
|
||||
recv <-chan []byte
|
||||
core *Core
|
||||
send chan<- []byte
|
||||
recv <-chan []byte
|
||||
reconfigure chan chan error
|
||||
}
|
||||
|
||||
// Initialises the adapter.
|
||||
@ -22,4 +14,5 @@ func (adapter *Adapter) init(core *Core, send chan<- []byte, recv <-chan []byte)
|
||||
adapter.core = core
|
||||
adapter.send = send
|
||||
adapter.recv = recv
|
||||
adapter.reconfigure = make(chan chan error, 1)
|
||||
}
|
||||
|
@ -22,10 +22,11 @@ import (
|
||||
// TODO: Add authentication
|
||||
|
||||
type admin struct {
|
||||
core *Core
|
||||
listenaddr string
|
||||
listener net.Listener
|
||||
handlers []admin_handlerInfo
|
||||
core *Core
|
||||
reconfigure chan chan error
|
||||
listenaddr string
|
||||
listener net.Listener
|
||||
handlers []admin_handlerInfo
|
||||
}
|
||||
|
||||
type admin_info map[string]interface{}
|
||||
@ -51,9 +52,25 @@ func (a *admin) addHandler(name string, args []string, handler func(admin_info)
|
||||
}
|
||||
|
||||
// init runs the initial admin setup.
|
||||
func (a *admin) init(c *Core, listenaddr string) {
|
||||
func (a *admin) init(c *Core) {
|
||||
a.core = c
|
||||
a.listenaddr = listenaddr
|
||||
a.reconfigure = make(chan chan error, 1)
|
||||
go func() {
|
||||
for {
|
||||
e := <-a.reconfigure
|
||||
a.core.configMutex.RLock()
|
||||
if a.core.config.AdminListen != a.core.configOld.AdminListen {
|
||||
a.listenaddr = a.core.config.AdminListen
|
||||
a.close()
|
||||
a.start()
|
||||
}
|
||||
a.core.configMutex.RUnlock()
|
||||
e <- nil
|
||||
}
|
||||
}()
|
||||
a.core.configMutex.RLock()
|
||||
a.listenaddr = a.core.config.AdminListen
|
||||
a.core.configMutex.RUnlock()
|
||||
a.addHandler("list", []string{}, func(in admin_info) (admin_info, error) {
|
||||
handlers := make(map[string]interface{})
|
||||
for _, handler := range a.handlers {
|
||||
@ -324,12 +341,30 @@ func (a *admin) init(c *Core, listenaddr string) {
|
||||
return admin_info{}, err
|
||||
}
|
||||
})
|
||||
a.addHandler("getNodeInfo", []string{"box_pub_key", "coords", "[nocache]"}, func(in admin_info) (admin_info, error) {
|
||||
a.addHandler("getNodeInfo", []string{"[box_pub_key]", "[coords]", "[nocache]"}, func(in admin_info) (admin_info, error) {
|
||||
var nocache bool
|
||||
if in["nocache"] != nil {
|
||||
nocache = in["nocache"].(string) == "true"
|
||||
}
|
||||
result, err := a.admin_getNodeInfo(in["box_pub_key"].(string), in["coords"].(string), nocache)
|
||||
var box_pub_key, coords string
|
||||
if in["box_pub_key"] == nil && in["coords"] == nil {
|
||||
var nodeinfo []byte
|
||||
a.core.router.doAdmin(func() {
|
||||
nodeinfo = []byte(a.core.router.nodeinfo.getNodeInfo())
|
||||
})
|
||||
var jsoninfo interface{}
|
||||
if err := json.Unmarshal(nodeinfo, &jsoninfo); err != nil {
|
||||
return admin_info{}, err
|
||||
} else {
|
||||
return admin_info{"nodeinfo": jsoninfo}, nil
|
||||
}
|
||||
} else if in["box_pub_key"] == nil || in["coords"] == nil {
|
||||
return admin_info{}, errors.New("Expecting both box_pub_key and coords")
|
||||
} else {
|
||||
box_pub_key = in["box_pub_key"].(string)
|
||||
coords = in["coords"].(string)
|
||||
}
|
||||
result, err := a.admin_getNodeInfo(box_pub_key, coords, nocache)
|
||||
if err == nil {
|
||||
var m map[string]interface{}
|
||||
if err = json.Unmarshal(result, &m); err == nil {
|
||||
@ -353,7 +388,11 @@ func (a *admin) start() error {
|
||||
|
||||
// cleans up when stopping
|
||||
func (a *admin) close() error {
|
||||
return a.listener.Close()
|
||||
if a.listener != nil {
|
||||
return a.listener.Close()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// listen is run by start and manages API connections.
|
||||
@ -363,7 +402,7 @@ func (a *admin) listen() {
|
||||
switch strings.ToLower(u.Scheme) {
|
||||
case "unix":
|
||||
if _, err := os.Stat(a.listenaddr[7:]); err == nil {
|
||||
a.core.log.Println("WARNING:", a.listenaddr[7:], "already exists and may be in use by another process")
|
||||
a.core.log.Warnln("WARNING:", a.listenaddr[7:], "already exists and may be in use by another process")
|
||||
}
|
||||
a.listener, err = net.Listen("unix", a.listenaddr[7:])
|
||||
if err == nil {
|
||||
@ -371,7 +410,7 @@ func (a *admin) listen() {
|
||||
case "@": // maybe abstract namespace
|
||||
default:
|
||||
if err := os.Chmod(a.listenaddr[7:], 0660); err != nil {
|
||||
a.core.log.Println("WARNING:", a.listenaddr[:7], "may have unsafe permissions!")
|
||||
a.core.log.Warnln("WARNING:", a.listenaddr[:7], "may have unsafe permissions!")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -385,10 +424,10 @@ func (a *admin) listen() {
|
||||
a.listener, err = net.Listen("tcp", a.listenaddr)
|
||||
}
|
||||
if err != nil {
|
||||
a.core.log.Printf("Admin socket failed to listen: %v", err)
|
||||
a.core.log.Errorf("Admin socket failed to listen: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
a.core.log.Printf("%s admin socket listening on %s",
|
||||
a.core.log.Infof("%s admin socket listening on %s",
|
||||
strings.ToUpper(a.listener.Addr().Network()),
|
||||
a.listener.Addr().String())
|
||||
defer a.listener.Close()
|
||||
@ -415,9 +454,9 @@ func (a *admin) handleRequest(conn net.Conn) {
|
||||
"status": "error",
|
||||
"error": "Unrecoverable error, possibly as a result of invalid input types or malformed syntax",
|
||||
}
|
||||
fmt.Println("Admin socket error:", r)
|
||||
a.core.log.Errorln("Admin socket error:", r)
|
||||
if err := encoder.Encode(&send); err != nil {
|
||||
fmt.Println("Admin socket JSON encode error:", err)
|
||||
a.core.log.Errorln("Admin socket JSON encode error:", err)
|
||||
}
|
||||
conn.Close()
|
||||
}
|
||||
@ -730,35 +769,20 @@ func (a *admin) getData_getSessions() []admin_nodeInfo {
|
||||
|
||||
// getAllowedEncryptionPublicKeys returns the public keys permitted for incoming peer connections.
|
||||
func (a *admin) getAllowedEncryptionPublicKeys() []string {
|
||||
pubs := a.core.peers.getAllowedEncryptionPublicKeys()
|
||||
var out []string
|
||||
for _, pub := range pubs {
|
||||
out = append(out, hex.EncodeToString(pub[:]))
|
||||
}
|
||||
return out
|
||||
return a.core.peers.getAllowedEncryptionPublicKeys()
|
||||
}
|
||||
|
||||
// addAllowedEncryptionPublicKey whitelists a key for incoming peer connections.
|
||||
func (a *admin) addAllowedEncryptionPublicKey(bstr string) (err error) {
|
||||
boxBytes, err := hex.DecodeString(bstr)
|
||||
if err == nil {
|
||||
var box crypto.BoxPubKey
|
||||
copy(box[:], boxBytes)
|
||||
a.core.peers.addAllowedEncryptionPublicKey(&box)
|
||||
}
|
||||
return
|
||||
a.core.peers.addAllowedEncryptionPublicKey(bstr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// removeAllowedEncryptionPublicKey removes a key from the whitelist for incoming peer connections.
|
||||
// If none are set, an empty list permits all incoming connections.
|
||||
func (a *admin) removeAllowedEncryptionPublicKey(bstr string) (err error) {
|
||||
boxBytes, err := hex.DecodeString(bstr)
|
||||
if err == nil {
|
||||
var box crypto.BoxPubKey
|
||||
copy(box[:], boxBytes)
|
||||
a.core.peers.removeAllowedEncryptionPublicKey(&box)
|
||||
}
|
||||
return
|
||||
a.core.peers.removeAllowedEncryptionPublicKey(bstr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send a DHT ping to the node with the provided key and coords, optionally looking up the specified target NodeID.
|
||||
@ -827,7 +851,7 @@ func (a *admin) admin_getNodeInfo(keyString, coordString string, nocache bool) (
|
||||
copy(key[:], keyBytes)
|
||||
}
|
||||
if !nocache {
|
||||
if response, err := a.core.nodeinfo.getCachedNodeInfo(key); err == nil {
|
||||
if response, err := a.core.router.nodeinfo.getCachedNodeInfo(key); err == nil {
|
||||
return response, nil
|
||||
}
|
||||
}
|
||||
@ -845,14 +869,14 @@ func (a *admin) admin_getNodeInfo(keyString, coordString string, nocache bool) (
|
||||
}
|
||||
response := make(chan *nodeinfoPayload, 1)
|
||||
sendNodeInfoRequest := func() {
|
||||
a.core.nodeinfo.addCallback(key, func(nodeinfo *nodeinfoPayload) {
|
||||
a.core.router.nodeinfo.addCallback(key, func(nodeinfo *nodeinfoPayload) {
|
||||
defer func() { recover() }()
|
||||
select {
|
||||
case response <- nodeinfo:
|
||||
default:
|
||||
}
|
||||
})
|
||||
a.core.nodeinfo.sendNodeInfo(key, coords, false)
|
||||
a.core.router.nodeinfo.sendNodeInfo(key, coords, false)
|
||||
}
|
||||
a.core.router.doAdmin(sendNodeInfoRequest)
|
||||
go func() {
|
||||
|
98
src/yggdrasil/awdl.go
Normal file
98
src/yggdrasil/awdl.go
Normal file
@ -0,0 +1,98 @@
|
||||
package yggdrasil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type awdl struct {
|
||||
link *link
|
||||
mutex sync.RWMutex // protects interfaces below
|
||||
interfaces map[string]*awdlInterface
|
||||
}
|
||||
|
||||
type awdlInterface struct {
|
||||
linkif *linkInterface
|
||||
rwc awdlReadWriteCloser
|
||||
peer *peer
|
||||
stream stream
|
||||
}
|
||||
|
||||
type awdlReadWriteCloser struct {
|
||||
fromAWDL chan []byte
|
||||
toAWDL chan []byte
|
||||
}
|
||||
|
||||
func (c awdlReadWriteCloser) Read(p []byte) (n int, err error) {
|
||||
if packet, ok := <-c.fromAWDL; ok {
|
||||
n = copy(p, packet)
|
||||
return n, nil
|
||||
}
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
func (c awdlReadWriteCloser) Write(p []byte) (n int, err error) {
|
||||
var pc []byte
|
||||
pc = append(pc, p...)
|
||||
c.toAWDL <- pc
|
||||
return len(pc), nil
|
||||
}
|
||||
|
||||
func (c awdlReadWriteCloser) Close() error {
|
||||
close(c.fromAWDL)
|
||||
close(c.toAWDL)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *awdl) init(l *link) error {
|
||||
a.link = l
|
||||
a.mutex.Lock()
|
||||
a.interfaces = make(map[string]*awdlInterface)
|
||||
a.mutex.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *awdl) create(name, local, remote string, incoming bool) (*awdlInterface, error) {
|
||||
rwc := awdlReadWriteCloser{
|
||||
fromAWDL: make(chan []byte, 1),
|
||||
toAWDL: make(chan []byte, 1),
|
||||
}
|
||||
s := stream{}
|
||||
s.init(rwc)
|
||||
linkif, err := a.link.create(&s, name, "awdl", local, remote, incoming, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
intf := awdlInterface{
|
||||
linkif: linkif,
|
||||
rwc: rwc,
|
||||
}
|
||||
a.mutex.Lock()
|
||||
a.interfaces[name] = &intf
|
||||
a.mutex.Unlock()
|
||||
go intf.linkif.handler()
|
||||
return &intf, nil
|
||||
}
|
||||
|
||||
func (a *awdl) getInterface(identity string) *awdlInterface {
|
||||
a.mutex.RLock()
|
||||
defer a.mutex.RUnlock()
|
||||
if intf, ok := a.interfaces[identity]; ok {
|
||||
return intf
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *awdl) shutdown(identity string) error {
|
||||
if intf, ok := a.interfaces[identity]; ok {
|
||||
close(intf.linkif.closed)
|
||||
intf.rwc.Close()
|
||||
a.mutex.Lock()
|
||||
delete(a.interfaces, identity)
|
||||
a.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
return errors.New("Interface not found or already closed")
|
||||
}
|
@ -18,6 +18,7 @@ import (
|
||||
type cryptokey struct {
|
||||
core *Core
|
||||
enabled bool
|
||||
reconfigure chan chan error
|
||||
ipv4routes []cryptokey_route
|
||||
ipv6routes []cryptokey_route
|
||||
ipv4cache map[address.Address]cryptokey_route
|
||||
@ -34,12 +35,75 @@ type cryptokey_route struct {
|
||||
// Initialise crypto-key routing. This must be done before any other CKR calls.
|
||||
func (c *cryptokey) init(core *Core) {
|
||||
c.core = core
|
||||
c.ipv4routes = make([]cryptokey_route, 0)
|
||||
c.reconfigure = make(chan chan error, 1)
|
||||
go func() {
|
||||
for {
|
||||
e := <-c.reconfigure
|
||||
var err error
|
||||
c.core.router.doAdmin(func() {
|
||||
err = c.core.router.cryptokey.configure()
|
||||
})
|
||||
e <- err
|
||||
}
|
||||
}()
|
||||
|
||||
if err := c.configure(); err != nil {
|
||||
c.core.log.Errorln("CKR configuration failed:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the CKR routes - this must only ever be called from the router
|
||||
// goroutine, e.g. through router.doAdmin
|
||||
func (c *cryptokey) configure() error {
|
||||
c.core.configMutex.RLock()
|
||||
defer c.core.configMutex.RUnlock()
|
||||
|
||||
// Set enabled/disabled state
|
||||
c.setEnabled(c.core.config.TunnelRouting.Enable)
|
||||
|
||||
// Clear out existing routes
|
||||
c.ipv6routes = make([]cryptokey_route, 0)
|
||||
c.ipv4routes = make([]cryptokey_route, 0)
|
||||
|
||||
// Add IPv6 routes
|
||||
for ipv6, pubkey := range c.core.config.TunnelRouting.IPv6Destinations {
|
||||
if err := c.addRoute(ipv6, pubkey); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Add IPv4 routes
|
||||
for ipv4, pubkey := range c.core.config.TunnelRouting.IPv4Destinations {
|
||||
if err := c.addRoute(ipv4, pubkey); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Clear out existing sources
|
||||
c.ipv6sources = make([]net.IPNet, 0)
|
||||
c.ipv4sources = make([]net.IPNet, 0)
|
||||
|
||||
// Add IPv6 sources
|
||||
c.ipv6sources = make([]net.IPNet, 0)
|
||||
for _, source := range c.core.config.TunnelRouting.IPv6Sources {
|
||||
if err := c.addSourceSubnet(source); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Add IPv4 sources
|
||||
c.ipv4sources = make([]net.IPNet, 0)
|
||||
for _, source := range c.core.config.TunnelRouting.IPv4Sources {
|
||||
if err := c.addSourceSubnet(source); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Wipe the caches
|
||||
c.ipv4cache = make(map[address.Address]cryptokey_route, 0)
|
||||
c.ipv6cache = make(map[address.Address]cryptokey_route, 0)
|
||||
c.ipv4sources = make([]net.IPNet, 0)
|
||||
c.ipv6sources = make([]net.IPNet, 0)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Enable or disable crypto-key routing.
|
||||
@ -128,7 +192,7 @@ func (c *cryptokey) addSourceSubnet(cidr string) error {
|
||||
|
||||
// Add the source subnet
|
||||
*routingsources = append(*routingsources, *ipnet)
|
||||
c.core.log.Println("Added CKR source subnet", cidr)
|
||||
c.core.log.Infoln("Added CKR source subnet", cidr)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -200,7 +264,7 @@ func (c *cryptokey) addRoute(cidr string, dest string) error {
|
||||
delete(*routingcache, k)
|
||||
}
|
||||
|
||||
c.core.log.Println("Added CKR destination subnet", cidr)
|
||||
c.core.log.Infoln("Added CKR destination subnet", cidr)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -294,7 +358,7 @@ func (c *cryptokey) removeSourceSubnet(cidr string) error {
|
||||
for idx, subnet := range *routingsources {
|
||||
if subnet.String() == ipnet.String() {
|
||||
*routingsources = append((*routingsources)[:idx], (*routingsources)[idx+1:]...)
|
||||
c.core.log.Println("Removed CKR source subnet", cidr)
|
||||
c.core.log.Infoln("Removed CKR source subnet", cidr)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -343,7 +407,7 @@ func (c *cryptokey) removeRoute(cidr string, dest string) error {
|
||||
for k := range *routingcache {
|
||||
delete(*routingcache, k)
|
||||
}
|
||||
c.core.log.Printf("Removed CKR destination subnet %s via %s\n", cidr, dest)
|
||||
c.core.log.Infoln("Removed CKR destination subnet %s via %s\n", cidr, dest)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,12 @@ package yggdrasil
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"regexp"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gologme/log"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||
@ -17,10 +18,20 @@ import (
|
||||
var buildName string
|
||||
var buildVersion string
|
||||
|
||||
type module interface {
|
||||
init(*Core, *config.NodeConfig) error
|
||||
start() error
|
||||
}
|
||||
|
||||
// The Core object represents the Yggdrasil node. You should create a Core
|
||||
// object for each Yggdrasil node you plan to run.
|
||||
type Core struct {
|
||||
// This is the main data structure that holds everything else for a node
|
||||
// We're going to keep our own copy of the provided config - that way we can
|
||||
// guarantee that it will be covered by the mutex
|
||||
config config.NodeConfig // Active config
|
||||
configOld config.NodeConfig // Previous config
|
||||
configMutex sync.RWMutex // Protects both config and configOld
|
||||
boxPub crypto.BoxPubKey
|
||||
boxPriv crypto.BoxPrivKey
|
||||
sigPub crypto.SigPubKey
|
||||
@ -33,16 +44,12 @@ type Core struct {
|
||||
admin admin
|
||||
searches searches
|
||||
multicast multicast
|
||||
nodeinfo nodeinfo
|
||||
tcp tcpInterface
|
||||
link link
|
||||
log *log.Logger
|
||||
ifceExpr []*regexp.Regexp // the zone of link-local IPv6 peers must match this
|
||||
}
|
||||
|
||||
func (c *Core) init(bpub *crypto.BoxPubKey,
|
||||
bpriv *crypto.BoxPrivKey,
|
||||
spub *crypto.SigPubKey,
|
||||
spriv *crypto.SigPrivKey) {
|
||||
func (c *Core) init() error {
|
||||
// TODO separate init and start functions
|
||||
// Init sets up structs
|
||||
// Start launches goroutines that depend on structs being set up
|
||||
@ -50,20 +57,104 @@ func (c *Core) init(bpub *crypto.BoxPubKey,
|
||||
if c.log == nil {
|
||||
c.log = log.New(ioutil.Discard, "", 0)
|
||||
}
|
||||
c.boxPub, c.boxPriv = *bpub, *bpriv
|
||||
c.sigPub, c.sigPriv = *spub, *spriv
|
||||
c.admin.core = c
|
||||
|
||||
boxPubHex, err := hex.DecodeString(c.config.EncryptionPublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
boxPrivHex, err := hex.DecodeString(c.config.EncryptionPrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sigPubHex, err := hex.DecodeString(c.config.SigningPublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sigPrivHex, err := hex.DecodeString(c.config.SigningPrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
copy(c.boxPub[:], boxPubHex)
|
||||
copy(c.boxPriv[:], boxPrivHex)
|
||||
copy(c.sigPub[:], sigPubHex)
|
||||
copy(c.sigPriv[:], sigPrivHex)
|
||||
|
||||
c.admin.init(c)
|
||||
c.searches.init(c)
|
||||
c.dht.init(c)
|
||||
c.sessions.init(c)
|
||||
c.multicast.init(c)
|
||||
c.peers.init(c)
|
||||
c.router.init(c)
|
||||
c.switchTable.init(c, c.sigPub) // TODO move before peers? before router?
|
||||
c.switchTable.init(c) // TODO move before peers? before router?
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the current build name. This is usually injected if built from git,
|
||||
// or returns "unknown" otherwise.
|
||||
// If any static peers were provided in the configuration above then we should
|
||||
// configure them. The loop ensures that disconnected peers will eventually
|
||||
// be reconnected with.
|
||||
func (c *Core) addPeerLoop() {
|
||||
for {
|
||||
// Get the peers from the config - these could change!
|
||||
c.configMutex.RLock()
|
||||
peers := c.config.Peers
|
||||
interfacepeers := c.config.InterfacePeers
|
||||
c.configMutex.RUnlock()
|
||||
|
||||
// Add peers from the Peers section
|
||||
for _, peer := range peers {
|
||||
c.AddPeer(peer, "")
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
// Add peers from the InterfacePeers section
|
||||
for intf, intfpeers := range interfacepeers {
|
||||
for _, peer := range intfpeers {
|
||||
c.AddPeer(peer, intf)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// Sit for a while
|
||||
time.Sleep(time.Minute)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateConfig updates the configuration in Core and then signals the
|
||||
// various module goroutines to reconfigure themselves if needed
|
||||
func (c *Core) UpdateConfig(config *config.NodeConfig) {
|
||||
c.configMutex.Lock()
|
||||
c.configOld = c.config
|
||||
c.config = *config
|
||||
c.configMutex.Unlock()
|
||||
|
||||
components := []chan chan error{
|
||||
c.admin.reconfigure,
|
||||
c.searches.reconfigure,
|
||||
c.dht.reconfigure,
|
||||
c.sessions.reconfigure,
|
||||
c.peers.reconfigure,
|
||||
c.router.reconfigure,
|
||||
c.router.tun.reconfigure,
|
||||
c.router.cryptokey.reconfigure,
|
||||
c.switchTable.reconfigure,
|
||||
c.tcp.reconfigure,
|
||||
c.multicast.reconfigure,
|
||||
}
|
||||
|
||||
for _, component := range components {
|
||||
response := make(chan error)
|
||||
component <- response
|
||||
if err := <-response; err != nil {
|
||||
c.log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetBuildName gets the current build name. This is usually injected if built
|
||||
// from git, or returns "unknown" otherwise.
|
||||
func GetBuildName() string {
|
||||
if buildName == "" {
|
||||
return "unknown"
|
||||
@ -88,47 +179,28 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
|
||||
c.log = log
|
||||
|
||||
if name := GetBuildName(); name != "unknown" {
|
||||
c.log.Println("Build name:", name)
|
||||
c.log.Infoln("Build name:", name)
|
||||
}
|
||||
if version := GetBuildVersion(); version != "unknown" {
|
||||
c.log.Println("Build version:", version)
|
||||
c.log.Infoln("Build version:", version)
|
||||
}
|
||||
|
||||
c.log.Println("Starting up...")
|
||||
c.log.Infoln("Starting up...")
|
||||
|
||||
var boxPub crypto.BoxPubKey
|
||||
var boxPriv crypto.BoxPrivKey
|
||||
var sigPub crypto.SigPubKey
|
||||
var sigPriv crypto.SigPrivKey
|
||||
boxPubHex, err := hex.DecodeString(nc.EncryptionPublicKey)
|
||||
if err != nil {
|
||||
c.configMutex.Lock()
|
||||
c.config = *nc
|
||||
c.configOld = c.config
|
||||
c.configMutex.Unlock()
|
||||
|
||||
c.init()
|
||||
|
||||
if err := c.tcp.init(c); err != nil {
|
||||
c.log.Errorln("Failed to start TCP interface")
|
||||
return err
|
||||
}
|
||||
boxPrivHex, err := hex.DecodeString(nc.EncryptionPrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sigPubHex, err := hex.DecodeString(nc.SigningPublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sigPrivHex, err := hex.DecodeString(nc.SigningPrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(boxPub[:], boxPubHex)
|
||||
copy(boxPriv[:], boxPrivHex)
|
||||
copy(sigPub[:], sigPubHex)
|
||||
copy(sigPriv[:], sigPrivHex)
|
||||
|
||||
c.init(&boxPub, &boxPriv, &sigPub, &sigPriv)
|
||||
c.admin.init(c, nc.AdminListen)
|
||||
|
||||
c.nodeinfo.init(c)
|
||||
c.nodeinfo.setNodeInfo(nc.NodeInfo, nc.NodeInfoPrivacy)
|
||||
|
||||
if err := c.tcp.init(c, nc.Listen, nc.ReadTimeout); err != nil {
|
||||
c.log.Println("Failed to start TCP interface")
|
||||
if err := c.link.init(c); err != nil {
|
||||
c.log.Errorln("Failed to start link interfaces")
|
||||
return err
|
||||
}
|
||||
|
||||
@ -137,72 +209,39 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
|
||||
}
|
||||
|
||||
if err := c.switchTable.start(); err != nil {
|
||||
c.log.Println("Failed to start switch")
|
||||
c.log.Errorln("Failed to start switch")
|
||||
return err
|
||||
}
|
||||
|
||||
c.sessions.setSessionFirewallState(nc.SessionFirewall.Enable)
|
||||
c.sessions.setSessionFirewallDefaults(
|
||||
nc.SessionFirewall.AllowFromDirect,
|
||||
nc.SessionFirewall.AllowFromRemote,
|
||||
nc.SessionFirewall.AlwaysAllowOutbound,
|
||||
)
|
||||
c.sessions.setSessionFirewallWhitelist(nc.SessionFirewall.WhitelistEncryptionPublicKeys)
|
||||
c.sessions.setSessionFirewallBlacklist(nc.SessionFirewall.BlacklistEncryptionPublicKeys)
|
||||
|
||||
if err := c.router.start(); err != nil {
|
||||
c.log.Println("Failed to start router")
|
||||
c.log.Errorln("Failed to start router")
|
||||
return err
|
||||
}
|
||||
|
||||
c.router.cryptokey.setEnabled(nc.TunnelRouting.Enable)
|
||||
if c.router.cryptokey.isEnabled() {
|
||||
c.log.Println("Crypto-key routing enabled")
|
||||
for ipv6, pubkey := range nc.TunnelRouting.IPv6Destinations {
|
||||
if err := c.router.cryptokey.addRoute(ipv6, pubkey); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
for _, source := range nc.TunnelRouting.IPv6Sources {
|
||||
if c.router.cryptokey.addSourceSubnet(source); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
for ipv4, pubkey := range nc.TunnelRouting.IPv4Destinations {
|
||||
if err := c.router.cryptokey.addRoute(ipv4, pubkey); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
for _, source := range nc.TunnelRouting.IPv4Sources {
|
||||
if c.router.cryptokey.addSourceSubnet(source); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.admin.start(); err != nil {
|
||||
c.log.Println("Failed to start admin socket")
|
||||
c.log.Errorln("Failed to start admin socket")
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.multicast.start(); err != nil {
|
||||
c.log.Println("Failed to start multicast interface")
|
||||
c.log.Errorln("Failed to start multicast interface")
|
||||
return err
|
||||
}
|
||||
|
||||
ip := net.IP(c.router.addr[:]).String()
|
||||
if err := c.router.tun.start(nc.IfName, nc.IfTAPMode, fmt.Sprintf("%s/%d", ip, 8*len(address.GetPrefix())-1), nc.IfMTU); err != nil {
|
||||
c.log.Println("Failed to start TUN/TAP")
|
||||
if err := c.router.tun.start(); err != nil {
|
||||
c.log.Errorln("Failed to start TUN/TAP")
|
||||
return err
|
||||
}
|
||||
|
||||
c.log.Println("Startup complete")
|
||||
go c.addPeerLoop()
|
||||
|
||||
c.log.Infoln("Startup complete")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stops the Yggdrasil node.
|
||||
func (c *Core) Stop() {
|
||||
c.log.Println("Stopping...")
|
||||
c.log.Infoln("Stopping...")
|
||||
c.router.tun.close()
|
||||
c.admin.close()
|
||||
}
|
||||
@ -244,12 +283,12 @@ func (c *Core) GetSubnet() *net.IPNet {
|
||||
|
||||
// Gets the nodeinfo.
|
||||
func (c *Core) GetNodeInfo() nodeinfoPayload {
|
||||
return c.nodeinfo.getNodeInfo()
|
||||
return c.router.nodeinfo.getNodeInfo()
|
||||
}
|
||||
|
||||
// Sets the nodeinfo.
|
||||
func (c *Core) SetNodeInfo(nodeinfo interface{}, nodeinfoprivacy bool) {
|
||||
c.nodeinfo.setNodeInfo(nodeinfo, nodeinfoprivacy)
|
||||
c.router.nodeinfo.setNodeInfo(nodeinfo, nodeinfoprivacy)
|
||||
}
|
||||
|
||||
// Sets the output logger of the Yggdrasil node after startup. This may be
|
||||
@ -264,13 +303,6 @@ func (c *Core) AddPeer(addr string, sintf string) error {
|
||||
return c.admin.addPeer(addr, sintf)
|
||||
}
|
||||
|
||||
// Adds an expression to select multicast interfaces for peer discovery. This
|
||||
// should be done before calling Start. This function can be called multiple
|
||||
// times to add multiple search expressions.
|
||||
func (c *Core) AddMulticastInterfaceExpr(expr *regexp.Regexp) {
|
||||
c.ifceExpr = append(c.ifceExpr, expr)
|
||||
}
|
||||
|
||||
// Adds an allowed public key. This allow peerings to be restricted only to
|
||||
// keys that you have selected.
|
||||
func (c *Core) AddAllowedEncryptionPublicKey(boxStr string) error {
|
||||
|
@ -14,15 +14,18 @@ import _ "golang.org/x/net/ipv6" // TODO put this somewhere better
|
||||
|
||||
import "fmt"
|
||||
import "net"
|
||||
import "log"
|
||||
import "regexp"
|
||||
import "encoding/hex"
|
||||
|
||||
import _ "net/http/pprof"
|
||||
import "net/http"
|
||||
import "runtime"
|
||||
import "os"
|
||||
|
||||
import "github.com/gologme/log"
|
||||
|
||||
import "github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||
import "github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||
import "github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||
import "github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||
|
||||
@ -52,7 +55,17 @@ func StartProfiler(log *log.Logger) error {
|
||||
func (c *Core) Init() {
|
||||
bpub, bpriv := crypto.NewBoxKeys()
|
||||
spub, spriv := crypto.NewSigKeys()
|
||||
c.init(bpub, bpriv, spub, spriv)
|
||||
hbpub := hex.EncodeToString(bpub[:])
|
||||
hbpriv := hex.EncodeToString(bpriv[:])
|
||||
hspub := hex.EncodeToString(spub[:])
|
||||
hspriv := hex.EncodeToString(spriv[:])
|
||||
c.config = config.NodeConfig{
|
||||
EncryptionPublicKey: hbpub,
|
||||
EncryptionPrivateKey: hbpriv,
|
||||
SigningPublicKey: hspub,
|
||||
SigningPrivateKey: hspriv,
|
||||
}
|
||||
c.init( /*bpub, bpriv, spub, spriv*/ )
|
||||
c.switchTable.start()
|
||||
c.router.start()
|
||||
}
|
||||
@ -84,9 +97,7 @@ func (c *Core) DEBUG_getPeers() *peers {
|
||||
}
|
||||
|
||||
func (ps *peers) DEBUG_newPeer(box crypto.BoxPubKey, sig crypto.SigPubKey, link crypto.BoxSharedKey) *peer {
|
||||
//in <-chan []byte,
|
||||
//out chan<- []byte) *peer {
|
||||
return ps.newPeer(&box, &sig, &link, "(simulator)") //, in, out)
|
||||
return ps.newPeer(&box, &sig, &link, "(simulator)", nil)
|
||||
}
|
||||
|
||||
/*
|
||||
@ -350,7 +361,7 @@ func (c *Core) DEBUG_init(bpub []byte,
|
||||
bpriv []byte,
|
||||
spub []byte,
|
||||
spriv []byte) {
|
||||
var boxPub crypto.BoxPubKey
|
||||
/*var boxPub crypto.BoxPubKey
|
||||
var boxPriv crypto.BoxPrivKey
|
||||
var sigPub crypto.SigPubKey
|
||||
var sigPriv crypto.SigPrivKey
|
||||
@ -358,7 +369,18 @@ func (c *Core) DEBUG_init(bpub []byte,
|
||||
copy(boxPriv[:], bpriv)
|
||||
copy(sigPub[:], spub)
|
||||
copy(sigPriv[:], spriv)
|
||||
c.init(&boxPub, &boxPriv, &sigPub, &sigPriv)
|
||||
c.init(&boxPub, &boxPriv, &sigPub, &sigPriv)*/
|
||||
hbpub := hex.EncodeToString(bpub[:])
|
||||
hbpriv := hex.EncodeToString(bpriv[:])
|
||||
hspub := hex.EncodeToString(spub[:])
|
||||
hspriv := hex.EncodeToString(spriv[:])
|
||||
c.config = config.NodeConfig{
|
||||
EncryptionPublicKey: hbpub,
|
||||
EncryptionPrivateKey: hbpriv,
|
||||
SigningPublicKey: hspub,
|
||||
SigningPrivateKey: hspriv,
|
||||
}
|
||||
c.init( /*bpub, bpriv, spub, spriv*/ )
|
||||
|
||||
if err := c.router.start(); err != nil {
|
||||
panic(err)
|
||||
@ -427,7 +449,8 @@ func (c *Core) DEBUG_addSOCKSConn(socksaddr, peeraddr string) {
|
||||
|
||||
//*
|
||||
func (c *Core) DEBUG_setupAndStartGlobalTCPInterface(addrport string) {
|
||||
if err := c.tcp.init(c, addrport, 0); err != nil {
|
||||
c.config.Listen = addrport
|
||||
if err := c.tcp.init(c /*, addrport, 0*/); err != nil {
|
||||
c.log.Println("Failed to start TCP interface:", err)
|
||||
panic(err)
|
||||
}
|
||||
@ -474,7 +497,8 @@ func (c *Core) DEBUG_addKCPConn(saddr string) {
|
||||
|
||||
func (c *Core) DEBUG_setupAndStartAdminInterface(addrport string) {
|
||||
a := admin{}
|
||||
a.init(c, addrport)
|
||||
c.config.AdminListen = addrport
|
||||
a.init(c /*, addrport*/)
|
||||
c.admin = a
|
||||
}
|
||||
|
||||
@ -492,7 +516,7 @@ func (c *Core) DEBUG_setLogger(log *log.Logger) {
|
||||
}
|
||||
|
||||
func (c *Core) DEBUG_setIfceExpr(expr *regexp.Regexp) {
|
||||
c.ifceExpr = append(c.ifceExpr, expr)
|
||||
c.log.Println("DEBUG_setIfceExpr no longer implemented")
|
||||
}
|
||||
|
||||
func (c *Core) DEBUG_addAllowedEncryptionPublicKey(boxStr string) {
|
||||
|
@ -65,11 +65,12 @@ type dhtReqKey struct {
|
||||
|
||||
// The main DHT struct.
|
||||
type dht struct {
|
||||
core *Core
|
||||
nodeID crypto.NodeID
|
||||
peers chan *dhtInfo // other goroutines put incoming dht updates here
|
||||
reqs map[dhtReqKey]time.Time // Keeps track of recent outstanding requests
|
||||
callbacks map[dhtReqKey]dht_callbackInfo // Search and admin lookup callbacks
|
||||
core *Core
|
||||
reconfigure chan chan error
|
||||
nodeID crypto.NodeID
|
||||
peers chan *dhtInfo // other goroutines put incoming dht updates here
|
||||
reqs map[dhtReqKey]time.Time // Keeps track of recent outstanding requests
|
||||
callbacks map[dhtReqKey]dht_callbackInfo // Search and admin lookup callbacks
|
||||
// These next two could be replaced by a single linked list or similar...
|
||||
table map[crypto.NodeID]*dhtInfo
|
||||
imp []*dhtInfo
|
||||
@ -78,6 +79,13 @@ type dht struct {
|
||||
// Initializes the DHT.
|
||||
func (t *dht) init(c *Core) {
|
||||
t.core = c
|
||||
t.reconfigure = make(chan chan error, 1)
|
||||
go func() {
|
||||
for {
|
||||
e := <-t.reconfigure
|
||||
e <- nil
|
||||
}
|
||||
}()
|
||||
t.nodeID = *t.core.GetNodeID()
|
||||
t.peers = make(chan *dhtInfo, 1024)
|
||||
t.callbacks = make(map[dhtReqKey]dht_callbackInfo)
|
||||
|
321
src/yggdrasil/link.go
Normal file
321
src/yggdrasil/link.go
Normal file
@ -0,0 +1,321 @@
|
||||
package yggdrasil
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
//"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||
)
|
||||
|
||||
type link struct {
|
||||
core *Core
|
||||
mutex sync.RWMutex // protects interfaces below
|
||||
interfaces map[linkInfo]*linkInterface
|
||||
awdl awdl // AWDL interface support
|
||||
// TODO timeout (to remove from switch), read from config.ReadTimeout
|
||||
}
|
||||
|
||||
type linkInfo struct {
|
||||
box crypto.BoxPubKey // Their encryption key
|
||||
sig crypto.SigPubKey // Their signing key
|
||||
linkType string // Type of link, e.g. TCP, AWDL
|
||||
local string // Local name or address
|
||||
remote string // Remote name or address
|
||||
}
|
||||
|
||||
type linkInterfaceMsgIO interface {
|
||||
readMsg() ([]byte, error)
|
||||
writeMsg([]byte) (int, error)
|
||||
close() error
|
||||
// These are temporary workarounds to stream semantics
|
||||
_sendMetaBytes([]byte) error
|
||||
_recvMetaBytes() ([]byte, error)
|
||||
}
|
||||
|
||||
type linkInterface struct {
|
||||
name string
|
||||
link *link
|
||||
peer *peer
|
||||
msgIO linkInterfaceMsgIO
|
||||
info linkInfo
|
||||
incoming bool
|
||||
force bool
|
||||
closed chan struct{}
|
||||
}
|
||||
|
||||
func (l *link) init(c *Core) error {
|
||||
l.core = c
|
||||
l.mutex.Lock()
|
||||
l.interfaces = make(map[linkInfo]*linkInterface)
|
||||
l.mutex.Unlock()
|
||||
|
||||
if err := l.awdl.init(l); err != nil {
|
||||
l.core.log.Errorln("Failed to start AWDL interface")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *link) create(msgIO linkInterfaceMsgIO, name, linkType, local, remote string, incoming, force bool) (*linkInterface, error) {
|
||||
// Technically anything unique would work for names, but lets pick something human readable, just for debugging
|
||||
intf := linkInterface{
|
||||
name: name,
|
||||
link: l,
|
||||
msgIO: msgIO,
|
||||
info: linkInfo{
|
||||
linkType: linkType,
|
||||
local: local,
|
||||
remote: remote,
|
||||
},
|
||||
incoming: incoming,
|
||||
force: force,
|
||||
}
|
||||
return &intf, nil
|
||||
}
|
||||
|
||||
func (intf *linkInterface) handler() error {
|
||||
// TODO split some of this into shorter functions, so it's easier to read, and for the FIXME duplicate peer issue mentioned later
|
||||
myLinkPub, myLinkPriv := crypto.NewBoxKeys()
|
||||
meta := version_getBaseMetadata()
|
||||
meta.box = intf.link.core.boxPub
|
||||
meta.sig = intf.link.core.sigPub
|
||||
meta.link = *myLinkPub
|
||||
metaBytes := meta.encode()
|
||||
// TODO timeouts on send/recv (goroutine for send/recv, channel select w/ timer)
|
||||
err := intf.msgIO._sendMetaBytes(metaBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
metaBytes, err = intf.msgIO._recvMetaBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
meta = version_metadata{}
|
||||
if !meta.decode(metaBytes) || !meta.check() {
|
||||
return errors.New("failed to decode metadata")
|
||||
}
|
||||
base := version_getBaseMetadata()
|
||||
if meta.ver > base.ver || meta.ver == base.ver && meta.minorVer > base.minorVer {
|
||||
intf.link.core.log.Errorln("Failed to connect to node: " + intf.name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer))
|
||||
return errors.New("failed to connect: wrong version")
|
||||
}
|
||||
// Check if we're authorized to connect to this key / IP
|
||||
if !intf.force && !intf.link.core.peers.isAllowedEncryptionPublicKey(&meta.box) {
|
||||
intf.link.core.log.Debugf("%s connection to %s forbidden: AllowedEncryptionPublicKeys does not contain key %s",
|
||||
strings.ToUpper(intf.info.linkType), intf.info.remote, hex.EncodeToString(meta.box[:]))
|
||||
intf.msgIO.close()
|
||||
return nil
|
||||
}
|
||||
// Check if we already have a link to this node
|
||||
intf.info.box = meta.box
|
||||
intf.info.sig = meta.sig
|
||||
intf.link.mutex.Lock()
|
||||
if oldIntf, isIn := intf.link.interfaces[intf.info]; isIn {
|
||||
intf.link.mutex.Unlock()
|
||||
// FIXME we should really return an error and let the caller block instead
|
||||
// That lets them do things like close connections on its own, avoid printing a connection message in the first place, etc.
|
||||
intf.link.core.log.Debugln("DEBUG: found existing interface for", intf.name)
|
||||
intf.msgIO.close()
|
||||
<-oldIntf.closed
|
||||
return nil
|
||||
} else {
|
||||
intf.closed = make(chan struct{})
|
||||
intf.link.interfaces[intf.info] = intf
|
||||
defer func() {
|
||||
intf.link.mutex.Lock()
|
||||
delete(intf.link.interfaces, intf.info)
|
||||
intf.link.mutex.Unlock()
|
||||
close(intf.closed)
|
||||
}()
|
||||
intf.link.core.log.Debugln("DEBUG: registered interface for", intf.name)
|
||||
}
|
||||
intf.link.mutex.Unlock()
|
||||
// Create peer
|
||||
shared := crypto.GetSharedKey(myLinkPriv, &meta.link)
|
||||
intf.peer = intf.link.core.peers.newPeer(&meta.box, &meta.sig, shared, intf.name, func() { intf.msgIO.close() })
|
||||
if intf.peer == nil {
|
||||
return errors.New("failed to create peer")
|
||||
}
|
||||
defer func() {
|
||||
// More cleanup can go here
|
||||
intf.link.core.peers.removePeer(intf.peer.port)
|
||||
}()
|
||||
// Finish setting up the peer struct
|
||||
out := make(chan []byte, 1)
|
||||
defer close(out)
|
||||
intf.peer.out = func(msg []byte) {
|
||||
defer func() { recover() }()
|
||||
out <- msg
|
||||
}
|
||||
intf.peer.linkOut = make(chan []byte, 1)
|
||||
themAddr := address.AddrForNodeID(crypto.GetNodeID(&intf.info.box))
|
||||
themAddrString := net.IP(themAddr[:]).String()
|
||||
themString := fmt.Sprintf("%s@%s", themAddrString, intf.info.remote)
|
||||
intf.link.core.log.Infof("Connected %s: %s, source %s",
|
||||
strings.ToUpper(intf.info.linkType), themString, intf.info.local)
|
||||
defer intf.link.core.log.Infof("Disconnected %s: %s, source %s",
|
||||
strings.ToUpper(intf.info.linkType), themString, intf.info.local)
|
||||
// Start the link loop
|
||||
go intf.peer.linkLoop()
|
||||
// Start the writer
|
||||
signalReady := make(chan struct{}, 1)
|
||||
signalSent := make(chan bool, 1)
|
||||
sendAck := make(chan struct{}, 1)
|
||||
go func() {
|
||||
defer close(signalReady)
|
||||
defer close(signalSent)
|
||||
interval := 4 * time.Second
|
||||
tcpTimer := time.NewTimer(interval) // used for backwards compat with old tcp
|
||||
defer util.TimerStop(tcpTimer)
|
||||
send := func(bs []byte) {
|
||||
intf.msgIO.writeMsg(bs)
|
||||
select {
|
||||
case signalSent <- len(bs) > 0:
|
||||
default:
|
||||
}
|
||||
}
|
||||
for {
|
||||
// First try to send any link protocol traffic
|
||||
select {
|
||||
case msg := <-intf.peer.linkOut:
|
||||
send(msg)
|
||||
continue
|
||||
default:
|
||||
}
|
||||
// No protocol traffic to send, so reset the timer
|
||||
util.TimerStop(tcpTimer)
|
||||
tcpTimer.Reset(interval)
|
||||
// Now block until something is ready or the timer triggers keepalive traffic
|
||||
select {
|
||||
case <-tcpTimer.C:
|
||||
intf.link.core.log.Debugf("Sending (legacy) keep-alive to %s: %s, source %s",
|
||||
strings.ToUpper(intf.info.linkType), themString, intf.info.local)
|
||||
send(nil)
|
||||
case <-sendAck:
|
||||
intf.link.core.log.Debugf("Sending ack to %s: %s, source %s",
|
||||
strings.ToUpper(intf.info.linkType), themString, intf.info.local)
|
||||
send(nil)
|
||||
case msg := <-intf.peer.linkOut:
|
||||
intf.msgIO.writeMsg(msg)
|
||||
case msg, ok := <-out:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
send(msg)
|
||||
util.PutBytes(msg)
|
||||
select {
|
||||
case signalReady <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
intf.link.core.log.Debugf("Sending packet to %s: %s, source %s",
|
||||
strings.ToUpper(intf.info.linkType), themString, intf.info.local)
|
||||
}
|
||||
}
|
||||
}()
|
||||
//intf.link.core.switchTable.idleIn <- intf.peer.port // notify switch that we're idle
|
||||
// Used to enable/disable activity in the switch
|
||||
signalAlive := make(chan bool, 1) // True = real packet, false = keep-alive
|
||||
defer close(signalAlive)
|
||||
go func() {
|
||||
var isAlive bool
|
||||
var isReady bool
|
||||
var sendTimerRunning bool
|
||||
var recvTimerRunning bool
|
||||
recvTime := 6 * time.Second // TODO set to ReadTimeout from the config, reset if it gets changed
|
||||
sendTime := time.Second
|
||||
sendTimer := time.NewTimer(sendTime)
|
||||
defer util.TimerStop(sendTimer)
|
||||
recvTimer := time.NewTimer(recvTime)
|
||||
defer util.TimerStop(recvTimer)
|
||||
for {
|
||||
intf.link.core.log.Debugf("State of %s: %s, source %s :: isAlive %t isReady %t sendTimerRunning %t recvTimerRunning %t",
|
||||
strings.ToUpper(intf.info.linkType), themString, intf.info.local,
|
||||
isAlive, isReady, sendTimerRunning, recvTimerRunning)
|
||||
select {
|
||||
case gotMsg, ok := <-signalAlive:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
util.TimerStop(recvTimer)
|
||||
recvTimerRunning = false
|
||||
isAlive = true
|
||||
if !isReady {
|
||||
// (Re-)enable in the switch
|
||||
intf.link.core.switchTable.idleIn <- intf.peer.port
|
||||
isReady = true
|
||||
}
|
||||
if gotMsg && !sendTimerRunning {
|
||||
// We got a message
|
||||
// Start a timer, if it expires then send a 0-sized ack to let them know we're alive
|
||||
util.TimerStop(sendTimer)
|
||||
sendTimer.Reset(sendTime)
|
||||
sendTimerRunning = true
|
||||
}
|
||||
if !gotMsg {
|
||||
intf.link.core.log.Debugf("Received ack from %s: %s, source %s",
|
||||
strings.ToUpper(intf.info.linkType), themString, intf.info.local)
|
||||
}
|
||||
case sentMsg, ok := <-signalSent:
|
||||
// Stop any running ack timer
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
util.TimerStop(sendTimer)
|
||||
sendTimerRunning = false
|
||||
if sentMsg && !recvTimerRunning {
|
||||
// We sent a message
|
||||
// Start a timer, if it expires and we haven't gotten any return traffic (including a 0-sized ack), then assume there's a problem
|
||||
util.TimerStop(recvTimer)
|
||||
recvTimer.Reset(recvTime)
|
||||
recvTimerRunning = true
|
||||
}
|
||||
case _, ok := <-signalReady:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if !isAlive {
|
||||
// Disable in the switch
|
||||
isReady = false
|
||||
} else {
|
||||
// Keep enabled in the switch
|
||||
intf.link.core.switchTable.idleIn <- intf.peer.port
|
||||
isReady = true
|
||||
}
|
||||
case <-sendTimer.C:
|
||||
// We haven't sent anything, so signal a send of a 0 packet to let them know we're alive
|
||||
select {
|
||||
case sendAck <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
case <-recvTimer.C:
|
||||
// We haven't received anything, so assume there's a problem and don't return this node to the switch until they start responding
|
||||
isAlive = false
|
||||
}
|
||||
}
|
||||
}()
|
||||
// Run reader loop
|
||||
for {
|
||||
msg, err := intf.msgIO.readMsg()
|
||||
if len(msg) > 0 {
|
||||
intf.peer.handlePacket(msg)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case signalAlive <- len(msg) > 0:
|
||||
default:
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
return nil
|
||||
}
|
130
src/yggdrasil/mobile.go
Normal file
130
src/yggdrasil/mobile.go
Normal file
@ -0,0 +1,130 @@
|
||||
// +build mobile
|
||||
|
||||
package yggdrasil
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gologme/log"
|
||||
|
||||
hjson "github.com/hjson/hjson-go"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||
)
|
||||
|
||||
// This file is meant to "plug the gap" for mobile support, as Gomobile will
|
||||
// not create headers for Swift/Obj-C etc if they have complex (non-native)
|
||||
// types. Therefore for iOS we will expose some nice simple functions. Note
|
||||
// that in the case of iOS we handle reading/writing to/from TUN in Swift
|
||||
// therefore we use the "dummy" TUN interface instead.
|
||||
|
||||
func (c *Core) addStaticPeers(cfg *config.NodeConfig) {
|
||||
if len(cfg.Peers) == 0 && len(cfg.InterfacePeers) == 0 {
|
||||
return
|
||||
}
|
||||
for {
|
||||
for _, peer := range cfg.Peers {
|
||||
c.AddPeer(peer, "")
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
for intf, intfpeers := range cfg.InterfacePeers {
|
||||
for _, peer := range intfpeers {
|
||||
c.AddPeer(peer, intf)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
time.Sleep(time.Minute)
|
||||
}
|
||||
}
|
||||
|
||||
// Starts a node with a randomly generated config.
|
||||
func (c *Core) StartAutoconfigure() error {
|
||||
mobilelog := MobileLogger{}
|
||||
logger := log.New(mobilelog, "", 0)
|
||||
nc := config.GenerateConfig(true)
|
||||
nc.IfName = "dummy"
|
||||
nc.AdminListen = "tcp://localhost:9001"
|
||||
nc.Peers = []string{}
|
||||
if hostname, err := os.Hostname(); err == nil {
|
||||
nc.NodeInfo = map[string]interface{}{"name": hostname}
|
||||
}
|
||||
if err := c.Start(nc, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
go c.addStaticPeers(nc)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Starts a node with the given JSON config. You can get JSON config (rather
|
||||
// than HJSON) by using the GenerateConfigJSON() function.
|
||||
func (c *Core) StartJSON(configjson []byte) error {
|
||||
mobilelog := MobileLogger{}
|
||||
logger := log.New(mobilelog, "", 0)
|
||||
nc := config.GenerateConfig(false)
|
||||
var dat map[string]interface{}
|
||||
if err := hjson.Unmarshal(configjson, &dat); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mapstructure.Decode(dat, &nc); err != nil {
|
||||
return err
|
||||
}
|
||||
nc.IfName = "dummy"
|
||||
if err := c.Start(nc, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
go c.addStaticPeers(nc)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generates mobile-friendly configuration in JSON format.
|
||||
func GenerateConfigJSON() []byte {
|
||||
nc := config.GenerateConfig(false)
|
||||
nc.IfName = "dummy"
|
||||
if json, err := json.Marshal(nc); err == nil {
|
||||
return json
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the node's IPv6 address.
|
||||
func (c *Core) GetAddressString() string {
|
||||
return c.GetAddress().String()
|
||||
}
|
||||
|
||||
// Gets the node's IPv6 subnet in CIDR notation.
|
||||
func (c *Core) GetSubnetString() string {
|
||||
return c.GetSubnet().String()
|
||||
}
|
||||
|
||||
// Gets the node's public encryption key.
|
||||
func (c *Core) GetBoxPubKeyString() string {
|
||||
return hex.EncodeToString(c.boxPub[:])
|
||||
}
|
||||
|
||||
// Gets the node's public signing key.
|
||||
func (c *Core) GetSigPubKeyString() string {
|
||||
return hex.EncodeToString(c.sigPub[:])
|
||||
}
|
||||
|
||||
// Wait for a packet from the router. You will use this when implementing a
|
||||
// dummy adapter in place of real TUN - when this call returns a packet, you
|
||||
// will probably want to give it to the OS to write to TUN.
|
||||
func (c *Core) RouterRecvPacket() ([]byte, error) {
|
||||
packet := <-c.router.tun.recv
|
||||
return packet, nil
|
||||
}
|
||||
|
||||
// Send a packet to the router. You will use this when implementing a
|
||||
// dummy adapter in place of real TUN - when the operating system tells you
|
||||
// that a new packet is available from TUN, call this function to give it to
|
||||
// Yggdrasil.
|
||||
func (c *Core) RouterSendPacket(buf []byte) error {
|
||||
packet := append(util.GetBytes(), buf[:]...)
|
||||
c.router.tun.send <- packet
|
||||
return nil
|
||||
}
|
12
src/yggdrasil/mobile_android.go
Normal file
12
src/yggdrasil/mobile_android.go
Normal file
@ -0,0 +1,12 @@
|
||||
// +build android
|
||||
|
||||
package yggdrasil
|
||||
|
||||
import "log"
|
||||
|
||||
type MobileLogger struct{}
|
||||
|
||||
func (nsl MobileLogger) Write(p []byte) (n int, err error) {
|
||||
log.Println(string(p))
|
||||
return len(p), nil
|
||||
}
|
62
src/yggdrasil/mobile_ios.go
Normal file
62
src/yggdrasil/mobile_ios.go
Normal file
@ -0,0 +1,62 @@
|
||||
// +build mobile,darwin
|
||||
|
||||
package yggdrasil
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation
|
||||
#import <Foundation/Foundation.h>
|
||||
void Log(const char *text) {
|
||||
NSString *nss = [NSString stringWithUTF8String:text];
|
||||
NSLog(@"%@", nss);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||
)
|
||||
|
||||
type MobileLogger struct {
|
||||
}
|
||||
|
||||
func (nsl MobileLogger) Write(p []byte) (n int, err error) {
|
||||
p = append(p, 0)
|
||||
cstr := (*C.char)(unsafe.Pointer(&p[0]))
|
||||
C.Log(cstr)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (c *Core) AWDLCreateInterface(name, local, remote string, incoming bool) error {
|
||||
if intf, err := c.link.awdl.create(name, local, remote, incoming); err != nil || intf == nil {
|
||||
c.log.Println("c.link.awdl.create:", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Core) AWDLShutdownInterface(name string) error {
|
||||
return c.link.awdl.shutdown(name)
|
||||
}
|
||||
|
||||
func (c *Core) AWDLRecvPacket(identity string) ([]byte, error) {
|
||||
if intf := c.link.awdl.getInterface(identity); intf != nil {
|
||||
read, ok := <-intf.rwc.toAWDL
|
||||
if !ok {
|
||||
return nil, errors.New("AWDLRecvPacket: channel closed")
|
||||
}
|
||||
return read, nil
|
||||
}
|
||||
return nil, errors.New("AWDLRecvPacket identity not known: " + identity)
|
||||
}
|
||||
|
||||
func (c *Core) AWDLSendPacket(identity string, buf []byte) error {
|
||||
packet := append(util.GetBytes(), buf[:]...)
|
||||
if intf := c.link.awdl.getInterface(identity); intf != nil {
|
||||
intf.rwc.fromAWDL <- packet
|
||||
return nil
|
||||
}
|
||||
return errors.New("AWDLSendPacket identity not known: " + identity)
|
||||
}
|
@ -4,33 +4,46 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
type multicast struct {
|
||||
core *Core
|
||||
sock *ipv6.PacketConn
|
||||
groupAddr string
|
||||
core *Core
|
||||
reconfigure chan chan error
|
||||
sock *ipv6.PacketConn
|
||||
groupAddr string
|
||||
myAddr *net.TCPAddr
|
||||
myAddrMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (m *multicast) init(core *Core) {
|
||||
m.core = core
|
||||
m.reconfigure = make(chan chan error, 1)
|
||||
go func() {
|
||||
for {
|
||||
e := <-m.reconfigure
|
||||
m.myAddrMutex.Lock()
|
||||
m.myAddr = m.core.tcp.getAddr()
|
||||
m.myAddrMutex.Unlock()
|
||||
e <- nil
|
||||
}
|
||||
}()
|
||||
m.groupAddr = "[ff02::114]:9001"
|
||||
// Check if we've been given any expressions
|
||||
if len(m.core.ifceExpr) == 0 {
|
||||
return
|
||||
if count := len(m.interfaces()); count != 0 {
|
||||
m.core.log.Infoln("Found", count, "multicast interface(s)")
|
||||
}
|
||||
// Ask the system for network interfaces
|
||||
m.core.log.Println("Found", len(m.interfaces()), "multicast interface(s)")
|
||||
}
|
||||
|
||||
func (m *multicast) start() error {
|
||||
if len(m.core.ifceExpr) == 0 {
|
||||
m.core.log.Println("Multicast discovery is disabled")
|
||||
if len(m.interfaces()) == 0 {
|
||||
m.core.log.Infoln("Multicast discovery is disabled")
|
||||
} else {
|
||||
m.core.log.Println("Multicast discovery is enabled")
|
||||
m.core.log.Infoln("Multicast discovery is enabled")
|
||||
addr, err := net.ResolveUDPAddr("udp", m.groupAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -55,6 +68,10 @@ func (m *multicast) start() error {
|
||||
}
|
||||
|
||||
func (m *multicast) interfaces() []net.Interface {
|
||||
// Get interface expressions from config
|
||||
m.core.configMutex.RLock()
|
||||
exprs := m.core.config.MulticastInterfaces
|
||||
m.core.configMutex.RUnlock()
|
||||
// Ask the system for network interfaces
|
||||
var interfaces []net.Interface
|
||||
allifaces, err := net.Interfaces()
|
||||
@ -75,8 +92,12 @@ func (m *multicast) interfaces() []net.Interface {
|
||||
// Ignore point-to-point interfaces
|
||||
continue
|
||||
}
|
||||
for _, expr := range m.core.ifceExpr {
|
||||
if expr.MatchString(iface.Name) {
|
||||
for _, expr := range exprs {
|
||||
e, err := regexp.Compile(expr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if e.MatchString(iface.Name) {
|
||||
interfaces = append(interfaces, iface)
|
||||
}
|
||||
}
|
||||
@ -85,13 +106,14 @@ func (m *multicast) interfaces() []net.Interface {
|
||||
}
|
||||
|
||||
func (m *multicast) announce() {
|
||||
var anAddr net.TCPAddr
|
||||
m.myAddrMutex.Lock()
|
||||
m.myAddr = m.core.tcp.getAddr()
|
||||
m.myAddrMutex.Unlock()
|
||||
groupAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var anAddr net.TCPAddr
|
||||
myAddr := m.core.tcp.getAddr()
|
||||
anAddr.Port = myAddr.Port
|
||||
destAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -103,6 +125,9 @@ func (m *multicast) announce() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
m.myAddrMutex.RLock()
|
||||
anAddr.Port = m.myAddr.Port
|
||||
m.myAddrMutex.RUnlock()
|
||||
for _, addr := range addrs {
|
||||
addrIP, _, _ := net.ParseCIDR(addr.String())
|
||||
if addrIP.To4() != nil {
|
||||
@ -157,6 +182,6 @@ func (m *multicast) listen() {
|
||||
}
|
||||
addr.Zone = from.Zone
|
||||
saddr := addr.String()
|
||||
m.core.tcp.connect(saddr, "")
|
||||
m.core.tcp.connect(saddr, addr.Zone)
|
||||
}
|
||||
}
|
||||
|
28
src/yggdrasil/multicast_darwin.go
Normal file
28
src/yggdrasil/multicast_darwin.go
Normal file
@ -0,0 +1,28 @@
|
||||
// +build darwin
|
||||
|
||||
package yggdrasil
|
||||
|
||||
import "syscall"
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
func multicastReuse(network string, address string, c syscall.RawConn) error {
|
||||
var control error
|
||||
var reuseport error
|
||||
var recvanyif error
|
||||
|
||||
control = c.Control(func(fd uintptr) {
|
||||
reuseport = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
|
||||
|
||||
// sys/socket.h: #define SO_RECV_ANYIF 0x1104
|
||||
recvanyif = unix.SetsockoptInt(int(fd), syscall.SOL_SOCKET, 0x1104, 1)
|
||||
})
|
||||
|
||||
switch {
|
||||
case reuseport != nil:
|
||||
return reuseport
|
||||
case recvanyif != nil:
|
||||
return recvanyif
|
||||
default:
|
||||
return control
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// +build linux darwin netbsd freebsd openbsd dragonflybsd
|
||||
// +build linux netbsd freebsd openbsd dragonflybsd
|
||||
|
||||
package yggdrasil
|
||||
|
||||
|
@ -170,7 +170,7 @@ func (m *nodeinfo) sendNodeInfo(key crypto.BoxPubKey, coords []byte, isResponse
|
||||
nodeinfo := nodeinfoReqRes{
|
||||
SendCoords: table.self.getCoords(),
|
||||
IsResponse: isResponse,
|
||||
NodeInfo: m.core.nodeinfo.getNodeInfo(),
|
||||
NodeInfo: m.getNodeInfo(),
|
||||
}
|
||||
bs := nodeinfo.encode()
|
||||
shared := m.core.sessions.getSharedKey(&m.core.boxPriv, &key)
|
||||
|
@ -5,6 +5,7 @@ package yggdrasil
|
||||
// Live code should be better commented
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@ -14,15 +15,14 @@ import (
|
||||
)
|
||||
|
||||
// The peers struct represents peers with an active connection.
|
||||
// Incomping packets are passed to the corresponding peer, which handles them somehow.
|
||||
// Incoming packets are passed to the corresponding peer, which handles them somehow.
|
||||
// In most cases, this involves passing the packet to the handler for outgoing traffic to another peer.
|
||||
// In other cases, it's link protocol traffic used to build the spanning tree, in which case this checks signatures and passes the message along to the switch.
|
||||
type peers struct {
|
||||
core *Core
|
||||
mutex sync.Mutex // Synchronize writes to atomic
|
||||
ports atomic.Value //map[switchPort]*peer, use CoW semantics
|
||||
authMutex sync.RWMutex
|
||||
allowedEncryptionPublicKeys map[crypto.BoxPubKey]struct{}
|
||||
core *Core
|
||||
reconfigure chan chan error
|
||||
mutex sync.Mutex // Synchronize writes to atomic
|
||||
ports atomic.Value //map[switchPort]*peer, use CoW semantics
|
||||
}
|
||||
|
||||
// Initializes the peers struct.
|
||||
@ -31,40 +31,55 @@ func (ps *peers) init(c *Core) {
|
||||
defer ps.mutex.Unlock()
|
||||
ps.putPorts(make(map[switchPort]*peer))
|
||||
ps.core = c
|
||||
ps.allowedEncryptionPublicKeys = make(map[crypto.BoxPubKey]struct{})
|
||||
ps.reconfigure = make(chan chan error, 1)
|
||||
go func() {
|
||||
for {
|
||||
e := <-ps.reconfigure
|
||||
e <- nil
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Returns true if an incoming peer connection to a key is allowed, either because the key is in the whitelist or because the whitelist is empty.
|
||||
// Returns true if an incoming peer connection to a key is allowed, either
|
||||
// because the key is in the whitelist or because the whitelist is empty.
|
||||
func (ps *peers) isAllowedEncryptionPublicKey(box *crypto.BoxPubKey) bool {
|
||||
ps.authMutex.RLock()
|
||||
defer ps.authMutex.RUnlock()
|
||||
_, isIn := ps.allowedEncryptionPublicKeys[*box]
|
||||
return isIn || len(ps.allowedEncryptionPublicKeys) == 0
|
||||
boxstr := hex.EncodeToString(box[:])
|
||||
ps.core.configMutex.RLock()
|
||||
defer ps.core.configMutex.RUnlock()
|
||||
for _, v := range ps.core.config.AllowedEncryptionPublicKeys {
|
||||
if v == boxstr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return len(ps.core.config.AllowedEncryptionPublicKeys) == 0
|
||||
}
|
||||
|
||||
// Adds a key to the whitelist.
|
||||
func (ps *peers) addAllowedEncryptionPublicKey(box *crypto.BoxPubKey) {
|
||||
ps.authMutex.Lock()
|
||||
defer ps.authMutex.Unlock()
|
||||
ps.allowedEncryptionPublicKeys[*box] = struct{}{}
|
||||
func (ps *peers) addAllowedEncryptionPublicKey(box string) {
|
||||
ps.core.configMutex.RLock()
|
||||
defer ps.core.configMutex.RUnlock()
|
||||
ps.core.config.AllowedEncryptionPublicKeys =
|
||||
append(ps.core.config.AllowedEncryptionPublicKeys, box)
|
||||
}
|
||||
|
||||
// Removes a key from the whitelist.
|
||||
func (ps *peers) removeAllowedEncryptionPublicKey(box *crypto.BoxPubKey) {
|
||||
ps.authMutex.Lock()
|
||||
defer ps.authMutex.Unlock()
|
||||
delete(ps.allowedEncryptionPublicKeys, *box)
|
||||
func (ps *peers) removeAllowedEncryptionPublicKey(box string) {
|
||||
ps.core.configMutex.RLock()
|
||||
defer ps.core.configMutex.RUnlock()
|
||||
for k, v := range ps.core.config.AllowedEncryptionPublicKeys {
|
||||
if v == box {
|
||||
ps.core.config.AllowedEncryptionPublicKeys =
|
||||
append(ps.core.config.AllowedEncryptionPublicKeys[:k],
|
||||
ps.core.config.AllowedEncryptionPublicKeys[k+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the whitelist of allowed keys for incoming connections.
|
||||
func (ps *peers) getAllowedEncryptionPublicKeys() []crypto.BoxPubKey {
|
||||
ps.authMutex.RLock()
|
||||
defer ps.authMutex.RUnlock()
|
||||
keys := make([]crypto.BoxPubKey, 0, len(ps.allowedEncryptionPublicKeys))
|
||||
for key := range ps.allowedEncryptionPublicKeys {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
func (ps *peers) getAllowedEncryptionPublicKeys() []string {
|
||||
ps.core.configMutex.RLock()
|
||||
defer ps.core.configMutex.RUnlock()
|
||||
return ps.core.config.AllowedEncryptionPublicKeys
|
||||
}
|
||||
|
||||
// Atomically gets a map[switchPort]*peer of known peers.
|
||||
@ -97,8 +112,8 @@ type peer struct {
|
||||
close func() // Called when a peer is removed, to close the underlying connection, or via admin api
|
||||
}
|
||||
|
||||
// Creates a new peer with the specified box, sig, and linkShared keys, using the lowest unocupied port number.
|
||||
func (ps *peers) newPeer(box *crypto.BoxPubKey, sig *crypto.SigPubKey, linkShared *crypto.BoxSharedKey, endpoint string) *peer {
|
||||
// Creates a new peer with the specified box, sig, and linkShared keys, using the lowest unoccupied port number.
|
||||
func (ps *peers) newPeer(box *crypto.BoxPubKey, sig *crypto.SigPubKey, linkShared *crypto.BoxSharedKey, endpoint string, closer func()) *peer {
|
||||
now := time.Now()
|
||||
p := peer{box: *box,
|
||||
sig: *sig,
|
||||
@ -108,6 +123,7 @@ func (ps *peers) newPeer(box *crypto.BoxPubKey, sig *crypto.SigPubKey, linkShare
|
||||
firstSeen: now,
|
||||
doSend: make(chan struct{}, 1),
|
||||
dinfo: make(chan *dhtInfo, 1),
|
||||
close: closer,
|
||||
core: ps.core}
|
||||
ps.mutex.Lock()
|
||||
defer ps.mutex.Unlock()
|
||||
@ -217,6 +233,7 @@ func (p *peer) handlePacket(packet []byte) {
|
||||
default:
|
||||
util.PutBytes(packet)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Called to handle traffic or protocolTraffic packets.
|
||||
@ -234,6 +251,7 @@ func (p *peer) handleTraffic(packet []byte, pTypeLen int) {
|
||||
func (p *peer) sendPacket(packet []byte) {
|
||||
// Is there ever a case where something more complicated is needed?
|
||||
// What if p.out blocks?
|
||||
atomic.AddUint64(&p.bytesSent, uint64(len(packet)))
|
||||
p.out(packet)
|
||||
}
|
||||
|
||||
@ -341,7 +359,7 @@ func (p *peer) handleSwitchMsg(packet []byte) {
|
||||
}
|
||||
|
||||
// This generates the bytes that we sign or check the signature of for a switchMsg.
|
||||
// It begins with the next node's key, followed by the root and the timetsamp, followed by coords being advertised to the next node.
|
||||
// It begins with the next node's key, followed by the root and the timestamp, followed by coords being advertised to the next node.
|
||||
func getBytesForSig(next *crypto.SigPubKey, msg *switchMsg) []byte {
|
||||
var loc switchLocator
|
||||
for _, hop := range msg.Hops {
|
||||
|
@ -37,19 +37,21 @@ import (
|
||||
// The router struct has channels to/from the tun/tap device and a self peer (0), which is how messages are passed between this node and the peers/switch layer.
|
||||
// The router's mainLoop goroutine is responsible for managing all information related to the dht, searches, and crypto sessions.
|
||||
type router struct {
|
||||
core *Core
|
||||
addr address.Address
|
||||
subnet address.Subnet
|
||||
in <-chan []byte // packets we received from the network, link to peer's "out"
|
||||
out func([]byte) // packets we're sending to the network, link to peer's "in"
|
||||
toRecv chan router_recvPacket // packets to handle via recvPacket()
|
||||
tun tunAdapter // TUN/TAP adapter
|
||||
adapters []Adapter // Other adapters
|
||||
recv chan<- []byte // place where the tun pulls received packets from
|
||||
send <-chan []byte // place where the tun puts outgoing packets
|
||||
reset chan struct{} // signal that coords changed (re-init sessions/dht)
|
||||
admin chan func() // pass a lambda for the admin socket to query stuff
|
||||
cryptokey cryptokey
|
||||
core *Core
|
||||
reconfigure chan chan error
|
||||
addr address.Address
|
||||
subnet address.Subnet
|
||||
in <-chan []byte // packets we received from the network, link to peer's "out"
|
||||
out func([]byte) // packets we're sending to the network, link to peer's "in"
|
||||
toRecv chan router_recvPacket // packets to handle via recvPacket()
|
||||
tun tunAdapter // TUN/TAP adapter
|
||||
adapters []Adapter // Other adapters
|
||||
recv chan<- []byte // place where the tun pulls received packets from
|
||||
send <-chan []byte // place where the tun puts outgoing packets
|
||||
reset chan struct{} // signal that coords changed (re-init sessions/dht)
|
||||
admin chan func() // pass a lambda for the admin socket to query stuff
|
||||
cryptokey cryptokey
|
||||
nodeinfo nodeinfo
|
||||
}
|
||||
|
||||
// Packet and session info, used to check that the packet matches a valid IP range or CKR prefix before sending to the tun.
|
||||
@ -61,10 +63,11 @@ type router_recvPacket struct {
|
||||
// Initializes the router struct, which includes setting up channels to/from the tun/tap.
|
||||
func (r *router) init(core *Core) {
|
||||
r.core = core
|
||||
r.reconfigure = make(chan chan error, 1)
|
||||
r.addr = *address.AddrForNodeID(&r.core.dht.nodeID)
|
||||
r.subnet = *address.SubnetForNodeID(&r.core.dht.nodeID)
|
||||
in := make(chan []byte, 32) // TODO something better than this...
|
||||
p := r.core.peers.newPeer(&r.core.boxPub, &r.core.sigPub, &crypto.BoxSharedKey{}, "(self)")
|
||||
p := r.core.peers.newPeer(&r.core.boxPub, &r.core.sigPub, &crypto.BoxSharedKey{}, "(self)", nil)
|
||||
p.out = func(packet []byte) {
|
||||
// This is to make very sure it never blocks
|
||||
select {
|
||||
@ -83,13 +86,17 @@ func (r *router) init(core *Core) {
|
||||
r.send = send
|
||||
r.reset = make(chan struct{}, 1)
|
||||
r.admin = make(chan func(), 32)
|
||||
r.nodeinfo.init(r.core)
|
||||
r.core.configMutex.RLock()
|
||||
r.nodeinfo.setNodeInfo(r.core.config.NodeInfo, r.core.config.NodeInfoPrivacy)
|
||||
r.core.configMutex.RUnlock()
|
||||
r.cryptokey.init(r.core)
|
||||
r.tun.init(r.core, send, recv)
|
||||
}
|
||||
|
||||
// Starts the mainLoop goroutine.
|
||||
func (r *router) start() error {
|
||||
r.core.log.Println("Starting router")
|
||||
r.core.log.Infoln("Starting router")
|
||||
go r.mainLoop()
|
||||
return nil
|
||||
}
|
||||
@ -124,6 +131,10 @@ func (r *router) mainLoop() {
|
||||
}
|
||||
case f := <-r.admin:
|
||||
f()
|
||||
case e := <-r.reconfigure:
|
||||
r.core.configMutex.RLock()
|
||||
e <- r.nodeinfo.setNodeInfo(r.core.config.NodeInfo, r.core.config.NodeInfoPrivacy)
|
||||
r.core.configMutex.RUnlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -463,7 +474,7 @@ func (r *router) handleNodeInfo(bs []byte, fromKey *crypto.BoxPubKey) {
|
||||
return
|
||||
}
|
||||
req.SendPermPub = *fromKey
|
||||
r.core.nodeinfo.handleNodeInfo(&req)
|
||||
r.nodeinfo.handleNodeInfo(&req)
|
||||
}
|
||||
|
||||
// Passed a function to call.
|
||||
|
@ -30,7 +30,7 @@ const search_MAX_SEARCH_SIZE = 16
|
||||
const search_RETRY_TIME = time.Second
|
||||
|
||||
// Information about an ongoing search.
|
||||
// Includes the targed NodeID, the bitmask to match it to an IP, and the list of nodes to visit / already visited.
|
||||
// Includes the target NodeID, the bitmask to match it to an IP, and the list of nodes to visit / already visited.
|
||||
type searchInfo struct {
|
||||
dest crypto.NodeID
|
||||
mask crypto.NodeID
|
||||
@ -42,13 +42,21 @@ type searchInfo struct {
|
||||
|
||||
// This stores a map of active searches.
|
||||
type searches struct {
|
||||
core *Core
|
||||
searches map[crypto.NodeID]*searchInfo
|
||||
core *Core
|
||||
reconfigure chan chan error
|
||||
searches map[crypto.NodeID]*searchInfo
|
||||
}
|
||||
|
||||
// Intializes the searches struct.
|
||||
func (s *searches) init(core *Core) {
|
||||
s.core = core
|
||||
s.reconfigure = make(chan chan error, 1)
|
||||
go func() {
|
||||
for {
|
||||
e := <-s.reconfigure
|
||||
e <- nil
|
||||
}
|
||||
}()
|
||||
s.searches = make(map[crypto.NodeID]*searchInfo)
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
// This includes coords, permanent and ephemeral keys, handles and nonces, various sorts of timing information for timeout and maintenance, and some metadata for the admin API.
|
||||
type sessionInfo struct {
|
||||
core *Core
|
||||
reconfigure chan chan error
|
||||
theirAddr address.Address
|
||||
theirSubnet address.Subnet
|
||||
theirPermPub crypto.BoxPubKey
|
||||
@ -101,6 +102,7 @@ func (s *sessionInfo) timedout() bool {
|
||||
// Additionally, stores maps of address/subnet onto keys, and keys onto handles.
|
||||
type sessions struct {
|
||||
core *Core
|
||||
reconfigure chan chan error
|
||||
lastCleanup time.Time
|
||||
// Maps known permanent keys to their shared key, used by DHT a lot
|
||||
permShared map[crypto.BoxPubKey]*crypto.BoxSharedKey
|
||||
@ -112,18 +114,29 @@ type sessions struct {
|
||||
byTheirPerm map[crypto.BoxPubKey]*crypto.Handle
|
||||
addrToPerm map[address.Address]*crypto.BoxPubKey
|
||||
subnetToPerm map[address.Subnet]*crypto.BoxPubKey
|
||||
// Options from the session firewall
|
||||
sessionFirewallEnabled bool
|
||||
sessionFirewallAllowsDirect bool
|
||||
sessionFirewallAllowsRemote bool
|
||||
sessionFirewallAlwaysAllowsOutbound bool
|
||||
sessionFirewallWhitelist []string
|
||||
sessionFirewallBlacklist []string
|
||||
}
|
||||
|
||||
// Initializes the session struct.
|
||||
func (ss *sessions) init(core *Core) {
|
||||
ss.core = core
|
||||
ss.reconfigure = make(chan chan error, 1)
|
||||
go func() {
|
||||
for {
|
||||
e := <-ss.reconfigure
|
||||
responses := make(map[crypto.Handle]chan error)
|
||||
for index, session := range ss.sinfos {
|
||||
responses[index] = make(chan error)
|
||||
session.reconfigure <- responses[index]
|
||||
}
|
||||
for _, response := range responses {
|
||||
if err := <-response; err != nil {
|
||||
e <- err
|
||||
continue
|
||||
}
|
||||
}
|
||||
e <- nil
|
||||
}
|
||||
}()
|
||||
ss.permShared = make(map[crypto.BoxPubKey]*crypto.BoxSharedKey)
|
||||
ss.sinfos = make(map[crypto.Handle]*sessionInfo)
|
||||
ss.byMySes = make(map[crypto.BoxPubKey]*crypto.Handle)
|
||||
@ -133,40 +146,28 @@ func (ss *sessions) init(core *Core) {
|
||||
ss.lastCleanup = time.Now()
|
||||
}
|
||||
|
||||
// Enable or disable the session firewall
|
||||
func (ss *sessions) setSessionFirewallState(enabled bool) {
|
||||
ss.sessionFirewallEnabled = enabled
|
||||
}
|
||||
// Determines whether the session firewall is enabled.
|
||||
func (ss *sessions) isSessionFirewallEnabled() bool {
|
||||
ss.core.configMutex.RLock()
|
||||
defer ss.core.configMutex.RUnlock()
|
||||
|
||||
// Set the session firewall defaults (first parameter is whether to allow
|
||||
// sessions from direct peers, second is whether to allow from remote nodes).
|
||||
func (ss *sessions) setSessionFirewallDefaults(allowsDirect bool, allowsRemote bool, alwaysAllowsOutbound bool) {
|
||||
ss.sessionFirewallAllowsDirect = allowsDirect
|
||||
ss.sessionFirewallAllowsRemote = allowsRemote
|
||||
ss.sessionFirewallAlwaysAllowsOutbound = alwaysAllowsOutbound
|
||||
}
|
||||
|
||||
// Set the session firewall whitelist - nodes always allowed to open sessions.
|
||||
func (ss *sessions) setSessionFirewallWhitelist(whitelist []string) {
|
||||
ss.sessionFirewallWhitelist = whitelist
|
||||
}
|
||||
|
||||
// Set the session firewall blacklist - nodes never allowed to open sessions.
|
||||
func (ss *sessions) setSessionFirewallBlacklist(blacklist []string) {
|
||||
ss.sessionFirewallBlacklist = blacklist
|
||||
return ss.core.config.SessionFirewall.Enable
|
||||
}
|
||||
|
||||
// Determines whether the session with a given publickey is allowed based on
|
||||
// session firewall rules.
|
||||
func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) bool {
|
||||
ss.core.configMutex.RLock()
|
||||
defer ss.core.configMutex.RUnlock()
|
||||
|
||||
// Allow by default if the session firewall is disabled
|
||||
if !ss.sessionFirewallEnabled {
|
||||
if !ss.isSessionFirewallEnabled() {
|
||||
return true
|
||||
}
|
||||
// Prepare for checking whitelist/blacklist
|
||||
var box crypto.BoxPubKey
|
||||
// Reject blacklisted nodes
|
||||
for _, b := range ss.sessionFirewallBlacklist {
|
||||
for _, b := range ss.core.config.SessionFirewall.BlacklistEncryptionPublicKeys {
|
||||
key, err := hex.DecodeString(b)
|
||||
if err == nil {
|
||||
copy(box[:crypto.BoxPubKeyLen], key)
|
||||
@ -176,7 +177,7 @@ func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) b
|
||||
}
|
||||
}
|
||||
// Allow whitelisted nodes
|
||||
for _, b := range ss.sessionFirewallWhitelist {
|
||||
for _, b := range ss.core.config.SessionFirewall.WhitelistEncryptionPublicKeys {
|
||||
key, err := hex.DecodeString(b)
|
||||
if err == nil {
|
||||
copy(box[:crypto.BoxPubKeyLen], key)
|
||||
@ -186,7 +187,7 @@ func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) b
|
||||
}
|
||||
}
|
||||
// Allow outbound sessions if appropriate
|
||||
if ss.sessionFirewallAlwaysAllowsOutbound {
|
||||
if ss.core.config.SessionFirewall.AlwaysAllowOutbound {
|
||||
if initiator {
|
||||
return true
|
||||
}
|
||||
@ -200,11 +201,11 @@ func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) b
|
||||
}
|
||||
}
|
||||
// Allow direct peers if appropriate
|
||||
if ss.sessionFirewallAllowsDirect && isDirectPeer {
|
||||
if ss.core.config.SessionFirewall.AllowFromDirect && isDirectPeer {
|
||||
return true
|
||||
}
|
||||
// Allow remote nodes if appropriate
|
||||
if ss.sessionFirewallAllowsRemote && !isDirectPeer {
|
||||
if ss.core.config.SessionFirewall.AllowFromRemote && !isDirectPeer {
|
||||
return true
|
||||
}
|
||||
// Finally, default-deny if not matching any of the above rules
|
||||
@ -264,13 +265,12 @@ func (ss *sessions) getByTheirSubnet(snet *address.Subnet) (*sessionInfo, bool)
|
||||
// Creates a new session and lazily cleans up old/timedout existing sessions.
|
||||
// This includse initializing session info to sane defaults (e.g. lowest supported MTU).
|
||||
func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo {
|
||||
if ss.sessionFirewallEnabled {
|
||||
if !ss.isSessionAllowed(theirPermKey, true) {
|
||||
return nil
|
||||
}
|
||||
if !ss.isSessionAllowed(theirPermKey, true) {
|
||||
return nil
|
||||
}
|
||||
sinfo := sessionInfo{}
|
||||
sinfo.core = ss.core
|
||||
sinfo.reconfigure = make(chan chan error, 1)
|
||||
sinfo.theirPermPub = *theirPermKey
|
||||
pub, priv := crypto.NewBoxKeys()
|
||||
sinfo.mySesPub = *pub
|
||||
@ -442,7 +442,7 @@ func (ss *sessions) handlePing(ping *sessionPing) {
|
||||
// Get the corresponding session (or create a new session)
|
||||
sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub)
|
||||
// Check the session firewall
|
||||
if !isIn && ss.sessionFirewallEnabled {
|
||||
if !isIn && ss.isSessionFirewallEnabled() {
|
||||
if !ss.isSessionAllowed(&ping.SendPermPub, false) {
|
||||
return
|
||||
}
|
||||
@ -539,6 +539,8 @@ func (sinfo *sessionInfo) doWorker() {
|
||||
} else {
|
||||
return
|
||||
}
|
||||
case e := <-sinfo.reconfigure:
|
||||
e <- nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -552,17 +554,30 @@ func (sinfo *sessionInfo) doSend(bs []byte) {
|
||||
}
|
||||
// code isn't multithreaded so appending to this is safe
|
||||
coords := sinfo.coords
|
||||
// Read IPv6 flowlabel field (20 bits).
|
||||
// Assumes packet at least contains IPv6 header.
|
||||
flowkey := uint64(bs[1]&0x0f)<<16 | uint64(bs[2])<<8 | uint64(bs[3])
|
||||
// Check if the flowlabel was specified
|
||||
if flowkey == 0 {
|
||||
// Does the packet meet the minimum UDP packet size? (others are bigger)
|
||||
if len(bs) >= 48 {
|
||||
// Is the protocol TCP, UDP, SCTP?
|
||||
// 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
|
||||
// Check the packet meets minimum UDP packet length
|
||||
if len(bs) >= 24 {
|
||||
// Is the protocol TCP, UDP or SCTP?
|
||||
if bs[9] == 0x06 || bs[9] == 0x11 || bs[9] == 0x84 {
|
||||
ihl := bs[0] & 0x0f * 4 // Header length
|
||||
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 {
|
||||
// Is the protocol TCP, UDP or SCTP?
|
||||
if bs[6] == 0x06 || bs[6] == 0x11 || bs[6] == 0x84 {
|
||||
// if flowlabel was unspecified (0), try to use known protocols' ports
|
||||
// protokey: proto | sport | dport
|
||||
flowkey = uint64(bs[6])<<32 /* proto */ |
|
||||
uint64(bs[40])<<24 | uint64(bs[41])<<16 /* sport */ |
|
||||
uint64(bs[42])<<8 | uint64(bs[43]) /* dport */
|
||||
@ -610,5 +625,8 @@ func (sinfo *sessionInfo) doRecv(p *wire_trafficPacket) {
|
||||
sinfo.updateNonce(&p.Nonce)
|
||||
sinfo.time = time.Now()
|
||||
sinfo.bytesRecvd += uint64(len(bs))
|
||||
sinfo.core.router.toRecv <- router_recvPacket{bs, sinfo}
|
||||
select {
|
||||
case sinfo.core.router.toRecv <- router_recvPacket{bs, sinfo}:
|
||||
default: // avoid deadlocks, maybe do this somewhere else?...
|
||||
}
|
||||
}
|
||||
|
141
src/yggdrasil/stream.go
Normal file
141
src/yggdrasil/stream.go
Normal file
@ -0,0 +1,141 @@
|
||||
package yggdrasil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||
)
|
||||
|
||||
// Test that this matches the interface we expect
|
||||
var _ = linkInterfaceMsgIO(&stream{})
|
||||
|
||||
type stream struct {
|
||||
rwc io.ReadWriteCloser
|
||||
inputBuffer []byte // Incoming packet stream
|
||||
frag [2 * streamMsgSize]byte // Temporary data read off the underlying rwc, on its way to the inputBuffer
|
||||
outputBuffer [2 * streamMsgSize]byte // Temporary data about to be written to the rwc
|
||||
}
|
||||
|
||||
func (s *stream) close() error {
|
||||
return s.rwc.Close()
|
||||
}
|
||||
|
||||
const streamMsgSize = 2048 + 65535
|
||||
|
||||
var streamMsg = [...]byte{0xde, 0xad, 0xb1, 0x75} // "dead bits"
|
||||
|
||||
func (s *stream) init(rwc io.ReadWriteCloser) {
|
||||
// TODO have this also do the metadata handshake and create the peer struct
|
||||
s.rwc = rwc
|
||||
// TODO call something to do the metadata exchange
|
||||
}
|
||||
|
||||
// writeMsg writes a message with stream padding, and is *not* thread safe.
|
||||
func (s *stream) writeMsg(bs []byte) (int, error) {
|
||||
buf := s.outputBuffer[:0]
|
||||
buf = append(buf, streamMsg[:]...)
|
||||
buf = wire_put_uint64(uint64(len(bs)), buf)
|
||||
padLen := len(buf)
|
||||
buf = append(buf, bs...)
|
||||
var bn int
|
||||
for bn < len(buf) {
|
||||
n, err := s.rwc.Write(buf[bn:])
|
||||
bn += n
|
||||
if err != nil {
|
||||
l := bn - padLen
|
||||
if l < 0 {
|
||||
l = 0
|
||||
}
|
||||
return l, err
|
||||
}
|
||||
}
|
||||
return len(bs), nil
|
||||
}
|
||||
|
||||
// readMsg reads a message from the stream, accounting for stream padding, and is *not* thread safe.
|
||||
func (s *stream) readMsg() ([]byte, error) {
|
||||
for {
|
||||
buf := s.inputBuffer
|
||||
msg, ok, err := stream_chopMsg(&buf)
|
||||
switch {
|
||||
case err != nil:
|
||||
// Something in the stream format is corrupt
|
||||
return nil, fmt.Errorf("message error: %v", err)
|
||||
case ok:
|
||||
// Copy the packet into bs, shift the buffer, and return
|
||||
msg = append(util.GetBytes(), msg...)
|
||||
s.inputBuffer = append(s.inputBuffer[:0], buf...)
|
||||
return msg, nil
|
||||
default:
|
||||
// Wait for the underlying reader to return enough info for us to proceed
|
||||
n, err := s.rwc.Read(s.frag[:])
|
||||
if n > 0 {
|
||||
s.inputBuffer = append(s.inputBuffer, s.frag[:n]...)
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Writes metadata bytes without stream padding, meant to be temporary
|
||||
func (s *stream) _sendMetaBytes(metaBytes []byte) error {
|
||||
var written int
|
||||
for written < len(metaBytes) {
|
||||
n, err := s.rwc.Write(metaBytes)
|
||||
written += n
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reads metadata bytes without stream padding, meant to be temporary
|
||||
func (s *stream) _recvMetaBytes() ([]byte, error) {
|
||||
var meta version_metadata
|
||||
frag := meta.encode()
|
||||
metaBytes := make([]byte, 0, len(frag))
|
||||
for len(metaBytes) < len(frag) {
|
||||
n, err := s.rwc.Read(frag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metaBytes = append(metaBytes, frag[:n]...)
|
||||
}
|
||||
return metaBytes, nil
|
||||
}
|
||||
|
||||
// This takes a pointer to a slice as an argument. It checks if there's a
|
||||
// complete message and, if so, slices out those parts and returns the message,
|
||||
// true, and nil. If there's no error, but also no complete message, it returns
|
||||
// nil, false, and nil. If there's an error, it returns nil, false, and the
|
||||
// error, which the reader then handles (currently, by returning from the
|
||||
// reader, which causes the connection to close).
|
||||
func stream_chopMsg(bs *[]byte) ([]byte, bool, error) {
|
||||
// Returns msg, ok, err
|
||||
if len(*bs) < len(streamMsg) {
|
||||
return nil, false, nil
|
||||
}
|
||||
for idx := range streamMsg {
|
||||
if (*bs)[idx] != streamMsg[idx] {
|
||||
return nil, false, errors.New("bad message")
|
||||
}
|
||||
}
|
||||
msgLen, msgLenLen := wire_decode_uint64((*bs)[len(streamMsg):])
|
||||
if msgLen > streamMsgSize {
|
||||
return nil, false, errors.New("oversized message")
|
||||
}
|
||||
msgBegin := len(streamMsg) + msgLenLen
|
||||
msgEnd := msgBegin + int(msgLen)
|
||||
if msgLenLen == 0 || len(*bs) < msgEnd {
|
||||
// We don't have the full message
|
||||
// Need to buffer this and wait for the rest to come in
|
||||
return nil, false, nil
|
||||
}
|
||||
msg := (*bs)[msgBegin:msgEnd]
|
||||
(*bs) = (*bs)[msgEnd:]
|
||||
return msg, true, nil
|
||||
}
|
@ -4,7 +4,7 @@ package yggdrasil
|
||||
// It routes packets based on distance on the spanning tree
|
||||
// In general, this is *not* equivalent to routing on the tree
|
||||
// It falls back to the tree in the worst case, but it can take shortcuts too
|
||||
// This is the part that makse routing reasonably efficient on scale-free graphs
|
||||
// This is the part that makes routing reasonably efficient on scale-free graphs
|
||||
|
||||
// TODO document/comment everything in a lot more detail
|
||||
|
||||
@ -162,6 +162,7 @@ type switchData struct {
|
||||
// All the information stored by the switch.
|
||||
type switchTable struct {
|
||||
core *Core
|
||||
reconfigure chan chan error
|
||||
key crypto.SigPubKey // Our own key
|
||||
time time.Time // Time when locator.tstamp was last updated
|
||||
drop map[crypto.SigPubKey]int64 // Tstamp associated with a dropped root
|
||||
@ -181,11 +182,14 @@ type switchTable struct {
|
||||
const SwitchQueueTotalMinSize = 4 * 1024 * 1024
|
||||
|
||||
// Initializes the switchTable struct.
|
||||
func (t *switchTable) init(core *Core, key crypto.SigPubKey) {
|
||||
func (t *switchTable) init(core *Core) {
|
||||
now := time.Now()
|
||||
t.core = core
|
||||
t.key = key
|
||||
locator := switchLocator{root: key, tstamp: now.Unix()}
|
||||
t.reconfigure = make(chan chan error, 1)
|
||||
t.core.configMutex.RLock()
|
||||
t.key = t.core.sigPub
|
||||
t.core.configMutex.RUnlock()
|
||||
locator := switchLocator{root: t.key, tstamp: now.Unix()}
|
||||
peers := make(map[switchPort]peerInfo)
|
||||
t.data = switchData{locator: locator, peers: peers}
|
||||
t.updater.Store(&sync.Once{})
|
||||
@ -277,6 +281,7 @@ func (t *switchTable) cleanPeers() {
|
||||
if now.Sub(peer.time) > switch_timeout+switch_throttle {
|
||||
// Longer than switch_timeout to make sure we don't remove a working peer because the root stopped responding.
|
||||
delete(t.data.peers, port)
|
||||
go t.core.peers.removePeer(port) // TODO figure out if it's safe to do this without a goroutine, or make it safe
|
||||
}
|
||||
}
|
||||
if _, isIn := t.data.peers[t.parent]; !isIn {
|
||||
@ -410,6 +415,7 @@ func (t *switchTable) unlockedHandleMsg(msg *switchMsg, fromPort switchPort, rep
|
||||
// Update the matrix of peer "faster" thresholds
|
||||
if reprocessing {
|
||||
sender.faster = oldSender.faster
|
||||
sender.time = oldSender.time
|
||||
} else {
|
||||
sender.faster = make(map[switchPort]uint64, len(oldSender.faster))
|
||||
for port, peer := range t.data.peers {
|
||||
@ -559,7 +565,7 @@ func (t *switchTable) getTable() lookupTable {
|
||||
|
||||
// Starts the switch worker
|
||||
func (t *switchTable) start() error {
|
||||
t.core.log.Println("Starting switch")
|
||||
t.core.log.Infoln("Starting switch")
|
||||
go t.doWorker()
|
||||
return nil
|
||||
}
|
||||
@ -774,6 +780,7 @@ func (t *switchTable) doWorker() {
|
||||
t.queues.bufs = make(map[string]switch_buffer) // Packets per PacketStreamID (string)
|
||||
idle := make(map[switchPort]struct{}) // this is to deduplicate things
|
||||
for {
|
||||
t.core.log.Debugf("Switch state: idle = %d, buffers = %d", len(idle), len(t.queues.bufs))
|
||||
select {
|
||||
case bytes := <-t.packetIn:
|
||||
// Try to send it somewhere (or drop it if it's corrupt or at a dead end)
|
||||
@ -808,6 +815,8 @@ func (t *switchTable) doWorker() {
|
||||
}
|
||||
case f := <-t.admin:
|
||||
f()
|
||||
case e := <-t.reconfigure:
|
||||
e <- nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,31 +15,28 @@ package yggdrasil
|
||||
// See version.go for version metadata format
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||
)
|
||||
|
||||
const tcp_msgSize = 2048 + 65535 // TODO figure out what makes sense
|
||||
const default_tcp_timeout = 6 * time.Second
|
||||
const tcp_ping_interval = (default_tcp_timeout * 2 / 3)
|
||||
const default_timeout = 6 * time.Second
|
||||
const tcp_ping_interval = (default_timeout * 2 / 3)
|
||||
|
||||
// The TCP listener and information about active TCP connections, to avoid duplication.
|
||||
type tcpInterface struct {
|
||||
core *Core
|
||||
reconfigure chan chan error
|
||||
serv net.Listener
|
||||
tcp_timeout time.Duration
|
||||
stop chan bool
|
||||
addr string
|
||||
mutex sync.Mutex // Protecting the below
|
||||
calls map[string]struct{}
|
||||
conns map[tcpInfo](chan struct{})
|
||||
@ -80,19 +77,48 @@ func (iface *tcpInterface) connectSOCKS(socksaddr, peeraddr string) {
|
||||
}
|
||||
|
||||
// Initializes the struct.
|
||||
func (iface *tcpInterface) init(core *Core, addr string, readTimeout int32) (err error) {
|
||||
func (iface *tcpInterface) init(core *Core) (err error) {
|
||||
iface.core = core
|
||||
iface.stop = make(chan bool, 1)
|
||||
iface.reconfigure = make(chan chan error, 1)
|
||||
go func() {
|
||||
for {
|
||||
e := <-iface.reconfigure
|
||||
iface.core.configMutex.RLock()
|
||||
updated := iface.core.config.Listen != iface.core.configOld.Listen
|
||||
iface.core.configMutex.RUnlock()
|
||||
if updated {
|
||||
iface.stop <- true
|
||||
iface.serv.Close()
|
||||
e <- iface.listen()
|
||||
} else {
|
||||
e <- nil
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
iface.tcp_timeout = time.Duration(readTimeout) * time.Millisecond
|
||||
if iface.tcp_timeout >= 0 && iface.tcp_timeout < default_tcp_timeout {
|
||||
iface.tcp_timeout = default_tcp_timeout
|
||||
return iface.listen()
|
||||
}
|
||||
|
||||
func (iface *tcpInterface) listen() error {
|
||||
var err error
|
||||
|
||||
iface.core.configMutex.RLock()
|
||||
iface.addr = iface.core.config.Listen
|
||||
iface.core.configMutex.RUnlock()
|
||||
|
||||
ctx := context.Background()
|
||||
lc := net.ListenConfig{
|
||||
Control: iface.tcpContext,
|
||||
}
|
||||
|
||||
iface.serv, err = net.Listen("tcp", addr)
|
||||
iface.serv, err = lc.Listen(ctx, "tcp", iface.addr)
|
||||
if err == nil {
|
||||
iface.mutex.Lock()
|
||||
iface.calls = make(map[string]struct{})
|
||||
iface.conns = make(map[tcpInfo](chan struct{}))
|
||||
iface.mutex.Unlock()
|
||||
go iface.listener()
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
@ -101,16 +127,42 @@ func (iface *tcpInterface) init(core *Core, addr string, readTimeout int32) (err
|
||||
// Runs the listener, which spawns off goroutines for incoming connections.
|
||||
func (iface *tcpInterface) listener() {
|
||||
defer iface.serv.Close()
|
||||
iface.core.log.Println("Listening for TCP on:", iface.serv.Addr().String())
|
||||
iface.core.log.Infoln("Listening for TCP on:", iface.serv.Addr().String())
|
||||
for {
|
||||
sock, err := iface.serv.Accept()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
iface.core.log.Errorln("Failed to accept connection:", err)
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-iface.stop:
|
||||
iface.core.log.Errorln("Stopping listener")
|
||||
return
|
||||
default:
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
go iface.handler(sock, true)
|
||||
}
|
||||
go iface.handler(sock, true)
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if we already have a connection to this node
|
||||
func (iface *tcpInterface) isAlreadyConnected(info tcpInfo) bool {
|
||||
iface.mutex.Lock()
|
||||
defer iface.mutex.Unlock()
|
||||
_, isIn := iface.conns[info]
|
||||
return isIn
|
||||
}
|
||||
|
||||
// Checks if we already are calling this address
|
||||
func (iface *tcpInterface) isAlreadyCalling(saddr string) bool {
|
||||
iface.mutex.Lock()
|
||||
defer iface.mutex.Unlock()
|
||||
_, isIn := iface.calls[saddr]
|
||||
return isIn
|
||||
}
|
||||
|
||||
// Checks if a connection already exists.
|
||||
// If not, it adds it to the list of active outgoing calls (to block future attempts) and dials the address.
|
||||
// If the dial is successful, it launches the handler.
|
||||
@ -122,25 +174,20 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) {
|
||||
if sintf != "" {
|
||||
callname = fmt.Sprintf("%s/%s", saddr, sintf)
|
||||
}
|
||||
quit := false
|
||||
iface.mutex.Lock()
|
||||
if _, isIn := iface.calls[callname]; isIn {
|
||||
quit = true
|
||||
} else {
|
||||
iface.calls[callname] = struct{}{}
|
||||
defer func() {
|
||||
// Block new calls for a little while, to mitigate livelock scenarios
|
||||
time.Sleep(default_tcp_timeout)
|
||||
time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
|
||||
iface.mutex.Lock()
|
||||
delete(iface.calls, callname)
|
||||
iface.mutex.Unlock()
|
||||
}()
|
||||
}
|
||||
iface.mutex.Unlock()
|
||||
if quit {
|
||||
if iface.isAlreadyCalling(callname) {
|
||||
return
|
||||
}
|
||||
iface.mutex.Lock()
|
||||
iface.calls[callname] = struct{}{}
|
||||
iface.mutex.Unlock()
|
||||
defer func() {
|
||||
// Block new calls for a little while, to mitigate livelock scenarios
|
||||
time.Sleep(default_timeout)
|
||||
time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
|
||||
iface.mutex.Lock()
|
||||
delete(iface.calls, callname)
|
||||
iface.mutex.Unlock()
|
||||
}()
|
||||
var conn net.Conn
|
||||
var err error
|
||||
if socksaddr != nil {
|
||||
@ -164,40 +211,57 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) {
|
||||
},
|
||||
}
|
||||
} else {
|
||||
dialer := net.Dialer{}
|
||||
dialer := net.Dialer{
|
||||
Control: iface.tcpContext,
|
||||
}
|
||||
if sintf != "" {
|
||||
ief, err := net.InterfaceByName(sintf)
|
||||
if err != nil {
|
||||
return
|
||||
} else {
|
||||
if ief.Flags&net.FlagUp == 0 {
|
||||
}
|
||||
if ief.Flags&net.FlagUp == 0 {
|
||||
return
|
||||
}
|
||||
addrs, err := ief.Addrs()
|
||||
if err == nil {
|
||||
dst, err := net.ResolveTCPAddr("tcp", saddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
addrs, err := ief.Addrs()
|
||||
if err == nil {
|
||||
dst, err := net.ResolveTCPAddr("tcp", saddr)
|
||||
for addrindex, addr := range addrs {
|
||||
src, _, err := net.ParseCIDR(addr.String())
|
||||
if err != nil {
|
||||
return
|
||||
continue
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
src, _, err := net.ParseCIDR(addr.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if (src.To4() != nil) == (dst.IP.To4() != nil) && src.IsGlobalUnicast() {
|
||||
dialer.LocalAddr = &net.TCPAddr{
|
||||
IP: src,
|
||||
Port: 0,
|
||||
}
|
||||
break
|
||||
if src.Equal(dst.IP) {
|
||||
continue
|
||||
}
|
||||
if !src.IsGlobalUnicast() && !src.IsLinkLocalUnicast() {
|
||||
continue
|
||||
}
|
||||
bothglobal := src.IsGlobalUnicast() == dst.IP.IsGlobalUnicast()
|
||||
bothlinklocal := src.IsLinkLocalUnicast() == dst.IP.IsLinkLocalUnicast()
|
||||
if !bothglobal && !bothlinklocal {
|
||||
continue
|
||||
}
|
||||
if (src.To4() != nil) != (dst.IP.To4() != nil) {
|
||||
continue
|
||||
}
|
||||
if bothglobal || bothlinklocal || addrindex == len(addrs)-1 {
|
||||
dialer.LocalAddr = &net.TCPAddr{
|
||||
IP: src,
|
||||
Port: 0,
|
||||
Zone: sintf,
|
||||
}
|
||||
break
|
||||
}
|
||||
if dialer.LocalAddr == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if dialer.LocalAddr == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conn, err = dialer.Dial("tcp", saddr)
|
||||
if err != nil {
|
||||
return
|
||||
@ -207,237 +271,21 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) {
|
||||
}()
|
||||
}
|
||||
|
||||
// This exchanges/checks connection metadata, sets up the peer struct, sets up the writer goroutine, and then runs the reader within the current goroutine.
|
||||
// It defers a bunch of cleanup stuff to tear down all of these things when the reader exists (e.g. due to a closed connection or a timeout).
|
||||
func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
|
||||
defer sock.Close()
|
||||
iface.setExtraOptions(sock)
|
||||
// Get our keys
|
||||
myLinkPub, myLinkPriv := crypto.NewBoxKeys() // ephemeral link keys
|
||||
meta := version_getBaseMetadata()
|
||||
meta.box = iface.core.boxPub
|
||||
meta.sig = iface.core.sigPub
|
||||
meta.link = *myLinkPub
|
||||
metaBytes := meta.encode()
|
||||
_, err := sock.Write(metaBytes)
|
||||
stream := stream{}
|
||||
stream.init(sock)
|
||||
local, _, _ := net.SplitHostPort(sock.LocalAddr().String())
|
||||
remote, _, _ := net.SplitHostPort(sock.RemoteAddr().String())
|
||||
remotelinklocal := net.ParseIP(remote).IsLinkLocalUnicast()
|
||||
name := "tcp://" + sock.RemoteAddr().String()
|
||||
link, err := iface.core.link.create(&stream, name, "tcp", local, remote, incoming, remotelinklocal)
|
||||
if err != nil {
|
||||
return
|
||||
iface.core.log.Println(err)
|
||||
panic(err)
|
||||
}
|
||||
if iface.tcp_timeout > 0 {
|
||||
sock.SetReadDeadline(time.Now().Add(iface.tcp_timeout))
|
||||
}
|
||||
_, err = sock.Read(metaBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
meta = version_metadata{} // Reset to zero value
|
||||
if !meta.decode(metaBytes) || !meta.check() {
|
||||
// Failed to decode and check the metadata
|
||||
// If it's a version mismatch issue, then print an error message
|
||||
base := version_getBaseMetadata()
|
||||
if meta.meta == base.meta {
|
||||
if meta.ver > base.ver {
|
||||
iface.core.log.Println("Failed to connect to node:", sock.RemoteAddr().String(), "version:", meta.ver)
|
||||
} else if meta.ver == base.ver && meta.minorVer > base.minorVer {
|
||||
iface.core.log.Println("Failed to connect to node:", sock.RemoteAddr().String(), "version:", fmt.Sprintf("%d.%d", meta.ver, meta.minorVer))
|
||||
}
|
||||
}
|
||||
// TODO? Block forever to prevent future connection attempts? suppress future messages about the same node?
|
||||
return
|
||||
}
|
||||
info := tcpInfo{ // used as a map key, so don't include ephemeral link key
|
||||
box: meta.box,
|
||||
sig: meta.sig,
|
||||
}
|
||||
// Quit the parent call if this is a connection to ourself
|
||||
equiv := func(k1, k2 []byte) bool {
|
||||
for idx := range k1 {
|
||||
if k1[idx] != k2[idx] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
if equiv(info.box[:], iface.core.boxPub[:]) {
|
||||
return
|
||||
}
|
||||
if equiv(info.sig[:], iface.core.sigPub[:]) {
|
||||
return
|
||||
}
|
||||
// Check if we're authorized to connect to this key / IP
|
||||
if incoming && !iface.core.peers.isAllowedEncryptionPublicKey(&info.box) {
|
||||
// Allow unauthorized peers if they're link-local
|
||||
raddrStr, _, _ := net.SplitHostPort(sock.RemoteAddr().String())
|
||||
raddr := net.ParseIP(raddrStr)
|
||||
if !raddr.IsLinkLocalUnicast() {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Check if we already have a connection to this node, close and block if yes
|
||||
info.localAddr, _, _ = net.SplitHostPort(sock.LocalAddr().String())
|
||||
info.remoteAddr, _, _ = net.SplitHostPort(sock.RemoteAddr().String())
|
||||
iface.mutex.Lock()
|
||||
if blockChan, isIn := iface.conns[info]; isIn {
|
||||
iface.mutex.Unlock()
|
||||
sock.Close()
|
||||
<-blockChan
|
||||
return
|
||||
}
|
||||
blockChan := make(chan struct{})
|
||||
iface.conns[info] = blockChan
|
||||
iface.mutex.Unlock()
|
||||
defer func() {
|
||||
iface.mutex.Lock()
|
||||
delete(iface.conns, info)
|
||||
iface.mutex.Unlock()
|
||||
close(blockChan)
|
||||
}()
|
||||
// Note that multiple connections to the same node are allowed
|
||||
// E.g. over different interfaces
|
||||
p := iface.core.peers.newPeer(&info.box, &info.sig, crypto.GetSharedKey(myLinkPriv, &meta.link), sock.RemoteAddr().String())
|
||||
p.linkOut = make(chan []byte, 1)
|
||||
in := func(bs []byte) {
|
||||
p.handlePacket(bs)
|
||||
}
|
||||
out := make(chan []byte, 1)
|
||||
defer close(out)
|
||||
go func() {
|
||||
// This goroutine waits for outgoing packets, link protocol traffic, or sends idle keep-alive traffic
|
||||
send := func(msg []byte) {
|
||||
msgLen := wire_encode_uint64(uint64(len(msg)))
|
||||
buf := net.Buffers{tcp_msg[:], msgLen, msg}
|
||||
buf.WriteTo(sock)
|
||||
atomic.AddUint64(&p.bytesSent, uint64(len(tcp_msg)+len(msgLen)+len(msg)))
|
||||
util.PutBytes(msg)
|
||||
}
|
||||
timerInterval := tcp_ping_interval
|
||||
timer := time.NewTimer(timerInterval)
|
||||
defer timer.Stop()
|
||||
for {
|
||||
select {
|
||||
case msg := <-p.linkOut:
|
||||
// Always send outgoing link traffic first, if needed
|
||||
send(msg)
|
||||
continue
|
||||
default:
|
||||
}
|
||||
// Otherwise wait reset the timer and wait for something to do
|
||||
timer.Stop()
|
||||
select {
|
||||
case <-timer.C:
|
||||
default:
|
||||
}
|
||||
timer.Reset(timerInterval)
|
||||
select {
|
||||
case _ = <-timer.C:
|
||||
send(nil) // TCP keep-alive traffic
|
||||
case msg := <-p.linkOut:
|
||||
send(msg)
|
||||
case msg, ok := <-out:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
send(msg) // Block until the socket write has finished
|
||||
// Now inform the switch that we're ready for more traffic
|
||||
p.core.switchTable.idleIn <- p.port
|
||||
}
|
||||
}
|
||||
}()
|
||||
p.core.switchTable.idleIn <- p.port // Start in the idle state
|
||||
p.out = func(msg []byte) {
|
||||
defer func() { recover() }()
|
||||
out <- msg
|
||||
}
|
||||
p.close = func() { sock.Close() }
|
||||
go p.linkLoop()
|
||||
defer func() {
|
||||
// Put all of our cleanup here...
|
||||
p.core.peers.removePeer(p.port)
|
||||
}()
|
||||
us, _, _ := net.SplitHostPort(sock.LocalAddr().String())
|
||||
them, _, _ := net.SplitHostPort(sock.RemoteAddr().String())
|
||||
themNodeID := crypto.GetNodeID(&info.box)
|
||||
themAddr := address.AddrForNodeID(themNodeID)
|
||||
themAddrString := net.IP(themAddr[:]).String()
|
||||
themString := fmt.Sprintf("%s@%s", themAddrString, them)
|
||||
iface.core.log.Println("Connected:", themString, "source", us)
|
||||
err = iface.reader(sock, in) // In this goroutine, because of defers
|
||||
if err == nil {
|
||||
iface.core.log.Println("Disconnected:", themString, "source", us)
|
||||
} else {
|
||||
iface.core.log.Println("Disconnected:", themString, "source", us, "with error:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// This reads from the socket into a []byte buffer for incomping messages.
|
||||
// It copies completed messages out of the cache into a new slice, and passes them to the peer struct via the provided `in func([]byte)` argument.
|
||||
// Then it shifts the incomplete fragments of data forward so future reads won't overwrite it.
|
||||
func (iface *tcpInterface) reader(sock net.Conn, in func([]byte)) error {
|
||||
bs := make([]byte, 2*tcp_msgSize)
|
||||
frag := bs[:0]
|
||||
for {
|
||||
if iface.tcp_timeout > 0 {
|
||||
sock.SetReadDeadline(time.Now().Add(iface.tcp_timeout))
|
||||
}
|
||||
n, err := sock.Read(bs[len(frag):])
|
||||
if n > 0 {
|
||||
frag = bs[:len(frag)+n]
|
||||
for {
|
||||
msg, ok, err2 := tcp_chop_msg(&frag)
|
||||
if err2 != nil {
|
||||
return fmt.Errorf("Message error: %v", err2)
|
||||
}
|
||||
if !ok {
|
||||
// We didn't get the whole message yet
|
||||
break
|
||||
}
|
||||
newMsg := append(util.GetBytes(), msg...)
|
||||
in(newMsg)
|
||||
util.Yield()
|
||||
}
|
||||
frag = append(bs[:0], frag...)
|
||||
}
|
||||
if err != nil || n == 0 {
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// These are 4 bytes of padding used to catch if something went horribly wrong with the tcp connection.
|
||||
var tcp_msg = [...]byte{0xde, 0xad, 0xb1, 0x75} // "dead bits"
|
||||
|
||||
// This takes a pointer to a slice as an argument.
|
||||
// It checks if there's a complete message and, if so, slices out those parts and returns the message, true, and nil.
|
||||
// If there's no error, but also no complete message, it returns nil, false, and nil.
|
||||
// If there's an error, it returns nil, false, and the error, which the reader then handles (currently, by returning from the reader, which causes the connection to close).
|
||||
func tcp_chop_msg(bs *[]byte) ([]byte, bool, error) {
|
||||
// Returns msg, ok, err
|
||||
if len(*bs) < len(tcp_msg) {
|
||||
return nil, false, nil
|
||||
}
|
||||
for idx := range tcp_msg {
|
||||
if (*bs)[idx] != tcp_msg[idx] {
|
||||
return nil, false, errors.New("Bad message!")
|
||||
}
|
||||
}
|
||||
msgLen, msgLenLen := wire_decode_uint64((*bs)[len(tcp_msg):])
|
||||
if msgLen > tcp_msgSize {
|
||||
return nil, false, errors.New("Oversized message!")
|
||||
}
|
||||
msgBegin := len(tcp_msg) + msgLenLen
|
||||
msgEnd := msgBegin + int(msgLen)
|
||||
if msgLenLen == 0 || len(*bs) < msgEnd {
|
||||
// We don't have the full message
|
||||
// Need to buffer this and wait for the rest to come in
|
||||
return nil, false, nil
|
||||
}
|
||||
msg := (*bs)[msgBegin:msgEnd]
|
||||
(*bs) = (*bs)[msgEnd:]
|
||||
return msg, true, nil
|
||||
iface.core.log.Debugln("DEBUG: starting handler for", name)
|
||||
err = link.handler()
|
||||
iface.core.log.Debugln("DEBUG: stopped handler for", name, err)
|
||||
}
|
||||
|
28
src/yggdrasil/tcp_darwin.go
Normal file
28
src/yggdrasil/tcp_darwin.go
Normal file
@ -0,0 +1,28 @@
|
||||
// +build darwin
|
||||
|
||||
package yggdrasil
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// WARNING: This context is used both by net.Dialer and net.Listen in tcp.go
|
||||
|
||||
func (iface *tcpInterface) tcpContext(network, address string, c syscall.RawConn) error {
|
||||
var control error
|
||||
var recvanyif error
|
||||
|
||||
control = c.Control(func(fd uintptr) {
|
||||
// sys/socket.h: #define SO_RECV_ANYIF 0x1104
|
||||
recvanyif = unix.SetsockoptInt(int(fd), syscall.SOL_SOCKET, 0x1104, 1)
|
||||
})
|
||||
|
||||
switch {
|
||||
case recvanyif != nil:
|
||||
return recvanyif
|
||||
default:
|
||||
return control
|
||||
}
|
||||
}
|
13
src/yggdrasil/tcp_other.go
Normal file
13
src/yggdrasil/tcp_other.go
Normal file
@ -0,0 +1,13 @@
|
||||
// +build !darwin
|
||||
|
||||
package yggdrasil
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// WARNING: This context is used both by net.Dialer and net.Listen in tcp.go
|
||||
|
||||
func (iface *tcpInterface) tcpContext(network, address string, c syscall.RawConn) error {
|
||||
return nil
|
||||
}
|
@ -5,6 +5,8 @@ package yggdrasil
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -42,22 +44,46 @@ func getSupportedMTU(mtu int) int {
|
||||
func (tun *tunAdapter) init(core *Core, send chan<- []byte, recv <-chan []byte) {
|
||||
tun.Adapter.init(core, send, recv)
|
||||
tun.icmpv6.init(tun)
|
||||
go func() {
|
||||
for {
|
||||
e := <-tun.reconfigure
|
||||
tun.core.configMutex.RLock()
|
||||
updated := tun.core.config.IfName != tun.core.configOld.IfName ||
|
||||
tun.core.config.IfTAPMode != tun.core.configOld.IfTAPMode ||
|
||||
tun.core.config.IfMTU != tun.core.configOld.IfMTU
|
||||
tun.core.configMutex.RUnlock()
|
||||
if updated {
|
||||
tun.core.log.Warnln("Reconfiguring TUN/TAP is not supported yet")
|
||||
e <- nil
|
||||
} else {
|
||||
e <- nil
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Starts the setup process for the TUN/TAP adapter, and if successful, starts
|
||||
// the read/write goroutines to handle packets on that interface.
|
||||
func (tun *tunAdapter) start(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||
if ifname == "none" {
|
||||
return nil
|
||||
func (tun *tunAdapter) start() error {
|
||||
tun.core.configMutex.RLock()
|
||||
ifname := tun.core.config.IfName
|
||||
iftapmode := tun.core.config.IfTAPMode
|
||||
addr := fmt.Sprintf("%s/%d", net.IP(tun.core.router.addr[:]).String(), 8*len(address.GetPrefix())-1)
|
||||
mtu := tun.core.config.IfMTU
|
||||
tun.core.configMutex.RUnlock()
|
||||
if ifname != "none" {
|
||||
if err := tun.setup(ifname, iftapmode, addr, mtu); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := tun.setup(ifname, iftapmode, addr, mtu); err != nil {
|
||||
return err
|
||||
if ifname == "none" || ifname == "dummy" {
|
||||
return nil
|
||||
}
|
||||
tun.mutex.Lock()
|
||||
tun.isOpen = true
|
||||
tun.mutex.Unlock()
|
||||
go func() { tun.core.log.Println("WARNING: tun.read() exited with error:", tun.read()) }()
|
||||
go func() { tun.core.log.Println("WARNING: tun.write() exited with error:", tun.write()) }()
|
||||
go func() { tun.core.log.Errorln("WARNING: tun.read() exited with error:", tun.read()) }()
|
||||
go func() { tun.core.log.Errorln("WARNING: tun.write() exited with error:", tun.write()) }()
|
||||
if iftapmode {
|
||||
go func() {
|
||||
for {
|
||||
|
@ -114,9 +114,9 @@ func (tun *tunAdapter) setupAddress(addr string) error {
|
||||
}
|
||||
|
||||
// Friendly output
|
||||
tun.core.log.Printf("Interface name: %s", tun.iface.Name())
|
||||
tun.core.log.Printf("Interface IPv6: %s", addr)
|
||||
tun.core.log.Printf("Interface MTU: %d", tun.mtu)
|
||||
tun.core.log.Infof("Interface name: %s", tun.iface.Name())
|
||||
tun.core.log.Infof("Interface IPv6: %s", addr)
|
||||
tun.core.log.Infof("Interface MTU: %d", tun.mtu)
|
||||
|
||||
// Create the MTU request
|
||||
var ir in6_ifreq_mtu
|
||||
@ -126,15 +126,15 @@ func (tun *tunAdapter) setupAddress(addr string) error {
|
||||
// Set the MTU
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(syscall.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 {
|
||||
err = errno
|
||||
tun.core.log.Printf("Error in SIOCSIFMTU: %v", errno)
|
||||
tun.core.log.Errorf("Error in SIOCSIFMTU: %v", errno)
|
||||
|
||||
// Fall back to ifconfig to set the MTU
|
||||
cmd := exec.Command("ifconfig", tun.iface.Name(), "mtu", string(tun.mtu))
|
||||
tun.core.log.Printf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " "))
|
||||
tun.core.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " "))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
tun.core.log.Printf("SIOCSIFMTU fallback failed: %v.", err)
|
||||
tun.core.log.Println(string(output))
|
||||
tun.core.log.Errorf("SIOCSIFMTU fallback failed: %v.", err)
|
||||
tun.core.log.Traceln(string(output))
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,15 +155,15 @@ func (tun *tunAdapter) setupAddress(addr string) error {
|
||||
// Set the interface address
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(SIOCSIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 {
|
||||
err = errno
|
||||
tun.core.log.Printf("Error in SIOCSIFADDR_IN6: %v", errno)
|
||||
tun.core.log.Errorf("Error in SIOCSIFADDR_IN6: %v", errno)
|
||||
|
||||
// Fall back to ifconfig to set the address
|
||||
cmd := exec.Command("ifconfig", tun.iface.Name(), "inet6", addr)
|
||||
tun.core.log.Printf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " "))
|
||||
tun.core.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " "))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
tun.core.log.Printf("SIOCSIFADDR_IN6 fallback failed: %v.", err)
|
||||
tun.core.log.Println(string(output))
|
||||
tun.core.log.Errorf("SIOCSIFADDR_IN6 fallback failed: %v.", err)
|
||||
tun.core.log.Traceln(string(output))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
// +build !mobile
|
||||
|
||||
package yggdrasil
|
||||
|
||||
// The darwin platform specific tun parts
|
||||
@ -16,7 +18,7 @@ import (
|
||||
// Configures the "utun" adapter with the correct IPv6 address and MTU.
|
||||
func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||
if iftapmode {
|
||||
tun.core.log.Printf("TAP mode is not supported on this platform, defaulting to TUN")
|
||||
tun.core.log.Warnln("TAP mode is not supported on this platform, defaulting to TUN")
|
||||
}
|
||||
config := water.Config{DeviceType: water.TUN}
|
||||
iface, err := water.New(config)
|
||||
@ -96,19 +98,19 @@ func (tun *tunAdapter) setupAddress(addr string) error {
|
||||
copy(ir.ifr_name[:], tun.iface.Name())
|
||||
ir.ifru_mtu = uint32(tun.mtu)
|
||||
|
||||
tun.core.log.Printf("Interface name: %s", ar.ifra_name)
|
||||
tun.core.log.Printf("Interface IPv6: %s", addr)
|
||||
tun.core.log.Printf("Interface MTU: %d", ir.ifru_mtu)
|
||||
tun.core.log.Infof("Interface name: %s", ar.ifra_name)
|
||||
tun.core.log.Infof("Interface IPv6: %s", addr)
|
||||
tun.core.log.Infof("Interface MTU: %d", ir.ifru_mtu)
|
||||
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(darwin_SIOCAIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 {
|
||||
err = errno
|
||||
tun.core.log.Printf("Error in darwin_SIOCAIFADDR_IN6: %v", errno)
|
||||
tun.core.log.Errorf("Error in darwin_SIOCAIFADDR_IN6: %v", errno)
|
||||
return err
|
||||
}
|
||||
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 {
|
||||
err = errno
|
||||
tun.core.log.Printf("Error in SIOCSIFMTU: %v", errno)
|
||||
tun.core.log.Errorf("Error in SIOCSIFMTU: %v", errno)
|
||||
return err
|
||||
}
|
||||
|
||||
|
19
src/yggdrasil/tun_dummy.go
Normal file
19
src/yggdrasil/tun_dummy.go
Normal file
@ -0,0 +1,19 @@
|
||||
// +build mobile
|
||||
|
||||
package yggdrasil
|
||||
|
||||
// This is to catch unsupported platforms
|
||||
// If your platform supports tun devices, you could try configuring it manually
|
||||
|
||||
// Creates the TUN/TAP adapter, if supported by the Water library. Note that
|
||||
// no guarantees are made at this point on an unsupported platform.
|
||||
func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||
tun.mtu = getSupportedMTU(mtu)
|
||||
return tun.setupAddress(addr)
|
||||
}
|
||||
|
||||
// We don't know how to set the IPv6 address on an unknown platform, therefore
|
||||
// write about it to stdout and don't try to do anything further.
|
||||
func (tun *tunAdapter) setupAddress(addr string) error {
|
||||
return nil
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// +build !mobile
|
||||
|
||||
package yggdrasil
|
||||
|
||||
// The linux platform specific tun parts
|
||||
@ -38,9 +40,9 @@ func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int
|
||||
}
|
||||
}
|
||||
// Friendly output
|
||||
tun.core.log.Printf("Interface name: %s", tun.iface.Name())
|
||||
tun.core.log.Printf("Interface IPv6: %s", addr)
|
||||
tun.core.log.Printf("Interface MTU: %d", tun.mtu)
|
||||
tun.core.log.Infof("Interface name: %s", tun.iface.Name())
|
||||
tun.core.log.Infof("Interface IPv6: %s", addr)
|
||||
tun.core.log.Infof("Interface MTU: %d", tun.mtu)
|
||||
return tun.setupAddress(addr)
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// +build !linux,!darwin,!windows,!openbsd,!freebsd,!netbsd
|
||||
// +build !linux,!darwin,!windows,!openbsd,!freebsd,!netbsd,!mobile
|
||||
|
||||
package yggdrasil
|
||||
|
||||
@ -28,6 +28,6 @@ func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int
|
||||
// We don't know how to set the IPv6 address on an unknown platform, therefore
|
||||
// write about it to stdout and don't try to do anything further.
|
||||
func (tun *tunAdapter) setupAddress(addr string) error {
|
||||
tun.core.log.Println("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr)
|
||||
tun.core.log.Warnln("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr)
|
||||
return nil
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
// delegate the hard work to "netsh".
|
||||
func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int) error {
|
||||
if !iftapmode {
|
||||
tun.core.log.Printf("TUN mode is not supported on this platform, defaulting to TAP")
|
||||
tun.core.log.Warnln("TUN mode is not supported on this platform, defaulting to TAP")
|
||||
}
|
||||
config := water.Config{DeviceType: water.TAP}
|
||||
config.PlatformSpecificParams.ComponentID = "tap0901"
|
||||
@ -34,16 +34,16 @@ func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int
|
||||
tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " "))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
tun.core.log.Printf("Windows netsh failed: %v.", err)
|
||||
tun.core.log.Println(string(output))
|
||||
tun.core.log.Errorf("Windows netsh failed: %v.", err)
|
||||
tun.core.log.Traceln(string(output))
|
||||
return err
|
||||
}
|
||||
cmd = exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=ENABLED")
|
||||
tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " "))
|
||||
output, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
tun.core.log.Printf("Windows netsh failed: %v.", err)
|
||||
tun.core.log.Println(string(output))
|
||||
tun.core.log.Errorf("Windows netsh failed: %v.", err)
|
||||
tun.core.log.Traceln(string(output))
|
||||
return err
|
||||
}
|
||||
// Get a new iface
|
||||
@ -58,9 +58,9 @@ func (tun *tunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int
|
||||
panic(err)
|
||||
}
|
||||
// Friendly output
|
||||
tun.core.log.Printf("Interface name: %s", tun.iface.Name())
|
||||
tun.core.log.Printf("Interface IPv6: %s", addr)
|
||||
tun.core.log.Printf("Interface MTU: %d", tun.mtu)
|
||||
tun.core.log.Infof("Interface name: %s", tun.iface.Name())
|
||||
tun.core.log.Infof("Interface IPv6: %s", addr)
|
||||
tun.core.log.Infof("Interface MTU: %d", tun.mtu)
|
||||
return tun.setupAddress(addr)
|
||||
}
|
||||
|
||||
@ -71,11 +71,11 @@ func (tun *tunAdapter) setupMTU(mtu int) error {
|
||||
fmt.Sprintf("interface=%s", tun.iface.Name()),
|
||||
fmt.Sprintf("mtu=%d", mtu),
|
||||
"store=active")
|
||||
tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " "))
|
||||
tun.core.log.Debugln("netsh command: %v", strings.Join(cmd.Args, " "))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
tun.core.log.Printf("Windows netsh failed: %v.", err)
|
||||
tun.core.log.Println(string(output))
|
||||
tun.core.log.Errorf("Windows netsh failed: %v.", err)
|
||||
tun.core.log.Traceln(string(output))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -88,11 +88,11 @@ func (tun *tunAdapter) setupAddress(addr string) error {
|
||||
fmt.Sprintf("interface=%s", tun.iface.Name()),
|
||||
fmt.Sprintf("addr=%s", addr),
|
||||
"store=active")
|
||||
tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " "))
|
||||
tun.core.log.Debugln("netsh command: %v", strings.Join(cmd.Args, " "))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
tun.core.log.Printf("Windows netsh failed: %v.", err)
|
||||
tun.core.log.Println(string(output))
|
||||
tun.core.log.Errorf("Windows netsh failed: %v.", err)
|
||||
tun.core.log.Traceln(string(output))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
Loading…
Reference in New Issue
Block a user